Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi DLL-EntryPoint mit Funktion tauschen? (https://www.delphipraxis.net/136609-dll-entrypoint-mit-funktion-tauschen.html)

Aphton 4. Jul 2009 12:37

Re: DLL-EntryPoint mit Funktion tauschen?
 
Ich würd dir die "CFF Explorer" Software empfehlen; sie gibt dir einen guten Einblick in das Innenleben einer PE Datei

MfG

himitsu 4. Jul 2009 12:49

Re: DLL-EntryPoint mit Funktion tauschen?
 
@Apollonius: uffff, na dann mal sehn ob ich das mit dem IMAGE_DATA_DIRECTORY auch noch hinbekomm :freak:
(aber aktuell geht es ja erstmal)

Was ich mich dann aber noch frage ist, gibt es dann 2 Exporttabellen, welche ich ändern muß, oder sind Beides die "Selben" (also die in .edata und IMAGE_DATA_DIRECTORY)



Ohhh, na dann hi MrEmbreD :hi:

Nja, cryptisch war's ja nicht ... nur etwas "eigenartig" berechnet. :mrgreen:
Aber hatte ja dennoch durch deinen Code den Weg gefunden und dank dem Hinweis von Apollonius wird nun auch die Prozedurliste ausgelesen.

jetzt muß ich nur noch rausbekommen, ob IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoin t auch als RVA definiert ist,
oder ob ich mir das auch noch umrechnen muß.

Delphi-Quellcode:
Program himXML_DLL_Modifizierer;

{$APPTYPE CONSOLE}

Uses Windows, SysUtils;

Type TSectionName = packed Array[0..IMAGE_SIZEOF_SHORT_NAME-1] of AnsiChar;

Var N:             String;
  H, M:            THandle;
  DOSHeader:       ^IMAGE_DOS_HEADER;
  NTHeader:        ^IMAGE_NT_HEADERS;
  SectionHeader:   ^IMAGE_SECTION_HEADER;
  ExportDirectory: ^IMAGE_EXPORT_DIRECTORY;
  Names:           PCardinal;
  InitProcedure, i: Integer;

Procedure WriteLn(Const S: String);
  Var OEMBuffer: Array[0..2047] of AnsiChar;

  Begin
    CharToOem(PChar(S), @OEMBuffer);
    System.WriteLn(OEMBuffer);
  End;

Procedure WriteError(Const S: String);
  Var S2: String;

  Begin
    S2 := SysErrorMessage(GetLastError);
    WriteLn(S + ' >> ' + S2);
  End;

Function RVA2RealPointer(RVA: LongWord): Pointer;
  Var
    SectionHeader2: ^IMAGE_SECTION_HEADER;
    i2:            Integer;

  Begin
    Result := nil;
    SectionHeader := Pointer(Integer(NTHeader) + SizeOf(IMAGE_NT_HEADERS));
    i2 := 0;
    While i2 < NTHeader.FileHeader.NumberOfSections do Begin
      If (RVA < SectionHeader.VirtualAddress + SectionHeader.SizeOfRawData)
          and (RVA >= SectionHeader.VirtualAddress) Then
        If Result <> nil Then Begin
          Result := Pointer(1);
          WriteLn(' found more than one sections that includet this RVA');
        End Else Result := Pointer(Integer(DOSHeader) + SectionHeader.PointerToRawData
          - SectionHeader.VirtualAddress + RVA);
      Inc(SectionHeader);
      Inc(i2);
    End;
    If Cardinal(Result) = 1 Then Result := nil;
  End;

Begin
  Try
    N := ExtractFilePath(ParamStr(0)) + 'himXML_DLL.dll';
    WriteLn('open "' + N + '"');
    H := CreateFile(PChar(N), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
    If H <> INVALID_HANDLE_VALUE Then Begin
      WriteLn('load file data');
      M := CreateFileMapping(H, nil, PAGE_READWRITE, 0, 0, nil);
      DOSHeader := MapViewOfFile(M, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, 0);
      If (M <> 0) and Assigned(DOSHeader) Then Begin
        WriteLn('check IMAGE_DOS_HEADER');
        If DOSHeader.e_magic = IMAGE_DOS_SIGNATURE Then Begin
          WriteLn('search and check IMAGE_NT_HEADERS');
          NTHeader := Pointer(Integer(DOSHeader) + DOSHeader._lfanew);
          If NTHeader.Signature = IMAGE_NT_SIGNATURE Then Begin
            WriteLn('search export section');
            SectionHeader := Pointer(Integer(NTHeader) + SizeOf(IMAGE_NT_HEADERS));
            ExportDirectory := nil;
            i := 0;
            While i < NTHeader.FileHeader.NumberOfSections do Begin
              WriteLn(' section "' + TSectionName(SectionHeader.Name) + '" found');
              If TSectionName(SectionHeader.Name) = '.edata' Then
                If ExportDirectory <> nil Then Begin
                  ExportDirectory := Pointer(1);
                  WriteLn(' found more than one .edata sections');
                End Else ExportDirectory := Pointer(Integer(DOSHeader) + SectionHeader.PointerToRawData);
              Inc(SectionHeader);
              Inc(i);
            End;
            If Cardinal(ExportDirectory) > 1 Then Begin
              WriteLn('search the init procedure');
              Names := RVA2RealPointer(LongWord(ExportDirectory.AddressOfNames));
              InitProcedure := 0;
              i := -1;
              While i < ExportDirectory.NumberOfNames do Begin
                WriteLn(' procedure "' + PAnsiChar(RVA2RealPointer(Names^)) + '" found');
                If PAnsiChar(RVA2RealPointer(Names^)) = 'initDLL' Then
                  If InitProcedure <> -1 Then Begin
                    InitProcedure := -2;
                    WriteLn(' found more than one init procedures');
                  End Else InitProcedure := i;
                Inc(Names);
                Inc(i);
              End;
              If InitProcedure >= 0 Then Begin


                If NTHeader.OptionalHeader.AddressOfEntryPoint <> 0 Then ;


                WriteLn('OK');
              End Else WriteError('not found');
            End Else WriteLn('not found');
          End Else WriteError('not found');
        End Else WriteError('not found');
        UnmapViewOfFile(DOSHeader);
      End Else WriteError('can''t load');
      CloseHandle(M);
    End Else WriteError('no access to file or not exists');
    CloseHandle(H);
  Except
    On E:Exception do WriteLn(E.Classname + ': ' + E.Message);
  End;
  ReadLn;
End.

Apollonius 4. Jul 2009 12:55

Re: DLL-EntryPoint mit Funktion tauschen?
 
Das Export-Directory liegt normalerweise am Anfang einer Section namens .edata. Der Loader sucht aber nicht nach der Section, sondern nach dem Directory. Der "richtige" Weg verwendet also das Directory; das ist schneller als das Iterieren durch die Sections und funktioniert auch, wenn ein Linker das Export-Directory nicht an den Anfang einer Section namens .edata legt.

himitsu 4. Jul 2009 13:05

Re: DLL-EntryPoint mit Funktion tauschen?
 
Nein, das ist nicht schneller. :tongue:

Denn wie ich grad mitbekommen habe, stehen die Adressen in diesem Directory als RVA drin
und demnach muß ich dennoch die Sektionen durchgehn und schauen wo es denn nun wirklich drin ist.

Aber da ich nun eine RVA2Pointer-Funktion hab und über das Directory vom Namen unabhängig bin, hab ich das Directory jetzt verwendet.

Delphi-Quellcode:
Program himXML_DLL_Modifizierer;

{$APPTYPE CONSOLE}

Uses Windows, SysUtils;

Type TSectionName = packed Array[0..IMAGE_SIZEOF_SHORT_NAME-1] of AnsiChar;

Var N:             String;
  H, M:            THandle;
  DOSHeader:       ^IMAGE_DOS_HEADER;
  NTHeader:        ^IMAGE_NT_HEADERS;
  SectionHeader:   ^IMAGE_SECTION_HEADER;
  ExportDirectory: ^IMAGE_EXPORT_DIRECTORY;
  Names:           PCardinal;
  InitProcedure, i: Integer;

Procedure WriteLn(Const S: String);
  Var OEMBuffer: Array[0..2047] of AnsiChar;

  Begin
    CharToOem(PChar(S), @OEMBuffer);
    System.WriteLn(OEMBuffer);
  End;

Procedure WriteError(Const S: String);
  Var S2: String;

  Begin
    S2 := SysErrorMessage(GetLastError);
    WriteLn(S + ' >> ' + S2);
  End;

Function RVA2RealPointer(RVA: LongWord): Pointer;
  Var
    SectionHeader2: ^IMAGE_SECTION_HEADER;
    i2:            Integer;

  Begin
    Result := nil;
    SectionHeader := Pointer(Integer(NTHeader) + SizeOf(IMAGE_NT_HEADERS));
    i2 := 0;
    While i2 < NTHeader.FileHeader.NumberOfSections do Begin
      If (RVA < SectionHeader.VirtualAddress + SectionHeader.SizeOfRawData)
          and (RVA >= SectionHeader.VirtualAddress) Then
        If Result <> nil Then Begin
          Result := Pointer(1);
          WriteLn(' found more than one sections that includet this RVA');
        End Else Result := Pointer(Integer(DOSHeader) + SectionHeader.PointerToRawData
          - SectionHeader.VirtualAddress + RVA);
      Inc(SectionHeader);
      Inc(i2);
    End;
    If Cardinal(Result) = 1 Then Result := nil;
  End;

Begin
  Try
    N := ExtractFilePath(ParamStr(0)) + 'himXML_DLL.dll';
    WriteLn('open "' + N + '"');
    H := CreateFile(PChar(N), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
    If H <> INVALID_HANDLE_VALUE Then Begin
      WriteLn('load file data');
      M := CreateFileMapping(H, nil, PAGE_READWRITE, 0, 0, nil);
      DOSHeader := MapViewOfFile(M, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, 0);
      If (M <> 0) and Assigned(DOSHeader) Then Begin
        WriteLn('check IMAGE_DOS_HEADER');
        If DOSHeader.e_magic = IMAGE_DOS_SIGNATURE Then Begin
          WriteLn('search and check IMAGE_NT_HEADERS');
          NTHeader := Pointer(Integer(DOSHeader) + DOSHeader._lfanew);
          If NTHeader.Signature = IMAGE_NT_SIGNATURE Then Begin
            WriteLn('search export section');
            //SectionHeader := Pointer(Integer(NTHeader) + SizeOf(IMAGE_NT_HEADERS));
            //ExportDirectory := nil;
            //i := 0;
            //While i < NTHeader.FileHeader.NumberOfSections do Begin
            //  WriteLn(' section "' + TSectionName(SectionHeader.Name) + '" found');
            //  If TSectionName(SectionHeader.Name) = '.edata' Then
            //    If ExportDirectory <> nil Then Begin
            //      ExportDirectory := Pointer(1);
            //      WriteLn(' found more than one .edata sections');
            //    End Else ExportDirectory := Pointer(Integer(DOSHeader) + SectionHeader.PointerToRawData);
            //  Inc(SectionHeader);
            //  Inc(i);
            //End;
            {}ExportDirectory := RVA2RealPointer(NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
            If Cardinal(ExportDirectory) > 1 Then Begin
              WriteLn('search the init procedure');
              Names := RVA2RealPointer(LongWord(ExportDirectory.AddressOfNames));
              InitProcedure := -1;
              i := 0;
              While i < ExportDirectory.NumberOfNames do Begin
                WriteLn(' procedure "' + PAnsiChar(RVA2RealPointer(Names^)) + '" found');
                If PAnsiChar(RVA2RealPointer(Names^)) = 'initDLL' Then
                  If InitProcedure <> -1 Then Begin
                    InitProcedure := -2;
                    WriteLn(' found more than one init procedures');
                  End Else InitProcedure := i;
                Inc(Names);
                Inc(i);
              End;
              If InitProcedure >= 0 Then Begin


                If NTHeader.OptionalHeader.AddressOfEntryPoint <> 0 Then ;


                WriteLn('OK');
              End Else WriteError('not found');
            End Else WriteLn('not found');
          End Else WriteError('not found');
        End Else WriteError('not found');
        UnmapViewOfFile(DOSHeader);
      End Else WriteError('can''t load');
      CloseHandle(M);
    End Else WriteError('no access to file or not exists');
    CloseHandle(H);
  Except
    On E:Exception do WriteLn(E.Classname + ': ' + E.Message);
  End;
  ReadLn;
End.
Aber jetzt muß ich dennoch testen, ob der EntryPoint ebenfalls als RVA definiert ist,
aber bei einem Wert von 213956 ( $000343C4 ) geh ich mal stark davon auß und müßte jetzt also nur noch die beiden Werte austauschen :)

brechi 4. Jul 2009 13:13

Re: DLL-EntryPoint mit Funktion tauschen?
 
Wenn du das manuell machen willst sollte das Programm lordpe reichen.
Ansonsten solltest du halt mal in meine Collection schauen da ist viel code mit der man auf die ExportSection und den EntryPoint zugreiften kann.

Eine alterantive Möglichkeit wäre die exportierte Funktion in die TLS Tabelle einzutragen. Dann wird diese Funktion vor DLL-Main aufgrufen. D.h. du musst nichts patchen (Entrypoint ändern). Da Delphi automatisch die TLS Tabelle erzeugt muss du die absolute Adresse nur hinzufügen.

Einfach in die .rData Section + 0x10 die absolute Exportaddresse eintragen.

himitsu 4. Jul 2009 13:49

Re: DLL-EntryPoint mit Funktion tauschen?
 
Nja, ich wollte mir ein Programm schreiben, welches nach dem Kompilieren die DLL automatisch patcht.

Und vor DLL-Main bringt mir nicht viel, da ich ja nach EXE-Main benötige (praktisch so als würde die DLL dynamisch und nicht statisch geladen :nerd: )


Sooo, hab grad mal einige TestProjekte erstellt.

EXE und DLL funktionieren erstmal normal:
beim Start der EXE kommt "EntryPoint is called"
und beim Aufruf der Prozedur "InitDLL is called",
also soweit alles OK.

Das Patchen geht (anscheinend) auch.

Nur dann der Test mit der geänderten DLL:
beim Start der Exe kommt (wie erhofft) "InitDLL is called",
wonach der Tausch also funktioniert hat :firejump:

blos zufrüh gefreut, denn beim Aufruf der Prozedur (welche den alten EntryPoint darstellt), knallt es :cry:

Delphi-Quellcode:
---------------------------
Benachrichtigung über Debugger-Problem
---------------------------
Im Projekt I:\Software+Hilfe\_Komponnten\himXML\DLLs\Test.exe sind zu viele auseinanderfolgende Exceptions aufgetreten: ''Zugriffsverletzung bei 0x00343700: Lesen von Adresse 0x01371e34''. Prozess wurde angehalten. Mit Einzelne Anweisung oder Start fortsetzen.
---------------------------
OK  Hilfe  
---------------------------
Konnte jetzt keine Infos finden, ob die Aufrufkonvention stimmt (aber das war doch ohne Parameter egal), also wird der EntryPoint wohl irgendwelche Parameter haben wollen? :gruebel:




[add]
ich glaub ich hab da was

http://msdn.microsoft.com/en-us/library/ms682583.aspx

mal sehn was passiert, wenn ich diese Werte zwischenspeicher und dem alten EntryPoint übergebe ...

[edit]
Anhang gelöscht (geht eh nicht)

himitsu 4. Jul 2009 17:28

Re: DLL-EntryPoint mit Funktion tauschen?
 
Arg, ich hatte eben meinen Testcode an einer DLL mit 2 Export-Funktionen getestet und da mußte ich feststellen, daß die Adressen in IMAGE_EXPORT_DIRECTORY.AddressOfFunctions nicht der Reinfolge in IMAGE_EXPORT_DIRECTORY.AddressOfNames entsprechen. :shock:

Hatte mich die ganze Zeit gewundert, warum die falsche Prozedur aufgerufen wird. :wall:

Nja, hab den "Index" in .AddressOfFunctions erstmal manuell angepaßt, um den Rest auszuprobieren, aber der läuft auch nicht so, wie er soll.

Die uallC hab ich grad durchgesehn und bin mein Problem bezüglich nicht wirklich schlauer geworden. :cry:


Bin jetzt erstmal am debuggen und versuch rauszufinden, warum es nicht läuft
und dann muß ich noch rausfinden, warum die Reihenfolge nicht stimmt.



Am Ende bleiben mir noch 2 Möglichkeiten:
* eine Dummy-DLL, welche als erstes statisch geladen wird
und wo ich dann im Nachhinein die eigentliche DLL dynamisch lade und die Imports der EXE mit den Verweisen zur neuen DLL hooke.

* alles aufgeben (dabei find ich die Objekte mit externen Funktionen echt geil) und es doch ganz anders machen :?


[add]
den ersten Fehler hab ich gefunden ... wie kann ich auch in eine ReadOnly MMF reinschreiben wollen :wall:


[edit]
Anhang gelöscht

himitsu 4. Jul 2009 19:12

Re: DLL-EntryPoint mit Funktion tauschen?
 
hier mal mein aktueller Versuch:

Leider hänge ich hierbei wegen was anderem (mach ich gleich 'nen anderen Thread dazu auf)
und zwar wird die MMF einfach nicht wiedergefunden. (existiert angeblich nicht)

Und das problem mit der Reihenfolge der beiden Namen- und Funktions-Tabellen hab ich auch noch nicht gelöst.

[edit]
anhang gelöscht ... nicht daß sich wer wundert

Apollonius 4. Jul 2009 19:53

Re: DLL-EntryPoint mit Funktion tauschen?
 
Die Positionen in den AddressOfFunctions- und AddressOfNames-Tabellen werden durch die AddressOfNameOrdinals-Tabelle vermittelt: Der i-te Name in der AddressOfNames-Tabelle gehört zum NameOrdinals[i]-ten Eintrag in der AddressOfFunctions-Tabelle.

himitsu 4. Jul 2009 20:00

Re: DLL-EntryPoint mit Funktion tauschen?
 
Zitat:

Zitat von Apollonius
Die Positionen in den AddressOfFunctions- und AddressOfNames-Tabellen werden durch die AddressOfNameOrdinals-Tabelle vermittelt: Der i-te Name in der AddressOfNames-Tabelle gehört zum NameOrdinals[i]-ten Eintrag in der AddressOfFunctions-Tabelle.

OK, das schau ich mir mal an ... dachte einfach in AddressOfNameOrdinals stehen die IndexWerte der Funktionen, welche man früher mal statt der Namen verwendete :oops:


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:31 Uhr.
Seite 2 von 3     12 3      

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