Porting Joachim's MemoryModule.c (load dll from memory)

Just starting out? Need help? Post your questions and find answers here.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Porting Joachim's MemoryModule.c (load dll from memory)

Post by Mistrel »

I'm using the source code from the October 24, 2010 commit from github:

https://github.com/fancycode/MemoryModu ... ryModule.c

I've had some intermittent hiccups while using this library so I wanted to see if I could piece together how it works by porting it to PureBasic. There are some parts I encountered that I wasn't sure I was converting properly.


Things to be aware of:

PerformBaseRelocation hasn't been tested because LocationDelta is false for this test. I don't know what I need to do to make it have a value so that it will call this function.

I'm fairly confident that CopySections() is fine. I went over it a couple times. The problem I've encountered is when execution hits BuildImportTable(). It's returning false because it's failing.. somewhere.

I'm stuck trying to decipher line 453 where I call "ReAllocateMemory". The original code calls "realloc". I don't understand this line at all. The first parameter should be "a memory block previously allocated with malloc, calloc or realloc to be reallocated", however, none of these functions are called elsewhere in the original library. Also, why does ReAllocateMemory succeed if I'm not using a valid MemoryID?

When VirtualAlloc is called, a desired address is always specified. Wouldn't this be a problem if, for example, two libraries want to be at the same address? How can it be assumed that this address will always be available while arbitrarily loading a library?

Any help would be appreciated.

Oh, and srod: I don't understand the math.. Image


Here is the DLL project I'm using:

Code: Select all

ProcedureDLL Test()
  MessageRequester("","Hello!")
EndProcedure
Here is the main project:

Code: Select all

XIncludeFile "LoadLibrary.pb"
DisableExplicit

