Sie sind auf Seite 1von 27

Hooking Tutorial

Ziele, Theorie, Praxis

By SilverDeath

Jaja, Protzig - ich wei^^

Aber irgendwie muss ich ja die Titelseite fllen...

Keine Sorge, kommt nicht wieder vor...

...

Vorwort:
Als Allererstes erstmal ein Dankeschn an: GameDeception - http://www.gamedeception.net/ OldSchoolHack - http://www.oldschoolhack.de/ TheBlackSlayer - http://www.oldschoolhack.de/forum/games/7718,releaseother-holzigto-injector-v13.html xNopex - http://www.elitepvpers.com/forum/coding-tutorials/518465-c-eigene-detours-teil1.html | http://www.elitepvpers.com/forum/coding-tutorials/519256-c-eigene-detours-teil2.html

Ein spezieller Dank geht hier noch an KN4CK3R, der die Ehre (^.^) hatte die Vorabversionen zu lesen und mich auf Fehler/Unstimmigkeiten/Unschnheiten/etc. hinwies. (Wenn Ihr noch Fehler findet - Alles KN4CK3Rs schuld - er hat sie nicht gesehen!^^) Warum schreibe ich dieses Tutorial? Aus teilweise nicht ganz uneigenntzigen Grnden. Man lernt selber, wenn man anderen erklrt. Weil ich selber gerade an dem Thema arbeite. Weil ich Tutorials mag und bisher kein ordentliches Tutorial zu dem Thema gefunden habe. Darum. - muss man fr alles einen Grund haben?

Wie schreibe ich dieses Tutorial? Ich neige dazu, wie Ihr vielleicht schon in "Warum" gesehen habt, alles immer in eine Art "Schritt-Tutorial", oder tabellarisch zu unterteilen gewhnt euch daran^^ Rechtschreibfehler solltet Ihr kaum finden, wenn Ihr welche findet, drft Ihr diese NICHT behalten, smtliche Rechtschreibfehler von mir haben ein individuelles Copyright und sind sofort an ihren Besitzer (mich) auszuhndigen! Eigentlich schreibe ich am Pc immer Alles grundstzlich klein - geht schneller und strt kaum beim Lesen, aber ich werde mich ausnahmsweise mal bemhen auf GrO- uND kLeiNSChrEIbuNg zu achten - keine Garantie... Ab und zu werde ich darauf hinweisen erst einmal selber nachzudenken und dann erst weiter zu lesen, lernt man mehr und - vielleicht habt ihr ja bessere Ideen als ich? Ich fge auch fters Querverweise ein, diese liest man geflligst, es sei denn, man will dumm bleiben. Nein, das ist kein Witz, wer die Querverweise nicht liest, wird NICHTS, aber auch wirklich GARNICHTS in diesem Tutorial verstehen, auch nicht "Erst einmal durchlesen, nachher kann man ja immer noch gucken...", Ihr werdet nach den ersten paar nicht gelesenen Querverweisen nichts mehr verstehen und das Tutorial seufzend lschen. Was braucht man fr dieses Tutorial? Pflicht:

Brain.exe Visual Studio - (ich benutze Visual Studio 2010, keine Garantie, das in der Express Version alles gleich aussieht...) Ida Pro Free Ollydbg Einen Injector Ein aktuelles DirectX SDK - (Darin findet ihr auch die beiden Example-Programme, falls ihr aus irgendeinem Grund nur diese PDF habt) Tiefere C/C++ Kenntnisse, oder hnliche Programmiersprache mit der man Dlls erstellen kann - alle Codesamples sind in C/C++, wenn Du diese Programmiersprache nicht ordentlich beherrscht, also z.B. nicht weit wie man Pointer dereferenziert etc. kannst du DIREKT HIER aufhren zu lesen!

Ansonsten ntzlich: Dependency Walker - http://www.dependencywalker.com/ Assembler Kenntnisse sind hilfreich - Hier eine Referenz. Downloaden und dann gehts direkt weiter mit der Hauptfrage dieses Tutorials:

Was ist Hooking?


In computer programming, the term hooking covers a range of techniques used to alter or augment the behavior of an operating system, of applications, or of other software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a "hook". -> https://en.wikipedia.org/wiki/Hooking Das sagt eigentlich schon alles. Hooking ist eine Methode um Funktionsaufrufe und sonstige Kommunikation zwischen Programmteilen mit anderen Programmteilen, dem System, anderen Programmen, etc. "umzuleiten", bzw. zu verflschen.

Was bringt einem so etwas, bezogen auf Gamehacking? In diesem Tutorial werde ich speziell auf 2 weitgehend Spielunabhngige Hooks eingehen. DirectInputHook - Erlaubt einem Tastendrcke abzufangen, oder selber welche an das Spiel zu senden. D3DHook - Erlaubt einem eigene Dinge auf das Spiel zu zeichnen, oder die Zeichnung vom Spiel zu manipulieren.

Die Technik des Hookens, die ich hier erlutern werde, ist grundlegend fr den Bereich GameHacking. Dreh- und Angelpunkt eines Hooks ist es in ein fremdes Programm erstmal hineinzukommen. Dafr hilft uns die Technik der DLL-Injection, auf die ich hier aber nicht nher eingehen werde (Vielleicht nchstes Tutorial?). Wir nehmen einfach Igromanrus DLL-Injector als gegeben und funktionierend hin.

Bauen der DLL


Bauen wir uns erst einmal eine leere DLL: HookingTutorial 1.0 #include <Windows.h> BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD Reason,LPVOID Reserved) { switch(Reason) { case DLL_PROCESS_ATTACH: break; case DLL_PROCESS_DETACH: break; } return 1; }

Schn leer, oder? Damit wir auch schn im Zielprozess arbeiten knnen erstellen wir uns nun einen Thread: HookingTutorial 1.1 DWORD WINAPI HookThread(void) { while (true) { ; // TODO: Add Code } return 0; }

Da es gerade beim Lernen zu einer Menge Fehler kommen wird erstellen wir uns noch eine Logfunktion:

HookingTutorial 1.2 #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <time.h> void add_log(char* format, ...) { HANDLE filehandle; DWORD dwReadBytes; char buffer[2048]; char writebuffer[2048]; va_list args; va_start(args, format); vsprintf (buffer, format, args); filehandle = CreateFile(L"Log.txt", GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0); SetFilePointer(filehandle, 0, 0, FILE_END); char date[18]; _strdate(date); date[8] = ' '; _strtime(date+9); sprintf_s(writebuffer, 2048, "Log Added (%s): %s\r\n", date, buffer); WriteFile(filehandle, writebuffer, strlen(writebuffer), &dwReadBytes, 0); CloseHandle(filehandle); }

Jetzt knnen wir den Thread in der DLL-Main erstellen und einen eventuellen Fehlschlag loggen: HookingTutorial 1.3 HANDLE tmpHandle = NULL; BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD Reason,LPVOID Reserved) { switch(Reason) { case DLL_PROCESS_ATTACH: add_log("==========LOG START=========="); add_log("DLL Attached"); add_log("Creating Thread..."); tmpHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&HookThread, 0, 0, 0); if (!tmpHandle) { add_log("ThreadCreation Failed!"); } break; case DLL_PROCESS_DETACH: add_log("DLL Detached"); add_log("==========LOG END==========\n\n\n"); break; } return 1; }

