AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi IMAGEBASE - wichtiges zu DLL und Co.
Tutorial durchsuchen
Ansicht
Themen-Optionen

IMAGEBASE - wichtiges zu DLL und Co.

Ein Tutorial von himitsu · begonnen am 29. Aug 2005 · letzter Beitrag vom 10. Dez 2009
Antwort Antwort
Benutzerbild von himitsu
himitsu
Registriert seit: 11. Okt 2003
Wenn man eine DLL erstellen möchte, dann steht man unter Anderem vor einer "schwierigen" Entscheidung:

"Wo um Himmels Willen soll ich meine DLL im Speicher ablegen?", oder so ähnlich.

Denn vollkommen willkürlich sollte/kann man diese nicht positionieren - vorallem nicht an der Standardadresse $00400000, denn dort liegt meistens schon die EXE rum.
Und dann wuseln bestimmt auch noch andere DLL's im selben Adressraum herum, welche entweder schon den gewünschten Bereich belegen, oder welche dann ebenfalls verschoben werden müssen, weil sich die eigene DLL dort breit gemacht hat.
Wenn man dann auch noch mehrere DLL's erstellt, welche gleichzeitig in der selben EXE integriert sind, wird es noch besser ... denn dort müßten natürlich all diese DLL's ihr eigenes Fleckchen finden.

Zu diesem Thema sei auch gleich mal die OH zitiert.
Für diejenigen, denen die OH noch unbekannt sein sollte, diese ist das Teil was aufgeht, wenn man die ersten Menüpunkte des Menüs "Hilfe" in Delphi anklickt.
Zitat von OH:
Image-Basisadresse

Syntax {$IMAGEBASE Zahl}

Die Direktive $IMAGEBASE definiert die Standardladeadresse für eine Anwendung, DLL oder ein Package. Das Argument Zahl muß ein 32-Bit-Intergerwert sein, der die Image-Basisadresse angibt. Zahl muß gleich oder größer sein als $00010000; die geringwertigen 16 Bit des Arguments werden ignoriert oder sollten Nullen sein. Zahl muß ein Vielfaches von 64K sein (die entsprechende Hexadezimalzahl muß also mit vier Nullen enden); ist das nicht der Fall, rundet der Compiler die Zahl auf das nächstkleinere Vielfache ab und gibt eine Meldung aus.

Wird ein Modul (Anwendung oder Bibliothek) in den Adressraum eines Prozesses geladen, versucht Windows, das Modul an dessen Standard-Image-Basisadresse abzulegen. Sollte das nicht möglich sein, weil die vorgegebene Adresse bereits belegt ist, wird das Modul an einer Adresse abgelegt, die Windows während der Laufzeit bestimmt (Adressverschiebung).
Für eine Änderung der Image-Basisadresse einer Anwendung gibt es so gut wie nie einen Grund. Dagegen sollten Sie für eine Bibliothek mit der Direktive $IMAGEBASE eine andere als die Standardadresse als Image-Basisadresse angeben, da die Standardadresse $00400000 wahrscheinlich immer belegt sein wird. Der empfohlene Adressbereich für DLL-Images liegt zwischen $40000000 und $7FFFFFFF. Adressen in diesem Bereich stehen unter Windows NT/2000 und Windows 95/98 immer für Prozesse zur Verfügung.

Sofern Windows eine DLL oder ein Package an der zugehörigen Image-Basisadresse ablegen konnte, wird die Ladezeit verringert, weil keine Anpassungen aufgrund von Adressverschiebungen erforderlich sind. Wenn zudem der vorgegebene Adressbereich in mehreren Prozessen, die die Bibliothek benutzen, verfügbar ist, können Teile des DLL-Image-Codes von den Prozessen gemeinsam benutzt werden, wodurch sich die Ladezeit verringert und weniger Arbeitsspeicher belegt wird.
Adressverschiebung:
Dabei werden nicht einfach nur die DLL-Daten verschoben, sondern es müssen auch noch alle "hardgecodeten" Pointer, far-Sprungaddressen, ... angepasst werden, da sich ja deren Ziele durch das Verschieben ändern.
Daher kann man sich bestimmt auch ganz gut vorstellen, daß man in solch einem Fall mit einigen Einbußen (Rechenzeit...) rechnen muß.

Es ist also schon "wichtig", daß man seinen DLL's ein freies Plätzchen sucht.

Zitat von Luckie:
Um die Adressen innerhalb eines Images zu verschieben, gibt es in jedem Image den sogenannten Realloctable. Es gibt Programme mit denen man diese Tabelle entfernen kann, um die Größe des Images zu reduzieren. Bei Exe-Dateien ist das meist kein Problem, da sie als erstes geladen werden und so die Baseaddress immer frei ist. Bei DLLs ist dies natürlich, wie du erklärt hast, nicht der Fall. Entfernt man aus einer DLL den Realloctable, kann Windows die neuen Adressen innerhalb der DLL nicht neu berechnen und die DLL kann nicht geladen werden.
Zur Definition von {$IMAGEBASE} seien nochmal erwähnt:
* die ImageBase ist ein 32-bit Pointer, welcher auf das Vielfache von 64 KB beschränkt ist.
$xxxx0000 < nur die xxxx sind veränderbar
* die möglichen Werte für die ImageBase sind:
$00010000 bis $7FFF0000
* es wird von Microsoft am Liebsten gesehn, wenn DLL's in folgendem Bereich abgelegt werden:
$40000000 und $7FFFFFFF

Leider ist dieses aber noch nicht allen bekannt und es gibt tatsächlich Viele, welche ihren DLL's noch nichtmal ein {$IMAGEBASE} spendieren - wo also die DLL immer "immer" im Adressbereich der EXE (ab $00400000) liegt und somit verschoben würde.

Hier ist schonmal eine erste kleine Liste von mehr, oder weniger wichtigen "DLL's".
Code:
$00400000..          Project
$00970000..$009C4FFF XercesXMLDom.dll
$009D0000..$00B7BFFF XercesLib.dll
$00D20000..$00D2AFFF BorlndMM.dll
$1A400000..$1A477FFF UrlMon.dll
$32600000..$32777FFF CC3260MT.dll
$41D00000..          FNS_UCC.dll
$51000000..$5104FFFF DDraw.dll
$5B0F0000..$5B123FFF UXTheme.dll
$5F0D0000..$5F195FFF OpenGL32.dll
$5F1A0000..$5F1B9FFF OLEPro32.dll
$62250000..$6226EFFF MAPI32.dll
$62E10000..$62E17FFF LPK.dll
$63000000..$63094FFF WinINet.dll
$68FC0000..$68FDDFFF GLU32.dll
$6DA00000..$6DA7BFFF DbgHelp.dll
$70D00000..$70E9FFFF GDIPlus.dll
$71950000..$71A33FFF ComCtl32.dll
$71A80000..$71A90FFF MPR.dll
$72F10000..$72F69FFF USP10.dll
$73B30000..$73B35FFF DCIMan32.dll
$73DC0000..$73DC2FFF LZ32.dll
$74CB0000..$74CD0FFF OLEDlg.dll
$76240000..$7624EFFF MSASN1.dll
$76260000..$762EAFFF Crypt32.dll
$76320000..$76324FFF MSImg32.dll
$76330000..$76349FFF IMM32.dll
$76350000..$76395FFF ComDlg32.dll
$76620000..$76704FFF SetupAPI.dll
$76730000..$76737FFF SHFolder.dll
$76AF0000..$76B1CFFF WinMM.dll
$76BB0000..$76BBAFFF PSAPI.dll
$76BF0000..$76C1AFFF WinTrust.dll
$76C50000..$76C71FFF ImageHlp.dll
$76E40000..$76E4CFFF RTUtils.dll
$76E70000..$76E99FFF TAPI32.dll
$770F0000..$7717AFFF OLEAut32.dll
$77180000..$77299FFF OLE32.dll
$772A0000..$77302FFF ShlWAPI.dll
$773A0000..$77B9DFFF Shell32.dll
$77BD0000..$77BD6FFF Version.dll
$77BE0000..$77C32FFF MSVCRT.dll
$77C40000..$77C7FFFF GDI32.dll
$77C90000..$77D04FFF RPCRT4.dll
$77D10000..$77D9CFFF User32.dll
$77DA0000..$77E39FFF ADVAPI32.dll
$77E40000..$77F36FFF Kernel32.dll
$77F40000..$77FEFFFF NTDLL.dll

Die folgende Prozedur ließt den Speicher der laufenden Anwendung aus
und speichert die erstellte Liste in einer Textdatei ab.
Diese ist für jene, die selber mal in ihrer Anwendung nachschauen wollen.

Als Anhang gibt es diese Prozedur auch nochmal in einer 'netten Unit verpackt.
Diese Unit muß als letztes in der Uses-Klausel der DPR eingetragen werden, da ja vor deren Aufruf auch alle anderen DLL's geladen werden müssten.
Und wenn man eine, oder mehrere DLL's dynamisch lädt, dann sollte man nach dem Laden dieser DLL's die Prozedur "LogModule" nochmals aufrufen.
Werden aber alle DLL's statisch eingebunden (was ja meistens der Fall ist), dann reicht es aus, wenn nur die Unit eingebunden wird, da "LogModule" im Initialization-Abschnitt ebenfalls schon einmal aufgerufen wird.
Delphi-Quellcode:
// © 1997-2005 by FNS Enterprize's™
// © 2003-2005 by himitsu @ Delphi-PRAXiS

Uses Windows, SysUtils; // Diese Units mit in der eigenen Uses-Klausel eintragen,
                         // sofern diese da noch nicht vorhanden sind.

Procedure LogModule;
  Var i, i2: Integer;
    F: TextFile;
    SystemInfo: TSystemInfo;
    MemoryBasicInformation: TMemoryBasicInformation;
    AllocationBase: Pointer;
    ModName: AnsiString;

  Begin
    AssignFile(F, ParamStr(0) + '.txt');
    Rewrite(F);
    VirtualQuery(Pointer(0), MemoryBasicInformation, SizeOf(MemoryBasicInformation));
    AllocationBase := MemoryBasicInformation.AllocationBase;
    i2 := 0;
    SetLength(ModName, MAX_PATH);
    SetLength(ModName, GetModuleFilenameA(THandle(AllocationBase), @ModName[1], MAX_PATH));
    GetSystemInfo(SystemInfo);
    i := SystemInfo.dwPageSize;
    Repeat
      VirtualQuery(Pointer(i), MemoryBasicInformation, SizeOf(MemoryBasicInformation));
      If MemoryBasicInformation.AllocationBase <> AllocationBase Then Begin
        If AllocationBase <> nil Then WriteLn(F, '$' + IntToHex(i2, 8) + '..$' + IntToHex(i - 1, 8) +
          ' ' + ExtractFileName(ModName));
        AllocationBase := MemoryBasicInformation.AllocationBase;
        i2 := i;
        SetLength(ModName, MAX_PATH);
        SetLength(ModName, GetModuleFilenameA(THandle(AllocationBase), @ModName[1], MAX_PATH));
      End;
      Inc(i, SystemInfo.dwPageSize);
    Until i <= 0;
    If AllocationBase <> nil Then WriteLn(F, '$' + IntToHex(i2, 8) + '..$7FFFFFFF ' +
      ExtractFileName(ModName));
    CloseFile(F);
  End;

// zum Schluß muß jetzt noch, "irgendwo" im Programm, die Prozedur "LogModule" aufgerufen werden.
PS: Als Bonus prüft die Prozedur in der Unit auch noch, ob die gefundene DLL mit einer der oben genannten DLL's und deren dort angegebenen ImageBase übereinstimmt.
Außerdem wird ebenfalls noch angegeben, ob die gefundene DLL verschoben wurde und wo sie eigentlich abgelegt werden wollte.

PSS: Auf die selbe Weiße könnte ein Programm auch nachprüfen, ob eine fremde DLL eingeschleußt wurde (DLL-Injektion).


Wenn jemand noch weitere "wichtige" DLL's und deren Addressbereich kennt, oder der Meinung ist, daß sich einige der oben genannten DLL's an einen anderen Plätzchen aufhalten, so möchte er/sie es uns bitte mitteilen.
Angehängte Dateien
Dateityp: pas logmoduleu_195.pas (6,7 KB, 48x aufgerufen)
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
 
11. Sep 2005, 12:54
Dieses Thema wurde von "Chakotay1308" von "Neuen Beitrag zur Code-Library hinzufügen" nach "Tutorials und Kurse" verschoben.
Eher ein Tutorial
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#3
  Alt 10. Dez 2009, 12:09
