Microsofts Detours Library 2.1 mit BCB?



  • Maverick schrieb:

    Irgendwas hab ich noch falsch gemacht, aber was 😕

    Eine Vermutung hätte ich da; bei meinen Versuchen trat ein Problem auf, dessen nicht eben naheliegende Lösung mit einem Unterschied zwischen BCC und MSVC in der Behandlung der statischen Referenzierung von DLL-Importen zusammenhing.

    Im Beispiel "simple" in simple.cpp wird das Detouring folgendermaßen durchgeführt:

    VOID (WINAPI * TrueSleep) (DWORD dwMilliseconds) = Sleep;
    
    VOID WINAPI TimedSleep(DWORD dwMilliseconds)
    {
        DWORD dwBeg = GetTickCount();
        TrueSleep(dwMilliseconds);
        DWORD dwEnd = GetTickCount();
    
        InterlockedExchangeAdd(&dwSlept, dwEnd - dwBeg);
    }
    
    BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
    {
        LONG error;
        (void)hinst;
        (void)reserved;
    
        if (dwReason == DLL_PROCESS_ATTACH) {
            printf("simple.dll: Starting.\n");
            fflush(stdout);
    
            DetourRestoreAfterWith();
    
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourAttach(&(PVOID&)TrueSleep, TimedSleep);
            error = DetourTransactionCommit();
    ...
    

    Zunächst wird also die globale Variable TrueSleep mit dem WinAPI-Import Sleep initialisiert (Z. 1), dann später an DetourAttach() übergeben (Z. 26). Was trivial klingt, ist die Ursache unseres Problems; um das nachvollziehen zu können, ist es nötig zu wissen, wie DLL-Importe funktionieren.

    Der Linker erstellt für alle Funktionen, die als __declspec (dllimport) markiert sind, eine Tabelle von Minimal-Stubs, die lediglich aus einer JMP-Anweisung mit einer Dummy-Adresse bestehen - eine Sprungtabelle. Der PE-Loader trägt, wenn das Modul geladen wird, in dieser Tabelle die tatsächlichen Adressen der Funktionen ein. Durch diesen Umweg wird dem Loader einiges an Arbeit erspart (unabhängig davon, wie oft eine importierte Funktion referenziert wurde, muß der Loader ihre tatsächliche Adresse nur einmal in der Sprungtabelle eintragen).
    Dies läßt sich sehr einfach im Disassembler (Strg+Alt+C) nachvollziehen. Wenn du probehalber den folgenden Code in simple.cpp einfügst:

    Sleep (0);
    

    , dann resultiert er in diesem Assembler-Code:

    simple.cpp.40: Sleep (0);
    003712A0 6A00             push $00
    003712A2 E8B51B0000       call $00372e5c
    

    Folgst du der CALL-Anweisung mit F7 (Einzelne Anweisung), so landest du in der Sprungtabelle:

    ...
    00372E50 FF2510713700     jmp dword ptr [$00377110]
    00372E56 FF2514713700     jmp dword ptr [$00377114]
    00372E5C FF2518713700     jmp dword ptr [$00377118] ; <-- hier
    00372E62 FF251C713700     jmp dword ptr [$0037711c]
    00372E68 FF2520713700     jmp dword ptr [$00377120]
    ...
    

    Folgst du auch der JMP-Anweisung mit F7, bist du am Ziel:

    kernel32.Sleep:
    7C802442 8BFF             mov edi,edi
    7C802444 55               push ebp
    7C802445 8BEC             mov ebp,esp
    7C802447 6A00             push $00
    7C802449 FF7508           push dword ptr [ebp+$08]
    7C80244C E84BFFFFFF       call $7c80239c
    7C802451 5D               pop ebp
    7C802452 C20400           ret $0004
    7C802455 90               nop 
    7C802456 90               nop 
    7C802457 90               nop 
    7C802458 FFFF             db $ff $ff 
    7C80245A FFFF             db $ff $ff 
    7C80245C 0000             add [eax],al
    7C80245E 0000             add [eax],al
    7C802460 322480           xor ah,[eax+eax*4]
    7C802463 7C90             jl $7c8023f5
    7C802465 90               nop
    

    Der BCC behandelt statische Referenzierungen importierter Funktionen so wie einen gewöhnlichen Funktionsaufruf. Deshalb bewirkt diese Anweisung

    VOID (WINAPI * TrueSleep) (DWORD dwMilliseconds) = Sleep;
    

    , daß in TrueSleep die Adresse der Stub-Funktion in der Sprungtabelle eingetragen wird. Hier - allerdings ist das nur eine auf der Tatsache, daß die mitgelieferten Beispiele dort funktionieren, basierende, nicht verifizierte Vermutung - verhält sich MSVC anders: nämlich wird dort entweder vom PE-Loader oder vom Startup-Code der Anwendung in TrueSleep die tatsächliche Adresse der importierten Funktion eingetragen.

    Dies hat nun aber zur Folge, daß du, wenn du DetoursAttach() mit TrueSleep aufrufst, die Adresse des Sprungtabellen-Stubs übergibst. Demnach sieht die Sprungtabelle nach dem Aufruf der Detours-Funktionen so aus:

    ...
    00372E50 FF2510713700     jmp dword ptr [$00377110]
    00372E56 FF2514713700     jmp dword ptr [$00377114]
    00372E5C E9E3E3FFFF       jmp TimedSleep(unsigned long)
    00372E61 CC               int 3
    00372E62 FF251C713700     jmp dword ptr [$0037711c]
    00372E68 FF2520713700     jmp dword ptr [$00377120]
    ...
    

    Detours hat wunschgemäß die Funktion, auf die der übergebene Funktionszeiger verwies, umgelegt - den Stub in der Sprungtabelle. Jeder weitere Aufruf von Sleep() aus dem Modul, in dem die Detours-Funktionen aufgerufen wurden, erfolgt über diese Sprungtabelle und endet daher auch wie erwartet in TimedSleep(). Jedoch hat jedes Modul natürlich seine eigene Sprungtabelle, und die Sprungtabelle von sleep5.exe bleibt von obiger Aktion natürlich völlig unberührt und verweist nach wie vor auf die tatsächliche WinAPI-Funktion Sleep in kernel32.dll.

    Das Problem kannst du lösen, indem du die TrueSleep zur Laufzeit zuweist:

    typedef VOID (WINAPI * Sleep_t) (DWORD dwMilliseconds);
    Sleep_t TrueSleep;
    
    VOID WINAPI TimedSleep(DWORD dwMilliseconds)
    {
        ...
    }
    
    BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
    {
        LONG error;
        (void)hinst;
        (void)reserved;
    
        if (dwReason == DLL_PROCESS_ATTACH) {
            printf("simple.dll: Starting.\n");
            fflush(stdout);
    
            DetourRestoreAfterWith();
    
            TrueSleep = reinterpret_cast <Sleep_t> (GetProcAddress (GetModuleHandle (TEXT ("kernel32.dll")),
                "Sleep")); // <--
    
            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());
            DetourAttach(&(PVOID&)TrueSleep, TimedSleep);
            error = DetourTransactionCommit();
    

    Danach verweist TrueSleep auf die tatsächliche Adresse der Funktion, und entsprechend verändert DetourAttach() auch die WinAPI-Funktion selbst (natürlich nur für den aktuellen Prozeß):

    kernel32.Sleep:
    7C802442 E9FDEDB683       jmp TimedSleep(unsigned long)
    7C802447 6A00             push $00
    7C802449 FF7508           push dword ptr [ebp+$08]
    7C80244C E84BFFFFFF       call $7c80239c
    7C802451 5D               pop ebp
    7C802452 C20400           ret $0004
    7C802455 90               nop
    ...
    


  • Danke für die ausführliche Erklärung. Werde das heute abend dann mal versuchen 🙂



  • vorab, hut ab, klasse thread und klasse inhalt.
    nur ich komm nicht weiter.

    hab beide statische libs detoured (16kb), detours (269kb) erstellt.
    danach ein projekt angefangen, worin ich ws32.send detouren will.

    habs nach deinem verfahren gemacht @audacia.

    True_Send = reinterpret_cast <PSEND> (GetProcAddress(GetModuleHandle("Ws2_32.dll"),"send"));
    

    in True_Send steht die addy von w32.send aber die detour libs wollen partout
    kein jmp erstellen.

    woran kann es jetzt liegen Oo?
    hab ich die libs falsch kompeliert?
    schafft vielleicht einer von euch ws32.send zu detouren?

    wäre cool, wenner mir da irgendwie weiterhelfen könntet.

    Greetz 🙂



  • retn schrieb:

    True_Send = reinterpret_cast <PSEND> (GetProcAddress(GetModuleHandle("Ws2_32.dll"),"send"));
    

    in True_Send steht die addy von w32.send aber die detour libs wollen partout
    kein jmp erstellen.

    Zunächst setzt die Verwendung von GetModuleHandle() natürlich voraus, daß das fragliche Modul bereits mit LoadLibrary() oder als statischer Import vom PE-Loader in den Prozeß geladen wurde. Weiterhin geben DetourAttach(), DetourAttachEx() und DetourTransactionCommit() einen Fehlercode zurück, der bei der Diagnose evtl. hilfreich ist. Und den Typen PSEND brauchst du auch nicht selbst zu deklarieren.

    Folgendes funktioniert einwandfrei bei mir:

    #define INCL_WINSOCK_API_TYPEDEFS 1
    #include <winsock2.h>
    #include <detours.h>
    
    WINSOCK_API_LINKAGE int WSAAPI replacement_send (SOCKET s, const char FAR * buf,
        int len, int flags)
    {
    
    }
    
    LPFN_SEND true_send;
    
    ...
    
        LoadLibrary (TEXT ("ws2_32.dll"));
    
        true_send = reinterpret_cast <LPFN_SEND> (GetProcAddress (
            GetModuleHandle (TEXT ("ws2_32.dll")), "send"));
    
        DetourTransactionBegin ();
        DetourAttach (&(PVOID&) true_send, replacement_send);
        DetourTransactionCommit ();
    


  • In True_Send stand ja die richtige Addy, von daher muss es geladen sein, habs ja nachgeguckt.
    Stimmt, an den Error code hab ich net gedacht und da ich den nun kenne, deprimiert es mich noch mehr xP.
    Fehlercode: (8) ERROR_NOT_ENOUGH_MEMORY kam bei rum. Und wenn ich mir die detours.cpp anschaue, gibts 3 möglichkeiten, die relevant wären.
    Irgendeine Idee? 😞
    Soviel Aufwand fürn kleinen Detour. ^^



  • retn schrieb:

    Irgendeine Idee? 😞

    Eine Debug-Version von detours.lib erstellen und mal durchsteppen? 🙂



  • geht net klar 😃
    hab mich auf die detour lib gefreut wien fuchs... und nu 😣
    nunja mal sehen, dtrotzdem thanks 😃



  • re 🙂

    also ich hab mal wieder ka. 😃

    in vc08 hab ich die detours ohne probs hinbekommen. jetzt zu borland.
    bin noch der überzeugung, dass ich die libs, dlls net richtig erzeuge, bzw. falsch oder garnet einbinde.

    also ich erstelle eine statische lib mit folgenden dateien:
    creatwth.cpp
    detours.cpp
    image.cpp
    disasm.cpp
    modules.cpp

    danach erstelle ich eine dll mit:
    detoured.cpp
    detoured.h

    mit extern "C" __declspec( dllexport ) HMODULE WINAPI Detoured();
    ein und kompeliere.

    k. klappt soweit.

    um detours zu verwenden, adde ich zu meinem dll projekt die statische lib.
    dann kommt folgendes
    [Linker Error] Error: Unresolved external 'Detoured' referenced from C:\DETOUR_BCB\DETOURS.LIB|detours

    so, wie verbinde ich jetzt die detoured.dll mit meiner dll und der statischen lib "detours"? 🙂

    hoffe, ihr guckt noch in den thread.

    greetz



  • retn schrieb:

    um detours zu verwenden, adde ich zu meinem dll projekt die statische lib.
    dann kommt folgendes
    [Linker Error] Error: Unresolved external 'Detoured' referenced from C:\DETOUR_BCB\DETOURS.LIB|detours

    Dann füge doch die Importbibliothek für detoured.dll auch hinzu 😉



  • will net ^^ ahhhh
    kannst du mir vll. die komplette lib,dll an wakkaelpaso@gmx.net schicken? 😃
    greetz



  • @retn: Steht doch schon da wie du die DLL/LIB erzeugen musst.

    Ich hab mir das andere "Problem" noch nicht angesehen, bin noch nicht dazu gekommen.



  • egal wie ich die kompeliere, es taucht immerwieder ein internes problem auf. wenns bei euch klappt, machts doch net nen umstand kurz zu schicken oder? ^^



  • retn schrieb:

    es taucht immerwieder ein internes problem auf.

    Wenn du das etwas genauer spezifizieren könntest, könnte man dir vielleicht auch helfen.

    retn schrieb:

    wenns bei euch klappt, machts doch net nen umstand kurz zu schicken oder? ^^

    Doch. Meines Wissens untersagt Microsoft die Weitergabe der Detours-Quelldateien.



  • retn schrieb:

    Fehlercode: (8) ERROR_NOT_ENOUGH_MEMORY kam bei rum. Und wenn ich mir die detours.cpp anschaue, gibts 3 möglichkeiten, die relevant wären.

    Hatte ja schon eräwhnt, was für ein Fehler kommt, wenn ich sie mal richtig kompeliere. Da aber ihr nicht so ein Problem habt, kann es ja nur sein, dass ich immernoch was falsch mache.

    Eine Sache die mir auffiel ist, dass wenn ich die Libs mitkompeliere und ein Detour starte, dass er sich nicht an die DETOURED.DLL heftet, so wie in der Visual C++ Version.

    Ansonsten wäre es genial, wenn einer von euch hier ein komplettes Tutorial für den Erstellungsvorgang für Borland macht. Das alles ist doch etwas sehr zerstreut auf der Seite.

    greetz 🙂



  • retn schrieb:

    retn schrieb:

    Fehlercode: (8) ERROR_NOT_ENOUGH_MEMORY kam bei rum. Und wenn ich mir die detours.cpp anschaue, gibts 3 möglichkeiten, die relevant wären.

    Hatte ja schon eräwhnt, was für ein Fehler kommt, wenn ich sie mal richtig kompeliere. Da aber ihr nicht so ein Problem habt, kann es ja nur sein, dass ich immernoch was falsch mache.

    Ah, du meinst immer noch den Laufzeitfehler. Bei deinem Post dachte ich zunächst, der Fehler träte beim Kompilieren auf.

    retn schrieb:

    Eine Sache die mir auffiel ist, dass wenn ich die Libs mitkompeliere und ein Detour starte, dass er sich nicht an die DETOURED.DLL heftet, so wie in der Visual C++ Version.

    Das verwundert mich; eigentlich müßte das gleichermaßen der Fall sein.
    Hast du mal ein Debug-Build erstellt? Was passiert in DetourAttachEx() an den Stellen, wo Detoured() explizit aufgerufen wird?

    retn schrieb:

    Ansonsten wäre es genial, wenn einer von euch hier ein komplettes Tutorial für den Erstellungsvorgang für Borland macht. Das alles ist doch etwas sehr zerstreut auf der Seite.

    Das werde ich bei Gelegenheit mal angehen.



  • ist noch jemand da, der sich mit dem thema beschäftigt?
    greetz



  • Sorry für Doppelpost. :).

    Hab folgendes Problem...

    Vorab Infos:
    Hab folgende Libs/DLLs erzeugt.

    Eine statische lib mit folgenden Dateien:
    creatwth.cpp
    detours.cpp
    image.cpp
    disasm.cpp
    modules.cpp

    Eine detoured.dll:
    detoured.cpp
    detoured.h

    mit extern "C" __declspec( dllexport ) HMODULE WINAPI Detoured();

    Alles klar soweit.
    Dann habe ich meine eigentliche DLL erstellt, dazu die detours.lib, detoured.lib sowie headers
    hinzugefügt.

    Kompeliere das Fehlerfrei. Folgender Code ist bis jetzt dabei:

    case DLL_PROCESS_ATTACH:
    
    			if (DetourRestoreAfterWith()==ERROR_INVALID_OPERATION)
    				MessageBox(0,"fu","restore",0);
    
    			sprintf(buf, "Error: %d, GetLastError: %d", error, GetLastError());
    			MessageBox(0,buf,"Info",0);
    
    			True_Send  = reinterpret_cast <PSEND> (GetProcAddress (GetModuleHandle (TEXT ("ws2_32.dll")), "send"));
    			True_Recv  = reinterpret_cast <PRECV> (GetProcAddress (GetModuleHandle (TEXT ("ws2_32.dll")), "recv"));
    
    			DetourTransactionBegin();
    			if (!DetourUpdateThread(GetCurrentThread())) MessageBox(0,"fu","update",0);
    			if (DetourAttach(&(PVOID&)True_Send, MySend) !=NO_ERROR) MessageBox(0,"fu","attach",0);
    			//DetourAttach(&(PVOID&)True_Recv, MyRecv);
    			if ( (error = DetourTransactionCommit()) != NO_ERROR) MessageBox(0,"fu","commit",0);
    
    			sprintf(buf, "Error: %d, SendAddr: %x", error, True_Send);
    			MessageBox(0,buf,"Info",0);
    
    			break;
    

    Das witzige dabei ist, dass ich jetzt für jeden Funktionsaufruf eine Messagebox bekomme, ergo Fehler. Hab direkt LastGetError nach dem ersten Funktionsaufruf von DetourRestoreAfterWith aufgerufen und was kam? Fehlercode: 126... ergo stimmt da was gewaltig net.

    Der Fehlercode besagt, dass er gewisse ModuleHandles net findet. Na toll, ich kann damit wenig anfangen, leider -.-
    greetz



  • Versuche mal, nicht die vom C++Builder übersetzte detoured.dll, sondern die als Binary beigelegte Version mithilfe von IMPLIB einzubinden.



  • Heyho,

    hab über "implib detoured.lib detoured.dll" die importlib erstellt.
    meinem projekt hinzugefügt, er linkt jetzt auf die von ms erstellte dll.
    aber dennoch bleiben die fehler.

    greetz



  • Dann bleibt dir wohl wenig anderes übrig, als mal ein Debug-Build der Detours-Library zu erstellen und durch den Detours-Quelltext durchzusteppen.


Anmelden zum Antworten