Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   DLL aus RES in Speicher laden und direkt verwenden. (https://www.delphipraxis.net/153460-dll-aus-res-speicher-laden-und-direkt-verwenden.html)

Schorschi5566 3. Aug 2010 21:14

DLL aus RES in Speicher laden und direkt verwenden.
 
Hallo DP,

in diesem Thread haben Himitsu und ich schon das Thema besprochen.

Es geht darum eine DLL direkt aus einer RES in den Speicher zu laden und dort zu verwenden. Der Umweg über das Filesystem würde also entfallen.

Hier habe ich das Thema gefunden und mal ausprobiert.

Es funktioniert fast. :-D

Mit der libmysql.dll mit der ich es getestet habe, schafft er die ersten paar Funktionen aber dann scheppert's.

Code:
---------------------------
Zugriffsverletzung bei Adresse 776D8C19 in Modul 'ntdll.dll'. Schreiben von Adresse 00000014.
---------------------------
Ich habe folgenden Code aus obiger Quelle verwendet, der ja angeblich D2010-tauglich sein soll.

Delphi-Quellcode:
function BTMemoryLoadLibary(fp_data: Pointer;
  const f_size: int64): PBTMemoryModule;
var
  lp_result: PBTMemoryModule;
  l_dos_header: TImageDosHeader;
  l_old_header: TImageNtHeaders;
  l_code, l_headers: Pointer;
  l_locationdelta: Cardinal;
  lp_DllEntry: PDllEntryProc;
  l_successfull: boolean;
begin
  lp_result := nil;
  Result := nil;
  try
    CopyMemory(@l_dos_header, fp_data, SizeOf(_IMAGE_DOS_HEADER));
    if (l_dos_header.e_magic <> IMAGE_DOS_SIGNATURE) then
    begin
      lastErrStr := 'BTMemoryLoadLibary: dll dos header is not valid';
      exit;
    end;
    CopyMemory(@l_old_header, IncF(fp_data, l_dos_header._lfanew),
      SizeOf(_IMAGE_NT_HEADERS));
    if l_old_header.Signature <> IMAGE_NT_SIGNATURE then
    begin
      lastErrStr := 'BTMemoryLoadLibary: IMAGE_NT_SIGNATURE is not valid';
      exit;
    end;
    // reserve memory for image of library
    l_code := VirtualAlloc(Pointer(l_old_header.OptionalHeader.ImageBase),
      l_old_header.OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE);
    if l_code = nil then
      // try to allocate memory at arbitrary position
      l_code := VirtualAlloc(nil, l_old_header.OptionalHeader.SizeOfImage,
        MEM_RESERVE, PAGE_READWRITE);
    if l_code = nil then
    begin
      lastErrStr := 'BTMemoryLoadLibary: VirtualAlloc failed';
      exit;
    end;
    // alloc space for the result record
    lp_result := PBTMemoryModule(HeapAlloc(GetProcessHeap(), 0,
        SizeOf(TBTMemoryModule)));
    lp_result^.codeBase := l_code;
    lp_result^.numModules := 0;
    lp_result^.modules := nil;
    lp_result^.initialized := false;
    // xy: is it correct to commit the complete memory region at once?
    // calling DllEntry raises an exception if we don't...
    VirtualAlloc(l_code, l_old_header.OptionalHeader.SizeOfImage, MEM_COMMIT,
      PAGE_READWRITE);
    // commit memory for headers
    l_headers := VirtualAlloc(l_code,
      l_old_header.OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE);
    // copy PE header to code
    CopyMemory(l_headers, fp_data,
      (UInt64(l_dos_header._lfanew)
          + l_old_header.OptionalHeader.SizeOfHeaders));
    lp_result^.headers := PImageNtHeaders
      (UInt64(l_headers) + l_dos_header._lfanew);
    // update position
    lp_result^.headers^.OptionalHeader.ImageBase := UInt64(l_code);
    // copy sections from DLL file block to new memory location
    CopySections(fp_data, l_old_header, lp_result);
    // adjust base address of imported data
    l_locationdelta := Cardinal
      (UInt64(l_code) - l_old_header.OptionalHeader.ImageBase);
    if l_locationdelta <> 0 then
      PerformBaseRelocation(lp_result, l_locationdelta);
    // load required dlls and adjust function table of imports
    if not BuildImportTable(lp_result) then
    begin
      lastErrStr := lastErrStr + ' BTMemoryLoadLibary: BuildImportTable failed';
      Abort;
    end;
    // mark memory pages depending on section headers and release
    // sections that are marked as "discardable"
    FinalizeSections(lp_result);
    // get entry point of loaded library
    if (lp_result^.headers^.OptionalHeader.AddressOfEntryPoint) <> 0 then
    begin
      lp_DllEntry := Pointer
        (UInt64(l_code)
          + lp_result^.headers^.OptionalHeader.AddressOfEntryPoint);
      if lp_DllEntry = nil then
      begin
        lastErrStr := 'BTMemoryLoadLibary: Get DLLEntyPoint failed';
        Abort;
      end;
      l_successfull := TDllEntryProc(lp_DllEntry)(UInt64(l_code),
        DLL_PROCESS_ATTACH, nil);
      if not l_successfull then
      begin
        lastErrStr := 'BTMemoryLoadLibary: Can''t attach library';
        Abort;
      end;
      lp_result^.initialized := true;
    end;
  except
    BTMemoryFreeLibrary(lp_result);
    exit;
  end;
  Result := lp_result;
end;

function BTMemoryGetProcAddress(fp_module: PBTMemoryModule;
  const fp_name: PChar): Pointer;
var
  l_idx: Integer;
  l_i: DWORD;
  l_nameRef: PDWORD;
  l_ordinal: PWord;
  l_exports: PImageExportDirectory;
  l_directory: PImageDataDirectory;
begin
  Result := nil;
  l_idx := -1;
  l_directory := GetHeaderDictionary(fp_module, IMAGE_DIRECTORY_ENTRY_EXPORT);
  if l_directory^.Size = 0 then
  begin
    lastErrStr := 'BTMemoryGetProcAddress: no export table found';
    exit;
  end;
  l_exports := IncF(fp_module^.codeBase, l_directory^.VirtualAddress);
  if ((l_exports^.NumberOfNames = 0) or (l_exports^.NumberOfFunctions = 0)) then
  begin
    lastErrStr := 'BTMemoryGetProcAddress: DLL doesn''t export anything';
    exit;
  end;
  // search function name in list of exported names
  l_nameRef := IncF(fp_module^.codeBase, l_exports^.AddressOfNames);
  l_ordinal := IncF(fp_module^.codeBase, l_exports^.ADDressOfNameOrdinals);
  for l_i := 0 to l_exports^.NumberOfNames - 1 do
  begin
    if StrComp(fp_name, PAnsiCharToPChar(IncF(fp_module^.codeBase, l_nameRef^))
      ) = 0 then
    begin
      l_idx := l_ordinal^;
      break;
    end;
    Inc(l_nameRef);
    Inc(l_ordinal);
  end;
  if (l_idx = -1) then
  begin
    lastErrStr := 'BTMemoryGetProcAddress: exported symbol not found';
    exit;
  end;
  if (UInt64(l_idx) > l_exports^.NumberOfFunctions - 1) then
  begin
    lastErrStr :=
      'BTMemoryGetProcAddress: name <-> ordinal number don''t match';
    exit;
  end;
  // AddressOfFunctions contains the RVAs to the "real" functions
  Result := IncF(fp_module^.codeBase,
    PUInt64(IncF(fp_module^.codeBase, IncF(l_exports^.AddressOfFunctions,
          l_idx * 4)))^);
end;
Zum Debugging: Die AV kommt bei mir bei mysql_real_connect, es wurden also schon einige Funktionen erfolgreich durchlaufen (mysql_get_client_version, mysql_init, mysql_ssl_set). mysql_real_connect wird aber von BTMemoryGetProcAddress auch noch gefunden. Erst beim Aufruf von mysql_real_connect passiert's.

Hat jemand eine funktionierende Variante oder sieht einen offensichtlichen Fehler im Code?


Grüße,
Uwe

P.S.: Hat sich erledigt. Jetzt funktioniert es. ;)

