Home > Delphi > Loading a DLL from memory

Loading a DLL from memory

November 25, 2014 Leave a comment Go to comments

Once in a while you come across a unit which is just fantastic, like how to load a dll from memory (read: loaded from a stream, a resource or even re-assembled from a text dump). I remember vividly looking for a way to load a DLL from memory ages ago, preferably a DLL i had stored in my application as a form-resource.

While I cannot for the life of me remember why this was so important at the time (probably wanted to embed some some third party library and register it in real-time), the question still remains: can a DLL be loaded from memory? And the answer apparently .. is yes.

Here is the code. Sadly I have no idea who wrote it, but I cannot take credit for this one.

Due to the messy output and overdone end else endif use my money is on a verbatim re-write of a C/C++ example somewhere.

Well, here it is!

unit uDllfromMem;

interface

uses sysutils, windows;

const
IMAGE_REL_BASED_HIGHLOW = 3;
IMAGE_ORDINAL_FLAG32 = DWORD($80000000);

type
PImageBaseRelocation = ^TImageBaseRelocation;

_IMAGE_BASE_RELOCATION = packed record
    VirtualAddress: DWORD;
    SizeOfBlock: DWORD;
end;

{$EXTERNALSYM _IMAGE_BASE_RELOCATION}
  TImageBaseRelocation = _IMAGE_BASE_RELOCATION;
  IMAGE_BASE_RELOCATION = _IMAGE_BASE_RELOCATION;
{$EXTERNALSYM IMAGE_BASE_RELOCATION}

PImageImportDescriptor = ^TImageImportDescriptor;
TImageImportDescriptor = packed record
  OriginalFirstThunk: dword;
  TimeDateStamp: dword;
  ForwarderChain: dword;
  Name: dword;
  FirstThunk: dword;
end;

PImageImportByName = ^TImageImportByName;
TImageImportByName = packed record
  Hint : WORD;
  Name : array[0..255] of Char;
end;

PImageThunkData = ^TImageThunkData;
TImageThunkData = packed record
  case integer of
  0 : (ForwarderString: PBYTE);
  1 : (FunctionPtr    : PDWORD);
  2 : (Ordinal        : DWORD);
  3 : (AddressOfData  : PImageImportByName);
end;

TDllEntryProc = function (hinstdll: THandle;
  fdwReason: DWORD;
  lpReserved: Pointer): BOOL; stdcall;

function memLoadLibrary(FileBase : Pointer) : Pointer;
function memGetProcAddress(Physbase : Pointer; NameOfFunction : String) : Pointer;
function memFreeLibrary(physbase : Pointer) : Boolean;

var
DllentryProc : TDLLEntryProc;

implementation

function GetSectionProtection(ImageScn: cardinal): cardinal;
begin
  Result := 0;
  if (ImageScn and IMAGE_SCN_MEM_NOT_CACHED) <> 0 then
  Result := Result or PAGE_NOCACHE;

  if (ImageScn and IMAGE_SCN_MEM_EXECUTE) <> 0 then
  begin
    if (ImageScn and IMAGE_SCN_MEM_READ)<> 0 then
    begin
      if (ImageScn and IMAGE_SCN_MEM_WRITE)<> 0 then
      Result := Result or PAGE_EXECUTE_READWRITE else
      Result := Result or PAGE_EXECUTE_READ
    end else
    if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
    Result := Result or PAGE_EXECUTE_WRITECOPY else
    Result := Result or PAGE_EXECUTE;
  end else
  if (ImageScn and IMAGE_SCN_MEM_READ)<> 0 then
  begin
    if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
    Result := Result or PAGE_READWRITE else
    Result := Result or PAGE_READONLY
  end else
  if (ImageScn and IMAGE_SCN_MEM_WRITE) <> 0 then
  Result := Result or PAGE_WRITECOPY else
  Result := Result or PAGE_NOACCESS;
end;

function memLoadLibrary(FileBase : Pointer) : Pointer;
var
pfilentheader : PIMAGENTHEADERS;
pfiledosheader: PIMAGEDOSHEADER;
pphysntheader : PIMAGENTHEADERS;
pphysdosheader: PIMAGEDOSHEADER;
physbase  : Pointer;
pphyssectionheader : PIMAGESECTIONHEADER;
i : Integer;
importsDir : PImageDataDirectory;
importsBase: Pointer;
importDesc :  PImageImportDescriptor;
importThunk: PImageThunkData;
dll_handle : Cardinal;
importbyname : pimageimportbyname;
relocbase : Pointer;
relocdata : PIMAGeBaseRElocation;
relocitem : PWORD;
reloccount : Integer;
dllproc : TDLLEntryProc;
begin
result := 0;

pfiledosheader := filebase;
pfilentheader  := Pointer(Cardinal(filebase) + pfiledosheader^._lfanew);

physbase := VirtualAlloc(Pointer(pfilentheader^.OptionalHeader.ImageBase),pfilentheader^.OptionalHeader.SizeOfImage,MEM_RESERVE,PAGE_READWRITE);
if Cardinal(physbase) = 0 Then begin
physbase := VirtualAlloc(0,pfilentheader^.OptionalHeader.SizeOfImage,MEM_RESERVE Or Mem_COMMIT,PAGE_READWRITE);
end;

CopyMemory(physbase,filebase,pfilentheader^.OptionalHeader.SizeOfHeaders);

pphysdosheader := physbase;
pphysntheader := Pointer(Cardinal(physbase) + pphysdosheader^._lfanew);
pphysntheader^.OptionalHeader.ImageBase := Cardinal(physbase);

pphyssectionheader := Pointer(Cardinal(pphysntheader) + SizeOf(TIMAGENTHEADERS));
for i := 0 To (pphysntheader^.FileHeader.NumberOfSections - 1) do begin
  if pphyssectionheader^.SizeOfRawData = 0 Then begin
    //keine raw data
    ZeroMemory(Pointer(Cardinal(physbase) + pphyssectionheader^.VirtualAddress),pphyssectionheader^.Misc.VirtualSize);
  end else begin
    //raw data vorhanden
    CopyMemory(Pointer(Cardinal(physbase) + pphyssectionheader^.VirtualAddress),Pointer(Cardinal(filebase) + pphyssectionheader^.PointerToRawData),pphyssectionheader^.SizeOfRawData);
  end;
  pphyssectionheader^.Misc.PhysicalAddress :=  Cardinal(physbase) + pphyssectionheader^.VirtualAddress;

  //next one please
  pphyssectionheader :=  Pointer(Cardinal(pphyssectionheader) + SizeOf(TIMAGESECTIONHEADER));
end;