Damit sieht unser gesamter Code nun so aus: HookingTutorial 1.4 #define _CRT_SECURE_NO_WARNINGS #include <Windows.h> #include <cstdio> #include <time.h> DWORD WINAPI HookThread(); void add_log(char* format, ...); HANDLE tmpHandle = NULL; BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD Reason,LPVOID Reserved) { switch(Reason) { case DLL_PROCESS_ATTACH: add_log("==========LOG START==========");

add_log("DLL Attached"); add_log("Creating Thread..."); tmpHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&HookThread, 0, 0, 0); if (!tmpHandle) { add_log("ThreadCreation Failed!"); } break; case DLL_PROCESS_DETACH: add_log("DLL Detached"); add_log("==========LOG END==========\n\n\n"); break; } return 1; } DWORD WINAPI HookThread(void) { add_log("Thread Created"); while (true) { Sleep(1000); ; // TODO: Add Code } return 0; } void add_log(char* format, ...) { HANDLE filehandle; DWORD dwReadBytes; char buffer[2048]; char writebuffer[2048]; va_list args; va_start(args, format); vsprintf (buffer, format, args); filehandle = CreateFile(L"Log.txt", GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0); SetFilePointer(filehandle, 0, 0, FILE_END); char date[18]; _strdate(date); date[8] = ' '; _strtime(date+9); sprintf_s(writebuffer, 2048, "Log Added (%s): %s\r\n", date, buffer); WriteFile(filehandle, writebuffer, strlen(writebuffer), &dwReadBytes, 0); CloseHandle(filehandle); }

So, unsere DLL haben wir nun, jetzt mssen wir uns berlegen, was wir nun wollen: Wir wollen eine Funktion die irgendwo im Speicher des Programmes liegt durch unsere eigene Funktion ersetzen. Wie machen wir das? Da C/C++ eine Sprache ist, die kompiliert wird, kann man nicht einfach so Sourcecode einschleusen - So geht es schonmal nicht...^^ Wenn man Assemblerkentnisse hat, kann man sich an dieser Stelle erstmal Gedanken machen, bevor man weiterliest...

Detours
Was sind Detours?
Detours ist eine Mglichkeit genau das zu tun, was wir wollen, nmlich eine Funktion auf unsere eigene Funktion umzubiegen. Theoretisch stellt Microsoft eine DetoursLib bereit, aber wir wollen natrlich selber Detouren, wre ja langweilig sonst. Also, wir brauchen eine Funktion 'DetourFunc', die eine beliebige Funktion auf unsere eigene umbiegt. Daraus ergeben sich folgende Anforderungen an unsere DetourFunc: Die Funktion bekommt 2 Adressen (Originalfunktion und unsere Hookfunktion) bergeben und biegt nun die 1. Funktion auf die 2. um. Das Ganze sollte mglichst C-Style sein und kein Assembler beinhalten. Die Funktion sollte mglichst universell sein. Mglichst sollten wir noch in der Lage sein die Originalfunktion nach dem Detour benutzen zu knnen.

Bauen der DetourFunc


Aus unseren obigen berlegungen ergibt sich nun schonmal der Prototyp der DetourFunc: HookingTutorial 2.0 void DetourFunc(PBYTE src, PBYTE dst);

So, was mssen wir nun tun? Wer sich in Assembler auskennt, kennt den sogenannten 'jmp'-Befehl. Dieser 'jmp'-Befehl ist das Equivalent zu dem 'goto'-Befehl aus C/C++, nur statt einem Label springt man eine relative Adresse an. Dieser 'jmp'-Befehl ist 5 Bytes gro, das erste Byte besteht aus dem Byte-Code 0xE9 direkt darauf folgt die relative Adresse zu der gesprungen werden soll. Wie gesagt, ist die Adresse relativ, d.h. die relative Adresse wre in unserem Fall (ZielFunktion-Originalfunktion)-5 (fr die 5 jmp bytes.). Adressen sind ein DWORD, also 4 Bytes gro, zusammen mit dem Jmp-Bytecode '0xE9' ergibt das unsere 5 Bytes. Wenn wir jetzt eine Hookfunktion bauen, die genau so aussieht wie die Originalfunktion, also gleiche Parameter/Rckgabetyp und dann die ersten 5 Bytes der Originalfunktion mit einem Jump zu unserer Hookfunktion berschreiben, sollte statt der Originalfunktion nun immer unsere Hookfunktion aufgerufen werden! Gut, implementieren wir das mal in C/C++: HookingTutorial 2.1 void DetourFunc(PBYTE src, const PBYTE dst) // Adresse der OriginalFunktion und Adresse der HookFunktion { src[0] = 0xE9; // jmp befehl in den anfang der Originalfunktion reinschreiben *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; // relative adresse zu dst eintragen return; }

Daran wird Windows aber zu meckern haben, leider darf man nicht in den code Bereich eines Programms einfach so reinschreiben. Also Mssen wir uns mit VirtualProtect die entsprechenden Rechte zusichern: HookingTutorial 2.2 void DetourFunc(PBYTE src, const PBYTE dst) // Adresse der OriginalFunktion und Adresse der HookFunktion { DWORD dwback; VirtualProtect(src, 5, PAGE_READWRITE, &dwback); // save ^ und readwrite rechte geben src[0] = 0xE9; // jmp befehl in den anfang der Originalfunktion reinschreiben *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; // relative adresse zu dst eintragen VirtualProtect(src, 5, dwback, &dwback); // vp wieder herstellen. return; }

Gut, das drfte funktionieren, aber haben wir alles beachtet? Gehen wir noch einmal unsere Bedingungen durch: Check Die Funktion bekommt 2 Adressen (Originalfunktion und unsere Hookfunktion) bergeben und biegt nun die 1. Funktion auf die 2. um.

Das Ganze sollte mglichst C-Style sein und kein Assembler beinhalten. Bis auf das schreiben von 0xE9 und der Adresse alles komplett C-Style, kein Assembler befehl. Die Funktion sollte mglichst universell sein. Sieht gut aus, oder? Mglichst sollen wir noch in der Lage sein die Originalfunktion nach dem Detour benutzen knnen. Verdammt. Was haben wir nochmal gemacht? Genau, wir haben die ersten 5 Bytes der Originalfunktion mit einem Jump berschrieben. Ein aufruf der Originalfunktion landet also zwangslufig in unserer Hook-Funktion. Selbst wenn wir jetzt den Jump "berspringen" also die funktion nach dem jump aufrufen (Originalfunktion+5), sind die ersten 5 Bytes endgltig weg. In 99% der Flle ist damit die Originalfunktion zerschossen und kaputt.

Neues Problem - neues Glck!


Wie lsen wir das? Naheliegend ist natrlich die ersten 5 Bytes zu speichern - und dann? Knnen schlecht jedesmal wenn wir die Originalfunktion brauchen die 5 gespeicherten Bytes wieder in die Originalfunktion schreiben... Die ganze Originalfunktion kopieren? Zu kompliziert. Wieder erst einmal selber nachdenken, ist ziemlich simpel.

Mglicher Lsungsweg: Wir Speichern die ersten 5+x Bytes ab, packen diese in ein sogenanntes "Trampolin"-buffer, das nach den gespeicherten Bytes einen Jump auf die Originalfunktion NACH den ersten 5 bytes beinhaltet. Damit ist die funktion wieder hergestellt, nur, dass wir nun statt der Originalfunktion unser Trampolin aufrufen, dass die abgespeicherten Bytes ausfhrt und dann zum Rest der Originalfunktion springt. Warum 5 + x Bytes abspeichern? Assembler Befehle sind unterschiedlich lang, wenn man einen Befehl "auseinanderreit" indem man nur die ersten 5 Bytes von einem z.B. 8 Byte langen Befehl kopiert und ausfhrt, zerschiets das ganze. Deswegen muss man jedesmal nachgucken, ob/wo am Anfang der zu hookenden Funktion die Assembler Befehle genau auf dem mindestens 5. Byte enden. Wie man das macht - komm ich nachher drauf zurck. Api Funktionen haben in der Regel genau 5 Byte-Code am Anfang.

Korrigieren der DetourFunc


Jetzt schreiben wir uns erstmal unsere neue DetourFunc: Sie soll jetzt einen Pointer auf das Trampolin zurckgeben und noch zustzlich die Anzahl der Bytes bergeben bekommen die sie abspeichern soll. Damit sieht der Prototyp nun folgendermaen aus: HookingTutorial 2.3 void* DetourFunc(PBYTE src, const PBYTE dst, const int len);

So, was nun? Als erstes sollten wir uns den Speicher fr unser Trampolin allokieren und die <len> Bytes aus Source hineinkopieren:

HookingTutorial 2.4 void DetourFunc(PBYTE src, const PBYTE dst, const int len) { DWORD dwback; BYTE* jmp = (BYTE*)malloc(len+5); VirtualProtect(src, 5, PAGE_READWRITE, &dwback); memcpy(jmp, src, len); src[0] = 0xE9; *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; VirtualProtect(src, 5, dwback, &dwback); return; }

Gesagt, getan, was fehlt noch? Genau, noch den Jump am ende des Trampolins einfgen, das geht genau so wie beim Umschreiben vorher, nur ist die relative Adresse diesmal (Originalfunktionsptr+<len>-(Trampolinptr+len))-5: HookingTutorial 2.5 void DetourFunc(PBYTE src, const PBYTE dst, const int len) { DWORD dwback; BYTE* jmp = (BYTE*)malloc(len+5); VirtualProtect(src, 5, PAGE_READWRITE, &dwback); memcpy(jmp, src, len); jmp += len; jmp[0] = 0xE9; *(DWORD*)(jmp+1) = (DWORD)(src + len - jmp) - 5; src[0] = 0xE9; *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; VirtualProtect(src, 5, dwback, &dwback); return; }

Fehlt noch irgendetwas? Ja, wir sollten vielleicht unserem Trampolin noch Execute Rechte geben, sonst meckert Windows wieder: HookingTutorial 2.6 void* DetourFunc(PBYTE src, const PBYTE dst, const int len) { DWORD dwback; BYTE* jmp = (BYTE*)malloc(len+5); VirtualProtect(src, len, PAGE_READWRITE, &dwback); memcpy(jmp, src, len); jmp += len; jmp[0] = 0xE9; *(DWORD*)(jmp+1) = (DWORD)(src + len - jmp) - 5; src[0] = 0xE9; *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; VirtualProtect(src, len, dwback, &dwback); VirtualProtect(jmp-len, len+5, PAGE_EXECUTE_READWRITE, &dwback); return (jmp - len); }

So, fertig! Nun haben wir das Handwerkszeug um eine Funktion zu hooken: Eine DLL Einen DLL injector Eine DetourFunktion

Fehlt uns noch was? Richtig, eine Zielanwendung inklusive einer Funktion, die wir hooken knnen!

Der Erste Hook


Ich werde als erstes einen DirectInput Hook vorstellen, da dieser um einiges einfacher ist, als der D3D-Hook, auch wenn eigentlich keine groen Unterschiede bestehen, man mit einem D3D-Hook aber mehr Mglichkeiten hat und ich dort eine andere Methode der Adressfindung vorstellen werde. Diesem Tutorial liegt das Microsoft DirectInput-Example-Programm "CustomFormat.exe" bei, ffnen und anschauen. Dieses Beispiel-Programm nutzt offensichtlich DirectInput um die Mausposition abzufragen, wir werden jetzt aber abstrakt vorgehen und so tuen als wssten wir nichts ber die Zielanwendung, auer dass sie hchstwahrscheinlich DirectInput benutzt um die Mausposition abzufragen. Die einfachste Methode um zu sehen, ob das Programm DirectInput benutzt ist, mit einem Debugger zu schauen, ob das Programm die dinput8.dll importiert. Machen wir das mal, wir attachen (File->Attach) OllyDbg an das CustomFormat Sample, drcken dann auf das E oben in der Leiste um uns die ausfhrbaren Module anzeigen zu lassen, sortieren nach Name und werden prompt fndig:

Gut, das Programm ld also die DINPUT8.dll, was ist in dieser DINPUT8.dll denn drin? Freundlich wie Ollydbg ist, gibt es uns direkt den Pfad der DLL mit an, gehen wir also in den system32 Ordner, suchen uns die DLL raus und laden sie mal per Drag&Drop in Dependency Walker:

Das Programm zeigt uns die Funktionen an, die exportiert werden und mit GetProcAddress abgefragt werden knnen (Kann man sich auch in IDA unter 'Exports' ansehen). Die letzten 4 sehen allgemein aus, die erste sieht wiederum sehr interessant aus, befragen wir mal die allmchtige MSDN. Aha, die Funktion erstellt ein DirectInput Objekt und gibt ein Interface auf dieses zurck. Die meisten Hooks setzen hier an und hooken diese Create Funktion und geben ein manipuliertes Interface zurck - Nachteil hieran ist, dass der Detour der Create Funktion erfolgt sein muss, bevor das Programm die Funktion aufruft. So Etwas mag ich berhaupt nicht - ich mchte injecten und detouren, wann ich es will und nicht beim Programmstart und hoffen, dass unser Detour schnell genug war. Also mssen wir weitergehen - wie sieht dieses Interface eigentlich aus? In der DLL muss dieses Interface ja Funktionen haben, unter Anderem eine, die den aktuellen Keyboardstate abfragt, vielleicht kommen wir ja an diese irgendwie ran? Wieder werfen wir einen Blick in die MSDN. Hier finden wir eine GetDeviceStatus Funktion, diese aktualisiert aber nur den Status der Device, die bergeben wird und liefert ein OK zurck, das reicht aber nicht fr uns, wir wollen das ganze gezielt manipulieren, wir mssen weiter schauen.

There's Inception everywhere! CreateDevice, das sieht doch schon besser aus, hier wird nun eine Device erstellt, die wieder ein Interface hat, schauen wir uns das mal genauer an. Was ist denn das? GetDeviceState, das sieht doch sehr gut aus! Bekommt 2 Parameter bergeben und liefert ein HRESULT zurck. Der erste Parameter gibt die Gre des darauf im 2. Parameter folgenden Buffers an, der 2. Parameter ist ein Buffer, der nun von der Funktion mit dem Status der Device gefllt werden soll - Perfekt, wenn wir nun diesen Buffer manipulieren knnten, htten wir alles, was wir wollten. Wir sehen auch direkt etwas Hochinteressantes, nmlich dass das Data-Format c_dfDIKeyboard, das zur Abfrage der Tastatur verwendet wird 256 Bytes gro ist. Da kann man ganz einfach prfen, ob cbData == 256 ist, was hchstwahrscheinlich auf ein c_dfDIKeyboard Data Format zurckzufhren ist und nun im Buffer lpvData unser Tastaturstatus landen wird, man jetzt also ganz einfach selber Tastendrcke dort hineinschreiben knnte! Aber immer langsam mit den jungen Pferden^^ 1. Fragt unser Beispiel-Programm nur die Maus ab und benutzt eine eigene Daten-Struktur. 2. Haben wir noch nicht die Adresse der Funktion, damit wir berhaupt etwas detouren knnen... Deswegen:

Finden der Adresse der Funktion

Hierzu laden wir die DLL wieder per Drag&Drop diesmal in IDA-Pro: Nach einiger Zeit Laden & Analyisieren ist er endlich fertig und wir knnen uns nun im "Functions"-Tab alle Funktionen anzeigen lassen. Erstaunlich wie IDA ist, holt er auch direkt die Namen von den nicht exportierten Funktionen irgendwoher und zeigt sie an. Mit Alt+T lsst sich nun nach einem String suchen, mit Strg+T weitersuchen. Die Funktion die wir suchen lautet GetDeviceState - wer wei, ausprobieren kostet nichts! Finden einige Funktionen, die noch etwas hinter " GetDeviceState"- haben, wie "Slow", "Direct" etc, die interessieren uns nicht. Dann Volltreffer!

Hmm, sieht gut aus, aber wieso 3 Parameter? Wir hatten doch eben nur 2? Das liegt daran, dass GetDeviceState eine Memberfunktion von der IDirectInputDevice8 ist. Dann wird als erster Parameter immer ein Pointer auf diese Device bergeben, d.h. in Wirklichkeit sieht die Funktion so aus: HookingTutorial 3.0 HRESULT __stdcall GetDeviceState(LPDIRECTINPUTDEVICE pDevice, DWORD cbData, LPVOID lpvData);

__stdcall ist nichts Anderes als die Standard WINAPI calling Convention, die uns IDA nach einem Doppelklick auf die Funktion auch enthllt.

So, wie bekommen wir jetzt die Adresse der Funktion? Statische Adressen gehen in Windows > XP leider nicht mehr, wir mssen also dynamisch vorgehen. Ich gehe da einen funktionierenden Mittelweg, wir werden nachher noch eine andere Methode kennenlernen, die etwas aufwendiger, aber in der Regel zuverlssiger ist. Ich nehme statische Offsets zu der Adresse der ffentlichen Funktionen, die sind in der Regel tatschlich statisch, zumindest bei dieser DLLVersion, das ist auch der Nachteil daran. Wir haben eben in DependencyWalker gesehen, dass die DirectInput8Create Funktion von auen zugnglich ist, also mit GetProcAddress abrufbar ist. Wenn wir jetzt in IDA die Entfernung zwischen dieser ffentlichen Funktion und unserer GetDeviceState Funktion nehmen, bruchten wir nachher nur per GetProcAddress die Adresse der DirectInput8Create Funktion zu holen, unseren in IDA berechneten Offset raufrechnen und htten die Adresse der GetDeviceState Funktion. Direkt mal ausrechnen. Per Stringsearch finden wir in IDA auch direkt die DirectInput8Create Funktion:

Diese befindet sich nun bei 0x0C34CC8E, einmal hochschielen - wo war nochmal GetDeviceState? Ah, bei 0x0C3469DD, auch Menschen, die kein Hex im Kopf knnen, sehen, dass die Adresse der DirectInput8Create Funktion grer ist, als die der GetDeviceState Funktion, also werden wir nachher den Offset von der Adresse der DirectInput8Create Funktion subtrahieren mssen. Rechnen wir uns erstmal die Differenz zwischen den beiden Adressen aus, indem wir den Windows-Taschenrechner auf Programmer einstellen und ihn in Hex rechnen lassen. Dann 0C34CC8E - 0C3469DD (ruhig mit copy + paste) =

0x62B1, das ist unser Offset! D.h. AddressOfGetDeviceState = AddressOfDirectInput8Create - 0x62B1 . Gut, damit htten wir alles, um mit unserem Hook zu beginnen!

Der Hook
So, eben haben wir festgestellt, wie der Prototyp der GetDeviceState Funktion aussieht, dadurch ergibt sich auch direkt der Prototyp unserer Hookfunktion, schlielich haben wir gesagt, dass die Hookfunktion von auen genau so aussehen muss wie die Originalfunktion, dabei inkludieren wir auch direkt den dinput header, damit wir die Konstanten und typedefs daraus benutzen knnen.

HookingTutorial 3.1 #define DIRECTINPUT_VERSION 0x0800 #include <dinput.h> HRESULT __stdcall hkGetDeviceState(LPDIRECTINPUTDEVICE lpDevice, DWORD cbData, LPVOID lpvData);

Nun brauchen wir noch einen Pointer der auf einen Funktionstyp von GetDeviceState zeigt, dafr machen wir uns am besten einen kleinen Typedef: HookingTutorial 3.2 typedef HRESULT(__stdcall* GetDeviceState_t)(LPDIRECTINPUTDEVICE, DWORD, LPVOID); GetDeviceState_t pGetDeviceState;

Jetzt mssen wir in unserem Thread nur noch ein Handle zu der dinput8.dll holen und dann unseren Offset draufrechnen und die Funktion detouren. Dazu erstmal ein paar Deklarationen. HookingTutorial 3.3 HMODULE hModDInput8 = NULL; FARPROC dwGetDeviceState = NULL; FARPROC dwDirectInput8Create = NULL;

und vor unserer Warteschleife im Thread: HookingTutorial 3.4 while (!hModDInput8) { add_log("Searching dinput8.dll..."); hModDInput8 = GetModuleHandle(L"dinput8.dll"); Sleep(100); } add_log("Found dinput8.dll: %x !", hModDInput8);

while (!dwDirectInput8Create) { add_log("Searching GetDeviceState..."); dwDirectInput8Create = GetProcAddress(hModDInput8, "DirectInput8Create"); Sleep(100); } add_log("Found DirectInput8Create: %x !", dwDirectInput8Create); dwGetDeviceState = (FARPROC) ((DWORD)dwDirectInput8Create - 0x62B1); add_log("GetDevicestate is here (DirectInput8Create - 0x62B1): %x", dwGetDeviceState); add_log("Hooking GetDeviceState..."); pGetDeviceState = (GetDeviceState_t) DetourFunc((PBYTE) dwGetDeviceState, (PBYTE) hkGetDeviceState, 5); add_log("Hooked GetDeviceState - Trampolin: %x - New: %x !", pGetDeviceState, hkGetDeviceState);

Ich hab jetzt hier einfach als <len> 5 eingetragen, mssen wir erstmal gucken, ob das geht. Dazu stellen wir in IDA erstmal ein, dass er Opcodes anzeigen soll, dafr stellen wir in Options->General "Number of opcode bytes" auf z.B. 10.

HookingTutorial 3.5 .text:0C3469DD 8B FF .text:0C3469DF 55 .text:0C3469E0 8B EC mov push mov edi, edi ebp ebp, esp

nun sehen wir, dass ein Assembler Befehle tatschlich genau auf dem 5. Byte endet, nmlich der 3, "mov ebp, esp". Wir brauchen also nur 5 Bytes zu speichern ("8B FF 55 8B EC") und knnen diese dann mit unserem Jump berschreiben. Wars das? Noch nicht ganz, wir mssen natrlich in unsere Hookfunktion noch irgendetwas reinschreiben^^ HookingTutorial 3.6 HRESULT __stdcall hkGetDeviceState(LPDIRECTINPUTDEVICE lpDevice, DWORD cbData, LPVOID lpvData) { return pGetDeviceState(lpDevice, cbData, lpvData); }

So wrde es jetzt nichts anderes tun, als die Originalfunktion mit den Originalparametern aufzurufen und den Originalrckgabewert zurckgeben... Laaaangweilig. Wie wre es, wenn wir den Input per Tastendruck 'freezen' und wieder 'entfreezen' knnten? Das wre doch mal etwas. Gut, dafr ndern wir jetzt erstmal in unserem Hauptthread unsere noch leere while schleife und deklarieren einen globalen bool Freeze. HookingTutorial 3.7 bool Freeze = false;

Und zum berprfen ob die linke Alt-Taste gedrckt wird benutzen wir GetAsyncKeyState, die einen Short zurckliefert dessen Most Significant Bit wir prfen, indem wir es mit einer 15 bits shifted to left 1 UNDen, dann noch 500ms Pause, damit wir Zeit haben die Taste wieder loszulassen^^: HookingTutorial 3.8 while (true) { if (GetAsyncKeyState(VK_LMENU) & 1<<15) { Freeze = !Freeze; Sleep(500); } Sleep(10); }

Unsere Hookfunktion mssen wir auch noch anpassen, dazu erstellen wir einfach einen statischen Buffer mit 256 Bytes, - unwahrscheinlich, dass die Mausstruktur grer ist - , speichern jedesmal wenn wir noch nicht freezen den derzeitigen Status in dem Buffer ab, und wenn Freeze gesetzt ist, kopieren wir den buffer in den lpvDataBuffer und geben damit immer den gleichen gespeicherten Buffer zurck. Das sieht dann z.B. so aus:

HookingTutorial 3.9 HRESULT __stdcall hkGetDeviceState(LPDIRECTINPUTDEVICE lpDevice, DWORD cbData, LPVOID lpvData) { static BYTE buffer[256]; HRESULT temp = DI_OK; if (!Freeze) { temp = pGetDeviceState(lpDevice, cbData, lpvData); // originalfunktion aufrufen memcpy(buffer, lpvData, cbData); } else { memcpy(lpvData, buffer, cbData); } return temp; }

Unsere gesamte DLL sieht damit nun so aus: HookingTutorial 3.10 #define _CRT_SECURE_NO_WARNINGS #define DIRECTINPUT_VERSION 0x0800 #include <Windows.h> #include <cstdio> #include <time.h> #include <dinput.h> DWORD WINAPI HookThread(); void add_log(char* format, ...); void* DetourFunc(PBYTE src, const PBYTE dst, const int len); HRESULT __stdcall hkGetDeviceState(LPDIRECTINPUTDEVICE lpDevice, DWORD cbData, LPVOID lpvData); typedef HRESULT(__stdcall* GetDeviceState_t)(LPDIRECTINPUTDEVICE, DWORD, LPVOID); GetDeviceState_t pGetDeviceState; HMODULE hModDInput8 = NULL; FARPROC dwGetDeviceState = NULL; FARPROC dwDirectInput8Create = NULL; HANDLE tmpHandle = NULL; bool Freeze = false; BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD Reason,LPVOID Reserved) { switch(Reason) { case DLL_PROCESS_ATTACH: add_log("==========LOG START=========="); add_log("DLL Attached"); add_log("Creating Thread..."); tmpHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&HookThread, 0, 0, 0); if (!tmpHandle) { add_log("ThreadCreation Failed!"); } break; case DLL_PROCESS_DETACH: add_log("DLL Detached"); add_log("==========LOG END==========\n\n\n"); break; } return 1; } DWORD WINAPI HookThread(void) { add_log("Thread Created"); while (!hModDInput8) { add_log("Searching dinput8.dll..."); hModDInput8 = GetModuleHandle(L"dinput8.dll"); Sleep(100); } add_log("Found dinput8.dll: %x !", hModDInput8);

while (!dwDirectInput8Create) {

add_log("Searching GetDeviceState..."); dwDirectInput8Create = GetProcAddress(hModDInput8, "DirectInput8Create"); Sleep(100); } add_log("Found DirectInput8Create: %x !", dwDirectInput8Create); dwGetDeviceState = (FARPROC) ((DWORD)dwDirectInput8Create - 0x62B1); add_log("GetDevicestate is here (DirectInput8Create - 0x62B1): %x", dwGetDeviceState); add_log("Hooking GetDeviceState..."); pGetDeviceState = (GetDeviceState_t) DetourFunc((PBYTE) dwGetDeviceState, (PBYTE) hkGetDeviceState, 5); add_log("Hooked GetDeviceState - Trampolin: %x - New: %x !", pGetDeviceState, hkGetDeviceState); while (true) { if (GetAsyncKeyState(VK_LMENU) & 1<<15) { Freeze = !Freeze; Sleep(500); } Sleep(10); } return 0; } void add_log(char* format, ...) { HANDLE filehandle; DWORD dwReadBytes; char buffer[2048]; char writebuffer[2048]; va_list args; va_start(args, format); vsprintf (buffer, format, args); filehandle = CreateFile(L"Log.txt", GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0); SetFilePointer(filehandle, 0, 0, FILE_END); char date[18]; _strdate(date); date[8] = ' '; _strtime(date+9); sprintf_s(writebuffer, 2048, "Log Added (%s): %s\r\n", date, buffer); WriteFile(filehandle, writebuffer, strlen(writebuffer), &dwReadBytes, 0); CloseHandle(filehandle); } void* DetourFunc(PBYTE src, const PBYTE dst, const int len) { DWORD dwback; BYTE* jmp = (BYTE*)malloc(len+5); VirtualProtect(src, len, PAGE_READWRITE, &dwback); memcpy(jmp, src, len); jmp += len; jmp[0] = 0xE9; *(DWORD*)(jmp+1) = (DWORD)(src + len - jmp) - 5; src[0] = 0xE9; *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; VirtualProtect(src, len, dwback, &dwback); VirtualProtect(jmp-len, len+5, PAGE_EXECUTE_READWRITE, &dwback); return (jmp - len); } HRESULT __stdcall hkGetDeviceState(LPDIRECTINPUTDEVICE lpDevice, DWORD cbData, LPVOID lpvData) { static BYTE buffer[256]; HRESULT temp = DI_OK; if (!Freeze) { temp = pGetDeviceState(lpDevice, cbData, lpvData); // originalfunktion aufrufen memcpy(buffer, lpvData, cbData); } else { memcpy(lpvData, buffer, cbData); } return temp; }

Kaum zu glauben - aber das wars schon!

Jetzt noch den HolzigTo Injector in den Release Ordner von unserem Projekt kopieren, in der HolzSettings.ini folgendes hinein:

HookingTutorial 3.11 [Settings] WinName=CustomFormat.exe DelayTime=5000

Dann die Injector.exe in den Namen unserer DLL umbenennen, also in meinem Fall wre das jetzt "HookingTutorialDLL.exe". Dann die CustomFormat.exe starten und den Injector starten:

Jetzt noch auf AutoInject klicken, wieder ins CustomFormat Sample Fenster gehen, ca. 5 Sekunden warten bis das Injector Fenster weggeht, Maus bewegen, linke Alt-Taste drcken, nochmal Maus bewegen, staunen, nochmal linke Alt-Taste Drcken, wieder Maus bewegen -> Pause machen/mit dem bisher Gelernten herumspielen/etwas essen... Gleich wird es nochmal ein oder zwei Ticken komplexer^^

Der D3DHook
Im Grunde ist die Vorgehensweise gleich wie beim DirectInputhook, ich werde hier aber nun die zuverlssigere Methode der Adressfindung vorstellen. Das Zielprogramm konnte ich leider nicht beilegen, ihr findet es aber in eurem DirectX SDK unter "[...]\Microsoft DirectX SDK (June 2010)\Samples\C++\Direct3D\Bin\x86\MultAnimation.exe". Diesmal heit die gesuchte DLL "d3d9.dll". Einen Attach mit Ollydbug und einen DLL-Scan mit DependencyWalker spter, sehen wir, dass das Programm DirectX 9 benutzt und die Funktion "Direct3DCreate9" unserer "DirectInput8Create" Funktion verdchtig hnlich sieht. Wieder das gleiche Spiel wie beim DirectInput-Hook, MSDN -> Ah, es wird wieder ein Interface erstellt, das wiederum ein CreateDevice besitzt, dessen Dessen Device-Interface E_I_N_I_G_E (und mehr) interessante Funktionen beherbergt. Wir werden jetzt erst einmal endscene() hooken. Diese Funktion wird immer am Ende eines Zeichenblocks ausgefhrt, in einem gut programmierten Programm sollte man pro Frame genau einen BeginScene und einen EndScene Aufruf haben, aber in der heutigen Zeit, wo immer alles schnell, schnell, schnell gehen muss beim Programmieren sieht man schon mal, dass das rund ~30x pro Frame passiert... Normalerweise wrden wir jetzt in IDA die Differenz zwischen der ffentlichen Funktion Direct3DCreate9 und endscene() ausrechnen, diesmal gehen wir aber anders vor. Diesmal benutzen wir um die Adresse der EndScene Funktion zu finden, ein sogenanntes "Search-Pattern", also ein Suchmuster. Dazu benutzen wir diese beiden Funktionen hier: HookingTutorial 4.0 bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) { for(;*szMask;++szMask,++pData,++bMask) { if(*szMask=='x' && *pData!=*bMask ) { return false; } } return (*szMask) == NULL; } DWORD dwFindPattern(DWORD dwAddress,DWORD dwLen,BYTE *bMask,char * szMask) { for(DWORD i=0; i < dwLen; i++) { if( bDataCompare( (BYTE*)( dwAddress+i ),bMask,szMask) ) { return (DWORD)(dwAddress+i); } } return 0; }

Wie man sieht, bekommt die dwFindPattern Funktion 4 Parameter: Eine Adresse an der er anfngt zu suchen dwLen die Lnge der Daten, die er durchsuchen soll Eine ByteMaske Eine char* Suchmaske

Diese dwFindPattern Funktion sucht also nach einer bestimmten ByteAbfolge in einem bestimmten Datenbereich und gibt die Adresse davon zurck, wenn er sie findet. Solche Masken knnen z.B. so aus sehen: HookingTutorial 4.0 "\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86" "xx????xx????xx"

So durchsucht er einen Datenbereich nach "C7 06 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 89 86", wobei die Fragezeichen fr unbekannte/beliebige Bytes stehen. So wrde er z.B. folgenden Ausdruck finden und dessen Adresse zurckgeben:

Hooking Tutorial 4.1 SuchMaske1: SuchMaske2: Speicher: C7 06 00 00 00 00 89 86 00 00 00 00 89 86 x x ? ? ? ? x x ? ? ? ? x x C7 06 12 34 56 78 89 86 87 65 43 21 89 86

Gut, wir knnen also nun bestimmte Byte Abfolgen suchen, was bringt uns das? Dazu muss man erstmal wissen, was VTables sind. Kurz, es ist eine Tabelle im Speicher, also ein Speicherabschnitt in dem hintereinander Adressen von allen virtuellen Methoden einer Klasse stehen. Mit dem Wissen, dass es so eine Tabelle gibt, wissen wir, dass es also irgendwo eine Cross-Referenz auf unsere endscene Funktion geben muss. Irgendwo wird es auch einen Pointer auf diese VTable geben, der direkt im Code steht, diese Code-Stelle, drfte auch bei Updates hchstwahrscheinlich unangetastet bleiben. Fr diese Code Stelle knnte man dann ein Searchpattern anhand der Opcodes der Assembler Befehle machen. Gut, finden wir jetzt aber erstmal die VTable:

Finden der VTable


Dazu laden wir die d3d9.dll in IDA. Nach einiger Lade- && Analysezeit gehen wir wieder in unseren schnen "Functions"-Tab, wo wir diesmal die EndScene Funktion suchen. Werden auch prompt fndig, diesmal interessiert uns die Adresse allerdings nicht:

Gut, erstmal einen Doppelklick drauf, damit wir in die IDA-View-A kommen:

Gut, da wir wissen, dass es in der VTable einen Pointer auf diese Funktion geben muss knnen wir nun nach einer Cross-Referenz suchen. Das geht in IDA ganz einfach, dazu klicken wir einfach auf die Anfangsadresse der Funktion, dann klicken wir oben im Men auf "Jump"->"Jump to cross reference" oder drcken STRG+X.

In dem nun aufpoppenden Fenster hat IDA 2 Referenzen gefunden, wir nehmen einfach mal die Obere und doppelklicken auf diese.

Huh, was ist denn das?

Jede Menge Pointer auf Funktionen - direkt hintereinander! Das sieht doch richtig gut aus, das drfte unsere VTable sein, nach der wir gesucht haben! Gut, was nun? Irgendwo wird auch eine Referenz auf diese VTable sein, also scrollen wir mal ein bisschen hoch, wo die VTable anfngt und suchen auf sie ebenfalls eine Cross-Referenz:

IDA findet wieder 2, nehmen wir mal die Obere:

Aha, hier wird die Adresse von der VTable in die Adresse von [esi] reingeschoben, wer kein Assembler kann - kein Problem, eigentlich ist das auch vollkommen uninteressant fr uns. Die Adresse der VTable ist hier ja 75414C38 ist - diese Adresse steht auch hier in dem Opcode (C7 06 38 4C 41 75) drin, aber in der Little Endian form, wer will kann sich hier einlesen: Kurz - Lang. Kurz, die Bytes werden einzeln "ausgetauscht". C7 06 ist der Opcode fr den mov [esi] befehl, d.h. unsere Adresse ist "38 4C 41 75". D.h. aus: 75 41 4C 38 wurde: 38 4C 41 75 Sie wurden also kreuzber vertauscht. Das bernimmt aber eh alles der Compiler fr uns, braucht uns nicht zu interessieren, - nur etwas Hintergrundwissen^^. Was allerdings interessanter ist, ist, dass hier direkt im Code unsere Adresse stehen wird! Wenn wir jetzt also ein Search-Pattern auf die Opcodes der umliegenden Assembler Befehle machen, sollten wir diese Stelle immer wiederfinden knnen! Solche Search-Patterns sind erstaunlich effizient, es reicht in der Regel die umliegenden 2-3 Befehle zu nehmen. Wir werden jetzt den mov [esi],<Adresse> und die beiden folgenden mov befehle nehmen. D.h. unsere Suchmaske wird so aussehen: HookingTutorial 4.2 C7 06 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 89 86 C7 06 ist unser Mov [esi] - Befehl ?? ?? ?? ?? Hier kommt nun unsere gesuchte Adresse 89 86 ist der nchste Mov-Befehl ?? ?? ?? ?? Es folgt wieder irgendeine Adresse, die uns nicht im geringsten interesiert... 89 86 der nchste Mov-Befehl Dann kme theoretisch noch eine Adresse, aber die Unbekannten am Ende knnen wir uns logischerweise sparen...

Gut, erstellen wir uns nun daraus unsere beiden Suchmasken fr unsere dwFindPattern-Funktion:

HookingTutorial 4.3 C7 06 ?? ?? ?? ?? 89 86 ?? ?? ?? ?? 89 86 Daraus wird unsere erste Suchmaske: "\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86" Und unsere 2. Suchmaske: "xx????xx????xx"

Jetzt schon einmal im Hinterkopf behalten, dass unsere gesuchte Adresse hier erst an der 3. Stelle steht! D.h. wir werden auf die Adresse, die die dwFindPattern zurckliefern wird noch 2 raufaddieren mssen! Gut, womit mssen wir unsere FindPattern Funktion noch fttern? Ah, einer StartAdresse, und einer Size in der gesucht werden soll. Die Startadresse ist logischerweise der Anfang der DLL, die knnen wir uns ganz einfach mit GetModuleHandle holen, die Gre der DLL gibt uns z.B. Olly an, wobei wir von dieser etwas abziehen sollten - falls der Searchpattern nicht gefunden werden sollte geht die bDataCompare Funktion eventuell in Gre der Maske ber die Gre hinaus und es gibt eventuell eine Access-Violation. Die Gre der DLL ist laut Olly 0x1C3000 gro, ok nehmen wir 0x128000.

Die AnfangsAdresse holen wir uns per GetModuleHandle, wie auch schon vorher beim DirectInputHook. also bauen wir uns nun unsere DirectInputHook DLL ein wenig um (nicht vergessen die Include und Lib folders vom DirectX-SDK in den Projekteigenschaften festzulegen!):

Der Code
HookingTutorial 4.4 #define _CRT_SECURE_NO_WARNINGS #include <Windows.h> #include <cstdio> #include <time.h> #include <d3d9.h> #include <d3dx9.h> DWORD WINAPI HookThread(); void add_log(char* format, ...); void* DetourFunc(PBYTE src, const PBYTE dst, const int len); bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask); DWORD dwFindPattern(DWORD dwAddress,DWORD dwLen,BYTE *bMask,char * szMask); HMODULE hModD3D9 = NULL; FARPROC dwEndScene = NULL; HANDLE tmpHandle = NULL; BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD Reason,LPVOID Reserved) { switch(Reason) { case DLL_PROCESS_ATTACH: add_log("==========LOG START=========="); add_log("DLL Attached"); add_log("Creating Thread..."); tmpHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&HookThread, 0, 0, 0); if (!tmpHandle) { add_log("ThreadCreation Failed!"); } break; case DLL_PROCESS_DETACH: add_log("DLL Detached"); add_log("==========LOG END==========\n\n\n"); break; } return 1;

} DWORD WINAPI HookThread(void) { add_log("Thread Created"); while (!hModD3D9) { add_log("Searching d3d9.dll..."); hModD3D9 = GetModuleHandle(L"d3d9.dll"); Sleep(100); } add_log("Found d3d9.dll: %x !", hModD3D9); while (true) { Sleep(500); } return 0; } void add_log(char* format, ...) { HANDLE filehandle; DWORD dwReadBytes; char buffer[2048]; char writebuffer[2048]; va_list args; va_start(args, format); vsprintf (buffer, format, args); filehandle = CreateFile(L"Log.txt", GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0); SetFilePointer(filehandle, 0, 0, FILE_END); char date[18]; _strdate(date); date[8] = ' '; _strtime(date+9); sprintf_s(writebuffer, 2048, "Log Added (%s): %s\r\n", date, buffer); WriteFile(filehandle, writebuffer, strlen(writebuffer), &dwReadBytes, 0); CloseHandle(filehandle); } void* DetourFunc(PBYTE src, const PBYTE dst, const int len) { DWORD dwback; BYTE* jmp = (BYTE*)malloc(len+5); VirtualProtect(src, len, PAGE_READWRITE, &dwback); memcpy(jmp, src, len); jmp += len; jmp[0] = 0xE9; *(DWORD*)(jmp+1) = (DWORD)(src + len - jmp) - 5; src[0] = 0xE9; *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; VirtualProtect(src, len, dwback, &dwback); VirtualProtect(jmp-len, len+5, PAGE_EXECUTE_READWRITE, &dwback); return (jmp - len); } bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) { for(;*szMask;++szMask,++pData,++bMask) { if(*szMask=='x' && *pData!=*bMask ) { return false; } } return (*szMask) == NULL; } DWORD dwFindPattern(DWORD dwAddress,DWORD dwLen,BYTE *bMask,char * szMask) { for(DWORD i=0; i < dwLen; i++) { if( bDataCompare( (BYTE*)( dwAddress+i ),bMask,szMask) ) { return (DWORD)(dwAddress+i); } } return 0; }

Gut, damit htten wir schon einmal die Grundstruktur. Was brauchen wir jetzt noch alles? Eine Hookfunktion die von auen genau so aussieht wie die Originalfunktion und irgendetwas sollten wir darin mglichst auch machen einen Pointer auf die VTable einen tempptr in dem wir die gefundene Adresse zwischenspeichern knnen,

Gut, fangen wir mal an: Die EndScene-Funktion ist ja wieder eine MemberFunktion von IDirect3DDevice9, d.h. wir bekommen einen Pointer auf diese Device. Dann brauchen wir auch noch unseren typedef und einen Funktionspointer davon. Das sieht dann so aus: HookingTutorial 4.5 HRESULT __stdcall hkEndScene(LPDIRECT3DDEVICE9 pDevice); typedef HRESULT(__stdcall* EndScene_t)(LPDIRECT3DDEVICE9); EndScene_t pEndScene;

Was fehlt noch -> *Auf Checkliste schiel*? Ah, ein Pointer auf die VTable, das ist einfach ein DWORD Pointer und dann brauchen wir noch eine tempadresse. Also: HookingTutorial 4.6 DWORD* VTableStart = NULL; DWORD tempadd = NULL;

Gut, jetzt mssen wir noch unsere hkEndScene Funktion definieren: HookingTutorial 4.7 HRESULT __stdcall hkEndScene(LPDIRECT3DDEVICE9 pDevice) { return pEndScene(pDevice); }

Was werd ich jetzt noch zu meckern haben? Richtig, nichts in der Funktion zu machen ist laaaangweilig! Ich wrde sagen, wir benutzen mal die Clear-Funktion der Direct3DDevice und malen uns ein schnes Quadrat links oben ins Fenster rein. Mmh ok, da brauchen wir noch eine D3DCOLOR, erstellen wir uns die mal: HookingTutorial 4.8 const D3DCOLOR txtGreen = D3DCOLOR_ARGB(255, 0, 255, 0);

Das Rectangle knnen wir uns auch in der Hookfunktion erstellen, also sieht die dann so aus: HookingTutorial 4.8 HRESULT __stdcall hkEndScene(LPDIRECT3DDEVICE9 pDevice) { D3DRECT rect = {10, 10, 110, 110}; pDevice->Clear(1, &rect, D3DCLEAR_TARGET, txtGreen, 0, 0); return pEndScene(pDevice); }

So, was fehlt noch? Mmmh unsere Hookfunktion wird noch garnicht aufgerufen, vielleicht sollten wir auch noch detouren?

Okok, dazu mssen wir erstmal unsere VTable finden, also fgen wir ein paar Zeilen in unserem Thread Hinzu: HookingTutorial 4.9 tempadd = dwFindPattern( (DWORD)hModD3D9, 0x128000, (PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86", "xx????xx????xx"); VTableStart = (DWORD*) *(DWORD*)(tempadd+2);

Gut, wo ist jetzt unsere EndScene-Adresse? h die stand irgendwo in der VTable, wer will kann jetzt nochmal IDA aufmachen und nachzhlen an wievielter Stelle die in der VTable stand, oder wir schauen einfach einmal hier nach.^^ Also noch die EndScene Adresse holen und dann hooken: HookingTutorial 4.10 dwEndScene = (FARPROC) VTableStart[42]; pEndScene = (EndScene_t) DetourFunc((PBYTE) dwEndScene, (PBYTE)hkEndScene, 5);

Das wars? Das wars! Hier noch einmal der gesamte Code: HookingTutorial 4.11 #define _CRT_SECURE_NO_WARNINGS #include <Windows.h> #include <cstdio> #include <time.h> #include <d3d9.h> #include <d3dx9.h> const D3DCOLOR txtGreen = D3DCOLOR_ARGB(255, 0, 255, 0); HRESULT __stdcall hkEndScene(LPDIRECT3DDEVICE9 pDevice); typedef HRESULT(__stdcall* EndScene_t)(LPDIRECT3DDEVICE9); EndScene_t pEndScene; DWORD WINAPI HookThread(); void add_log(char* format, ...); void* DetourFunc(PBYTE src, const PBYTE dst, const int len); bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask); DWORD dwFindPattern(DWORD dwAddress,DWORD dwLen,BYTE *bMask,char * szMask); HMODULE hModD3D9 = NULL; FARPROC dwEndScene = NULL; HANDLE tmpHandle = NULL; DWORD* VTableStart = NULL; DWORD tempadd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDll,DWORD Reason,LPVOID Reserved) { switch(Reason) { case DLL_PROCESS_ATTACH: add_log("==========LOG START=========="); add_log("DLL Attached"); add_log("Creating Thread..."); tmpHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&HookThread, 0, 0, 0); if (!tmpHandle) { add_log("ThreadCreation Failed!"); } break; case DLL_PROCESS_DETACH: add_log("DLL Detached"); add_log("==========LOG END==========\n\n\n"); break; } return 1;

} DWORD WINAPI HookThread(void) { add_log("Thread Created"); while (!hModD3D9) { add_log("Searching d3d9.dll..."); hModD3D9 = GetModuleHandle(L"d3d9.dll"); Sleep(100); } add_log("Found d3d9.dll: %x !", hModD3D9); tempadd = dwFindPattern((DWORD)hModD3D9, 0x128000, (PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86", "xx????xx????xx"); VTableStart = (DWORD*) *(DWORD*)(tempadd+2); dwEndScene = (FARPROC) VTableStart[42]; pEndScene = (EndScene_t) DetourFunc((PBYTE) dwEndScene, (PBYTE)hkEndScene, 5); while (true) { Sleep(500); } return 0; } void add_log(char* format, ...) { HANDLE filehandle; DWORD dwReadBytes; char buffer[2048]; char writebuffer[2048]; va_list args; va_start(args, format); vsprintf (buffer, format, args); filehandle = CreateFile(L"Log.txt", GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0); SetFilePointer(filehandle, 0, 0, FILE_END); char date[18]; _strdate(date); date[8] = ' '; _strtime(date+9); sprintf_s(writebuffer, 2048, "Log Added (%s): %s\r\n", date, buffer); WriteFile(filehandle, writebuffer, strlen(writebuffer), &dwReadBytes, 0); CloseHandle(filehandle); } void* DetourFunc(PBYTE src, const PBYTE dst, const int len) { DWORD dwback; BYTE* jmp = (BYTE*)malloc(len+5); VirtualProtect(src, len, PAGE_READWRITE, &dwback); memcpy(jmp, src, len); jmp += len; jmp[0] = 0xE9; *(DWORD*)(jmp+1) = (DWORD)(src + len - jmp) - 5; src[0] = 0xE9; *(DWORD*)(src+1) = (DWORD)(dst - src) - 5; VirtualProtect(src, len, dwback, &dwback); VirtualProtect(jmp-len, len+5, PAGE_EXECUTE_READWRITE, &dwback); return (jmp - len); } bool bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) { for(;*szMask;++szMask,++pData,++bMask) { if(*szMask=='x' && *pData!=*bMask ) { return false; } } return (*szMask) == NULL; } DWORD dwFindPattern(DWORD dwAddress,DWORD dwLen,BYTE *bMask,char * szMask) { for(DWORD i=0; i < dwLen; i++) { if( bDataCompare( (BYTE*)( dwAddress+i ),bMask,szMask) ) {

return (DWORD)(dwAddress+i); } } return 0; } HRESULT __stdcall hkEndScene(LPDIRECT3DDEVICE9 pDevice) { D3DRECT rect = {10, 10, 110, 110}; pDevice->Clear(1, &rect, D3DCLEAR_TARGET, txtGreen, 0, 0); return pEndScene(pDevice); }

Eine Injection spter strahlt uns folgendes entgegen:

Gratulation!
Jetzt knnt ihr noch DrawIndexedPrimitive, etc. hooken um euch Farbmodels etc. zu machen, wie man das macht, mache ich vielleicht auch noch ein ausfhrlicheres Tutorial, oder ihr schaut euch einfach mal etwas bei GameDeception und Google um! Wer will kann sich zur bung jetzt auch ein SearchPattern fr den DirectInput Hook machen, ich empfehle es sehr! Hier eine Mglichkeit: HookingTutorial 5.0 dwFindPattern((DWORD)hModDInput8, 0x29000, (PBYTE)"\x57\x8B\x00\x00\x68\x00\x00\x00\x00\x56", "xx??x????x"); VTableStart = (DWORD*) *(DWORD*)(tempadd+5); dwGetDeviceState = (FARPROC) VTableStart[9];

Nachwort

Ich hoffe es hat euch soviel Spa gemacht, wie es mir gemacht hat, und ihr habt alles verstanden^^ Man sieht Sich auf OSH - Guten Rutsch!

SilverDeath
31.12.2011