Zacherl 4. Aug 2010 01:42

AW: DLL aus RES in Speicher laden und direkt verwenden.
 
Wo lag der Fehler? Habe selbst mal versucht so eine funktion zu implementieren. Funktioniert auch wunderbar nur bei der ntdll.dll gibts ab und an auch mal ne Exception. Was mir aufgefallen ist, ist dass in deinem Code die Relocations gar nicht geparst werden.

Schorschi5566 4. Aug 2010 09:03

AW: DLL aus RES in Speicher laden und direkt verwenden.
 
Hallo Zacherl,

es lag daran, dass ich nicht abgefangen hatte, dass BTMemoryLoadLibary mehrfach aufgerufen werden konnte.

Wenn die DLL nur einmal im Speicher ist, funktioniert es bisher einwandfrei. :D

Zitat:

Zitat von Zacherl
Was mir aufgefallen ist, ist dass in deinem Code die Relocations gar nicht geparst werden.

Müsste man mal Martin Offenwanger fragen, der der Autor der Delphiversion ist.

Für D2010 habe ich nur einen Cast ändern müssen.

Ansonsten habe ich nur noch auf die aktuellste Version umgestellt. Die gibt es hier.


Grüße,
Uwe

[EDIT]Habe mal die verwendete Version ins erste Posting gestellt...[/EDIT]

Schorschi5566 5. Aug 2010 20:13

AW: DLL aus RES in Speicher laden und direkt verwenden.
 
Kann mir jemand sagen, wie man die "Offene Frage"-Markierung weg bekommt?

Danke, schonmal. ;)

himitsu 6. Aug 2010 07:25

AW: DLL aus RES in Speicher laden und direkt verwenden.
 
Schau mal nach, ob sich nicht da oben in den Themen-Optionen was versteckt,
ansonsten in den erweiterten Beitragseditor (oder 'nen neuen Beitrag/Post erstellen) und dann da unten das Häckchen wegmachen.

Schorschi5566 6. Aug 2010 21:59

AW: DLL aus RES in Speicher laden und direkt verwenden.
 
Hmm, jetzt steht oben, dass es eine offene Frage von Dir ist. :lol:

Mal sehen, was jetzt passiert... :roll:

[EDIT]Ahh, hat geklappert...danke![/EDIT]


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:03 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz