2010-05-27 11 views
9

Ich habe ein Programm, das DLLs lädt und ich muss eine der nicht exportierten Funktionen aufrufen, die es enthält. Gibt es eine Möglichkeit, dies über die Suche in einem Debugger oder auf andere Weise zu tun? Bevor jemand fragt, ja ich habe die Prototypen und Sachen für die Funktionen.Aufruf einer nicht-exportierten Funktion in einer DLL

Antwort

0

Ich fürchte, es gibt keine "sichere" Möglichkeit, dies zu tun, wenn die referierte Bibliothek ihr Objekt (class/func) nicht explizit exportiert. Weil Sie keine Ahnung haben, wo das erforderliche Objekt im Code-Speicher abgebildet ist.

Mithilfe von RE-Tools können Sie jedoch einen Offset für ein interessiertes Objekt in der Bibliothek finden und dann zu einer beliebigen bekannten exportierten Objektadresse hinzufügen, um den "echten" Speicherort zu erhalten. Bereiten Sie danach einen Funktionsprototyp usw. vor und übertragen Sie ihn zur Verwendung in Ihre lokale Struktur.

+0

Was sind RE-Tools? – Nilbert

+0

Disassembler, und seine wahrscheinlich gute Idee, diejenigen zu finden, die Assembly Interpretieren Fähigkeit bieten (PE ist eine gute) – YeenFei

10

Ja es gibt, zumindest eine Art, aber es ist keine gute Idee.

In C/C++ alles ein Funktionszeiger ist, ist eine Adresse im Speicher. Wenn Sie also irgendwie die Adresse dieser Funktion finden könnten, könnten Sie sie anrufen.

Lassen Sie mich jedoch einige Fragen stellen, woher wissen Sie, dass diese DLL diese Funktion enthält? Hast du den Quellcode? Ansonsten weiß ich nicht, wie Sie sicher wissen können, dass diese Funktion existiert oder ob es sicher ist anzurufen. Aber wenn Sie den Quellcode haben, dann geben Sie einfach die Funktion frei. Wenn der DLL Writer diese Funktion nicht verfügbar machte, erwarten Sie nie, dass Sie ihn aufrufen, und Sie können die Implementierung jederzeit ändern/entfernen.

Warnungen beiseite, können Sie die Funktion Adresse finden, wenn Sie Debug-Symbole oder eine MAP file haben, können Sie den Offset in der DLL finden. Wenn Sie nichts außer der DLL haben, gibt es keine Möglichkeit zu wissen, wo diese Funktion in der DLL vorhanden ist - es ist nicht in der DLL selbst gespeichert.

Sobald Sie den Offset haben, können Sie dann, dass wie so in den Code einfügen:

const DWORD_PTR funcOffset = 0xDEADBEEF; 
typedef void (*UnExportedFunc)(); 

.... 
void CallUnExportedFunc() { 
    // This will get the DLL base address (which can vary) 
    HMODULE hMod = GetModuleHandle("My.dll"); 
    // Calcualte the acutal address 
    DWORD_PTR funcAddress = (DWORD_PTR)hMod + funcOffset; 
    // Cast the address to a function poniter 
    UnExportedFunc func = (UnExportedFunc)funcAddress; 
    // Call the function 
    func(); 
} 

auch erkennen, dass die von dieser Funktion Offset wechselt mit jedem der DLL neu erstellt wird, so ist dies sehr zerbrechlich und lassen sage ich noch einmal, keine gute Idee.

+0

Leider war das Problem, die Adresse in der DLL zu finden, und ja, alles, was ich habe, ist die DLL, aber ich bin mir sicher Die Funktion existiert darin. Was meinen Sie mit "Wenn Sie nichts außer der DLL haben, dann gibt es keine Möglichkeit zu wissen, wo diese Funktion in der DLL existiert - sie wird nicht in der DLL selbst gespeichert."? – Nilbert

+0

Was ich zu sagen versuchte, ist, dass die DLL keine Zuordnung zwischen dem Namen der Funktion die Adresse innerhalb der DLL enthält, in der sich dieser Code befindet. Sie finden den Namen der Funktion nirgendwo in der DLL selbst. – shf301

+1

Sie können versuchen, die DLL in OllyDBG zu suchen und zu finden, wo die Funktion ist, aber es kann sehr schwierig sein –

1

