Hook MessageBoxA



  • Hallo c++ Community,

    ich benötige hilfe bei einem Hook.
    Im Inet finden sich hooks über DLL Injection zu genüge, wie jedoch sieht es aus wenn ich mein eigenes Programm "hooken" möchte?

    Also ich habe ein Programm, dieses läd eine Fremd DLL und führt dann einige Methoden dessen aus. Bis dahin alles kein Problem, jedoch ist die Fehlerbehandlung von der DLL miest, denn wenn ich falsche Daten übergebe dann wird nicht über den return oder über den Pointer dies mitgeteilt, sondern über eine MessageBox. Da ich jedoch den Fehler gerne auswerten möchte, benötige ich nun einen Hook.

    (Das ganze Passiert derzeit in Ruby über FFI, wer hier Erfahrungen hat kann mir natürlich auch damit direkt helfen, C++ Code ist auch ok oder halt einfach etwas Hilfestellung wie ich das Problem angehen könnte.)



  • Ok, mir fällt gerade eine Lösung ein weil ich im Moment mit dem Thema arbeite, aber die ist wirklich sehr brachial und erfordert doch einiges Tiefenverständnis. Zunächst aber nur mal kurz eine Warnung vorweg, dass ein Hook von MessageBoxA() dir auch nur die Möglichkeit gibt die Parameter dafür (also WindowHandle, Text, Caption, usw.) auszuwerten - wenn Dir der Text reicht, z.B. zum Parsen eines angezeigten Error Codes, dann ist alles okay. Wenn nicht, dann wirst Du zum Punkt des Fehlers gehen müssen, also dort WO die MessageBoxA() aufgerufen wird, und da wird die Sache wirklich komplex.

    Hier ist jetzt mein Vorschlag, mach Dich aber auf etwas viel Theorie gefasst. Je nach dem wie versiert Du bist wirst Du einiges schon selbst wissen, oder aber auch fast nichts. In letzterem Falle lies bitte sehr genau, es ist nie schlecht zu wissen wie sowas funktioniert:

    Jede .exe oder .dll die eine andere Funktion aus einem anderen File benötigt (in Deinem Falle benötigt Deine Fremd-DLL die MessageBoxA() aus User32.dll ) verfügt in ihrem Fileheader über eine sogenanntes "Import Directory". Das ist eine Art Inhaltsverzeichnis wo drin steht, welche "Symbole" (das sind einfach nur Namen für Speicheradressen) Du von wo "importierst". Analog dazu gibt es ein "Export Directory", was letztendlich das Gegenteil bedeutet. Aber hier ien kurzes Beispiel:

    Meine User32.dll hat in ihrem "Export Directory" folgenden Eintrag:

    Symbol: MessageBoxA
    RVA: 0x00065F69
    

    Das bedeutet so viel wie: "Ich habe ein Symbol MessageBoxA , das auf diese relative Virtuelle Adresse (RVA) gemapped ist". Die RVA ist im Prinzip nichts anderes als der Offset in der DLL dem dieses Symbol zugeordnet ist. Der Name des Symbols kann übrigens irgendwas sein, ist aber meistens der blanke Name einer Funktion ( extern "C" ) oder der dekorierte Name ( extern "C++" ).

    De facto heißt das, dass der Einstiegspunkt für die Funktion MessageBoxA() genau an der Adresse 0x00065F69 in der Datei User32.dll beginnt. Du könntest jetzt mit nem Hexeditor an diese Stelle springen und wärst am Start der Funktion. Oder ich könnte jetzt auch einfach ein LoadLibraryA() machen, und zu dem Handle den ich zurückbekomme (z.B. 0x00400000) diese Adresse hinzufügen = 0x00465F69 und das als Funktionspointer aufrufen - es würde genauso funktionieren als wenn ich ein GetProcAddress() machen würde.

    Auf der Gegenseite, also in deiner Fremd-DLL, hast Du dieses "Import Directory". Darin gibt es jetzt eine Liste mit Modulen die importiert werden, und welche Symbole daraus. Bei Dir wäre da eben die User32.dll mit einem Eintrag für MessageBoxA mit dabei. Diese Liste nennt sich Import Address Table (IAT) und ist wichtig für das was wir vorhaben. Was jetzt beim Laden der Fremd-DLL passiert ist nun das spannende, also gut aufpassen:

    Der Loader lädt Deine Fremd-DLL plump komplett in den Speicher ein, das gesamte File (und damit auch die IAT), guckt in das Import Directory und schaut welche Module importiert werden. Jedes Modul das noch nicht geladen ist lädt er zusätzlich rein und beginnt die Fremd-DLL und die importierte DLL zu verknüpfen. Das passiert über diese IAT. Der Loader guckt nach, wo MessageBoxA() aus der User32.dll im Moment im Speicher liegt und schreibt die virtuelle Adresse in den entsprechenden Eintrag in der IAT. Also das heißt, dass in der IAT der Fremd-DLL jetzt die Adresse auf die Funktion steht. Aber wie hilft uns das?

    Alle Aufrufe nach MessageBoxA() in dem Code der Fremd-DLL springen in den entsprechenden Eintrag in der IAT, und von dort dann in die eigentliche MessageBoxA() in der User32.dll :

    +----------------------+     +--------------------+    +-------------------+
    |      Fremd-DLL       |     |    IAT Fremd-DLL   |    |     User32.dll    |
    +----------------------+     +--------------------+    +-------------------+
    |                      |     |                    |    |                   |
    | Aufruf MessageBoxA() --------->  Jump-Thunk  --------->  MessageBoxA()   |
    | - Adr: <code offset> |     | - Adr: 0x12345678  |    | - Adr: 0x00465F69 |
    | - Val: 0x12345678    |     | - Val: 0x00465F69  |    | - Val: <code>     |
    +----------------------+     +--------------------+    +-------------------+
    

    Das Wort "Jump Thunk" ist im Prinzip eine Bezeichnung für eine Art "Sprungbrett" und es besteht meistens nur aus einem Sprungbefehl der den Call weiterleitet.

    Gehen wir kurz durch, was bisher alles passiert wäre wenn wir zu einem Aufruf kommen:

    1. Dein Prozess startet.
    2. Dein Prozess lädt die Fremd-DLL sowie alle anderen gebrauchten DLLs via LoadLibraryA() oder statisches linken.
    3. Bei jedem geladenen Modul wird das komplette File in den Speicher geladen.
    4. Der Loader guckt nach, wo sich die importierten Funktionen eines Moduls tatsächlich gerade im Speicher befinden, und patched die entsprechenden Einträge in der IAT.
    5. Ein Aufruf in eine andere DLL geht hardcoded in den Eintrag in der IAT und dieser, vorher korrigiert von dem Loader in Schritt 4), leitet den Aufruf direkt weiter an die gewünschte Funktion.

    Was ich jetzt vorschlage ist, dass Du irgendwann wenn Du bereit bist die Fehler auszuwerten (kann auch gleich am Start des Programmes sein, aber nach dem die Fremd-DLL geladen wurde) die Einträge in der IAT manipulierst/umbiegst. Du schreibst am Besten eine kleine eigene Funktion die der Signatur von MessageBoxA() entspricht und schreibst die Adresse der Funktion in die entsprechende Stelle in der IAT. Du überschreibst also den Wert 0x00465F69 aus obigem Beispiel mit der Adresse Deiner Funktion.

    Alle Aufrufe nach MessageBoxA() aus der Fremd-DLL würden demnach an Dich gehen - wahlweise kannst Du dann von Deiner eigenen Funktion wieder die MessageBox ausgeben. Da hättest Du einen perfekten Hook weil Du Dich wirklich direkt in den Aufruft reinhängst.

    Wenn Dir das aber zu hoch ist, oder den Aufwand nicht wert (weil der Initialaufwand ist schon extrem hoch, Du musst noch Relocation Fixups beachten, oder memoryalignte Sections), dann warte lieber bis jemand eine einfachere Lösung hat, ich bin mir sicher da gibt es welche. Ich bin halt wie anfangs erwähnt gerade in dem Umfeld am arbeiten, daher ist mir das das erste was in den Sinn kommt 😞

    Aber in jedem Fall lohnt sich's da mal bisschen rumzustochern in dem Thema, schon allein der Erfahrung wegen.

    Grüßle!



  • http://research.microsoft.com/en-us/projects/detours/

    Damit lassen sich Funktionen hooken, das oben beschriebene wird dir da abgenommen.



  • @PuerNoctis

    Mit der IAT Methode ist es nur möglich, implizit eingebundene Funktionen
    zu hooken. Bei explizit gebundenen Funktionen, mittels LoadLibrary und
    GetProcAddress, wird kein IAT-Eintrag erstellt!



  • @-lowbyte-
    Okay, das ist natürlich klar, die Adresse wird über die ExportTable der Ziel-DLL und der LoadBase ermittelt und geht dann an der IAT natürlich vorbei. Das meinst Du doch, oder? Sollte ich in meinem Beitrag evtl. noch erwähnen, danke.

    Interessant finde ich aber was patrick246 gepostet hat, das kannte ich noch gar nicht...



  • entschuldigt das ich jetzt erst schreibe, hatte das Problem am Fr. noch gelöst bekommen, ich war einfach was verwirrt. In meiner eigenen Anwendung brauche ich ja nicht die DLL zu injizieren sondern einfach normal zu laden und dann kann ich den hook Code ja einfach ausführen.
    Habe nach etwas suchen einen fertigen Code gefunden den ich nur gering anpassen musste um dann auch mit dem Fehler aus der msgBox zu arbeiten:

    #include <Windows.h>
    //#include "functions.h"
    int WINAPI OurMessageBoxA(HWND hwnd,LPCSTR text,LPCSTR title,UINT type);
    LPCSTR point;
    
    extern "C" void install_hook() {
        DWORD our_address = reinterpret_cast<DWORD>(&OurMessageBoxA);
        unsigned char funcMem[7];
        //mov eax, our_address
        funcMem[0] = 0xb8;
        funcMem[1] =  our_address & 0xFF;
        funcMem[2] = ( our_address >> 8 ) & 0xFF;
        funcMem[3] = ( our_address >> 16 ) & 0xFF;
        funcMem[4] = ( our_address >> 24 ) & 0xFF;
    
        //jmpeax
        funcMem[5]=0xff;
        funcMem[6]=0xe0;
    
        DWORD messageBoxA_address = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
    
        DWORD oldProtection;
        VirtualProtect((LPVOID)messageBoxA_address,1024,PAGE_EXECUTE_READWRITE,&oldProtection);
        memcpy((void*)messageBoxA_address,funcMem,7);
        VirtualProtect((LPVOID)messageBoxA_address,1024,oldProtection,0);
    }
    
    extern "C" LPCSTR get_text(){
        return point;
    }
    
    int WINAPI OurMessageBoxA(HWND hwnd,LPCSTR text,LPCSTR title,UINT type) 
    {
        point = text;
        return IDOK;
    }
    

    Mein Chefentwickler sagte noch, ich könne am besten den String noch kopieren falls die Fremdanwendung noch mal auf den Pointer zugreift, da ich jedoch nur synchron eine Methode aufrufe und danach den Pointer abfrage und dies keine Probleme gibt, habe ich es nun so gelassen.

    @PuerNoctis -- WOW danke dafür, das meiste wusste ich schon. Aber war schon informativ das ganze, ich wünschte mein Ausbilder würde sich auch mal so viel mühe machen >.> Aber der versteht das hier eh ja nicht 😮
    Kann ich dir eine kleine menge an Bitcoins spenden? So was muss doch belohnt werden ^^

    Achso und noch was, ich habe die ganze Zeit schon mit OllyDbg gearbeitet, wenn du das ganze dann dort debuggt, bekommst du wirklich eine schöne Sicht darauf was du da machst, kannst halt direkt sehen was wo falsch läuft im Maschinen Code ^^



  • @DeeKay0

    Bitcoins brauchst Du mir nicht spenden, ich versuch gerne zu helfen 🙂
    Ich nutze übrigens IDA Pro 😉



  • PuerNoctis schrieb:

    @DeeKay0

    Bitcoins brauchst Du mir nicht spenden, ich versuch gerne zu helfen 🙂
    Ich nutze übrigens DIA Pro 😉

    Ok, vielen dank. Habe noch nicht mit IDA Pro gearbeitet, bin froh mit OllyDbg etwas klar zu kommen. 😃 Reserve Engineering ist ja immer so ne Sache.

    ^^ Naja es wären ja nur ein paar Cent gewesen, wenn ich eine Möglichkeit sehe coins zu senden / empfangen, nutze ich diese immer. 🙂 Mag es halt vorantreiben mit den btc ^^


Anmelden zum Antworten