Delphi-PRAXiS

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)

himitsu 3. Jul 2009 23:17


DLL-EntryPoint mit Funktion tauschen?
 
Die Frage ist ganz einfach:

(Wie) Kann kann man den DLL-EntryPoint mit einer Exportfunktion tauschen?


Der Hintergrund ist eben so einfach:

Ich möchte praktisch, daß z.B. Unit-Initialisierungen etwas später vorgenommen werden,
speziell die von der SysUtils und Co.

Es handelt sich um eine statisch verlinkte DLL und ich hätt gern, daß diese Initialisierungen erst nach den Intitialisierungen der EXE geschehen (indem ich's dann manuell über eine "Init"-Prozedur aufrufe).

Ansonsten müßte ich Einiges mehr umstellen und ...
es handelt sich um die DLL-Version mit Objekten meines himXML
... und außerdem würde dann der MemoryManager der DLL und nicht wie in der aktuell hochgeladenen Version der der EXE verwendet. (obwohl das notfalls auch ginge, nur daß man dann, wie bei ShareMem etwas mehr mit der Unitreihenfolge aufpassen muß)

Obwohl ... wo ich das hier grad so schreib ... das ShareMem-Problem wäre ja nicht soooo schlimm, aber ich wollte halt ungern unbedingt am MemoryManagement fremder Anwendungen rumspielen und wenn da dann wer einen "alternativen" MemoryManager verwenden wöllte, dann müßte er diesen in der DLL einbauen, da dann der der EXE mit dem der DLL überschrieben würde (im aktuellen himXML ist es ja so, daß die DLL da noch dynamisch geladen wird und den MM der EXE verpaßt bekommt ... in etwa so).

Außerdem hab ich jetzt schon etwas Zeit in dieses Problem gesteckt
und die direkte External-Verlinkung der Objekte gefällt mir halt besser, als das "verbigen" eines Objektes mit virtuellen Methoden, wie es in der aktuell onlinestehenden Version der Fall ist.
Und zusätzlich hatte ich es geschafft die RTTIs der Objekte in der DLL und Exe abzugleichen (is und as funktionieren mit der selben Objektinstanz in beiden Modulen) ... wäre schade das jetzt wieder ändern zu müssen :?

0xF48CBFF 3. Jul 2009 23:55

Re: DLL-EntryPoint mit Funktion tauschen?
 
Du kannst din der PE-Datei einfach unter IMAGE_NT_HEADERS->OptionalHeader.AddressOfEntryPoint den Einstiegspunkt überschreiben und dort den Offset deiner Prozedur oder Funktion abgeben. Aber das ist nicht gerade die beste Lösung in meinen Augen.

An deiner Stelle würde ich ein neues Datensegment anlegen, wo sich ein einfacher CALL befindet, der deine gewünschte Prozedur oder Funktion aufruft. Aus der PE kannst du den Entrypoint auslesen und dort den CALL aus den Datensegment aufrufen. Das sollte eigentlich ohne Probleme bei Funktionieren. Im Grunde kannst du dir das wie eine "Umleitung" vorstellen. ;)

0xF48CBFF :wink:

himitsu 4. Jul 2009 09:45

Re: DLL-EntryPoint mit Funktion tauschen?
 
OK, den EntryPoint hab ich nun, jetzt muß ich wohl nur noch die Sections durchsehn, die Export-Tabelle finden und da meine Init-Prozedur finden :gruebel:

Aber in dem Code da seh ich noch nicht so durch und Brechi hat seinen Code ja nicht veröffentlicht. :wink:
Nja, ich versuch mich noch durchzuwurteln und hoff bald die Sectionen zu finden. :stupid:


Nur das mit dem neuen Datensegment hab ich jetzt nicht so verstanden, also wie mir dieses helfen soll,
bzw. wie ich dann den "Call" zum "alten" EntryPoint darin aufrufen müßte? :gruebel:


[add]
OK, ich hab endlich die SectionHeader-Liste gefunden, jetzt nur noch des komische Offset x+$d0 in dem Record suchen und als x.Irgendwas schreiben.

Und dann mal sehn wo ich die Exporttabelle finde.

[add]
ich glaub .edata klingt gut :nerd:

Aphton 4. Jul 2009 10:05

Re: DLL-EntryPoint mit Funktion tauschen?
 
Zitat:

Zitat von himitsu
Aber in dem Code da seh ich noch nicht so durch und Brechi hat seinen Code ja nicht veröffentlicht. :wink:
Nja, ich versuch mich noch durchzuwurteln und hoff bald die Sectionen zu finden. :stupid:

Da kann ich dir evt. helfen:
Code:
--Also am Anfang der Echse befindet sich der DOS-Header. Den einfach auslesen - am besten in eine Variable des Typs _Image_Dos_Header
--Im _lfanew von diesem Typ steht der Offset zum nächsten Header, dem NT-Header; Typbezeichnung: _IMAGE_NT_HEADERS
--_IMAGE_NT_HEADERS besteht seinerseits aus folgenden 3 Typen:
Signature, FileHeader, OptionalHeader.
Im FileHeader findest du dann folgendes: NumberOfSections-> Das ist die Anzahl der Sections, welche gleich nach diesem Header (_Image_NT_Headers) folgen.
--Die Sections liest du dann anschließend am besten in ein Array aus TImageSectionHeader ein.
Zitat:

Zitat von himitsu
... wie ich dann den "Call" zum "alten" EntryPoint darin aufrufen müßte? :gruebel:

Falls du die Echse Hex-weise bearbeiten willst (asm), dann reicht das hier schon vollkommen aus:
Code:
  mov eax, <AdresseVomOrginalenEntryPoint>
  jmp eax
Hoffe ich konnt' helfen

MfG

Apollonius 4. Jul 2009 10:11

Re: DLL-EntryPoint mit Funktion tauschen?
 
Was wäre denn das Problem dabei, einfach die RVAs der exportierten Funktion im Export-Directory und des Entry Points zu vertauschen? Dann muss man keinerlei tiefgreifende Veränderungen in der Datei vornehmen.

himitsu 4. Jul 2009 11:57

Re: DLL-EntryPoint mit Funktion tauschen?
 
@Apollonius: so hatte ich es mir auch gedacht ... nur muß ich es erstmal hinbekommen. :nerd:

@Aphton: das Aufrufen selber wäre dann nicht das Problem, aber ich müßte dann im Programm doch auch erstmal diese Section finden, um sie aufzurufen .... bei deiner Progedur laß ich mir dagegen einfach von GetProcAddress helfen. :stupid:

Inzuwischen bin ich so weit ... ich hab die ganzen Sections gefunden, den EntryPoint und auch die Export-Tabele,

aber dann komm ich nicht weiter .. beim Auslesen der Funktionsnamen. :wall:

Delphi-Quellcode:
P := ImageSection + Integer(ExportDirectory.AddressOfNames);
P müßte hier auf den ersten Namen stehen, aber wenn ich drauf zugreifen will, dann knallt's

In IMAGE_EXPORT_DIRECTORY.AddressOfNames steht die Adresse als RVA (Relative Virtual Adress), also relativ zur ImageBase in der geladenen DLL, wenn ich das richtig verstanden hab.

Nun hab ich die DLL aber nicht geladen und dachte mir, ich nehm das "einfach" relativ zum Imageabbild (.data Sektion) in der Datei, aber da bekomm ich ja eine Exception und grad eben geht es zufällig mal, aber er ließt nur unverständlichen Mißt (also irgendwas Anderes, nur keine Namen)

[edit]
OK, hab grad mitbekommen, daß Namensliste nicht aus den Namen selber besteht, sondern aus einem Array of "Zeigern" auf die Namen ...

vorher so
Delphi-Quellcode:
Var
  ImageSection, P: PAnsiChar;

asm int 3 end;
                WriteLn('search the init procedure');
                InitProcedure := 0;
                P := ImageSection + Integer(ExportDirectory.AddressOfNames);
                i := -1;
                While i < ExportDirectory.NumberOfNames do Begin
                  WriteLn(' procedure/function "' + P + '" found');
                  If P = 'initDLL' Then
                    If InitProcedure <> -1 Then Begin
                      InitProcedure := -2;
                      WriteLn(' found more than one init procedures');
                    End Else InitProcedure := i;
                  Inc(P, lstrlenA(P) + 1);
                  Inc(i);
                End;
                If InitProcedure >= 0 Then Begin

jetzt so , aber geht immernoch nicht

ich vermute erstmal, daß ich irgendwas bei der Verwendung des RVA falsch mach

Delphi-Quellcode:
Program himXML_DLL_Modifizierer;

{$APPTYPE CONSOLE}

Uses Windows, SysUtils;

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;

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

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

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');
          NTHeaders := Pointer(Integer(DOSHeader) + DOSHeader._lfanew);
          If NTHeaders.Signature = IMAGE_NT_SIGNATURE Then Begin
            WriteLn('search image and export section');
            SectionHeader := Pointer(Integer(NTHeaders) + SizeOf(IMAGE_NT_HEADERS));
            ImageSection := nil;
            ExportDirectory := nil;
            i := 0;
            While i < NTHeaders.FileHeader.NumberOfSections do Begin
              WriteLn(' section "' + TSectionName(SectionHeader.Name) + '" found');
              If TSectionName(SectionHeader.Name) = '.data' Then
                If ImageSection <> nil Then Begin
                  ImageSection := Pointer(1);
                  WriteLn(' found more than one .data sections');
                End Else ImageSection := Pointer(Integer(DOSHeader) + SectionHeader.PointerToRawData);
              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(ImageSection) > 1 Then Begin
              If Cardinal(ExportDirectory) > 1 Then Begin
asm int 3 end;
                WriteLn('search the init procedure');
                InitProcedure := 0;
                Names := Pointer(ImageSection + Integer(ExportDirectory.AddressOfNames));
                i := -1;
                While i < ExportDirectory.NumberOfNames do Begin
                  WriteLn(' procedure/function "' + PAnsiChar(Integer(ImageSection) + Names^) + '" found');
                  If PAnsiChar(Integer(ImageSection) + 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 NTHeaders.OptionalHeader.AddressOfEntryPoint <> 0 Then ;


                  WriteLn('OK');
                End Else WriteError('not found');
              End Else WriteLn('export section not found');
            End Else WriteLn('image section 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.

[add]
in meinen Anfängen dieses Codes hatte ich ja Codes von Anderen verwendet, z.B. Beispiele aus Google und der DP
und dem MSDN, also die Beschreibungen Structuren.

Delphi-Quellcode:
// so in etwa hatte es mr_emre_d
SectionHeader := Pointer(Integer(NTHeaders) + $D0);
// das hatte ich übersetzt und mir gedacht, was is'n das für'n Sch***,
// und warum sollte die Textionstabelle dort anfangen *schonrauchendenkopfhatte*
SectionHeader := @NTHeaders.OptionalHeader.DataDirectory[11].VirtualAddress;

// aber mr_emre_d hatte ja in seiner Schleife quasi Inc(SectionHeader) vorher
// und daher der komisch Offset

SectionHeader := Pointer(Integer(NTHeaders) + $D0);
i := 0;
While i < NTHeaders.FileHeader.NumberOfSections do Begin
  Inc(SectionHeader);
  If TSectionName(SectionHeader.Name) = '.edata' Then Break;
  Inc(i);
End;

// also warum einfach, wenn schwer geht -.-°
SectionHeader := Pointer(Integer(NTHeaders) + SizeOf(IMAGE_NT_HEADERS));
i := 0;
While i < NTHeaders.FileHeader.NumberOfSections do Begin
  If TSectionName(SectionHeader.Name) = '.edata' Then Break;
  Inc(SectionHeader);
  Inc(i);
End;

Apollonius 4. Jul 2009 12:08

Re: DLL-EntryPoint mit Funktion tauschen?
 
Das Export-Directory hat grundsätzlich erstmal nichts mit der Export-Section zu tun (klar, normalerweise liegt das Directory in der Section, aber das ist nicht verpflichtend). Du findest einen Directory Header am Ende des Optional Headers.
Du stehst hier vor dem Problem, eine RVA in einen Dateioffset umzurechnen. Dazu musst du über alle Sections iterieren, überprüfen, in welcher Section die RVA liegt und dann über den Dateioffset der Section zum Dateioffset der RVA kommen.

himitsu 4. Jul 2009 12:24

Re: DLL-EntryPoint mit Funktion tauschen?
 
Zitat:

Zitat von Apollonius
Das Export-Directory hat grundsätzlich erstmal nichts mit der Export-Section zu tun (klar, normalerweise liegt das Directory in der Section, aber das ist nicht verpflichtend). Du findest einen Directory Header am Ende des Optional Headers.

Hmmmm, also wenn du IMAGE_NT_HEADERS.OptionalHeader.DataDirectory meinst ... k.A. was ich mit den Daten darin anfangen soll. :oops:

Naj, zumindestens handelt es sich immer um eine bestimmte Delphi-Datei und ich vertraue jetzt einfach mal drauf, daß Borland äh Codegar oder Embarcadero da nicht einfach mal was dran ändern werden und ich es weiterhin da finde. :stupid:

Zitat:

Zitat von Apollonius
Du stehst hier vor dem Problem, eine RVA in einen Dateioffset umzurechnen. Dazu musst du über alle Sections iterieren, überprüfen, in welcher Section die RVA liegt und dann über den Dateioffset der Section zum Dateioffset der RVA kommen.

Na dann versuch ich mir da mal 'ne RVA2RealPointer (oder so) zu basteln :freak:

Aphton 4. Jul 2009 12:28

Re: DLL-EntryPoint mit Funktion tauschen?
 
Liste der Anhänge anzeigen (Anzahl: 1)
@Himitsu[Addendum]

Sorry wegen der blöden, kryptischen Beschreibung...
(Ja, ich bin der Emre)

Ich häng mal ein Projekt von mir, welche noch nicht beendet wurde und auch nicht wird, weils mich nicht mehr interessiert bzw. meine Ziele im jetztigen Zustand erfüllt...
Evt. kannst du dir da was abschaun.
PS: Ich hatte nie vor, das Programm zu veröffentlichen. Sourcecode ist leider diesmal auch ein wenig kryptisch UND ich weiß, dass es einen Memory-leak gibt..

MfG

Apollonius 4. Jul 2009 12:33

Re: DLL-EntryPoint mit Funktion tauschen?
 
Zitat:

Zitat von himitsu
Hmmmm, also wenn du IMAGE_NT_HEADERS.OptionalHeader.DataDirectory meinst ... k.A. was ich mit den Daten darin anfangen soll. :oops:

Das PE-Format gibt bestimmte Directories vor, z.B. Imports, Exports, Relocations, Ressourcen usw. Jedes dieser Directories hat eine Nummer, beispielsweise ist das Export-Directory das Erste, hat also die Nummer Null. Alle Directories findest du bei MSDN-Library durchsuchenIMAGE_DATA_DIRECTORY.

Edit: Die PE-Spezifikation schreibt übrigens keine Section-Namen vor - niemand kann dich hindern, deine Code-Section "Klaus" zu nennen.

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:

Apollonius 4. Jul 2009 20:14

Re: DLL-EntryPoint mit Funktion tauschen?
 
Beim Import per Index wird einfach der Index der Funktion in der AddressOfFunctions-Tabelle plus dem Base-Wert (im Directory verzeichnet) angegeben. Es folgt, dass Import per Index extrem schnell ist.

himitsu 4. Jul 2009 20:34

Re: DLL-EntryPoint mit Funktion tauschen?
 
Joar, den Index kann man ja immernoch in Delphi nutzen und ich glaub das alte ShareMM von Borland nutze es noch.

Aber ich glaub die PCs sind heute recht schnell, so daß man da auch verständliche Namen nutzen kann und sich nicht in Unmassen Indizes verheddern muß. :nerd:


Nja, mit dieser kleinen Änderung läuft schonmal der Patcher. :firejump:
Delphi-Quellcode:
//P: PCardinal;
If InitProcedure >= 0 Then Begin
  i := PWordArray(RVA2RealPointer(LongWord(ExportDirectory.AddressOfNameOrdinals)))[InitProcedure];
  P := @PPointerArray(RVA2RealPointer(LongWord(ExportDirectory.AddressOfFunctions)))[i];
  Temp := P^;
  P^ := NTHeader.OptionalHeader.AddressOfEntryPoint;
  NTHeader.OptionalHeader.AddressOfEntryPoint := Temp;
  WriteLn('OK');
Dann fehlt ja nur noch ein funktionierender Code in DLL und EXE.


Also danke erstmal für den DLL-Patcher. :love:

himitsu 4. Jul 2009 22:47

Re: DLL-EntryPoint mit Funktion tauschen?
 
Liste der Anhänge anzeigen (Anzahl: 1)
soooooooooo, es läuft nun endlich :dp:


Ich weiß zwar immernoch nicht, was mit den MMFs los ist, aber egal erstmal.

Hatte diese nur verwenden wollen, weil ich nicht wußte wie/wann/wo der Speicher für globale Variablen resserviert wird und ich deshalb vorm "vollständigen" Laden der DLL ich keine nutzen wollte.


Also vielen herzlichen Dank nochmals

und nun kann mein himXML endlich in die nächste Phase übergehn.

Reverse-ShareMemoryManagement (tolles Wort niwa?) macht vieles Einfacher.
(also eine statische DLL nutzen den MM der EXE und nicht andersrum, wie man es sonst kennt)

himitsu 5. Jul 2009 08:40

Re: DLL-EntryPoint mit Funktion tauschen?
 
Och menno, ich war doch nicht der erste Irre, welcher auf so'ne doofe Idee gekommen ist. http://fnse.de/S01/1LK.gif
Antivir
TR/ATRAPS.Gen - Trojan


nur finde ich irgendwie keine richtigen Beschreibungen zu diesem Trojaner :gruebel:

[add]
grad hier in 'nem anderem thread gesehn ... dieses VirusTotal.com-Seitchen ist ja cool :shock:
http://www.virustotal.com/de/analisi...77e-1246780177

Code:
Antivirus          Version      letzte Akt.  Ergebnis
a-squared          4.5.0.18      2009.07.05    Trojan-Downloader.Delphi!IK
AntiVir            7.9.0.204     2009.07.03    TR/ATRAPS.Gen
Ikarus             T3.1.1.64.0   2009.07.05    Trojan-Downloader.Delphi
McAfee-GW-Edition  6.8.5         2009.07.05    Heuristic.BehavesLike.Win32.Trojan.L
auch wenn ich nicht weiß, wie man da auf einen Downloader kommen soll, da ist doch nix zum Downloaden drin? :gruebel:

brechi 5. Jul 2009 14:16

Re: DLL-EntryPoint mit Funktion tauschen?
 
PEid erkennt z.b. den Typ des Programms (delphi, c++) nach den Daten am EntryPoint. Viele Viren neutzen ja eben die Technik Code anzufügen und dann den Entrypoint zu verbiegen um den Virencode vorher aufzurufen und dann im anschluß den originalen Code aufzurufen. Je nachdem wie einfach die Erkennung eingebaut wurde könnten da FalsePositives auftreten (Bevhaves like...).

Jetzt nochmal zum Thema: willst du irgendwann den origianlen EIP wieder aufrufen? wenn nicht schreib an den anfangscode (EIP) einen Jump bzw. patch die initialisierung vom MM manager raus (mussu halt debuggen).

Willst du lediglich Code ausführen bevor die Main-Funktion (DLL/EXE-main) und damit die Inititialisierung des MM Managers aufgerufen wird, trag deien funktion in die TLS-Tabelle ein.

himitsu 5. Jul 2009 15:06

Re: DLL-EntryPoint mit Funktion tauschen?
 
Also ich möchte/sollte natürlich die originale Startprozedur irgendwann und rechtzeitig, aber nicht zu früh ausführen, sonst hat Delphi wohl ein paar Probleme, wenn ich ihm die Initialisierung mopse. :lol:

Debuggen und Adressen hartcoden geht nicht, da es ja ein Opensourceprojekt für mehrere Delphiversionen ist. :stupid:

Ja und das Viren, Würmchen und Pferdchen sowas nutzen, war mir schon klar, nur wie die da auf downloadende Tierchen kommen bleibt mir schleierhaft. :angel2:

Aber zum Glück ist das ja nur für ein krankes "Zusatzmodul" meines himXML, wo man die Klassen aus einer DLL aufrufen kann und ganz eherlich, es gibt notfalls auch noch andere Wege, wo allerdings überall der MM der DLL genutzt wird oder wo man die DLL dynamisch laden muß und grad diese beiden Dinge möcht ich halt nicht.
Ja und ich bin halt verspielt (so würd ich es mal sagen) und wollt es jetzt einfach mal so versuchen

Und im Prinzip geht es ja nun (abgesehn von 2-3 Virenscannern, aber was soll's).


Nja, wenn ich mal ganz verspielt bin und viel Zeit hab, dann könnte ich bestimmt die Sektion vergrößern, die ganzen Realocations und sonstwas ändern, vor dem EntryCode einen Jump einfügen und zu meinem Code hopsen, den Rest also verschieben und dann bei meiner späteren Initialisierung hinter dem Jump reinspringen :freak:

Ja, ich hatte gestern schon die kranke Idee mal einige fremde Codes (falls es z.B. dich nicht gestört hätte) und auch meine neuen eigenen Cödechen zu nehmen und alles in einer Klasse zum Auslesen-/Bearbeiten von Executables zu kapseln :lol: (aber ob ich dafür so schnell auch noch Zeit finde)


Aber eine Frage hab ich noch ... kann es sein, daß die EIP eine Funktion ist? (seitdem ich Result := 1) stehen hab, gibt es keine Exceptions mehr, davor gab es noch ein paar.

brechi 5. Jul 2009 15:55

Re: DLL-EntryPoint mit Funktion tauschen?
 
also der EIP zeigt einfach auf eine Funktion vom typ

Code:
BOOL WINAPI
DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:
            // Code to run when the DLL is loaded
            break;

        case DLL_PROCESS_DETACH:
            // Code to run when the DLL is freed
            break;

        case DLL_THREAD_ATTACH:
            // Code to run when a thread is created during the DLL's lifetime
            break;

        case DLL_THREAD_DETACH:
            // Code to run when a thread ends normally.
            break;
    }
    return TRUE;
}
in c++ wird halt direkt diese aufgerufen, in delphi wird eine automatisch erzeugt und die initalisierungen vorgenommen. dann wird das begin end. von Delphi aufgerufen.

Delphi-Quellcode:
var
DLLProcNext: procedure(Reason: Integer); stdcall = nil;
procedure DLLMain(Reason: Integer); stdcall;
begin
case Reason of
DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstance);
DLL_THREAD_ATTACH:
;
DLL_THREAD_DETACH:
;
DLL_PROCESS_DETACH:
;
end;
if Assigned(DLLProcNext) then DLLProcNext(Reason);
end;
begin
DLLProcNext := Pointer(InterlockedExchange(Integer(DLLProc), Integer(@DLLMain)));
DLLMain(DLL_PROCESS_ATTACH);
end.
D.h. deine Exportfunktion (die du statt EIP einträgst) sollte auch den obigen aufbau (mit den drei parameter von c++) haben.

Eine TLS Funktion wird zumindest immer beim laden und entladen der dll/exe aufgerufen. D.h. da wurde die originale dllmain noch nicht aufgerufen (und kein mm installiert). Wenn du eh irgendwann die dllmain aufrufst, dann kannst du den code der vor der initialsierung durch delphi aufgerufen werden soll eben als TLS eintragen. So wie ich es bsiher mitbekommen habe ist es genau das was du wünscht.

Eine funktion die in der TLS steht wird vor der initialisierung von Delphi code (initialization) aufgerufen.

http://uall.cheat-project.com/uallpdf/asquared.pdf
#bypass3
die funktion SearchLoadCode wird vor dem initalisieren des mm usw. aufgerufen.

himitsu 5. Jul 2009 16:16

Re: DLL-EntryPoint mit Funktion tauschen?
 
Ja, ein Code vor der Inititailisierung von Delphi (der DLL) bringt mir nicht viel, da ich in meinem Fall den Code doch nach der Initialisierung der EXE benötige (ich benötige ja Infos, welche die Initialisierung der EXE erst noch bereitstellen muß), also selbst wenn ich was in TLS eintrage und es noch vor dem DelphiCode startet, dann wird die Delphi-Initialisierung der statischen DLL dennoch vor der der EXE ausgeführt.


OK, entweder hatte ich das BOOL WINAPI übersehn, oder die DllMain, welche ich nach kurzem Suchen in Google fand, war falsch :gruebel:

na Gut, dann werd' ich mal Integer durch LongBool ersetzen :)

brechi 5. Jul 2009 17:49

Re: DLL-EntryPoint mit Funktion tauschen?
 
Sorry ich versteh immer noch nicht was du genau haben willst :)
Kannst du mal aufschreiben wie das jetzt aufgerufen wird und wie du das haben wilslt also z.b.

exe-main
loadlibrary
dll-main
meincode <---
initcode

himitsu 5. Jul 2009 18:29

Re: DLL-EntryPoint mit Funktion tauschen?
 
im prinzip so:
exe-main > mein-dll-code > dll-main > ...

aber bei einer statisch geladenen DLL ist es ja so
dll-main > exe-main > ...

allerdings will ich die EXE nicht direkt manipulieren und mein Init-Code soll dort einfach nur via .PAS eingebunden werden können

ist halt etwas blöd, was die Speicherinitialisierung in der DLL betrifft, würde das für eine dynamische Bindung sprechen, aber die Verlinkung meiner Klassen mit der DLL ist statisch


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:19 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