Wenn die gewünschte Funktion nicht exportiert wird, wird sie nicht in die Exportadressentabelle aufgenommen. Angenommen, Visual Studio wurde verwendet, um diese DLL zu erstellen, und Sie haben die zugeordnete PDB-Datei (Programmdatenbank), dann können Sie die DIA (Debug Interface Access) API von Microsoft verwenden, um die gewünschte Funktion entweder nach Name oder ungefähr nach Signatur zu suchen.

Sobald Sie die Funktion (Symbol) von der PDB haben, haben Sie auch seine RVA (relative virtuelle Adresse). Sie können den RVA der Basisadresse des geladenen Moduls hinzufügen, um die absolute virtuelle Adresse im Speicher zu bestimmen, in der die Funktion gespeichert ist. Dann können Sie einen Funktionsaufruf über diese Adresse tätigen.


Alternativ kann, wenn dies nur eine einmalige Sache, die Sie tun müssen (dh Sie keine programmatische Lösung benötigen), können Sie in den Debugging Tools for Windows Toolkit verwenden windbg.exe zu Ihrem Prozess anhang und finde die Adresse der Funktion, die dir wichtig ist. In WinDbg können Sie den Befehl x verwenden, um Symbole in einem Modul zu untersuchen.

Zum Beispiel können Sie x mymodule!*foo* tun, um alle Funktionen zu sehen, deren Name "foo" enthält. Solange Symbole (PDB) für Ihr Modul geladen sind, werden Ihnen auch die Nicht-Export-Funktionen angezeigt. Verwenden Sie .hh x, um Hilfe zum Befehl x zu erhalten.

+0

ich schätze es saugt dann wenn ich nur die .DLL habe und nicht die .pdb dann richtig? – Nilbert

+0

Ja, es wäre sehr schwierig, Ihre nicht exportierte Funktion ohne PDB-Informationen zu finden. Sie könnten versuchen, einen Disassembler zu verwenden, um zu erraten, wo es sich befindet, indem Sie nach x86-Prolog/Epilogen suchen und einen Schätz-und-Prüf-Ansatz wählen, siehe http://www.smidgeonsoft.prohosting.com/pebrowse-pro-file-viewer.html –

0

Die allgemeinste Art und Weise dies zu tun (und es ist immer noch eine schlechte Idee, wie alle anderen bereits hingewiesen) ist, den DLL-Code zur Laufzeit nach dem Laden zu scannen und nach einem bekannten, einzigartigen Codeabschnitt zu suchen Funktion, und verwenden Sie dann Code, der dem in der Antwort von shf301 ähnlich ist, um ihn zu nennen. Wenn Sie wissen, dass sich die DLL nicht ändern wird, sollte jede Lösung funktionieren, die auf der Ermittlung des Offsets in der DLL basiert.

Um diesen einzigartigen Codeabschnitt zu finden, zerlegen Sie die DLL mit einem Disassembler, der Ihnen neben der Assembler-Mnemonik auch den Maschinencode anzeigen kann (ich kann mir nichts vorstellen, was das nicht tun würde) und pass auf für Call- und JMP-Anweisungen.

Ich hatte tatsächlich etwas Ähnliches einmal tun ein binäres Patch auf einem DOS-exe anzuwenden; Es handelte sich um einen Bugfix, und der Code stand nicht unter Revisionskontrolle, so dass dies der einzige Weg war, ihn zu beheben.

würde ich wirklich gespannt sein, zu wissen, warum diese benötigen, übrigens.

7

Ich weiß, diese Frage ist ziemlich alt, aber shf301 hat die richtige Idee hier. Das einzige, was ich hinzufügen würde, ist eine Mustersuche in der Zielbibliothek zu implementieren. Wenn Sie IDA oder OllyDbg haben, können Sie nach der Funktion suchen und die Binär-/Hex-Daten anzeigen, die die Startadresse dieser Funktion umgeben.

In den meisten Fällen gibt es eine binäre Signatur sein, die selten ändert. Die Signatur kann Platzhalter halten, die zwischen Builds ändern können, aber letztendlich sollte es mindestens ein erfolgreicher Treffer, während für dieses Muster suchen, es sei denn, extrem drastischen Veränderungen aufgetreten sind zwischen (an welchem ​​Punkt baut, könnten Sie einfach die neue Signatur herauszufinden, für die bestimmte Version).


Die Art und Weise, dass Sie eine binäre Mustersuche implementieren würde, ist wie folgt:

bool bCompare(const PBYTE pData, const PBYTE bMask, const PCHAR szMask) 
{ 
    for(;*szMask;++szMask,++pData,++bMask) 
      if(*szMask=='x' && *pData!=*bMask) 
        return 0; 
    return (*szMask) == NULL; 
} 

DWORD FindPattern(DWORD dwAddress, DWORD dwLen, PBYTE bMask, PCHAR szMask) 
{ 
    for(DWORD i=0; i<dwLen; i++) 
      if (bCompare((PBYTE)(dwAddress+i),bMask,szMask)) 
        return (DWORD)(dwAddress+i); 
    return 0; 
} 


Beispiel Nutzung:

typedef void (*UnExportedFunc)(); 

//... 
void CallUnExportedFunc() 
{ 
    // This will get the DLL base address (which can vary) 
    HMODULE hMod = GetModuleHandleA("My.dll"); 

    // Get module info 
    MODULEINFO modinfo = { NULL, }; 
    GetModuleInformation(GetCurrentProcess(), hMod, &modinfo, sizeof(modinfo)); 

    // This will search the module for the address of a given signature 
    DWORD dwAddress = FindPattern(
     hMod, modinfo.SizeOfImage, 
     (PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86", 
     "xx????xx????xx" 
    ); 

    // Calculate the acutal address 
    DWORD_PTR funcAddress = (DWORD_PTR)hMod + dwAddress; 

    // Cast the address to a function poniter 
    UnExportedFunc func = (UnExportedFunc)funcAddress; 

    // Call the function 
    func(); 
} 


Die Art und Weise, dass dies funktioniert, ist im Vorbeigehen die Basisadresse der geladenen Bibliothek über GetModuleHandle, wobei die zu suchende Länge (in Bytes), die zu suchenden binären Daten und ein ma angegeben werden sk gibt an, welche Bytes der Binärzeichenfolge gültig sind ('x') und welche zu übersehen sind ('?'). Die Funktion durchläuft dann den Speicherbereich des geladenen Moduls und sucht nach einer Übereinstimmung. In einigen Fällen kann es mehrere Übereinstimmungen geben. In diesem Fall ist es sinnvoll, die Signatur etwas deutlicher zu machen, wenn nur eine Übereinstimmung vorhanden ist.

Nochmals, Sie müssten die erste binäre Suche in einer Disassemblierungsanwendung durchführen, um zu wissen, was diese Signatur ist, aber sobald Sie das haben, sollte diese Methode ein wenig besser funktionieren, als den Funktionsoffset jedes Mal manuell zu finden das Ziel ist gebaut. Hoffe das hilft.

0

Auch wenn Sie die Funktionsadresse finden können, ist es im Allgemeinen nicht sicher, eine Funktion aufzurufen, die von einem Compiler erstellt wurde, der dachte, dass er eine "private" internal-use-only-Funktion ausführt.

Moderne Compiler mit link-time-optimization enabled kann eine spezielle Version einer Funktion, die nur das tut, was die spezifischen Anrufer brauchen, es zu tun.

Gehen Sie nicht davon aus, dass ein Block von Maschinencode, der wie die gewünschte Funktion aussieht, tatsächlich der Standard-ABI folgt und alles implementiert, was der Quellcode sagt.

In gccs Fall verwendet es spezielle Namen für spezialisierte Versionen einer Funktion, die nicht inline sind, aber einen speziellen Fall (wie konstante Propagierung) von mehreren Anrufern ausnutzen.

z.B. in diesem objdump -drwC Ausgang (wo -C ist demangle):

42944c: e8 cf 13 0E 00 Anruf 50a820 429.451: 48 8b 7b 48 mov RDI, QWORD PTR [RBX + 0x48] 429.455: 48 89 ee mov rsi, rbp 429458: e8 b3 10 0e 00 Aufruf 50a510

gcc emittiert Code, der zwei verschiedene Klone derselben Funktion aufruft, die auf zwei verschiedene Kompilierzeitkonstanten spezialisiert sind. (Dies ist von http://endless-sky.github.io/, die dringend LTO braucht, weil sogar trivial Accessorfunktionen für seine XY-Position Klasse sind in Point.cpp, nicht Point.h, so dass sie nur durch LTO inlined werden.)

LTO können sogar .lto_priv machen statische Versionen der Daten: wie

mov rcx,QWORD PTR [rip+0x412ff7]  # 83dbe0 <_ZN12_GLOBAL__N_116playerGovernmentE.lto_priv.898> 

Also selbst wenn Sie eine Funktion finden, das so aussieht, was Sie wollen, ist es von einem neuen Ort aufrufen könnten die Annahmen, die Link-Zeitoptimierung in Anspruch genommen haben verletzen.

Verwandte Themen