DLL Hook + UDP Hole punch



  • Hi, ich will eine sehr alte (WinSock 1.1)-Anwendung dahingehend erweitern, dass es auch Daten von Computern außerhalb das NATs empfangen kann, undzwar ohne dass eine explizit eine Portweiterleitung eingestellt werden muss. Mir ist klar, dass das nicht für symmetrische NATs funktioniert, aber das ist erst mal egal.
    Vorweg: ich bin was das "Hacken" angeht, mangels bisherigem Interesse, nicht sonderlich bewandert.

    Mein Problem: die Anwendung benutzt eine DLL, in der alle WinSock-Sachen abgewickelt werden. Mit Olly und ein paar Breakpoints habe ich die Stelle meines Begehrens gefunden (innerhalb der DLL), die ich für ideal halte, das Hole punching zu integrieren. Innerhalb einer Prozedur wird ein (SOCK_DGRAM)-Socket erstellt und anschließend auf einen lokalen Port gebunden. Anstatt den CALL auf ws2_32.dll:bind() durchzuführen, würde ich gerne auf eine eigene Funktion CALLen, in der ich vom Server die IP:Port-Paare der Peers empfange und jeweils UDP-Packets versende (auf eben diesem Port, der zuvor von der DLL erstellt wurde), um das "Hole zu punchen", bevor das Programm wie üblich weitergeht als wäre nichts gewesen.

    Der Code ist natürlich zu High-Level, um ihn als Assembler zu schreiben, den müsste ich sicher aus einer DLL laden. Ich weiß zwar, dass es wohl sowas wie DLL-Injections gibt, aber alles was ich fand, zielte immer darauf ab, dass man eine DLL in einen Fremdprozess injectet (und zusätzlich auch noch die absolute Speicheradresse des OP-Codes kennt, den man ersetzen will), aber in meinem Fall ist die Funktion ja selbst ebenfalls in einer DLL und hat dauernd eine andere Adresse. Wie kann ich hier vorgehen? Den Prozess die DLL laden lassen, irgendwie die Basisadresse des Moduls ermitteln und dann mit einem relativen Offset darauf zugreifen? Sorry, falls das naiv klingt, ich verstehe nicht wirklich viel mehr als WinSock und etwas Assembler.



  • Hört sich alles umständlich an. Kannst du nicht einfach einen eigenen Prozess (den du im Griff hast) als Proxy davorhängen?

    Wenn das die einzige Stelle ist, wo bind benutzt wird, kannst du den IAT Eintrag patchen, dann wird grundsätzlich deine Funktion aufgerufen.



  • > Hört sich alles umständlich an. Kannst du nicht einfach einen eigenen Prozess (den du im Griff hast) als Proxy davorhängen?

    Hört sich ja ganz nett an. Aber einfach ist das sicher auch nicht. Dafür müsste man erst irgendwelche WinSock-Sachen hooken, damit alle Pakete auf einem bestimmten Port abgefangen werden und auf dem Socket, der eine hole-punch-Verbindung hat, weitersenden. Klingt für mich viel frickeliger.



  • Falcon Punch schrieb:

    Den Prozess die DLL laden lassen, irgendwie die Basisadresse des Moduls ermitteln und dann mit einem relativen Offset darauf zugreifen?

    So ungefähr.

    Mechanics schrieb:

    Wenn das die einzige Stelle ist, wo bind benutzt wird, kannst du den IAT Eintrag patchen, dann wird grundsätzlich deine Funktion aufgerufen.

    Wenn aber nicht, dann muss man schon ein bisschen mehr machen. Da gäbe es mehrere Möglichkeiten, je nachdem wie der ASM Code aussieht.
    Jo, oder Proxy, ginge natürlich auch, je nach genauen Anforderungen.

    Ich biete dir an, dir gegen Entgelt bei der Umsetzung zu helfen (oder auch komplett) und nebenbei auch Dinge zu erklären (zB per Teamviewer).



  • bind() gibt's leider mehrfach in der DLL.

    Was genau meint ihr mit Proxy? Soll ein externer Prozess den UDP-traffic abfangen? Ist das wirklich einfacher?



  • Falcon Punch schrieb:

    Soll ein externer Prozess den UDP-traffic abfangen? Ist das wirklich einfacher?

    Eher nicht.

    Ich würd's dann mal mit nem Codecave oder Detours-Hook versuchen.



  • Okay, ich habe mal eine Code Cave probiert. Einfach per OllyDbg etwas Code in das Datensegment der DLL gelegt, sieht jetzt ungefähr so aus:

    ...
    
    JMP <MyFirstCodeCave>
    NOP
    NOP
    PUSH ... ; pushes für Bind <--- <ReturnPoint>
    PUSH ...
    ...
    CALL <ws2_32.dll.bind>
    
    ...
    
    PUSH 0 ; pushes für MessageBoxA
    PUSH <MyString> // diese Pointer zeigen irgendwo in den Datensegment, wo die Bytes der Zeichenkette einfach rumliegen
    PUSH <MyTitle>
    PUSH 0
    CALL <user32.dll.MessageBoxA>
    ... ; ein paar Befehle, die durch die Modifikation überschrieben wurden
    JMP <ReturnPoint>
    

    Die Adressen <MyString> und <MyTitle> habe ich hardgecodet, genauso wie die JMP-Adressen (im Debugger fangen alle bei 0x1000000 an, was wohl typisch für DLLs ist). Wenn ich nun das Programm starte, haben sich die ganzen Adressen geändert (was irgendwo logisch ist), allerdings habe ich den Debugger mal drüberlaufen lassen und sehe, dass die JMPs alle funktionieren, nur z.B. <MyString> liegt falsch. Wieso funktioniert JMPs immernoch?
    Ziel der Übung ist bei mir, dass ich hier nun mit Assembler LoadLibrary() und GetProcAddress() aufrufen will, aber dafür muss ich ja ein paar Strings ablegen und deren Adressen kennen. Was kann ich da machen?



  • Falcon Punch schrieb:

    Ziel der Übung ist bei mir, dass ich hier nun mit Assembler LoadLibrary() und GetProcAddress() aufrufen will, aber dafür muss ich ja ein paar Strings ablegen und deren Adressen kennen. Was kann ich da machen?

    Brauchst du das? Kannst du die IAT der Dll nicht statisch so modifizieren, dass deine zusätzliche Dll mitgeladen und eine Funktion importiert wird? Dann könntest du in der Initialisierungsroutine der Dll schon Code ausführen, und du hättest dann auch relativ problemlos die Adresse deiner Funktion im Speicher.



  • > Brauchst du das? Kannst du die IAT der Dll nicht statisch so modifizieren, dass deine zusätzliche Dll mitgeladen und eine Funktion importiert wird?

    Das wäre natürlich extrem praktisch. Aber ist das einfacher? Kommt mir mindestens genauso schwierig vor, die PE zu verändern.



  • Ich glaube mit CFF Explorer geht das mit der IAT. Alternativ injecten oder DetourCreateProcessWithDll nutzen.

    In ASM könntest du glaube ich auch den String char für char auf den Stack pushen und so übergeben.

    Das Codecave kannst du übrigens auch in die DLL packen, die du dann zB. injectest. Stichwort __declspec(naked). Innerhalb solch einer Funktion gibt es allerdings ein paar Beschränkungen. Wenn du darin aber wieder eine (normale) Funktion aufrust, sollten sich diese aufheben (iirc).



  • Falcon Punch schrieb:

    Das wäre natürlich extrem praktisch. Aber ist das einfacher? Kommt mir mindestens genauso schwierig vor, die PE zu verändern.

    Ich kann dir jetzt keine Tools nennen (aber der von Gregson genannte CFF Explorer schaut interessant aus), aber ich stell mir das relativ einfach vor, in dem Sinne, dass es da eben schon fertige Tools geben müsste, die einfach paar Sections von der PE Datei anpassen. Da muss du nichts programmieren oder austüfteln.


Anmelden zum Antworten