Mir ist grad aufgefallen, daß ich seit 'ner ganzen Weile eine EXE dazu hier rumliegen hab, welche andere Programme entsprechend ausließt und anzeigt.

Nja, hier isse also:
(ich dachte eigentlich, ich hätte es irgendwo mal mit hochgeladen )

Anbei auch ein Screenshot vom Firefox ... mal sehn wem auffällt, daß da irgendein Idiot versucht unmassen DLLs mit der selben ImageBase zu laden.
Miniaturansicht angehängter Grafiken
unbenannt_198.png  
Angehängte Dateien
Dateityp: exe imagebase_146.exe (471,0 KB, 39x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

 
Delphi 2006 Professional
 
#4
  Alt 10. Dez 2009, 12:20
Ich glaube, das lohnt den Aufwand nicht. Nach einem Servicepack oder Update oder Patch, können die DLLs schon wieder wo anders liegen.

Zitat:
Daher kann man sich bestimmt auch ganz gut vorstellen, daß man in solch einem Fall mit einigen Einbußen (Rechenzeit...) rechnen muß.

Es ist also schon "wichtig", daß man seinen DLL's ein freies Plätzchen sucht.
Ich glaube, die Einbussen sind so gering, dass dies nicht wirklich ins Gewicht fällt.

Aber es fehlt ein wichtiger Hinweis bei dir, wenn ich ihn nicht überlesen habe. Um die Adressen innerhalb eines Images zu verschieben, gibt es in jedem Image den sogenannten Realloctable. Es gibt Programme mit denen man diese Tabelle entfernen kann, um die Größe des Images zu reduzieren. Bei Exe-Dateien ist das meist kein Problem, da sie als erstes geladen werden und so die Baseaddress immer frei ist. Bei DLLs ist dies natürlich, wie du erklärt hast, nicht der Fall. Entfernt man aus einer DLL den Realloctable, kann Windows die neuen Adressen innerhalb der DLL nicht neu berechnen und die DLL kann nicht geladen werden.
Michael
  Mit Zitat antworten Zitat
Benutzerbild von Mithrandir
Mithrandir
 
#5
  Alt 10. Dez 2009, 12:20
Zitat:
---------------------------
Imagebase
---------------------------
Zugriffsverletzung bei Adresse 6A852A6B in Modul 'dbghelp.dll'. Lesen von Adresse 00000004.
---------------------------
OK
---------------------------
Ich hab SmallTune ausgewählt, OS ist Windows 7 32 bit... Passiert aber auch bei anderen Prozessen...
米斯蘭迪爾
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

 
Delphi 12 Athens
 
#6
  Alt 10. Dez 2009, 12:28
Ich muß zugeben, diese Anwendung wurde noch nicht "ausgiebig" unter Win7/Vista getestet.
Wollte sie eigentlich (wenn ich mal die etwas mehr Zeit frei hab) überarbeiten/neu schreiben.
Ist ja, wie man eventuell sieht, noch eine "uralte" Delphi 7-Anwendung.

Aber ganz im Ernst, ich wüßte jetzt nichtmal wo da ein Fehler in meinem Code sein sollte und schiebe erstmal die Schuld auf Delphi (oder vom wem diese böse DLL stammt) ... mal sehn, ob sich da schnell was ändern läßt.

@Luckie:
Die Windows-eigenen DLLs liegen vorwiegend in einem eigenem "Bereich" und da sollte man seine EXE/DLL eh nicht reinverlagern.
Aber es bringt erstmal grundsätzlich was, wenn man bei einer DLL nicht die selbe ImageBase nutzt, wie für seine Anwendung.
außerdem verschieben viele ihre Dateien nicht im RAM, nachdem sie einmal positioniert sind, also dürfte sich garnicht soviel oft ändern.
  Mit Zitat antworten Zitat
Assertor

 
Turbo C++
 
#7
  Alt 10. Dez 2009, 13:23
Hallo,

als Ergänzung, da sicherlich viele das nicht so mitbekommen haben: Delphi unterstützt natürlich auch ASLR und NX.

Michael Howard MSDN Blog

Seit dem D2007 Compiler ist auch gerade der Switch für ASLR interessant (Address Space Layout Randomization ab Vista):
{$DYNAMICBASE ON} Gruß Assertor
Frederik
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:37 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