; ***************************************************************************** ; MemDLL.pb ; by Luis ; http://luis.no-ip.net ; ***************************************************************************** #MEMDLL_CONF_DEBUG_VERBOSE = 0 ; only for testing #MEMDLL_CONF_DEBUG_FORCE_RELOCATION = 0 ; only for testing Structure IMAGE_IMPORT_DESCRIPTOR ; from winnt.h OriginalFirstThunk.l TimeDateStamp.l ForwarderChain.l Name.l FirstThunk.l EndStructure Structure IMAGE_THUNK_DATA ; from winnt.h StructureUnion ForwarderString.i Function.i Ordinal.i AddressOfData.i EndStructureUnion EndStructure Structure IMAGE_IMPORT_BY_NAME ; from winnt.h Hint.w ; some linkers may set this value to 0 Name.a[0] EndStructure Structure IMAGE_BASE_RELOCATION ; from winnt.h VirtualAddress.l SizeOfBlock.l EndStructure #IMAGE_DOS_SIGNATURE = $5A4D ; MZ (Mark Zbikowski's DOS format) #IMAGE_NT_SIGNATURE = $00004550 ; PE00 (PE format) #IMAGE_NT_OPTIONAL_HDR32_MAGIC = $010B ; 32 bit PE #IMAGE_NT_OPTIONAL_HDR64_MAGIC = $020B ; 64 bit PE #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_DIR64 = 10 ; file header characteristics #IMAGE_FILE_RELOCS_STRIPPED = 1 #IMAGE_FILE_EXECUTABLE_IMAGE = 2 #IMAGE_FILE_LINE_NUMS_STRIPPED = 4 #IMAGE_FILE_LOCAL_SYMS_STRIPPED = 8 #IMAGE_FILE_AGGRESIVE_WS_TRIM = 16 #IMAGE_FILE_LARGE_ADDRESS_AWARE = 32 #IMAGE_FILE_BYTES_REVERSED_LO = 128 #IMAGE_FILE_32BIT_MACHINE = 256 #IMAGE_FILE_DEBUG_STRIPPED = 512 #IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 1024 #IMAGE_FILE_NET_RUN_FROM_SWAP = 2048 #IMAGE_FILE_SYSTEM = 4096 #IMAGE_FILE_DLL = 8192 #IMAGE_FILE_UP_SYSTEM_ONLY = 16384 #IMAGE_FILE_BYTES_REVERSED_HI = 32768 ; data directories #IMAGE_DIRECTORY_ENTRY_EXPORT = 0 #IMAGE_DIRECTORY_ENTRY_IMPORT = 1 #IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 #IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 #IMAGE_DIRECTORY_ENTRY_SECURITY = 4 #IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 #IMAGE_DIRECTORY_ENTRY_DEBUG = 6 #IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 ; both are 7 #IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 ; both are 7 #IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 #IMAGE_DIRECTORY_ENTRY_TLS = 9 #IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 #IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 #IMAGE_DIRECTORY_ENTRY_IAT = 12 #IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 #IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 #IMAGE_SCN_CNT_CODE = 32 #IMAGE_SCN_CNT_INITIALIZED_DATA = 64 #IMAGE_SCN_CNT_UNINITIALIZED_DATA = 128 #IMAGE_SCN_MEM_DISCARDABLE = $2000000 #IMAGE_SCN_MEM_NOT_CACHED = $4000000 #IMAGE_SCN_MEM_NOT_PAGED = $8000000 #IMAGE_SCN_MEM_SHARED = $10000000 #IMAGE_SCN_MEM_EXECUTE = $20000000 #IMAGE_SCN_MEM_READ = $40000000 #IMAGE_SCN_MEM_WRITE = $80000000 Prototype iMemDll_ModuleEntryPoint (hinstDLL, fdwReason, lpvReserved) CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86) #IMAGE_ORDINAL_FLAG = $80000000 CompilerElse #IMAGE_ORDINAL_FLAG = $8000000000000000 CompilerEndIf Macro iMemDll_AlignAddressTo (a, b) (((a + (b - 1)) / b) * b) EndMacro Structure iMEMDLL_LIBRARY_INFO ImportedDllName$ ImportedDllHandle.i EndStructure Structure iMEMDLL_IMPORTED_LIBRARIES List lst_Libraries.iMEMDLL_LIBRARY_INFO() EndStructure : Structure iMEMDLL Map map_Modules.iMEMDLL_IMPORTED_LIBRARIES() ModuleEntryPoint.iMemDll_ModuleEntryPoint EndStructure : Global MEMDLL.iMEMDLL CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Procedure iMemDLL_ListAllExportedByName (*module) Protected *DOS_Header.IMAGE_DOS_HEADER Protected *PE_Header.IMAGE_NT_HEADERS Protected *IDD_Export.IMAGE_DATA_DIRECTORY Protected *ExportDirectory.IMAGE_EXPORT_DIRECTORY Protected *FuncTable, *OrdinalTable, *FuncAddr, *NameTableThunk Protected i *DOS_Header = *module *PE_Header = *module + *DOS_Header\e_lfanew *IDD_Export = @*PE_Header\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_EXPORT] *ExportDirectory = *module + *IDD_Export\VirtualAddress *FuncTable = *DOS_Header + *ExportDirectory\AddressOfFunctions *OrdinalTable = *DOS_Header + *ExportDirectory\AddressOfNameOrdinals *NameTableThunk = *DOS_Header + *ExportDirectory\AddressOfNames For i = 0 To *ExportDirectory\NumberOfNames - 1 *FuncAddr = *module + PeekL(*FuncTable + PeekW(*OrdinalTable + i * SizeOf(Word)) * SizeOf(Long)) Debug PeekS(*module + PeekL(*NameTableThunk + i * SizeOf(Long)), -1, #PB_Ascii) + " = $" + Hex(*FuncAddr) Next EndProcedure CompilerEndIf Procedure.i iMemDLL_LookForSectionRawData (*module) Protected *DOS_Header.IMAGE_DOS_HEADER = *module Protected *PE_Header.IMAGE_NT_HEADERS = *module + *DOS_Header\e_lfanew Protected *SectionHeader.IMAGE_SECTION_HEADER = *PE_Header + SizeOf(IMAGE_NT_HEADERS) Protected *IDD_BaseReloc.IMAGE_DATA_DIRECTORY = *PE_Header\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_BASERELOC] Protected i, *raw, *rva = *IDD_BaseReloc\VirtualAddress ; Looks through all the available sections to see where the passed RVA is pointing. ; Once the related section is found returns an offset from the start of the base address of the file ; to the start of the raw data for that section. For i = 1 To *PE_Header\FileHeader\NumberOfSections If (*rva >= *SectionHeader\VirtualAddress) And (*rva < *SectionHeader\VirtualAddress + *SectionHeader\VirtualSize) *raw = *SectionHeader\PointerToRawData + (*rva - *SectionHeader\VirtualAddress) Break EndIf *SectionHeader + SizeOf(IMAGE_SECTION_HEADER) Next ProcedureReturn *raw EndProcedure Procedure.i iMemDLL_CopySections (*module, *image) Protected *DOS_Header.IMAGE_DOS_HEADER = *module Protected *PE_Header.IMAGE_NT_HEADERS = *module + *DOS_Header\e_lfanew Protected *DestSection, PaddingLenght, i Protected *FirstSectionHeader.IMAGE_SECTION_HEADER = *PE_Header + SizeOf(IMAGE_NT_HEADERS) Protected *SectionHeader.IMAGE_SECTION_HEADER = *FirstSectionHeader Protected NumberOfSections Protected SectionAlignment SectionAlignment = *PE_Header\OptionalHeader\SectionAlignment NumberOfSections = *PE_Header\FileHeader\NumberOfSections CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "DataDirectory array size = " + *PE_Header\OptionalHeader\NumberOfRvaAndSizes Debug "Number of sections = " + NumberOfSections Debug "" Debug "The copying of the sections to the allocated module in memory starts here ..." Debug "" CompilerEndIf For i = 1 To NumberOfSections CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Protected output$ : output$ = "" Debug "Section n." + i + " = " + PeekS(@*SectionHeader\SecName, SizeOf(*SectionHeader\SecName), #PB_Ascii) If *SectionHeader\Characteristics & #IMAGE_SCN_CNT_CODE output$ + "CODE " EndIf If *SectionHeader\Characteristics & #IMAGE_SCN_CNT_INITIALIZED_DATA output$ + "INITIALIZED_DATA " EndIf If *SectionHeader\Characteristics & #IMAGE_SCN_CNT_UNINITIALIZED_DATA output$ + "UNITIALIZED_DATA " EndIf If *SectionHeader\Characteristics & #IMAGE_SCN_MEM_SHARED output$ + "SHAREABLE " EndIf If *SectionHeader\Characteristics & #IMAGE_SCN_MEM_DISCARDABLE output$ + "DISCARDABLE " EndIf If *SectionHeader\Characteristics & #IMAGE_SCN_MEM_EXECUTE output$ + "EXECUTABLE " EndIf If *SectionHeader\Characteristics & #IMAGE_SCN_MEM_READ output$ + "READABLE " EndIf If *SectionHeader\Characteristics & #IMAGE_SCN_MEM_WRITE output$ + "WRITEABLE " EndIf If output$ Debug Left(output$, Len(output$)-1) EndIf Debug "VirtualSize = " + *SectionHeader\VirtualSize Debug "SizeOfRawData = " + *SectionHeader\SizeOfRawData Debug "SectionAlignment = " + SectionAlignment Debug "RVA = $" + Hex(*SectionHeader\VirtualAddress) Debug "RAW = $" + Hex(*module + *SectionHeader\VirtualAddress) CompilerEndIf *DestSection = *module + *SectionHeader\VirtualAddress ; This field holds the RVA to where the loader should map the section. If *SectionHeader\SizeOfRawData = 0 ; if zero it's a section of uninitialized data ; copy the section data to the appropriate offset in the module If *PE_Header\OptionalHeader\SectionAlignment > 0 ; minimum size if unitialiazed is then SectionAlignment FillMemory(*DestSection, *PE_Header\OptionalHeader\SectionAlignment, 0) EndIf EndIf If *SectionHeader\SizeOfRawData > 0 CopyMemory(*image + *SectionHeader\PointerToRawData, *DestSection, *SectionHeader\VirtualSize) ; I think this should be VirtualSize and not SizeOfRawData ; if VirtualSize > SizeOfRawData the remaining bytes after SizeOfRawData should be preferably set to zero If *SectionHeader\VirtualSize > *SectionHeader\SizeOfRawData PaddingLenght = iMemDLL_AlignAddressTo (*SectionHeader\VirtualSize, SectionAlignment) - *SectionHeader\SizeOfRawData FillMemory(*DestSection + *SectionHeader\SizeOfRawData, PaddingLenght, 0) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Padding = " + PaddingLenght CompilerEndIf EndIf EndIf *SectionHeader + SizeOf(IMAGE_SECTION_HEADER) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" CompilerEndIf Next EndProcedure Procedure.i iMemDLL_AddToImportedLibraries (*module, DllName$, hdll) If FindMapElement(MEMDLL\map_Modules(), Str(*module)) = 0 ; module not found in the map If AddMapElement(MEMDLL\map_Modules(), Str(*module)) = 0 ; but trying to add it fails Goto fail EndIf EndIf If AddElement(MEMDLL\map_Modules()\lst_Libraries()) = 0 ; add element to the list of dlls for the specific module Goto fail EndIf MEMDLL\map_Modules()\lst_Libraries()\ImportedDllName$ = DllName$ MEMDLL\map_Modules()\lst_Libraries()\ImportedDllHandle = hdll ProcedureReturn 1 fail: CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "AddToImportedLibraries() failed !" CompilerEndIf ProcedureReturn 0 EndProcedure Procedure iMemDLL_FreeImportedLibraries (*module) If FindMapElement(MEMDLL\map_Modules(), Str(*module)) ForEach MEMDLL\map_Modules()\lst_Libraries() ; cycle through the list of dlls for the specific module CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Unloading " + MEMDLL\map_Modules()\lst_Libraries()\ImportedDllName$ CompilerEndIf FreeLibrary_(MEMDLL\map_Modules()\lst_Libraries()\ImportedDllHandle) Next ClearList(MEMDLL\map_Modules()\lst_Libraries()) ; just to be sure, empty the list DeleteMapElement(MEMDLL\map_Modules()) ; remove module EndIf EndProcedure Procedure.i iMemDLL_GetProcAddressHelp (*module, func$, ordinal) Protected *DOS_Header.IMAGE_DOS_HEADER = *module Protected *PE_Header.IMAGE_NT_HEADERS = *module + *DOS_Header\e_lfanew Protected *IDD_Export.IMAGE_DATA_DIRECTORY = *PE_Header\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_EXPORT] Protected *ExportDirectory.IMAGE_EXPORT_DIRECTORY = *module + *IDD_Export\VirtualAddress Protected *FuncTable, *OrdinalTable, *FuncAddr, *NameTableThunk Protected OrdinalBaseValue, i, dot Protected DllFwdFull$, DllFwdLib$, DllFwdFunc$, DllFwdHandle *FuncTable = *DOS_Header + *ExportDirectory\AddressOfFunctions *OrdinalTable = *DOS_Header + *ExportDirectory\AddressOfNameOrdinals *NameTableThunk = *DOS_Header + *ExportDirectory\AddressOfNames *FuncAddr = 0 If ordinal OrdinalBaseValue = *ExportDirectory\Base If ordinal < OrdinalBaseValue Or ordinal > OrdinalBaseValue + *ExportDirectory\NumberOfFunctions ProcedureReturn 0 EndIf *FuncAddr = *module + PeekL(*FuncTable + ((ordinal - OrdinalBaseValue) * SizeOf(Long))) Else For i = 0 To *ExportDirectory\NumberOfNames - 1 If func$ = PeekS(*module + PeekL(*NameTableThunk + i * SizeOf(Long)), -1, #PB_Ascii) *FuncAddr = *module + PeekL(*FuncTable + PeekW(*OrdinalTable + i * SizeOf(Word)) * SizeOf(Long)) Break EndIf Next EndIf ; if the function address is inside the export directory range it can't be a real function pointer ; and that means we have found a forwarded function If (*FuncAddr >= *ExportDirectory) And (*FuncAddr < *ExportDirectory + *IDD_Export\Size) DllFwdFull$ = PeekS(*FuncAddr,-1,#PB_Ascii) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug " >> " + func$ + " is being forwarded to " + DllFwdFull$ CompilerEndIf dot = FindString(DllFwdFull$, ".") If dot DllFwdLib$ = Left(DllFwdFull$, dot-1) DllFwdFunc$ = Mid(DllFwdFull$, dot+1) EndIf DllFwdHandle = GetModuleHandle_(DllFwdLib$) ; check if already there If DllFwdHandle *FuncAddr = iMemDLL_GetProcAddressHelp(DllFwdHandle, DllFwdFunc$, 0) Goto exit EndIf DllFwdHandle = LoadLibrary_(DllFwdLib$) ; if not already there, load it If DllFwdHandle *FuncAddr = iMemDLL_GetProcAddressHelp(DllFwdHandle, DllFwdFunc$, 0) If iMemDLL_AddToImportedLibraries(*module, DllFwdLib$, DllFwdHandle) = 0 *FuncAddr = 0 ; signal failure EndIf Goto exit EndIf *FuncAddr = 0 EndIf exit: CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 If *FuncAddr = 0 If ordinal = 0 Debug "Address not found for " + func$ Else Debug "Address not found for ordinal " + ordinal EndIf EndIf CompilerEndIf ProcedureReturn *FuncAddr EndProcedure Procedure.i iMemDLL_LoadFromImportTable (*module) Protected *DOS_Header.IMAGE_DOS_HEADER = *module Protected *PE_Header.IMAGE_NT_HEADERS = *module + *DOS_Header\e_lfanew Protected *IDD_Import.IMAGE_DATA_DIRECTORY = *PE_Header\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_IMPORT] Protected *FirstImportDesc.IMAGE_IMPORT_DESCRIPTOR = *module + *IDD_Import\VirtualAddress Protected *ImportDesc.IMAGE_IMPORT_DESCRIPTOR = *FirstImportDesc Protected *NameTableThunk.IMAGE_THUNK_DATA, *AddressTableThunk.IMAGE_THUNK_DATA, *Thunk.IMAGE_THUNK_DATA Protected *ImportByName.IMAGE_IMPORT_BY_NAME Protected DllName$, func$, hdll, ordinal CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "The loading of the imported DLLs and the patching of function addresses starts here ..." CompilerEndIf While IsBadReadPtr_(*ImportDesc, SizeOf(IMAGE_IMPORT_DESCRIPTOR)) = 0 And *ImportDesc\Name DllName$ = PeekS(*module + *ImportDesc\Name, -1, #PB_Ascii) hdll = LoadLibrary_(DllName$) If hdll = 0 CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Unable to load library " + DllName$ + ". Abort." CompilerEndIf Goto fail EndIf CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" Debug "Loaded " + DllName$ + " = $" + Hex(hdll) CompilerEndIf If iMemDLL_AddToImportedLibraries(*module, DllName$, hdll) = 0 Goto fail EndIf *NameTableThunk = *module + *ImportDesc\OriginalFirstThunk *AddressTableThunk = *module + *ImportDesc\FirstThunk If *ImportDesc\OriginalFirstThunk *Thunk = *NameTableThunk ElseIf *AddressTableThunk *Thunk = *AddressTableThunk EndIf If *Thunk = 0 CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Both the name table and the addres table looks empty for " + DllName$ + ". Abort." CompilerEndIf Goto fail EndIf While *Thunk\AddressOfData ordinal = 0 If *Thunk\Ordinal & #IMAGE_ORDINAL_FLAG ; this is an ordinal entry (unlikely) ordinal = *Thunk\Ordinal & $ffff func$ = Hex(ordinal) Else *ImportByName = *module + *Thunk\AddressOfData ; this is an import by name entry func$ = PeekS(@*ImportByName\Name[0], -1, #PB_Ascii) EndIf *AddressTableThunk\Function = iMemDLL_GetProcAddressHelp(hdll, func$, ordinal) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 If ordinal Debug " (" + ordinal + ") -> $" + Hex(*AddressTableThunk\Function) Else Debug " " + func$ + " -> $" + Hex(*AddressTableThunk\Function) EndIf CompilerEndIf *Thunk + SizeOf(IMAGE_THUNK_DATA) *AddressTableThunk + SizeOf(IMAGE_THUNK_DATA) Wend *ImportDesc + SizeOf(IMAGE_IMPORT_DESCRIPTOR) Wend ProcedureReturn 1 fail: iMemDLL_FreeImportedLibraries(*module) ProcedureReturn 0 EndProcedure Procedure iMemDLL_PatchRelocationTable (*module, *BaseReloc.IMAGE_BASE_RELOCATION, *PreferredBase) Protected *DOS_Header.IMAGE_DOS_HEADER = *module Protected *PE_Header.IMAGE_NT_HEADERS = *module + *DOS_Header\e_lfanew Protected *IDD_BaseReloc.IMAGE_DATA_DIRECTORY = *PE_Header\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_BASERELOC] Protected *CurReloc.IMAGE_BASE_RELOCATION = *BaseReloc Protected *RelocEnd.IMAGE_BASE_RELOCATION = *BaseReloc + *IDD_BaseReloc\Size Protected *CurWordEntry.Word, *TargetRelocItem.Integer, *SectionBase Protected Delta = *module - *PreferredBase Protected RelocationsCount, RelocTypeWord, i CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Protected output$ Protected total_expected, total_valid, partial_valid Debug "Actual module base = $" + Hex(*module) Debug "Preferred image base = $" + Hex(*PreferredBase) Debug "Delta = " + Delta CompilerEndIf While (*CurReloc < *RelocEnd) And *CurReloc\VirtualAddress RelocationsCount = (*CurReloc\SizeOfBlock - SizeOf(IMAGE_BASE_RELOCATION)) / SizeOf(Word) ; relocations count CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" Debug "Start of a new relocation block." Debug "Size of block = " + *CurReloc\SizeOfBlock Debug "Expected relocation entries = " + RelocationsCount CompilerEndIf *CurWordEntry = *CurReloc + SizeOf(IMAGE_BASE_RELOCATION) *SectionBase = *module + *CurReloc\VirtualAddress CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 total_expected + RelocationsCount partial_valid = 0 CompilerEndIf For i = 1 To RelocationsCount RelocTypeWord = (*CurWordEntry\w >> 12) & $000F ; binary shifted word instead of the arithmetic one (mask out the high 12 bits after the shift) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 output$ = Str(i) +" RVA ($" + Hex(*CurReloc\VirtualAddress + *CurWordEntry\w & $0fff) + "), " If (RelocTypeWord) = #IMAGE_REL_BASED_HIGHLOW output$ + "IMAGE_REL_BASED_HIGHLOW" ElseIf (RelocTypeWord) = #IMAGE_REL_BASED_DIR64 output$ + "IMAGE_REL_BASED_DIR64" Else output$ + Str(RelocTypeWord) EndIf CompilerEndIf If (RelocTypeWord = #IMAGE_REL_BASED_HIGHLOW) Or (RelocTypeWord = #IMAGE_REL_BASED_DIR64) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 total_valid + 1 partial_valid + 1 CompilerEndIf *TargetRelocItem = *SectionBase + (*CurWordEntry\w & $0fff) ; only the low 12 bits of the word *TargetRelocItem\i + Delta CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 output$ + ", $" + Hex(*TargetRelocItem\i + Delta) CompilerEndIf EndIf CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug output$ CompilerEndIf *CurWordEntry + SizeOf(Word) Next CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Valid relocation entries processed = " + partial_valid CompilerEndIf *CurReloc = *CurReloc + *CurReloc\SizeOfBlock Wend CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" Debug "Total expected relocation entries = " + total_expected Debug "Total valid relocation entries processed = " + total_valid Debug "" CompilerEndIf EndProcedure Procedure.i iMemDLL_SectorToVirtualMemProtectionFlags (secp) Protected vmp Protected executable, readable, writable Protected Dim vmflags(1,1,1) vmflags(0,0,0) = #PAGE_NOACCESS vmflags(0,0,1) = #PAGE_WRITECOPY vmflags(0,1,0) = #PAGE_READONLY vmflags(0,1,1) = #PAGE_READWRITE vmflags(1,0,0) = #PAGE_EXECUTE vmflags(1,0,1) = #PAGE_EXECUTE_WRITECOPY vmflags(1,1,0) = #PAGE_EXECUTE_READ vmflags(1,1,1) = #PAGE_EXECUTE_READWRITE If (secp & #IMAGE_SCN_MEM_EXECUTE) executable = #True EndIf If (secp & #IMAGE_SCN_MEM_READ) readable = #True EndIf If (secp & #IMAGE_SCN_MEM_WRITE) writable = #True EndIf vmp = vmflags(executable, readable, writable) If (secp & #IMAGE_SCN_MEM_NOT_CACHED) vmp = vmp | #PAGE_NOCACHE EndIf ProcedureReturn vmp EndProcedure Procedure.i iMemDLL_SetProtectionFlagsForSections (*module) Protected *DOS_Header.IMAGE_DOS_HEADER = *module Protected *PE_Header.IMAGE_NT_HEADERS = *module + *DOS_Header\e_lfanew Protected *SectionHeader.IMAGE_SECTION_HEADER Protected *Section, OldProtect, NewProtect, i, SectionSize ; set PE header to readonly VirtualProtect_(*module, *PE_Header\OptionalHeader\SizeOfHeaders, #PAGE_READONLY, @OldProtect) *SectionHeader = *PE_Header + SizeOf(IMAGE_NT_HEADERS) For i = 1 To *PE_Header\FileHeader\NumberOfSections *Section = *module + *SectionHeader\VirtualAddress SectionSize = *SectionHeader\VirtualSize ; I think this should be VirtualSize and not SizeOfRawData (cfr. Joachim Bauch) If *SectionHeader\Characteristics & #IMAGE_SCN_MEM_DISCARDABLE ; let's throw away any temporary section CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Discarding section " + PeekS(@*SectionHeader\SecName, SizeOf(*SectionHeader\SecName), #PB_Ascii) + " (size = " + SectionSize + ")" CompilerEndIf VirtualFree_(*Section, SectionSize, #MEM_DECOMMIT) *SectionHeader + SizeOf(IMAGE_SECTION_HEADER) ; do not forget this ! Continue EndIf ; translate section protection to VAS protections NewProtect = iMemDLL_SectorToVirtualMemProtectionFlags (*SectionHeader\Characteristics) If VirtualProtect_(*Section, SectionSize, NewProtect, @OldProtect) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Protected output$ output$ = "Protecting section " + PeekS(@*SectionHeader\SecName, SizeOf(*SectionHeader\SecName), #PB_Ascii) + " (" Select NewProtect Case #PAGE_NOACCESS output$ + "PAGE_NOACCESS" Case #PAGE_WRITECOPY output$ + "PAGE_WRITECOPY" Case #PAGE_READONLY output$ + "PAGE_READONLY" Case #PAGE_READWRITE output$ + "PAGE_READWRITE" Case #PAGE_EXECUTE output$ + "PAGE_EXECUTE" Case #PAGE_EXECUTE_WRITECOPY output$ + "PAGE_EXECUTE_WRITECOPY" Case #PAGE_EXECUTE_READ output$ + "PAGE_EXECUTE_READ" Case #PAGE_EXECUTE_READWRITE output$ + "PAGE_EXECUTE_READWRITE" Default output$ + "???" EndSelect Debug output$ + ")" CompilerEndIf Else CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Error in VirtualProtect() for section n. " + i CompilerEndIf Goto fail EndIf *SectionHeader + SizeOf(IMAGE_SECTION_HEADER) Next ProcedureReturn 1 fail: ProcedureReturn 0 EndProcedure Procedure.i MemDLL_GetProcAddress (*module, func$) ProcedureReturn iMemDLL_GetProcAddressHelp (*module, func$, 0) EndProcedure Procedure MemDLL_FreeLibrary (*module) Protected *DOS_Header.IMAGE_DOS_HEADER = *module Protected *PE_Header.IMAGE_NT_HEADERS = *module + *DOS_Header\e_lfanew If *PE_Header\OptionalHeader\AddressOfEntryPoint ; notify a detach to the DLL MemDll\ModuleEntryPoint = *module + *PE_Header\OptionalHeader\AddressOfEntryPoint MemDll\ModuleEntryPoint(*module, #DLL_PROCESS_DETACH, 0) EndIf iMemDLL_FreeImportedLibraries(*module) VirtualFree_(*module, 0, #MEM_RELEASE) EndProcedure Procedure.i MemDLL_LoadLibrary (*image) Protected *DOS_Header.IMAGE_DOS_HEADER Protected *PE_Header.IMAGE_NT_HEADERS Protected *BaseReloc.IMAGE_BASE_RELOCATION Protected *PreferredBase, *module Protected RetCode, SizeOfImage, SizeOfHeaders, OffsetToRawDataOfRelocationTable, flgRelocationIsRequired ;**************************************************************************************************** ; check if all it is as it should be ... ;**************************************************************************************************** If *image = #Null CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "The passed memory address is null. Abort." CompilerEndIf Goto fail EndIf *DOS_Header = *image ; the image of the DLL starts with the old DOS header If *DOS_Header\e_magic <> #IMAGE_DOS_SIGNATURE CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "DOS header signature is missing. Abort." CompilerEndIf Goto fail EndIf *PE_Header = *image + *DOS_Header\e_lfanew ; follows the link to the PE header ; check for a valid PE If *PE_Header\Signature <> #IMAGE_NT_SIGNATURE CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "PE header signature is missing. Abort." CompilerEndIf Goto fail EndIf If *PE_Header\FileHeader\Characteristics & #IMAGE_FILE_DLL = 0 ; check if this is a valid DLL CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "This is not a DLL. Abort." CompilerEndIf Goto fail EndIf CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86) If *PE_Header\OptionalHeader\Magic <> #IMAGE_NT_OPTIONAL_HDR32_MAGIC ; we expect a 32 bit DLL CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "This is not a 32 bit DLL. Abort." CompilerEndIf Goto fail EndIf CompilerElse If *PE_Header\OptionalHeader\Magic <> #IMAGE_NT_OPTIONAL_HDR64_MAGIC ; we expect a 64 bit DLL CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "This is not a 64 bit DLL. Abort." CompilerEndIf Goto fail EndIf CompilerEndIf *PreferredBase = *PE_Header\OptionalHeader\ImageBase SizeOfImage = *PE_Header\OptionalHeader\SizeOfImage SizeOfHeaders = *PE_Header\OptionalHeader\SizeOfHeaders CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "DLL preferred base address = $" + Hex(*PreferredBase) CompilerEndIf ;**************************************************************************************************** ; allocate the module in virtual address space (VAS) ;**************************************************************************************************** CompilerIf #MEMDLL_CONF_DEBUG_FORCE_RELOCATION = 1 Protected *PreferredBaseForcedToReserved = VirtualAlloc_(*PreferredBase, SizeOfImage, #MEM_RESERVE, #PAGE_READWRITE) CompilerEndIf ; we first try to allocate it to the preferred base address *module = VirtualAlloc_(*PreferredBase, SizeOfImage, #MEM_RESERVE, #PAGE_READWRITE) If *module = #Null ; that space must be already reserved for something... no luck flgRelocationIsRequired = 1 ; take note of it CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "The preferred base address is already in use, relocation is required." CompilerEndIf ; do we have the relocation table ? If (*PE_Header\OptionalHeader\DataDirectory[#IMAGE_DIRECTORY_ENTRY_BASERELOC]\Size = 0) Or (*PE_Header\FileHeader\Characteristics & #IMAGE_FILE_RELOCS_STRIPPED) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "... and the module is not relocatable. Abort." CompilerEndIf Goto fail EndIf EndIf If flgRelocationIsRequired ; we try to allocate it somewhere else *module = VirtualAlloc_(#Null, SizeOfImage, #MEM_RESERVE, #PAGE_READWRITE) If *module = #Null CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "An error happened trying to allocate the module at a new address. Abort." CompilerEndIf Goto fail EndIf EndIf ; now we have a good address range all for ourself, let's commit the memory *module = VirtualAlloc_(*module, SizeOfImage, #MEM_COMMIT, #PAGE_READWRITE) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "Module loaded at $" + Hex(*module) Debug "End of module at $" + Hex(*module + SizeOfImage) Debug "Size of the Module = $" + Hex(SizeOfImage) If (SizeOfImage % *PE_Header\OptionalHeader\SectionAlignment) <> 0 Debug "WARNING: module's size is not a multiple of SectionAlignment." EndIf CompilerEndIf ;**************************************************************************************************** ; copy DOS header, PE header and the section table arrays in one shot to the allocated module ;**************************************************************************************************** CopyMemory(*image, *module, SizeOfHeaders) ; a simple CopyMemory() is enough ;**************************************************************************************************** ; copy the sections each one at its appropriate starting address ;**************************************************************************************************** iMemDLL_CopySections(*module, *image) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" Debug "List of all functions exported by name ..." Debug "" iMemDLL_ListAllExportedByName(*module) ; just a convenience, could be useful to have Debug "" CompilerEndIf ;**************************************************************************************************** ; load imports (the other DLLs required by this one) and patch the function addresses ;**************************************************************************************************** If iMemDLL_LoadFromImportTable(*module) = 0 CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "An error happened while processing the import table. Abort." CompilerEndIf Goto fail EndIf ;**************************************************************************************************** ; update the relocation table with new addresses if it is required ;**************************************************************************************************** If flgRelocationIsRequired OffsetToRawDataOfRelocationTable = iMemDLL_LookForSectionRawData (*module) ; This is the RVA to the first entry of type IMAGE_BASE_RELOCATION in the relocation table. *BaseReloc = *image + OffsetToRawDataOfRelocationTable CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" Debug "The relocation of entries in the relocation table starts here ..." Debug "" Debug "Offset to relocation table data = $" + Hex(OffsetToRawDataOfRelocationTable) Debug "Absolute start of the relocation table data = $" + Hex(*BaseReloc) CompilerEndIf iMemDLL_PatchRelocationTable(*module, *BaseReloc, *PreferredBase) EndIf CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 If flgRelocationIsRequired = 0 Debug "" Debug "Relocation is not required, DLL was loaded at the preferred address." Debug "" EndIf CompilerEndIf ;**************************************************************************************************** ; cycle through all sections and apply the appropriate protection settings ;**************************************************************************************************** If iMemDLL_SetProtectionFlagsForSections(*module) = 0 CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "An error happened while setting section protections. Abort." CompilerEndIf Goto fail EndIf ;**************************************************************************************************** ; If there is a valid entry point, send a #DLL_PROCESS_ATTACH notification ;**************************************************************************************************** If *PE_Header\OptionalHeader\AddressOfEntryPoint MemDll\ModuleEntryPoint = *module + *PE_Header\OptionalHeader\AddressOfEntryPoint RetCode = MemDll\ModuleEntryPoint(*module, #DLL_PROCESS_ATTACH, 0) CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" Debug "ModuleEntryPoint -> #DLL_PROCESS_ATTACH = " + RetCode CompilerEndIf If RetCode = 0 Goto fail EndIf EndIf CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" CompilerEndIf CompilerIf #MEMDLL_CONF_DEBUG_FORCE_RELOCATION = 1 If *PreferredBaseForcedToReserved VirtualFree_(*PreferredBaseForcedToReserved, 0, #MEM_RELEASE) EndIf CompilerEndIf Debug *module ProcedureReturn *module ; all done fail: If *module iMemDLL_FreeImportedLibraries(*module) VirtualFree_(*module, 0, #MEM_RELEASE) EndIf CompilerIf #MEMDLL_CONF_DEBUG_VERBOSE = 1 Debug "" CompilerEndIf CompilerIf #MEMDLL_CONF_DEBUG_FORCE_RELOCATION = 1 If *PreferredBaseForcedToReserved VirtualFree_(*PreferredBaseForcedToReserved, 0, #MEM_RELEASE) EndIf CompilerEndIf ProcedureReturn 0 ; something went wrong EndProcedure ; IDE Options = PureBasic 5.44 LTS (Windows - x86) ; CursorPosition = 729 ; FirstLine = 171 ; Folding = AAAAAAQYCAw ; EnableUnicode ; EnableThread ; EnableXP ; EnableAdmin ; EnableOnError