; Lib=OpenLibrary(#PB_Any,"test.dll")
; Ptr=GetFunction(Lib,"Test")
; 
; CallFunctionFast(Ptr)

FileID=ReadFile(#PB_Any,"test.dll")

If Not FileID
  Debug "Error"
EndIf

Mem=AllocateMemory(Lof(FileID))
ReadData(FileID,Mem,Lof(FileID))

CloseFile(FileID)

MemoryLoadLibrary(Mem)
; Debug MemoryGetProcAddress(Mem,"Test")
And my port of MemoryModule.c. It's a little messy since I've still yet to get everything working:

Code: Select all

;/
;/ Memory DLL loading code
;/ Version 0.0.2
;/
;/ Copyright (c) 2004-2010 by Joachim Bauch / mail@joachim-bauch.de
;/ http://www.joachim-bauch.de
;/
;/ The contents of this file are subject to the Mozilla Public License Version
;/ 1.1 (the "License") you may not use this file except in compliance with
;/ the License. You may obtain a copy of the License at
;/ http://www.mozilla.org/MPL/
;/
;/ Software distributed under the License is distributed on an "AS IS" basis,
;/ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
;/ for the specific language governing rights and limitations under the
;/ License.
;/
;/ The Original Code is MemoryModule.c
;/
;/ The Initial Developer of the Original Code is Joachim Bauch.
;/
;/ Portions created by Joachim Bauch are Copyright (C) 2004-2010
;/ Joachim Bauch. All Rights Reserved.
;/
;/ http://github.com/fancycode/MemoryModule
;/ October 24, 2010

EnableExplicit

Global SectionData
Global BaseMemory
Global HeapMemory
Global VirtualAlloc1
Global VirtualAlloc2
Global VirtualAlloc3
Global VirtualAlloc4
Global VirtualAlloc5
Global VirtualAlloc6
Global VirtualAlloc7

; Structure IMAGE_EXPORT_DIRECTORY
;   Characteristics.l
;   TimeDateStamp.l
;   MajorVersion.w
;   MinorVersion.w
;   Name.l
;   Base.l
;   NumberOfFunctions.l
;   NumberOfNames.l
;   AddressOfFunctions.l ;/ RVA from base of image
;   AddressOfNames.l ;/ RVA from base of image
;   AddressOfNameOrdinals.l ;/ RVA from base of image
; EndStructure

Structure IMAGE_IMPORT_BY_NAME
  Hint.w
  Name.b[1]
EndStructure

Structure MEMORYMODULE
  *Headers.IMAGE_NT_HEADERS ;/ Is this *Headers?
  *CodeBase
  *Modules
  NumModules.i
  Initialized.i
EndStructure

Structure IMAGE_BASE_RELOCATION
  VirtualAddress.l
  SizeOfBlock.l
EndStructure

Structure IMAGE_IMPORT_DESCRIPTOR
  ;/ 0 for terminating null import descriptor RVA to original unbound IAT (#PIMAGE_THUNK_DATA)
  StructureUnion
    Characteristics.l
    OriginalFirstThunk.l
  EndStructureUnion
  TimeDateStamp.l ;/ 0 if not bound, -1 if bound, and real date\time stamp in #IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) O.W. date/time stamp of DLL bound to (Old BIND)
  ForwarderChain.l ;/ -1 if no forwarders
  Name.l
  FirstThunk.l ;/ RVA to IAT (if bound this IAT has actual addresses)
EndStructure

#IMAGE_SIZEOF_SHORT_NAME=8

Structure IMAGE_SECTION_HEADER
  Name.b[#IMAGE_SIZEOF_SHORT_NAME]
  StructureUnion
    PhysicalAddress.l
    VirtualSize.l
  EndStructureUnion
  VirtualAddress.l
  SizeOfRawData.l
  PointerToRawData.l
  PointerToRelocations.l
  PointerToLinenumbers.l
  NumberOfRelocations.w
  NumberOfLinenumbers.w
  Characteristics.l
EndStructure

; Structure IMAGE_DOS_HEADER
;   e_magic.w ;/ Magic number
;   e_cblp.w ;/ Bytes on last page of file
;   e_cp.w ;/ Pages in file
;   e_crlc.w ;/ Relocations
;   e_cparhdr.w ;/ Size of header in paragraphs
;   e_minalloc.w ;/ Minimum extra paragraphs needed
;   e_maxalloc.w ;/ Maximum extra paragraphs needed
;   e_ss.w ;/ Initial (relative) SS value
;   e_sp.w ;/ Initial SP value
;   e_csum.w ;/ Checksum
;   e_ip.w ;/ Initial IP value
;   e_cs.w ;/ Initial (relative) CS value
;   e_lfarlc.w ;/ File address of relocation table
;   e_ovno.w ;/ Overlay number
;   e_res1.w[4] ;/ Reserved words
;   e_oemid.w ;/ OEM identifier (For e_oeminfo)
;   e_oeminfo.w ;/ OEM information e_oemid specific
;   e_res2.w[10] ;/ Reserved words
;   e_lfanew.l ;/ File address of new exe header
; EndStructure

; Structure IMAGE_DATA_DIRECTORY
;   VirtualAddress.l
;   Size.l
; EndStructure


Structure IMAGE_OPTIONAL_HEADER32
  ;/ Standard fields.
  Magic.w
  MajorLinkerVersion.b
  MinorLinkerVersion.b
  SizeOfCode.l
  SizeOfInitializedData.l
  SizeOfUninitializedData.l
  AddressOfEntryPoint.l
  BaseOfCode.l
  BaseOfData.l
  
  ;/ NT additional fields.
  ImageBase.l
  SectionAlignment.l
  FileAlignment.l
  MajorOperatingSystemVersion.w
  MinorOperatingSystemVersion.w
  MajorImageVersion.w
  MinorImageVersion.w
  MajorSubsystemVersion.w
  MinorSubsystemVersion.w
  Win32VersionValue.l
  SizeOfImage.l
  SizeOfHeaders.l
  CheckSum.l
  Subsystem.w
  DllCharacteristics.w
  SizeOfStackReserve.l
  SizeOfStackCommit.l
  SizeOfHeapReserve.l
  SizeOfHeapCommit.l
  LoaderFlags.l
  NumberOfRvaAndSizes.l
  DataDirectory.IMAGE_DATA_DIRECTORY[#IMAGE_NUMBEROF_DIRECTORY_ENTRIES]
EndStructure

; Structure IMAGE_FILE_HEADER
;   Machine.w
;   NumberOfSections.w
;   TimeDateStamp.l
;   PointerToSymbolTable.l
;   NumberOfSymbols.l
;   SizeOfOptionalHeader.w
;   Characteristics.w
; EndStructure

; Structure IMAGE_NT_HEADERS
;   Signature.l
;   FileHeader.IMAGE_FILE_HEADER
;   OptionalHeader.IMAGE_OPTIONAL_HEADER32
; EndStructure
    
#IMAGE_SIZEOF_BASE_RELOCATION=SizeOf(IMAGE_BASE_RELOCATION)

#IMAGE_SCN_CNT_INITIALIZED_DATA=$00000040
#IMAGE_SCN_CNT_UNINITIALIZED_DATA=$00000080
#IMAGE_SCN_MEM_NOT_CACHED=$04000000
#IMAGE_SCN_MEM_DISCARDABLE=$02000000
#IMAGE_SCN_MEM_EXECUTE=$20000000
#IMAGE_SCN_MEM_READ=$40000000
#IMAGE_SCN_MEM_WRITE=$80000000

#IMAGE_REL_BASED_ABSOLUTE=0
#IMAGE_REL_BASED_HIGH=1
#IMAGE_REL_BASED_LOW=2
#IMAGE_REL_BASED_HIGHLOW=3
#IMAGE_REL_BASED_HIGHADJ=4
#IMAGE_REL_BASED_MIPS_JMPADDR=5
#IMAGE_REL_BASED_SECTION=6
#IMAGE_REL_BASED_REL32=7
#IMAGE_REL_BASED_MIPS_JMPADDR16=9
#IMAGE_REL_BASED_IA64_IMM64=9
#IMAGE_REL_BASED_DIR64=10
#IMAGE_REL_BASED_HIGH3ADJ=11

#IMAGE_NT_SIGNATURE=$00004550
#IMAGE_DOS_SIGNATURE=$5A4D

#IMAGE_ORDINAL_FLAG=$80000000

; Structure MEMORYMODULE
;   headers.PIMAGE_NT_HEADERS
;   *codeBase
;   *modules
;   numModules.i
;   initialized.i
; EndStructure

;typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)

;#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]

Declare IMAGE_FIRST_SECTION(*Header.IMAGE_NT_HEADERS)
Declare CopySections(*SectionData, *OldHeaders.IMAGE_NT_HEADERS, *Module.MEMORYMODULE)
Declare FinalizeSections(*Module.MEMORYMODULE)
Declare PerformBaseRelocation(*Module.MEMORYMODULE, Delta.i)
Declare.i BuildImportTable(*Module.MEMORYMODULE)
Declare.i MemoryLoadLibrary(*SectionData)
Declare MemoryGetProcAddress(*Module.MEMORYMODULE, ModuleName.s)
Declare MemoryFreeLibrary(*Module.MEMORYMODULE)

Procedure IMAGE_FIRST_SECTION(*Header.IMAGE_NT_HEADERS)
  If Not *Header
    ProcedureReturn 0
  EndIf
  
  ProcedureReturn *Header+OffsetOf(IMAGE_NT_HEADERS\OptionalHeader)+*Header\FileHeader\SizeOfOptionalHeader
EndProcedure

Procedure CopySections(*SectionData, *OldHeaders.IMAGE_NT_HEADERS, *Module.MEMORYMODULE)
  Protected *CodeBase
  Protected *Dest
  Protected *Section.IMAGE_SECTION_HEADER
  Protected Size.i
  Protected i.i
  
  *CodeBase=*Module\codeBase
  *Section=IMAGE_FIRST_SECTION(*Module\headers)
  
  For i=0 To *Module\headers\FileHeader\NumberOfSections-1
    If i
      *Section+SizeOf(IMAGE_SECTION_HEADER)
    EndIf
    
    If Not *Section\SizeOfRawData
      ;/ Section doesn't contain data in the dll itself, but may define
      ;/ uninitialized data
      Size=*OldHeaders\OptionalHeader\SectionAlignment
      
      If Size>0
        *Dest=VirtualAlloc_(*CodeBase+*Section\VirtualAddress,Size,#MEM_COMMIT,#PAGE_READWRITE)
        *Section\PhysicalAddress=*Dest
      EndIf
      
      ;/ Section is empty
      Continue
    EndIf
    
    ;/ Commit memory block and copy data from dll
    *Dest=VirtualAlloc_(*CodeBase+*Section\VirtualAddress,*Section\SizeOfRawData,#MEM_COMMIT,#PAGE_READWRITE)
    
    CopyMemory(*SectionData+*Section\PointerToRawData,*Dest,*Section\SizeOfRawData)
    *Section\PhysicalAddress=*Dest
  Next i
EndProcedure

; ;/ Protection flags for memory pages (Executable, Readable, Writeable)
; Static int ProtectionFlags[2][2][2]={
;   {
;     ;/ not executable
;     {PAGE_NOACCESS,PAGE_WRITECOPY},
;     {PAGE_READONLY,PAGE_READWRITE},
;   },{
;     ;/ executable
;     {PAGE_EXECUTE,PAGE_EXECUTE_WRITECOPY},
;     {PAGE_EXECUTE_READ,PAGE_EXECUTE_READWRITE},
;   },
; }

Procedure FinalizeSections(*Module.MEMORYMODULE)
  Protected *Section.IMAGE_SECTION_HEADER
  Protected ImageOffset.i
  Protected Executable
  Protected Readable
  Protected Writeable
  Protected Protect
  Protected OldProtect
  Protected Size
  Protected i.i
  
  Dim ProtectionFlags.b(2,2,2)

  ProtectionFlags(0,0,0)=$01
  ProtectionFlags(0,0,1)=$08
  ProtectionFlags(0,1,0)=$02
  ProtectionFlags(0,1,1)=$04
  ProtectionFlags(1,0,0)=$10
  ProtectionFlags(1,0,1)=$80
  ProtectionFlags(1,1,0)=$20
  ProtectionFlags(1,1,1)=$40
  
  *Section=IMAGE_FIRST_SECTION(*Module\headers)
  
  CompilerIf #PB_Compiler_Processor=#PB_Processor_x64
    ImageOffset=*Module\headers\OptionalHeader\ImageBase&$ffffffff00000000
  CompilerEndIf
  
  ;/ Loop through all sections and change access flags
  For i=0 To *Module\headers\FileHeader\NumberOfSections-1
    If i
      *Section+SizeOf(IMAGE_SECTION_HEADER)
    EndIf
    
    Executable=#False
    Readable=#False
    Writeable=#False
    
    If *Section\Characteristics&#IMAGE_SCN_MEM_EXECUTE
      Executable=#True
    EndIf
    
    If *Section\Characteristics&#IMAGE_SCN_MEM_READ
      Readable=#True
    EndIf
    
    If *Section\Characteristics&#IMAGE_SCN_MEM_WRITE
      Writeable=#True
    EndIf
    
    If *Section\Characteristics &#IMAGE_SCN_MEM_DISCARDABLE
      ;/ Section is not needed any more and can safely be freed
      VirtualFree_(*Section\PhysicalAddress|ImageOffset,*Section\SizeOfRawData,#MEM_DECOMMIT)
      Continue
    EndIf

    ;/ Determine protection flags based on characteristics
    Protect=ProtectionFlags(executable,readable,writeable)
    If *Section\Characteristics&#IMAGE_SCN_MEM_NOT_CACHED
      Protect=Protect|#PAGE_NOCACHE
    EndIf

    ;/ Determine size of region
    Size=*Section\SizeOfRawData
    If Not Size
      If *Section\Characteristics&#IMAGE_SCN_CNT_INITIALIZED_DATA
        Size=*Module\headers\OptionalHeader\SizeOfInitializedData
      ElseIf *Section\Characteristics&#IMAGE_SCN_CNT_UNINITIALIZED_DATA
        Size=*Module\headers\OptionalHeader\SizeOfUninitializedData
      EndIf
    EndIf
    
    If Size>0
      ;/ Change memory access flags
      If Not VirtualProtect_(*Section\PhysicalAddress|ImageOffset,Size,Protect,@OldProtect)
        Debug "Error protecting memory page"
      EndIf
    EndIf
  Next i
  
EndProcedure

Procedure PerformBaseRelocation(*Module.MEMORYMODULE, Delta.i)
  Protected *CodeBase
  Protected *Directory.IMAGE_DATA_DIRECTORY
  Protected *Relocation.IMAGE_BASE_RELOCATION
  Protected *PatchAddr
  Protected Type.i
  Protected Offset.i
  Protected *Dest
  Protected *RelInfo
  Protected i.i
  
  *CodeBase=*Module\codeBase
  *Directory=*Module\headers\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_BASERELOC]
  
  If *Directory\Size>0
    *Relocation=*CodeBase+*Directory\VirtualAddress
    
    ;/ Matt: While loop here?
    While *Relocation\VirtualAddress>0
      *Dest=*CodeBase+*Relocation\VirtualAddress
      *RelInfo=*Relocation+#IMAGE_SIZEOF_BASE_RELOCATION
      
      For i=0 To *Relocation\SizeOfBlock-#IMAGE_SIZEOF_BASE_RELOCATION/2-1
        If i
          *RelInfo+SizeOf(Word) ;/ Will with be ok when porting to 64-bit? (was unsigned short)
        EndIf
        
        ;/ the upper 4 bits define the type of relocation
        Type=*RelInfo>>12
        
        ;/ the lower 12 bits define the offset
        Offset=*RelInfo&$fff
        
        Select Type
          ;/ Skip relocation for absolute addresses
          Case #IMAGE_REL_BASED_ABSOLUTE
          Case #IMAGE_REL_BASED_HIGHLOW,#IMAGE_REL_BASED_DIR64:
            *PatchAddr=*Dest+Offset
            *PatchAddr+Delta
            Break
          Default
            ;Debug "Unknown relocation: "+Str(Type) ;/ Some types are unhandled for x86 or unexpected?
        EndSelect
      Next i
      
      ;/ Advance to next relocation block
      *Relocation+*Relocation\SizeOfBlock
    Wend
  EndIf
EndProcedure

Procedure.i BuildImportTable(*Module.MEMORYMODULE)
  Protected Result=1
  Protected *CodeBase
  Protected *Directory.IMAGE_DATA_DIRECTORY
  Protected *ImportDesc.IMAGE_IMPORT_DESCRIPTOR
  Protected *ThunkRef
  Protected *FuncRef
  Protected Lib
  Protected *ThunkData.IMAGE_IMPORT_BY_NAME
  
  *CodeBase=*Module\codeBase
  
  *Directory=*Module\headers\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_IMPORT]
  If *Directory\Size>0
    *ImportDesc=*CodeBase+*Directory\VirtualAddress
    
    ;/ Matt: While loop here?
    ;/ Matt: Is it really ok to loop IsBadReadPtr..?
    While Not IsBadReadPtr_(*ImportDesc,SizeOf(IMAGE_IMPORT_DESCRIPTOR)) And *ImportDesc\Name
      ;/ Matt: This code originally compared the result of LoadLibrary to #INVALID_HANDLE_VALUE.. is that even a valid result?
      Lib=LoadLibrary_(*CodeBase+*ImportDesc\Name)
      If Not Lib
        Debug "Can't load library"
        
        Result=0
        Break
      EndIf
      
      Debug "Check this code"
      Protected Mem=ReAllocateMemory(*Module\modules,(*Module\numModules+1)*(SizeOf(Integer)))
      Debug Mem
      
      *Module\modules=Mem
      If Not *Module\modules
        Result=0
        Break
      EndIf
      
      ;/ Matt: PokeI here?
      PokeI(*Module\modules+*Module\numModules+1,Lib)
      
      If *ImportDesc\OriginalFirstThunk
        *ThunkRef=*CodeBase+*ImportDesc\OriginalFirstThunk
        *FuncRef=*CodeBase+*ImportDesc\FirstThunk
      Else
        ;/ No hint table
        *ThunkRef=*CodeBase+*ImportDesc\FirstThunk
        *FuncRef=*CodeBase+*ImportDesc\FirstThunk
      EndIf
      
      Debug "May be a bug here"
      ;/ Matt: Pointers get a little funky here. I don't think I translated it correctly
      While *ThunkRef
        ;/ IMAGE_SNAP_BY_ORDINAL(*ThunkRef)
        ;/ IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG) != 0) 
        If Not *ThunkRef&#IMAGE_ORDINAL_FLAG
          ;/ IMAGE_ORDINAL(*ThunkRef)
          ;/ IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff)
          *FuncRef=GetProcAddress_(Lib,*ThunkRef&$ffff)
        Else
          *ThunkData=*CodeBase+*ThunkRef
          *FuncRef=GetProcAddress_(Lib,*ThunkData\Name)
        EndIf
        
        If Not *FuncRef
          Result=0
          Break
        EndIf
        
        *ThunkRef+SizeOf(Integer)
        *FuncRef+SizeOf(Integer)
      Wend

      If Not Result
        Break
      EndIf
      
      *ImportDesc+SizeOf(IMAGE_IMPORT_DESCRIPTOR)
    Wend
  EndIf

  ProcedureReturn Result
EndProcedure

Procedure.i MemoryLoadLibrary(*SectionData)
  Protected *Result.MEMORYMODULE
  Protected *DosHeader.IMAGE_DOS_HEADER
  Protected *OldHeader.IMAGE_NT_HEADERS
  Protected *Code
  Protected *Headers
  Protected LocationDelta.i
  Protected *DllEntry
  Protected Successful.b
  
  BaseMemory=*SectionData
  
  *DosHeader=*SectionData
  If Not *DosHeader\e_magic=#IMAGE_DOS_SIGNATURE
    Debug "Not a valid executable file."
    ProcedureReturn #False
  EndIf
  
  ;/ Matt: Conversion may be wrong here. Something might need to be dereferenced
  *OldHeader=*SectionData+*DosHeader\e_lfanew
  
  If Not *OldHeader\Signature=#IMAGE_NT_SIGNATURE
    Debug "No PE header found."
    ProcedureReturn #False
  EndIf
  
  ;/ reserve memory for image of library
  *Code=VirtualAlloc_(*OldHeader\OptionalHeader\ImageBase,*OldHeader\OptionalHeader\SizeOfImage,#MEM_RESERVE,#PAGE_READWRITE)
  
  VirtualAlloc1=*Code
  
  If Not *Code
      ;/ Try to allocate memory at arbitrary position
      *Code=VirtualAlloc_(0,*OldHeader\OptionalHeader\SizeOfImage,#MEM_RESERVE,#PAGE_READWRITE)
    If Not *Code
      Debug "Can't reserve memory"
      ProcedureReturn #False
    EndIf
  EndIf
  
  *Result=HeapAlloc_(GetProcessHeap_(),0,SizeOf(MEMORYMODULE))
  *Result\codeBase=*Code
  *Result\numModules=0
  *Result\modules=0
  *Result\initialized=0
  
  HeapMemory=*Result
  
  ;/ XXX: is it correct to commit the complete memory region at once?
  ;/ calling DllEntry raises an exception if we don't...
  VirtualAlloc_(*Code,*OldHeader\OptionalHeader\SizeOfImage,#MEM_COMMIT,#PAGE_READWRITE)
  
  ;/ Commit memory for headers
  *Headers=VirtualAlloc_(*Code,*OldHeader\OptionalHeader\SizeOfHeaders,#MEM_COMMIT,#PAGE_READWRITE)
  
  VirtualAlloc2=*Headers
  
  ;/ Copy PE header to code
  CopyMemory(*DosHeader,*Headers,*DosHeader\e_lfanew+*OldHeader\OptionalHeader\SizeOfHeaders)
  
  ;/ Matt: Conversion may be wrong here. Something might need to be dereferenced
  *Result\headers=*SectionData+*DosHeader\e_lfanew
  
  ;/ Update position
  *Result\headers\OptionalHeader\ImageBase=*Code

  ;/ Copy sections from DLL file block to new memory location
  CopySections(*SectionData,*OldHeader,*Result)
  
  ;/ Adjust base address of imported data
  LocationDelta=*Code-*OldHeader\OptionalHeader\ImageBase
  If LocationDelta
    PerformBaseRelocation(*Result,LocationDelta)
  EndIf
  
  
;   Debug "Result "+Str(*Result.MEMORYMODULE-HeapMemory)
; ;   Debug "DosHeader "+Str(*DosHeader.IMAGE_DOS_HEADER-BaseMemory)
; ;   Debug "OldHeader "+Str(*OldHeader.IMAGE_NT_HEADERS-BaseMemory)
; ;   Debug "Code "+Str(*Code-VirtualAlloc1)
; ;   Debug "Headers "+Str(*Headers-VirtualAlloc2)
;   Debug "LocationDelta "+Str(LocationDelta.i)
; ;   Debug "DllEntry "+Str(*DllEntry)
; ;   Debug "Successful "+Str(Successful.b)
;   
  
  
  ;/ Load required dlls and adjust function table of imports
  If Not BuildImportTable(*Result)
    Debug "Error!"
    
    MemoryFreeLibrary(*Result)
    ProcedureReturn #False
  EndIf
  
  Debug "No Error!"
  End
  
  ;/ Mark memory pages depending on section headers and release
  ;/ sections that are marked as "discardable"
  FinalizeSections(*Result)
  
  ;/ Get entry point of loaded library
  If *Result\headers\OptionalHeader\AddressOfEntryPoint
    *DllEntry=*Code+*Result\headers\OptionalHeader\AddressOfEntryPoint
    If Not *DllEntry
      Debug "Library has no entry point."
      MemoryFreeLibrary(*Result)
      ProcedureReturn #False
    EndIf
    
    ;/ Notify library about attaching to process
    Successful=CallFunctionFast(*DllEntry,*Code,#DLL_PROCESS_ATTACH,0)
    
    If Not Successful
      Debug "Can't attach library."
      MemoryFreeLibrary(*Result)
      ProcedureReturn #False
    EndIf
    
    *Result\initialized=1
  EndIf

  ProcedureReturn *Result
EndProcedure

Procedure MemoryGetProcAddress(*Module.MEMORYMODULE, ModuleName.s)
  Protected *CodeBase
  Protected Index.i
  Protected *NameRef
  Protected *Ordinal
  Protected *Exports.IMAGE_EXPORT_DIRECTORY
  Protected *Directory.IMAGE_DATA_DIRECTORY
  Protected i
  
  *CodeBase=*Module\codeBase
  *Directory=*Module\headers\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_EXPORT]
  Index.i=-1
  
  If Not *Directory\Size
    ;/ No export table found
    ProcedureReturn #False
  EndIf
  
  *Exports=*CodeBase+*Directory\VirtualAddress
  If Not *Exports\NumberOfNames Or Not *Exports\NumberOfFunctions
    ;/ DLL doesn't export anything
    ProcedureReturn #False
  EndIf
  
  ;/ Search function name in list of exported names
  *NameRef=*CodeBase+*Exports\AddressOfNames
  *Ordinal=*CodeBase+*Exports\AddressOfNameOrdinals
  For i=0 To *Exports\NumberOfNames-1
    If i
      *NameRef+SizeOf(Long)
      *Ordinal+SizeOf(Word)
    EndIf
    
    If ModuleName.s=PeekS(*CodeBase+*NameRef)
      ;/ Matt: Is this right?
      Index=PeekW(*Ordinal)
      Break
    EndIf
  Next i
  
  If Index=-1
    ;/ Exported symbol not found
    ProcedureReturn #False
  EndIf
  
  If Index>*Exports\NumberOfFunctions
    ;/ Name <-> ordinal numbers don't match
    ProcedureReturn #False
  EndIf
  
  ;/ AddressOfFunctions contains the RVAs to the "real" functions
  
  ;/ PeekI here?
  ProcedureReturn *CodeBase+PeekI(*CodeBase+*Exports\AddressOfFunctions+(Index*4))
EndProcedure

Procedure MemoryFreeLibrary(*Module.MEMORYMODULE)
  Protected *DllEntry
  Protected i.i
  
  If *Module
    If *Module\initialized
      ;/ Notify library about detaching from process
      *DllEntry=*Module\codeBase+*Module\headers\OptionalHeader\AddressOfEntryPoint
      CallFunctionFast(*Module\codeBase,#DLL_PROCESS_DETACH,0)
      *Module\initialized=0
    EndIf

    If *Module\modules
      ;/ Free previously opened libraries
      For i=0 To *Module\numModules-1
        ;/ Matt: Is this a valid comparison? Maybe it should compare against 0?
        ;/ Matt: Changed this from comparison to "#INVALID_HANDLE_VALUE"
        If Not PeekI(*Module\modules+i)
          FreeLibrary_(PeekI(*Module\modules+i))
        EndIf
      Next i
      
      FreeMemory(*Module\modules)
    EndIf
    
    If *Module\codeBase
      ;/ Release memory of library
      VirtualFree_(*Module\codeBase,0,#MEM_RELEASE)
    EndIf
  EndIf
  
  HeapFree_(GetProcessHeap_(),0,*Module)
EndProcedure
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Porting Joachim's MemoryModule.c (load dll from memory)

Post by Demivec »

Mistrel wrote:I'm stuck trying to decipher line 453 where I call "ReAllocateMemory". The original code calls "realloc". I don't understand this line at all. The first parameter should be "a memory block previously allocated with malloc, calloc or realloc to be reallocated", however, none of these functions are called elsewhere in the original library. Also, why does ReAllocateMemory succeed if I'm not using a valid MemoryID?
Regarding how ReAllocateMemory() functions:
PB Manual wrote:*MemoryID = ReAllocateMemory(*MemoryID, Size)

Allocates (when parameter *MemoryID is 0) a new or re-allocates (with a valid *MemoryID as parameter) an existing contiguous memory area according to the specified size (in bytes).
Thorium
Addict
Addict
Posts: 1305
Joined: Sat Aug 15, 2009 6:59 pm

Re: Porting Joachim's MemoryModule.c (load dll from memory)

Post by Thorium »

I saw some mistakes in the code, but need to eat first. I will write more later.
Mistrel wrote: When VirtualAlloc is called, a desired address is always specified. Wouldn't this be a problem if, for example, two libraries want to be at the same address? How can it be assumed that this address will always be available while arbitrarily loading a library?
No, thats what relocations are for. By processing the relocation info you fix all memory references that rely on the base address.
The image base is just the desired address. You can load it to any other address, as long as you process the relocations.
Thorium
Addict
Addict
Posts: 1305
Joined: Sat Aug 15, 2009 6:59 pm

Re: Porting Joachim's MemoryModule.c (load dll from memory)

Post by Thorium »

It's actualy hard to read. The variable names are misleading.

Code: Select all

MemoryLoadLibrary(*SectionData)
Why *SectionData?
Shouldnt it be something like *Dll?
This is very misleading because of sections are a part of the PE format.

Code: Select all

*OldHeader=*SectionData+*DosHeader\e_lfanew
This line is correct, but again naming is very misleading.
Why *OldHeader? The pointer you are calculating points to the NtHeader.

Code: Select all

;/ reserve memory for image of library
  *Code=VirtualAlloc_(*OldHeader\OptionalHeader\ImageBase,*OldHeader\OptionalHeader\SizeOfImage,#MEM_RESERVE,#PAGE_READWRITE)
Again misleading naming. Why *Code? Thats not the buffer for a code section. Thats the buffer for the image. So why not call it *PeImage or something like that?

At that point i stop reading the code because it's just to confusing because of the bad naming.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Porting Joachim's MemoryModule.c (load dll from memory)

Post by Mistrel »

Why *SectionData?
Shouldnt it be something like *Dll?
This is very misleading because of sections are a part of the PE format.
Because the original parameter was "Data" which is a keyword and cannot be used in PureBasic. For clarity while comparing the PureBasic source code to the original I wanted to keep variable names similar.

I would "love" to clarify this code. Right now it's more or less a 1:1 conversion with a new minor changes.
No, thats what relocations are for. By processing the relocation info you fix all memory references that rely on the base address.
The image base is just the desired address. You can load it to any other address, as long as you process the relocations.
That was my understanding. But then why is this parameter being passed to VirtualAlloc instead of 0? If the specified address is unavailable it will fail.
Allocates (when parameter *MemoryID is 0) a new or re-allocates (with a valid *MemoryID as parameter) an existing contiguous memory area according to the specified size (in bytes).
By the way it's been programmed I would assume at some point this parameter will be something other than 0.
Thorium
Addict
Addict
Posts: 1305
Joined: Sat Aug 15, 2009 6:59 pm

Re: Porting Joachim's MemoryModule.c (load dll from memory)

Post by Thorium »

Mistrel wrote: That was my understanding. But then why is this parameter being passed to VirtualAlloc instead of 0? If the specified address is unavailable it will fail.
Allocates (when parameter *MemoryID is 0) a new or re-allocates (with a valid *MemoryID as parameter) an existing contiguous memory area according to the specified size (in bytes).
By the way it's been programmed I would assume at some point this parameter will be something other than 0.
Thats the quote for Reallocate from PB. Do not use that for VirtualAlloc! It's not the same. PB's memory commands are heap based the VirtualX API functions are based on virtual memory space.
You should try to reserve memory on the base address with VirtualAlloc and if it fails allocate with VirtualAlloc with 0 as address parameter. Thats because you dont need to process the relocations if the dll can be loaded to it's base address. So loading to the base address is faster. Well that was the idea behind it. There is no big difference nowadays.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Re: Porting Joachim's MemoryModule.c (load dll from memory)

Post by Mistrel »

Thorium wrote:You should try to reserve memory on the base address with VirtualAlloc and if it fails allocate with VirtualAlloc with 0 as address parameter. Thats because you dont need to process the relocations if the dll can be loaded to it's base address.
Alright. That makes perfect sense.

Can you explain why the original code attempts to use realloc where it does? Also, you say not to use ReAllocateMemory, however according to Wikipedia:

"In C, the library function malloc is used to allocate a block of memory on the heap", which I think should be the same function for realloc.

I still don't understand why there is a realloc when no "malloc" has been called anywhere.
Thorium wrote:Do not use that for VirtualAlloc! It's not the same. PB's memory commands are heap based the VirtualX API functions are based on virtual memory space.
I understand this and have no intention of replacing these commands.
Post Reply