importsBase := Pointer(Cardinal(physbase) + pphysntheader^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
importDesc := importsBase;
while (importDesc.Name) <> 0 Do begin
  dll_handle := LoadLibrary(pchar(Cardinal(physbase) + importdesc.Name));
  importDesc.ForwarderChain := dll_handle;
  importThunk := Pointer(Cardinal(physbase) +  importDesc.FirstThunk);
  while importThunk.Ordinal <> 0 Do begin
    importbyname := Pointer(Cardinal(physbase) + importThunk.Ordinal);
    //Später noch überprüfen ob OriginalFirstThunk = 0
    if (importThunk.Ordinal and IMAGE_ORDINAL_FLAG32) <> 0 Then
    begin //ordinal
      importThunk.FunctionPtr := GetProcaddress(dll_handle,pchar(importThunk.Ordinal and $ffff))
    end else begin //normal
      importThunk.FunctionPtr := GetProcAddress(dll_handle,importByname.name);
    end;
  //next one, please
  importThunk := Pointer(Cardinal(importThunk) + SizeOf(TIMAGETHUNKDATA));
  end;
//next one, please
importDesc := Pointer(Cardinal(importDesc) + sizeOf(TIMAGEIMPORTDESCRIPTOR));
end;

relocbase := Pointer(Cardinal(physbase)
  + pphysntheader.OptionalHeader.DataDirectory
  [IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
relocData := RElocbase;

while (Cardinal(relocdata) -  Cardinal(relocbase)) < pphysntheader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size do begin
  reloccount := trunc((relocdata.SizeOfBlock - 8) / 2);
  relocitem  := Pointer(Cardinal(relocdata) + 8);
  For i := 0 To (reloccount - 1) do
  begin
    if (relocitem^ shr 12) = IMAGE_REL_BASED_HIGHLOW Then begin
      Inc(PDWord(Cardinal(physbase) + relocdata.VirtualAddress
      + (relocitem^ and $FFF))^,(Cardinal(physbase) - pfilentheader.OptionalHeader.ImageBase));
    end;
    relocitem := Pointer(Cardinal(relocitem) + SizeOf(WORD));
  end;

  //next one please
  relocdata := Pointer(Cardinal(relocdata) + relocdata.SizeOfBlock);
end;

pphyssectionheader := Pointer(Cardinal(pphysntheader) + SizeOf(TIMAGENTHEADERS));
For i := 0 To (pphysntheader^.FileHeader.NumberOfSections - 1) do
begin
  VirtualProtect(Pointer(Cardinal(physbase)
    + pphyssectionheader^.VirtualAddress),pphyssectionheader^.Misc.VirtualSize,
  GetSectionProtection(pphyssectionheader.Characteristics),nil);
  pphyssectionheader := Pointer(Cardinal(pphyssectionheader)
    + SizeOf(TIMAGESECTIONHEADER));
end;

dllEntryproc := Pointer(Cardinal(physbase) + pphysntheader.OptionalHeader.AddressOfEntryPoint);
dllEntryproc(cardinal(physbase),DLL_PROCESS_ATTACH,nil);

result := physbase;
end;

function memGetProcAddress(Physbase : Pointer; NameOfFunction : String) : Pointer;
var
  pdosheader: PIMAGEDOSHEADER;
  pntheader : PIMAGENTHEADERS;
  pexportdir: PImageExportDirectory;
  i : Integer;
  pexportname : PDWORD;
  pexportordinal : PWORD;
  pexportFunction : PDWORD;
begin
  result := 0;
  pdosheader := physbase;
  pntheader := Pointer(Cardinal(physbase) + pdosheader._lfanew);

  pexportdir := Pointer(Cardinal(physbase)
    + pntheader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

  if pexportdir.NumberOfFunctions Or pexportdir.NumberOfNames  >0 then
  Begin
    pexportName    := Pointer(Cardinal(physbase) + Cardinal(pexportDir.AddressOfNames));
    pexportordinal := Pointer(Cardinal(physbase) + Cardinal(pexportDir.AddressOfNameOrdinals));
    pexportFunction:= Pointer(Cardinal(physbase) + Cardinal(pexportDir.AddressOfFunctions));

    For i := 0 To (pexportdir.NumberOfNames - 1) do
    begin
      if StrComp(pchar(Pointer(Cardinal(physbase) + pexportName^)),
      pchar(NameOfFunction)) = 0 Then
      begin
        result := pchar(Pointer(Cardinal(physbase) + pexportFunction^));
        break;
      end;

      //next one, please
      Inc(pexportFunction);
      Inc(pexportName);
      Inc(pexportOrdinal);
    end;
  end;
end;

function memFreeLibrary(physbase : Pointer) : Boolean;
begin
  try
    result := true;
    dllEntryProc(Cardinal(physbase),DLL_PROCESS_DETACH,0);
    VirtualFree(physbase,0,MEM_RELEASE);
  except
    result := false;
  end;
end;

end.
Advertisements
  1. November 29, 2014 at 11:18 pm

    I had searched a lot on internet but not found the way to successfully implement in my experiment program.

    I want to link a static library (.a) file in my Lazarus/Linux program.
    This is a “rtl.a” file which is just a bunch of compressed .o static files such as (TypInfo.o, Math.o, Variants.o, etc.). This static library (rtl.a file) is used by the old Delphi/Kylix. 🙂

    I discovered that I can add a directive {$LINK rtl.a} in my lazarus/Linux program, and everything is fine, It seems that link the object, my program is successfully compiled!

    I have the crazy idea to to use an external function that resides in this static library (rtl.a).
    I guess I could use one of those external functions that resides in a object file (TypInfo.o or Math.o) f.i, but I couldn’t find out a way to perform this.

    I need to declare a function to use it, something like this:

    function Max(const A, B: Integer): Integer; cdecl;
    external name ‘FVCOMMON_$$_MAX$LONGINT$LONGINT$$LONGINT’;

    Sadly I have no idea how to use/declare an external function, any idea?

  2. November 29, 2014 at 11:19 pm

    I had searched a lot on internet but not found the way to successfully implement in my experiment
    program. Someone please help me on this.

    Hi, I want to link a static library (.a) file in my Lazarus/Linux program.
    This is a “rtl.a” file which is just a bunch of compressed .o static files such as
    (TypInfo.o, Math.o, Variants.o, etc.). This static library (rtl.a file) is used by the old Delphi/Kylix.

    I discovered that I can add a directive {$LINK rtl.a} in my lazarus/Linux program, and everything
    is fine, It seems that link the object, my program is successfully compiled!

    I have the crazy idea to to use an external function that resides in this static library (rtl.a).
    I guess I could use one of those external functions that resides in a object file (TypInfo.o or Math.o) f.i,
    but I couldn’t find out a way to perform this.

    I have to declare a function to use it, something like this:

    function Max(const A, B: Integer): Integer; cdecl;
    external name ‘FVCOMMON_$$_MAX$LONGINT$LONGINT$$LONGINT’;

    Sadly I have no idea how to use/declare an external function, any idea?

  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: