Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
832 lines (728 sloc) 25.6 KB
// To compile under FPC, Delphi mode must be used
// Also define CPUX64 for simplicity
{$IFDEF FPC}
{$mode delphi}
{$IFDEF CPU64}
{$DEFINE CPUX64}
{$ENDIF}
{$ENDIF}
unit MemoryModule;
{ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Memory DLL loading code
* ------------------------
*
* Original C Code
* Memory DLL loading code
* Version 0.0.4
*
* Copyright (c) 2004-2015 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
* 2.0 (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-2015
* Joachim Bauch. All Rights Reserved.
*
* ================== MemoryModule "Conversion to Delphi" ==================
*
* Copyright (c) 2015 by Fr0sT / https://github.com/Fr0sT-Brutal
*
* Initially based on the code by:
* Copyright (c) 2005 - 2006 by Martin Offenwanger / coder@dsplayer.de / http://www.dsplayer.de
* Carlo Pasolini / cdpasop@hotmail.it / http://pasotech.altervista.org
*
* NOTE
* This code is Delphi translation of original C code taken from https://github.com/fancycode/MemoryModule
* (commit dc173ca from Mar 1, 2015).
* Resource loading and exe loading, custom functions, user data not implemented yet.
* Tested under RAD Studio XE2 and XE6 32/64-bit, Lazarus 32-bit
* }
interface
uses
Windows;
type
TMemoryModule = Pointer;
{ ++++++++++++++++++++++++++++++++++++++++++++++++++
*** Memory DLL loading functions Declaration ***
-------------------------------------------------- }
// return value is nil if function fails
function MemoryLoadLibary(Data: Pointer): TMemoryModule; stdcall;
// return value is nil if function fails
function MemoryGetProcAddress(Module: TMemoryModule; const Name: PAnsiChar): Pointer; stdcall;
// free module
procedure MemoryFreeLibrary(Module: TMemoryModule); stdcall;
implementation
{ ++++++++++++++++++++++++++++++++++++++++
*** Missing Windows API Definitions ***
---------------------------------------- }
{$IF NOT DECLARED(IMAGE_BASE_RELOCATION)}
type
{$ALIGN 4}
IMAGE_BASE_RELOCATION = record
VirtualAddress: DWORD;
SizeOfBlock: DWORD;
end;
{$ALIGN ON}
PIMAGE_BASE_RELOCATION = ^IMAGE_BASE_RELOCATION;
{$IFEND}
// Types that are declared in Pascal-style (ex.: PImageOptionalHeader); redeclaring them in C-style
{$IF NOT DECLARED(PIMAGE_DATA_DIRECTORY)}
type PIMAGE_DATA_DIRECTORY = ^IMAGE_DATA_DIRECTORY;
{$IFEND}
{$IF NOT DECLARED(PIMAGE_SECTION_HEADER)}
type PIMAGE_SECTION_HEADER = ^IMAGE_SECTION_HEADER;
{$IFEND}
{$IF NOT DECLARED(PIMAGE_EXPORT_DIRECTORY)}
type PIMAGE_EXPORT_DIRECTORY = ^IMAGE_EXPORT_DIRECTORY;
{$IFEND}
{$IF NOT DECLARED(PIMAGE_DOS_HEADER)}
type PIMAGE_DOS_HEADER = ^IMAGE_DOS_HEADER;
{$IFEND}
{$IF NOT DECLARED(PIMAGE_NT_HEADERS)}
type PIMAGE_NT_HEADERS = ^IMAGE_NT_HEADERS;
{$IFEND}
{$IF NOT DECLARED(PUINT_PTR)}
type PUINT_PTR = ^UINT_PTR;
{$IFEND}
// Missing constants
const
IMAGE_REL_BASED_ABSOLUTE = 0;
IMAGE_REL_BASED_HIGHLOW = 3;
IMAGE_REL_BASED_DIR64 = 10;
// Things that are incorrectly defined at least up to XE6 (miss x64 mapping)
{$IFDEF CPUX64}
type
PIMAGE_TLS_DIRECTORY = PIMAGE_TLS_DIRECTORY64;
const
IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG64;
{$ENDIF}
{ +++++++++++++++++++++++++++++++++++++++++++++++
*** Internal MemoryModule Const Definition ***
----------------------------------------------- }
const
IMAGE_SIZEOF_BASE_RELOCATION = SizeOf(IMAGE_BASE_RELOCATION);
{$IFDEF CPUX64}
HOST_MACHINE = IMAGE_FILE_MACHINE_AMD64;
{$ELSE}
HOST_MACHINE = IMAGE_FILE_MACHINE_I386;
{$ENDIF}
type
{ +++++++++++++++++++++++++++++++++++++++++++++++
*** Internal MemoryModule Type Definition ***
----------------------------------------------- }
TMemoryModuleRec = record
Headers: PIMAGE_NT_HEADERS;
CodeBase: Pointer;
Modules: array of HMODULE;
NumModules: Integer;
Initialized: Boolean;
IsRelocated: Boolean;
PageSize: DWORD;
end;
PMemoryModule = ^TMemoryModuleRec;
TDllEntryProc = function(hinstDLL: HINST; fdwReason: DWORD; lpReserved: Pointer): BOOL; stdcall;
TSectionFinalizeData = record
Address: Pointer;
AlignedAddress: Pointer;
Size: SIZE_T;
Characteristics: DWORD;
Last: Boolean;
end;
// Explicitly export these functions to allow hooking of their origins
function GetProcAddress_Internal(hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall; external kernel32 name 'GetProcAddress';
function LoadLibraryA_Internal(lpLibFileName: LPCSTR): HMODULE; stdcall; external kernel32 name 'LoadLibraryA';
function FreeLibrary_Internal(hLibModule: HMODULE): BOOL; stdcall; external kernel32 name 'FreeLibrary';
// Just an imitation to allow using try-except block. DO NOT try to handle this
// like "on E do ..." !
procedure Abort;
begin
raise TObject.Create;
end;
// Copy from SysUtils to get rid of this unit
function StrComp(const Str1, Str2: PAnsiChar): Integer;
var
P1, P2: PAnsiChar;
begin
P1 := Str1;
P2 := Str2;
while True do
begin
if (P1^ <> P2^) or (P1^ = #0) then
Exit(Ord(P1^) - Ord(P2^));
Inc(P1);
Inc(P2);
end;
end;
{ +++++++++++++++++++++++++++++++++++++++++++++++++++++
*** Missing WinAPI macros ***
----------------------------------------------------- }
{$IF NOT DECLARED(IMAGE_ORDINAL)}
// #define IMAGE_ORDINAL64(Ordinal) (Ordinal & 0xffff)
// #define IMAGE_ORDINAL32(Ordinal) (Ordinal & 0xffff)
function IMAGE_ORDINAL(Ordinal: NativeUInt): Word; inline;
begin
Result := Ordinal and $FFFF;
end;
{$IFEND}
{$IF NOT DECLARED(IMAGE_SNAP_BY_ORDINAL)}
// IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0)
// IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0)
function IMAGE_SNAP_BY_ORDINAL(Ordinal: NativeUInt): Boolean; inline;
begin
Result := ((Ordinal and IMAGE_ORDINAL_FLAG) <> 0);
end;
{$IFEND}
{ +++++++++++++++++++++++++++++++++++++++++++++++++++++
*** Helper functions ***
----------------------------------------------------- }
function GET_HEADER_DICTIONARY(Module: PMemoryModule; Idx: Integer): PIMAGE_DATA_DIRECTORY;
begin
Result := PIMAGE_DATA_DIRECTORY(@(Module.Headers.OptionalHeader.DataDirectory[Idx]));
end;
function ALIGN_DOWN(Address: Pointer; Alignment: DWORD): Pointer;
begin
Result := Pointer(UIntPtr(Address) and not (Alignment - 1));
end;
function CopySections(data: Pointer; old_headers: PIMAGE_NT_HEADERS; module: PMemoryModule): Boolean;
var
i, Size: Integer;
CodeBase: Pointer;
dest: Pointer;
Section: PIMAGE_SECTION_HEADER;
begin
CodeBase := Module.CodeBase;
Section := PIMAGE_SECTION_HEADER(IMAGE_FIRST_SECTION(Module.Headers{$IFNDEF FPC}^{$ENDIF}));
for i := 0 to Module.Headers.FileHeader.NumberOfSections - 1 do
begin
// Section doesn't contain data in the dll itself, but may define
// uninitialized Data
if Section.SizeOfRawData = 0 then
begin
Size := Old_headers.OptionalHeader.SectionAlignment;
if Size > 0 then
begin
dest := VirtualAlloc(PByte(CodeBase) + Section.VirtualAddress,
Size,
MEM_COMMIT,
PAGE_READWRITE);
if dest = nil then
Exit(False);
// Always use position from file to support alignments smaller
// than page Size.
dest := PByte(CodeBase) + Section.VirtualAddress;
Section.Misc.PhysicalAddress := DWORD(dest);
ZeroMemory(dest, Size);
end;
// Section is empty
Inc(Section);
Continue;
end; // if
// commit memory block and copy Data from dll
dest := VirtualAlloc(PByte(CodeBase) + Section.VirtualAddress,
Section.SizeOfRawData,
MEM_COMMIT,
PAGE_READWRITE);
if dest = nil then
Exit(False);
// Always use position from file to support alignments smaller
// than page Size.
dest := PByte(CodeBase) + Section.VirtualAddress;
CopyMemory(dest, PByte(Data) + Section.PointerToRawData, Section.SizeOfRawData);
Section.Misc.PhysicalAddress := DWORD(dest);
Inc(Section);
end; // for
Result := True;
end;
// Protection flags for memory pages (Executable, Readable, Writeable)
const
ProtectionFlags: array[Boolean, Boolean, Boolean] of DWORD =
(
(
// not executable
(PAGE_NOACCESS, PAGE_WRITECOPY),
(PAGE_READONLY, PAGE_READWRITE)
),
(
// executable
(PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY),
(PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE)
)
);
function GetRealSectionSize(Module: PMemoryModule; Section: PIMAGE_SECTION_HEADER): DWORD;
begin
Result := Section.SizeOfRawData;
if Result = 0 then
if (Section.Characteristics and IMAGE_SCN_CNT_INITIALIZED_DATA) <> 0 then
Result := Module.Headers.OptionalHeader.SizeOfInitializedData
else if (Section.Characteristics and IMAGE_SCN_CNT_UNINITIALIZED_DATA) <> 0 then
Result := Module.Headers.OptionalHeader.SizeOfUninitializedData;
end;
function FinalizeSection(Module: PMemoryModule; const SectionData: TSectionFinalizeData): Boolean;
var
protect, oldProtect: DWORD;
executable, readable, writeable: Boolean;
begin
if SectionData.Size = 0 then
Exit(True);
if (SectionData.Characteristics and IMAGE_SCN_MEM_DISCARDABLE) <> 0 then
begin
// Section is not needed any more and can safely be freed
if (SectionData.Address = SectionData.AlignedAddress) and
( SectionData.Last or
(Module.Headers.OptionalHeader.SectionAlignment = Module.PageSize) or
(SectionData.Size mod Module.PageSize = 0)
) then
// Only allowed to decommit whole pages
VirtualFree(SectionData.Address, SectionData.Size, MEM_DECOMMIT);
Exit(True);
end;
// determine protection flags based on Characteristics
executable := (SectionData.Characteristics and IMAGE_SCN_MEM_EXECUTE) <> 0;
readable := (SectionData.Characteristics and IMAGE_SCN_MEM_READ) <> 0;
writeable := (SectionData.Characteristics and IMAGE_SCN_MEM_WRITE) <> 0;
protect := ProtectionFlags[executable][readable][writeable];
if (SectionData.Characteristics and IMAGE_SCN_MEM_NOT_CACHED) <> 0 then
protect := protect or PAGE_NOCACHE;
// change memory access flags
Result := VirtualProtect(SectionData.Address, SectionData.Size, protect, oldProtect);
end;
function FinalizeSections(Module: PMemoryModule): Boolean;
var
i: Integer;
Section: PIMAGE_SECTION_HEADER;
imageOffset: UIntPtr;
SectionData: TSectionFinalizeData;
sectionAddress, AlignedAddress: Pointer;
sectionSize: DWORD;
begin
Section := PIMAGE_SECTION_HEADER(IMAGE_FIRST_SECTION(Module.Headers{$IFNDEF FPC}^{$ENDIF}));
{$IFDEF CPUX64}
imageOffset := (NativeUInt(Module.CodeBase) and $ffffffff00000000);
{$ELSE}
imageOffset := 0;
{$ENDIF}
SectionData.Address := Pointer(UIntPtr(Section.Misc.PhysicalAddress) or imageOffset);
SectionData.AlignedAddress := ALIGN_DOWN(SectionData.Address, Module.PageSize);
SectionData.Size := GetRealSectionSize(Module, Section);
SectionData.Characteristics := Section.Characteristics;
SectionData.Last := False;
Inc(Section);
// loop through all sections and change access flags
for i := 1 to Module.Headers.FileHeader.NumberOfSections - 1 do
begin
sectionAddress := Pointer(UIntPtr(Section.Misc.PhysicalAddress) or imageOffset);
AlignedAddress := ALIGN_DOWN(SectionData.Address, Module.PageSize);
sectionSize := GetRealSectionSize(Module, Section);
// Combine access flags of all sections that share a page
// TODO(fancycode): We currently share flags of a trailing large Section
// with the page of a first small Section. This should be optimized.
if (SectionData.AlignedAddress = AlignedAddress) or
(PByte(SectionData.Address) + SectionData.Size > PByte(AlignedAddress)) then
begin
// Section shares page with previous
if (Section.Characteristics and IMAGE_SCN_MEM_DISCARDABLE = 0) or
(SectionData.Characteristics and IMAGE_SCN_MEM_DISCARDABLE = 0) then
SectionData.Characteristics := (SectionData.Characteristics or Section.Characteristics) and not IMAGE_SCN_MEM_DISCARDABLE
else
SectionData.Characteristics := SectionData.Characteristics or Section.Characteristics;
SectionData.Size := PByte(sectionAddress) + sectionSize - PByte(SectionData.Address);
Inc(Section);
Continue;
end;
if not FinalizeSection(Module, SectionData) then
Exit(False);
SectionData.Address := sectionAddress;
SectionData.AlignedAddress := AlignedAddress;
SectionData.Size := sectionSize;
SectionData.Characteristics := Section.Characteristics;
Inc(Section);
end; // for
SectionData.Last := True;
if not FinalizeSection(Module, SectionData) then
Exit(False);
Result := True;
end;
function ExecuteTLS(Module: PMemoryModule): Boolean;
var
CodeBase: Pointer;
directory: PIMAGE_DATA_DIRECTORY;
tls: PIMAGE_TLS_DIRECTORY;
callback: PPointer; // =^PIMAGE_TLS_CALLBACK;
// TLS callback pointers are VA's (ImageBase included) so if the module resides at
// the other ImageBage they become invalid. This routine relocates them to the
// actual ImageBase.
// The case seem to happen with DLLs only and they rarely use TLS callbacks.
// Moreover, they probably don't work at all when using DLL dynamically which is
// the case in our code.
function FixPtr(OldPtr: Pointer): Pointer;
begin
Result := Pointer(NativeInt(OldPtr) - Module.Headers.OptionalHeader.ImageBase + NativeInt(CodeBase));
end;
begin
Result := True;
CodeBase := Module.CodeBase;
directory := GET_HEADER_DICTIONARY(Module, IMAGE_DIRECTORY_ENTRY_TLS);
if directory.VirtualAddress = 0 then
Exit;
tls := PIMAGE_TLS_DIRECTORY(PByte(CodeBase) + directory.VirtualAddress);
// Delphi syntax is quite awkward when dealing with proc pointers so we have to
// use casts to untyped pointers
callback := Pointer(tls.AddressOfCallBacks);
if callback <> nil then
begin
callback := FixPtr(callback);
while callback^ <> nil do
begin
PIMAGE_TLS_CALLBACK(FixPtr(callback^))(CodeBase, DLL_PROCESS_ATTACH, nil);
Inc(callback);
end;
end;
end;
function PerformBaseRelocation(Module: PMemoryModule; Delta: NativeInt): Boolean;
var
i: Cardinal;
CodeBase: Pointer;
directory: PIMAGE_DATA_DIRECTORY;
relocation: PIMAGE_BASE_RELOCATION;
dest: Pointer;
relInfo: ^UInt16;
patchAddrHL: PDWORD;
{$IFDEF CPUX64}
patchAddr64: PULONGLONG;
{$ENDIF}
relType, offset: Integer;
begin
CodeBase := Module.CodeBase;
directory := GET_HEADER_DICTIONARY(Module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
if directory.Size = 0 then
Exit(Delta = 0);
relocation := PIMAGE_BASE_RELOCATION(PByte(CodeBase) + directory.VirtualAddress);
while relocation.VirtualAddress > 0 do
begin
dest := Pointer(PByte(CodeBase) + relocation.VirtualAddress);
relInfo := Pointer(PByte(relocation) + IMAGE_SIZEOF_BASE_RELOCATION);
for i := 0 to Trunc(((relocation.SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2)) - 1 do
begin
// the upper 4 bits define the type of relocation
relType := relInfo^ shr 12;
// the lower 12 bits define the offset
offset := relInfo^ and $FFF;
case relType of
IMAGE_REL_BASED_ABSOLUTE:
// skip relocation
;
IMAGE_REL_BASED_HIGHLOW:
begin
// change complete 32 bit address
patchAddrHL := Pointer(PByte(dest) + offset);
Inc(patchAddrHL^, Delta);
end;
{$IFDEF CPUX64}
IMAGE_REL_BASED_DIR64:
begin
patchAddr64 := Pointer(PByte(dest) + offset);
Inc(patchAddr64^, Delta);
end;
{$ENDIF}
end;
Inc(relInfo);
end; // for
// advance to next relocation block
relocation := PIMAGE_BASE_RELOCATION(PByte(relocation) + relocation.SizeOfBlock);
end; // while
Result := True;
end;
function BuildImportTable(Module: PMemoryModule): Boolean; stdcall;
var
CodeBase: Pointer;
directory: PIMAGE_DATA_DIRECTORY;
importDesc: PIMAGE_IMPORT_DESCRIPTOR;
thunkRef: PUINT_PTR;
funcRef: ^FARPROC;
handle: HMODULE;
thunkData: PIMAGE_IMPORT_BY_NAME;
begin
CodeBase := Module.CodeBase;
Result := True;
directory := GET_HEADER_DICTIONARY(Module, IMAGE_DIRECTORY_ENTRY_IMPORT);
if directory.Size = 0 then
Exit(True);
importDesc := PIMAGE_IMPORT_DESCRIPTOR(PByte(CodeBase) + directory.VirtualAddress);
while (not IsBadReadPtr(importDesc, SizeOf(IMAGE_IMPORT_DESCRIPTOR))) and (importDesc.Name <> 0) do
begin
handle := LoadLibraryA_Internal(PAnsiChar(PByte(CodeBase) + importDesc.Name));
if handle = 0 then
begin
SetLastError(ERROR_MOD_NOT_FOUND);
Result := False;
Break;
end;
try
SetLength(Module.Modules, Module.NumModules + 1);
except
FreeLibrary_Internal(handle);
SetLastError(ERROR_OUTOFMEMORY);
Result := False;
Break;
end;
Module.Modules[Module.NumModules] := handle;
Inc(Module.NumModules);
if importDesc.OriginalFirstThunk <> 0 then
begin
thunkRef := Pointer(PByte(CodeBase) + importDesc.OriginalFirstThunk);
funcRef := Pointer(PByte(CodeBase) + importDesc.FirstThunk);
end
else
begin
// no hint table
thunkRef := Pointer(PByte(CodeBase) + importDesc.FirstThunk);
funcRef := Pointer(PByte(CodeBase) + importDesc.FirstThunk);
end;
while thunkRef^ <> 0 do
begin
if IMAGE_SNAP_BY_ORDINAL(thunkRef^) then
funcRef^ := GetProcAddress_Internal(handle, PAnsiChar(IMAGE_ORDINAL(thunkRef^)))
else
begin
thunkData := PIMAGE_IMPORT_BY_NAME(PByte(CodeBase) + thunkRef^);
funcRef^ := GetProcAddress_Internal(handle, PAnsiChar(@(thunkData.Name)));
end;
if funcRef^ = nil then
begin
Result := False;
Break;
end;
Inc(funcRef);
Inc(thunkRef);
end; // while
if not Result then
begin
FreeLibrary_Internal(handle);
SetLastError(ERROR_PROC_NOT_FOUND);
Break;
end;
Inc(importDesc);
end; // while
end;
{ +++++++++++++++++++++++++++++++++++++++++++++++++++++
*** Memory DLL loading functions Implementation ***
----------------------------------------------------- }
function MemoryLoadLibary(Data: Pointer): TMemoryModule; stdcall;
var
dos_header: PIMAGE_DOS_HEADER;
old_header: PIMAGE_NT_HEADERS;
code, Headers: Pointer;
locationdelta: NativeInt;
sysInfo: SYSTEM_INFO;
DllEntry: TDllEntryProc;
successfull: Boolean;
Module: PMemoryModule;
begin
Result := nil; Module := nil;
try
dos_header := PIMAGE_DOS_HEADER(Data);
if (dos_header.e_magic <> IMAGE_DOS_SIGNATURE) then
begin
SetLastError(ERROR_BAD_EXE_FORMAT);
Exit;
end;
// old_header = (PIMAGE_NT_HEADERS)&((const unsigned char * )(Data))[dos_header->e_lfanew];
old_header := PIMAGE_NT_HEADERS(PByte(Data) + dos_header._lfanew);
if old_header.Signature <> IMAGE_NT_SIGNATURE then
begin
SetLastError(ERROR_BAD_EXE_FORMAT);
Exit;
end;
{$IFDEF CPUX64}
if old_header.FileHeader.Machine <> IMAGE_FILE_MACHINE_AMD64 then
{$ELSE}
if old_header.FileHeader.Machine <> IMAGE_FILE_MACHINE_I386 then
{$ENDIF}
begin
SetLastError(ERROR_BAD_EXE_FORMAT);
Exit;
end;
if (old_header.OptionalHeader.SectionAlignment and 1) <> 0 then
begin
// Only support section alignments that are a multiple of 2
SetLastError(ERROR_BAD_EXE_FORMAT);
Exit;
end;
// reserve memory for image of library
// XXX: is it correct to commit the complete memory region at once?
// calling DllEntry raises an exception if we don't...
code := VirtualAlloc(Pointer(old_header.OptionalHeader.ImageBase),
old_header.OptionalHeader.SizeOfImage,
MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE);
if code = nil then
begin
// try to allocate memory at arbitrary position
code := VirtualAlloc(nil,
old_header.OptionalHeader.SizeOfImage,
MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE);
if code = nil then
begin
SetLastError(ERROR_OUTOFMEMORY);
Exit;
end;
end;
Module := PMemoryModule(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, SizeOf(TMemoryModuleRec)));
if Module = nil then
begin
VirtualFree(code, 0, MEM_RELEASE);
SetLastError(ERROR_OUTOFMEMORY);
Exit;
end;
// memory is zeroed by HeapAlloc
Module.CodeBase := code;
GetNativeSystemInfo({$IFDEF FPC}@{$ENDIF}sysInfo);
Module.PageSize := sysInfo.dwPageSize;
// commit memory for Headers
Headers := VirtualAlloc(code,
old_header.OptionalHeader.SizeOfHeaders,
MEM_COMMIT,
PAGE_READWRITE);
// copy PE header to code
CopyMemory(Headers, dos_header, old_header.OptionalHeader.SizeOfHeaders);
// result->Headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(Headers))[dos_header->e_lfanew];
Module.Headers := PIMAGE_NT_HEADERS(PByte(Headers) + dos_header._lfanew);
// copy sections from DLL file block to new memory location
if not CopySections(Data, old_header, Module) then
Abort;
// adjust base address of imported data
locationdelta := NativeInt(code) - old_header.OptionalHeader.ImageBase;
if locationdelta <> 0 then
Module.IsRelocated := PerformBaseRelocation(Module, locationdelta)
else
Module.IsRelocated := True;
// load required dlls and adjust function table of imports
if not BuildImportTable(Module) then
Abort;
// mark memory pages depending on Section Headers and release
// sections that are marked as "discardable"
if not FinalizeSections(Module) then
Abort;
// TLS callbacks are executed BEFORE the main loading
if not ExecuteTLS(Module) then
Abort;
// get entry point of loaded library
if Module.Headers.OptionalHeader.AddressOfEntryPoint <> 0 then
begin
@DllEntry := Pointer(PByte(code) + Module.Headers.OptionalHeader.AddressOfEntryPoint);
// notify library about attaching to process
successfull := DllEntry(HINST(code), DLL_PROCESS_ATTACH, nil);
if not successfull then
begin
SetLastError(ERROR_DLL_INIT_FAILED);
Abort;
end;
Module.Initialized := True;
end;
Result := Module;
except
// cleanup
MemoryFreeLibrary(Module);
Exit;
end;
end;
function MemoryGetProcAddress(Module: TMemoryModule; const Name: PAnsiChar): Pointer; stdcall;
var
CodeBase: Pointer;
Idx: Integer;
i: DWORD;
nameRef: PDWORD;
ordinal: PWord;
exportDir: PIMAGE_EXPORT_DIRECTORY;
directory: PIMAGE_DATA_DIRECTORY;
temp: PDWORD;
mmodule: PMemoryModule;
begin
Result := nil;
mmodule := PMemoryModule(Module);
CodeBase := mmodule.CodeBase;
directory := GET_HEADER_DICTIONARY(mmodule, IMAGE_DIRECTORY_ENTRY_EXPORT);
// no export table found
if directory.Size = 0 then
begin
SetLastError(ERROR_PROC_NOT_FOUND);
Exit;
end;
exportDir := PIMAGE_EXPORT_DIRECTORY(PByte(CodeBase) + directory.VirtualAddress);
// DLL doesn't export anything
if (exportDir.NumberOfNames = 0) or (exportDir.NumberOfFunctions = 0) then
begin
SetLastError(ERROR_PROC_NOT_FOUND);
Exit;
end;
// search function name in list of exported names
nameRef := Pointer(PByte(CodeBase) + exportDir.AddressOfNames);
ordinal := Pointer(PByte(CodeBase) + exportDir.AddressOfNameOrdinals);
Idx := -1;
for i := 0 to exportDir.NumberOfNames - 1 do
begin
if StrComp(Name, PAnsiChar(PByte(CodeBase) + nameRef^)) = 0 then
begin
Idx := ordinal^;
Break;
end;
Inc(nameRef);
Inc(ordinal);
end;
// exported symbol not found
if (Idx = -1) then
begin
SetLastError(ERROR_PROC_NOT_FOUND);
Exit;
end;
// name <-> ordinal number don't match
if (DWORD(Idx) > exportDir.NumberOfFunctions) then
begin
SetLastError(ERROR_PROC_NOT_FOUND);
Exit;
end;
// AddressOfFunctions contains the RVAs to the "real" functions {}
temp := Pointer(PByte(CodeBase) + exportDir.AddressOfFunctions + Idx*4);
Result := Pointer(PByte(CodeBase) + temp^);
end;
procedure MemoryFreeLibrary(Module: TMemoryModule); stdcall;
var
i: Integer;
DllEntry: TDllEntryProc;
mmodule: PMemoryModule;
begin
if Module = nil then Exit;
mmodule := PMemoryModule(Module);
if mmodule.Initialized then
begin
// notify library about detaching from process
@DllEntry := Pointer(PByte(mmodule.CodeBase) + mmodule.Headers.OptionalHeader.AddressOfEntryPoint);
DllEntry(HINST(mmodule.CodeBase), DLL_PROCESS_DETACH, nil);
end;
if Length(mmodule.Modules) <> 0 then
begin
// free previously opened libraries
for i := 0 to mmodule.NumModules - 1 do
if mmodule.Modules[i] <> 0 then
FreeLibrary_Internal(mmodule.Modules[i]);
SetLength(mmodule.Modules, 0);
end;
if mmodule.CodeBase <> nil then
// release memory of library
VirtualFree(mmodule.CodeBase, 0, MEM_RELEASE);
HeapFree(GetProcessHeap(), 0, mmodule);
end;
end.
You can’t perform that action at this time.