pBackup ist die kopie von dem überschriebenen speicher (später mehr)
lpOriginal ist die original funktions adresse
lpHook ist die funktion, die wir anspringen wollen!
lpSub ist eine zwischenfunktion, die lpHook ausführt, und auf lpReturn zurück springt.
lpReturn ist eine backup funktion, die den überschriebenen speicher ausführt, und zu der adresse von originalen+überschriebene byte länge springt.
als nächstes sollte ein globale variable erstellt werden, in der wir ALLE hook informationen speichern (auch mehrere). dazu nehme ich std::vector<typename>. das sieht so aus:
jetzt kommen wir zum interessanten teil, dem eigentlichen hook!
ein hook funktioniert so:
er überschreibt die ersten bytes einer funktion, mit den bytes die dafür sorgen dass die funktion als erstes an eine andere funktions adresse springt.
von dieser funktionsadresse KANN man die überschriebenen bytes ausführen, und an die original adresse + überschriebene byte länge springen, um die originale funktion aufzurufen.
da ich noch eine subfunktion habe die das alles für mich übernimmt (an neue adresse springen, und hinterher das backup ausführen), wird der letztere teil eher unnötig!
die subfunktion sieht bei mir so aus:
Code:
__declspec(naked) void hkSub()
{
__asm
{
pushfd //register backup
pushad //register backup
call 0xDEADBEEF //platzhalter für die neue funktion
popad //register backup laden
popfd //register backup laden
jmp 0xDEADBEEF //platzhalter für die backup funktion
}
}
die platzhalter werden später durch die "richtigen" adressen ersetzt.
Code:
[...]
#define ASM_JMP 0xE9 //byte-op-code für den JMP befehl!
[...]
SHOOKINFO AttachHook(LPVOID lpOriginal,LPVOID lpNew)
{
DWORD dwOldProtect;
SHOOKINFO info;
info.lpSub = VirtualAlloc(NULL,sizeof(&hkSub),MEM_COMMIT,PAGE_EXECUTE_READWRITE);
info.lpReturn = VirtualAlloc(NULL,10,MEM_COMMIT,PAGE_EXECUTE_READWRITE); //wie ich auf 10 komme? überschriebene bytes(5)+jmp(1)+DWORD(4)
VirtualProtect(lpOriginal,5,PAGE_EXECUTE_READWRITE,&dwOldProtect); //wie ich auf 5 komme? jmp(1)+DWORD(4)
info.pBackup = new BYTE[5];
memcpy(&info.pBackup[0],lpOriginal,5);
memcpy(lpOriginal,(void*)ASM_JMP,1); //jmp an den anfang schreiben
memcpy((void*)((DWORD)lpOriginal+1),info.lpSub,4); //adresse hinterher schreiben!
VirtualProtect(lpOriginal,5,dwOldProtect,0);
memcpy(info.lpSub,&hkSub,sizeof(&hkSub));
memcpy((void*)((DWORD)info.lpSub+3),lpNew,4); //das erste 0xDEADBEEF durch lpNew ersetzen!
memcpy((void*)((DWORD)info.lpSub+10),info.lpReturn,4); //das zweite 0xDEADBEEF durch info.lpReturn ersetzen!
memcpy(info.lpReturn,&info.pBuffer[0],5); //die überschriebenen bytes in die backup funktion schreiben
memcpy((void*)((DWORD)info.lpReturn+5),(void*)ASM_JMP,1); //jmp schreiben
memcpy((void*)((DWORD)info.lpReturn+6),(void*)((DWORD)lpOriginal+5),4); //die adresse hinterher schreiben, wo der jmp befehl auf die sub funktion zuende ist!
return info;
}
startet jetzt IDA, und analysiert das spiel! (könnte etwas dauern)
wenn IDA fertig ist, guckt mal in die register karte "Functions", und sucht nach einer funktion die interessant sein könnte. Ob die funktion euch interessiert, könnt ihr am namen lesen (wenn sie einen hat).
nehmen wir mal an, die funktion liegt an adresse 0x00618942.
dann muss euer code so aussehen:
das man das nicht 1 zu 1 verwenden kann ist klar
aber aus der erklärung kann man sich vieles ableiten
ich arbeite eh anders wenn ich es schreibe dann würd es so aussehen
und das geht ganz sicher unter 64bit
nur als bsp
1. Passen 64bit-Addressen nicht in ein DWORD (4byte unsigned long). Hier würde sich DWORD_PTR anbieten, das ist ein typedef auf unsigned long bei 32bit und ein unsigned long long bei 64bit.
2. Das du mit den 32bit Registern bei 64bit Programmen recht blöd dastehst sollte auch klar sein
3. Deine Return-Value ist sogar unter 32bit falsch, die gängigen Calling Conventions nutzen prinzipiell bei 32bit den ganzen EAX-Register zur Rückgabe, nur AL zu nutzen ist nicht gerade geschickt.
pBackup ist die kopie von dem überschriebenen speicher (später mehr)
lpOriginal ist die original funktions adresse
lpHook ist die funktion, die wir anspringen wollen!
lpSub ist eine zwischenfunktion, die lpHook ausführt, und auf lpReturn zurück springt.
lpReturn ist eine backup funktion, die den überschriebenen speicher ausführt, und zu der adresse von originalen+überschriebene byte länge springt.
als nächstes sollte ein globale variable erstellt werden, in der wir ALLE hook informationen speichern (auch mehrere). dazu nehme ich std::vector<typename>. das sieht so aus:
jetzt kommen wir zum interessanten teil, dem eigentlichen hook!
ein hook funktioniert so:
er überschreibt die ersten bytes einer funktion, mit den bytes die dafür sorgen dass die funktion als erstes an eine andere funktions adresse springt.
von dieser funktionsadresse KANN man die überschriebenen bytes ausführen, und an die original adresse + überschriebene byte länge springen, um die originale funktion aufzurufen.
da ich noch eine subfunktion habe die das alles für mich übernimmt (an neue adresse springen, und hinterher das backup ausführen), wird der letztere teil eher unnötig!
die subfunktion sieht bei mir so aus:
Code:
__declspec(naked) void hkSub()
{
__asm
{
pushfd //register backup
pushad //register backup
call 0xDEADBEEF //platzhalter für die neue funktion
popad //register backup laden
popfd //register backup laden
jmp 0xDEADBEEF //platzhalter für die backup funktion
}
}
die platzhalter werden später durch die "richtigen" adressen ersetzt.
Code:
[...]
#define ASM_JMP 0xE9 //byte-op-code für den JMP befehl!
[...]
SHOOKINFO AttachHook(LPVOID lpOriginal,LPVOID lpNew)
{
DWORD dwOldProtect;
SHOOKINFO info;
info.lpSub = VirtualAlloc(NULL,sizeof(&hkSub),MEM_COMMIT,PAGE_EXECUTE_READWRITE);
info.lpReturn = VirtualAlloc(NULL,10,MEM_COMMIT,PAGE_EXECUTE_READWRITE); //wie ich auf 10 komme? überschriebene bytes(5)+jmp(1)+DWORD(4)
VirtualProtect(lpOriginal,5,PAGE_EXECUTE_READWRITE,&dwOldProtect); //wie ich auf 5 komme? jmp(1)+DWORD(4)
info.pBackup = new BYTE[5];
memcpy(&info.pBackup[0],lpOriginal,5);
memcpy(lpOriginal,(void*)ASM_JMP,1); //jmp an den anfang schreiben
memcpy((void*)((DWORD)lpOriginal+1),info.lpSub,4); //adresse hinterher schreiben!
VirtualProtect(lpOriginal,5,dwOldProtect,0);
memcpy(info.lpSub,&hkSub,sizeof(&hkSub));
memcpy((void*)((DWORD)info.lpSub+3),lpNew,4); //das erste 0xDEADBEEF durch lpNew ersetzen!
memcpy((void*)((DWORD)info.lpSub+10),info.lpReturn,4); //das zweite 0xDEADBEEF durch info.lpReturn ersetzen!
memcpy(info.lpReturn,&info.pBuffer[0],5); //die überschriebenen bytes in die backup funktion schreiben
memcpy((void*)((DWORD)info.lpReturn+5),(void*)ASM_JMP,1); //jmp schreiben
memcpy((void*)((DWORD)info.lpReturn+6),(void*)((DWORD)lpOriginal+5),4); //die adresse hinterher schreiben, wo der jmp befehl auf die sub funktion zuende ist!
return info;
}
startet jetzt IDA, und analysiert das spiel! (könnte etwas dauern)
wenn IDA fertig ist, guckt mal in die register karte "Functions", und sucht nach einer funktion die interessant sein könnte. Ob die funktion euch interessiert, könnt ihr am namen lesen (wenn sie einen hat).
nehmen wir mal an, die funktion liegt an adresse 0x00618942.
dann muss euer code so aussehen:
ich hoffe da sind keine fehler drin, und dass es hilfreich war
ganz nett in der theorie, aber du hast etwa vergessen.
nämlich wenn am anfang der funktion nur eine instruction mit 1 byte ist und du damit auch eine zweite zum teil überschreibst.
oder wenn die instruction größer als 5 bytes ist.
dann wird das in die hose gehen, da dadurch entweder keine gültigen oder einfach die falschen instructions entstehen.
klar, bei api funktionen gibts normalerweise am anfang eine sinnlose instruction wie mov edi,edi aber bei anderen funktionen, wo sofort am anfang push ebp steht, haste ein problem.
da klappt das so nicht, denn in diesem falle müsste man die opcodes parsen und dann die richtige länge an bytes sichern. d.h. du nimmst entweder eine fertige disassembler lib, wie die von Olly oder BeaEngine oder du schreibst deine eigene Wenn du wirklich nur vor hast, den Hook am Anfang zu platzieren, kannste die meisten opcodes vernachlässigen und nur nach den gängigsten am anfang einer funktion suchen (unwahrscheinlich dass direkt am anfang zb. gecalled wird). aber wie gesagt, ganz ohne gehts nur bei api funktionen
1. Passen 64bit-Addressen nicht in ein DWORD (4byte unsigned long). Hier würde sich DWORD_PTR anbieten, das ist ein typedef auf unsigned long bei 32bit und ein unsigned long long bei 64bit.
2. Das du mit den 32bit Registern bei 64bit Programmen recht blöd dastehst sollte auch klar sein
3. Deine Return-Value ist sogar unter 32bit falsch, die gängigen Calling Conventions nutzen prinzipiell bei 32bit den ganzen EAX-Register zur Rückgabe, nur AL zu nutzen ist nicht gerade geschickt.
geht es zu 100% außerdem ist es nur ein teil von einer weit größeren funktion vergess nicht das ist nicht das 1x1
beim programmieren führen viele wege nach oben ich hab dir nur gezeigt wie ich es aufrufe die komplette funktion hab ich nicht kopiert es ist nur ein kleiner teil von weit mehr
das ist von CoD4 checkt ob ein gegener unsichtbar ist und färbt dann das boxesp um damit kannst dur die denken was da noch alles nötig ist das es klappt
da hängt noch eine riesige struct dran und es funktioniert ohne probleme
nur weil du ein wegfährst muss ich den nicht auch fahren wenn es noch andere gibt
und ruf es so auf
BOOL funktion( CEntity* Ent )
und nicht mit
__declspec
das macht schon unterschiede so nu genug diskutiert jeder macht es anderes wichtig ist das es geht
ganz nett in der theorie, aber du hast etwa vergessen.
nämlich wenn am anfang der funktion nur eine instruction mit 1 byte ist und du damit auch eine zweite zum teil überschreibst.
oder wenn die instruction größer als 5 bytes ist.
dann wird das in die hose gehen, da dadurch entweder keine gültigen oder einfach die falschen instructions entstehen.
klar, bei api funktionen gibts normalerweise am anfang eine sinnlose instruction wie mov edi,edi aber bei anderen funktionen, wo sofort am anfang push ebp steht, haste ein problem.
da klappt das so nicht, denn in diesem falle müsste man die opcodes parsen und dann die richtige länge an bytes sichern. d.h. du nimmst entweder eine fertige disassembler lib, wie die von Olly oder BeaEngine oder du schreibst deine eigene Wenn du wirklich nur vor hast, den Hook am Anfang zu platzieren, kannste die meisten opcodes vernachlässigen und nur nach den gängigsten am anfang einer funktion suchen (unwahrscheinlich dass direkt am anfang zb. gecalled wird). aber wie gesagt, ganz ohne gehts nur bei api funktionen
ich habe dabei ziemlich allgemein gedacht
natürlich wenn man ganz am anfang z.b.
Code:
mov edi, edi
call 0x00000001
kommt am ende nur scheisse raus!
das sollte eigendlich nur eine grundanleitung sein
[c++]hooks 11/02/2009 - C/C++ - 2 Replies brauche hilfe bei meinen hooks!
ms detours will ich nich benutzen (vorallem weil die nich für meine zwecke laufen :D), ausserdem um weiterzu lernen eigenes system schreiben!
mein problem is wenn ich in einer funktion die ersten 5 bytes mit jmp dword überschreibe, und z.b. das 2 byte auch nen jmp dword ist, bleibt am ende 1 byte über!
in meiner subfunc speicher ich die register mit pushad, call , popad, den überschriebenen code hier einfügen, und dann jmp ....
Hooks mit VB? 04/22/2007 - .NET Languages - 21 Replies Nabend,
Ich versuch mich momentan den Tinytoon Hook hiermit zum implementieren, da mich Forceshock langsam.... :eek:
Wär nett wenn jemand noch sinnvolle Beispiele dazu hätte, ua. selbst Erfahrung damit hat.
Option Explicit
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long ...