Windows API
Die Windows API (Application Programming Interface) ist eine Schnittstelle, die es einer Windows-Anwendung ermöglicht, mit dem Betriebsystem zu kommunizieren und bestimmte Operationen durchzuführen. Dazu bietet sie eine Sammlung von Funktionen an, die verpackt in Programmbibliotheken (DLL-Dateien) bereitstehen.
Inhaltsverzeichnis |
erweiterte Programmierschnittstellen
Hohe Programmiersprachen bieten Funktionsbibliotheken an, welche die Benutzung der Windows API erleichtern, indem sie eine einfach strukturierte Schnittstelle zur Windows API abbilden. Delphi beispielsweise stellt eine Sammlung von Funktionen bereit (die VCL: Visual Component Library), die es dem Programmierer erheblich erleichtert, ein GUI für seine Anwendung zu erstellen.
API-Hooking
Das API-Hooking beschreibt ein Verfahren, das es ermöglicht, die Funktionen der Windows-API zu verändern. Dabei wird der Aufruf der gehookten API Funktion abgefangen und stattdessen wird auf die eigene veränderte Funktion verwiesen, die die Aufgabe der API Funktion übernimmt.
Als Beispiel gibt es eine API-Funktion namens FindNextFile (eigentlich sind es zwei: FindNextFileA und FindNextFileW). Sie gibt als Ergebnis die nächste gefundene Datei im aktuellen Ordner an. Wird diese API nun gehookt und derart verändert, dass sie jede Datei namens "geheim.exe" nicht auflistet, bliebe die Datei im Explorer und anderen Programmen solange unauffindbar, bis der Hook wieder deinstalliert wird. Aus diesem Grund werden auch Virenscanner und andere Produkte diese Datei nicht untersuchen (sie können sie schlicht nicht „sehen“).
Es gibt Anwendungen, die diese Technik nutzen, um versteckt zu bleiben. Nicht nur als Datei, sondern auch als laufender Prozess. Solche Anwendungen nennt man Rootkits.
IAT Hooking
In der IAT (Import Address Table) sind die Adressen zu sämtlichen API-Funktionen im Programm hinterlegt. Beim IAT-Hooking wird die Adresse der Funktion in der IAT mit der Adresse der eigenen Funktion überschrieben. Mit dieser Methode lassen sich daher ausschließlich Funktionen hooken, die auch über die IAT angesprochen (adressiert) werden.
Inline Hooking
Beim Inline-Hooking manipuliert man die Funktion, die man hooken will; die IAT bleibt unberührt. Mit einem Inline Hook kann man nicht nur die API hooken, sondern auch sämtliche anderen Funktionen.
Inline Hooking anhand eines Tutorials erklärt
Als Beispiel verwenden wir in unserem Programm eine Funktion Namens output(int i) die von dem Programm dazu verwendet wird, eine Zahl auszugeben. Mit unserem Hook wollen wir verhindern, dass output(5) aufgerufen wird - alle anderen Werte sollen weiterhin erlaubt sein. Dafür müssen wir das Programm dazu bringen, statt output() unsere Funktion namens check(int i) aufzurufen.
Eine Funktion die den Platz einer anderen einnimmt, muss immer dieselben Parameter haben. Dort testen wir, ob i ungleich 5 ist und rufen die dann output() auf.
Hier ist das Testprogramm:
#include <iostream>
using namespace std;
void output(int );
int main(void)
{
output(4);
output(5);
system("PAUSE");
return 0;
}
void output(int i)
{
if(i == 5)
cout << "5 - DAS war wohl nichts!\n";
else
cout << i << " ist eine erlaubte Zahl!\n";
}
output() durch check() ersetzen
Jetzt geht’s ans Hooken: Zunächst gilt es, output() durch check() zu ersetzen. Dazu schreibt man an den Anfang von output() einen Sprungbefehl, der nach check() zeigt.
Hier ein Testcode für die "check"-Routine:
void check(int i)
{
cout << "In check,i=" << i << "!\n";
}
Um output() auf check() umzulenken, müssen wir Code in einem laufendem Programm verändern können.
Speicher manipulieren:
Zuerst öffnen wir einen laufenden Prozess, in diesem Fall unseren eigenen:
HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
Der erste Parameter gibt die geforderten Rechte an, wobei der 3. die Prozess-ID des zu öffnenden Prozesses (hier den eigenen) liefert. Jetzt sind wir in der Lage, den virtuellen Speicher des Prozesses auszulesen und zu beschreiben:
DWORD rw = 0; ReadProcessMemory(hproc,(LPCVOID)adress,&buffer,sizeof(buffer),&rw); //und WriteProcessMemory(hproc,(LPVOID)adress,&buffer,sizeof(buffer),&rw);
- Der 1. Parameter ist ein Handle auf einen geöffneten Prozess
- Der 2. Parameter die Adresse an der geschrieben/gelesen wird
- Der 3. Parameter ein Pointer auf eine Variable die die Daten enthält die geschrieben werden / in die die Daten gelesen werden
- Der 4. Parameter enthält die Anzahl der Bytes die geschrieben/gelesen werden
- Der 5. ist ein Pointer auf eine Variable die später die Anzahl der geschriebenen/gelesenen Bytes enthält
Am Schluss dann noch mit
CloseHandle(hproc);
den Prozess schließen. Das ist schon alles.
Zurück zum Hooking:
Zunächst einmal gilt es nun, die Adresse unserer Funktion zu ermitteln:
unsigned adresse = (unsigned)&output;
Dann sollte man noch wissen, dass in Maschinensprache(Opcodes) ein Sprungbefehl mit 0xE9 angegeben wird, gefolgt von der Sprungweite. Ein Sprung schaut zum Beispiel so aus:
E987BE0000
Wenn man jetzt von der Adresse "Absprung" zur Adresse "Ziel" springen will, bekommt man den Wert hinter dem E9 wie folgt heraus:
unsigned sprungoffset(unsigned absprung, unsigned ziel)
{
unsigned weg;
absprung += 5; //Da der Sprungbefehl 5 Bytes groß ist und sich der Prozessor beim "Absprung"
//am Ende des 5Byte langen Befehls befindet
weg = ziel - absprung; //Naja, die Entfernung halt ;)
return weg;
}
Jetzt wird an die direkte Adresse von output() ein 0xE9 (1 Byte) und dann an output+1 den Sprungweg von output zu check geschrieben:
#include <iostream> #include <windows.h> using namespace std; void output(int ); void check(int ); unsigned sprungoffset(unsigned ,unsigned );
int main(void)
{
/* Speicher ändern */
HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
DWORD rw = 0;
unsigned adress_von_output = (unsigned) &output, sprungweg;
unsigned char byte = 0xE9;
/* berechnet den Sprungweg */
sprungweg = sprungoffset((unsigned)&output,(unsigned)&check);
/* schreibt 0xE9 nach output und erhöht die Adresse */
WriteProcessMemory(hproc, (LPVOID)adress_von_output++, &byte, sizeof(byte), &rw);
/* schreibt den weg nach dem E9 um den Sprungbefehl zu vervollständigen */
WriteProcessMemory(hproc, (LPVOID)adress_von_output, &sprungweg, sizeof(sprungweg), &rw);
/* Ende */
output(4);
output(5);
system("PAUSE");
return 0;
}
void output(int i)
{
if(i == 5)
cout << "5 - DAS war wohl nichts!\n";
else
cout << i << " ist eine erlaubte Zahl!\n";
}
void check(int i)
{
cout << "In check,i=" << i << "!\n";
}
unsigned sprungoffset(unsigned absprung,unsigned ziel)
{
unsigned weg;
absprung += 5;
weg = ziel - absprung;
return weg;
}
Was jetzt passiert: Wenn der Prozessor output() aufruft, trifft er auf einen Jump zu check(), diesem folgt er und führt statt output() nun check() aus. Allerdings ist output() ist nicht mehr zu gebrauchen, da hier einfach 5 Bytes überschrieben wurden, ohne sie zu speichern, was bei API-Funktionen wohl schwer zu verkraften sein wird. Also gilt es nun, Code zu schreiben, der die ersten Bytes von output() speichert, um sie dann später wieder zu verwenden.
Dazu müssen die ersten x Bytes der Funktion output() eingelesen und in ein NOP-Array geschrieben werden, um sie zu sichern. x muss größer gleich 5 sein, da ein Sprungbefehl in Opcodes genau 5 Bytes groß ist. x muss aber auch groß genug sein, um keinen Befehl zu zerreisen (dazu gleich mehr).
Danach wird anstelle der ersten 5 Bytes von output() ein Jumpbefehl nach check() geschrieben. Hinter die x gesicherten Bytes (im NOP-Array) schreibt man dann einen Jump in die Originalfunktion zurück. Wir kitten also die vorher zerrissene Funktion durch den Jump. Der Jump darf aber nicht direkt zur Original Funktion gehen, da dort ja unser Jumpbefehl zu check() steht.
Wenn man nun das NOP-Array als Funktion aufruft, erreicht man die normale Funktion, da dort erst die gesicherten x Bytes ausgeführt werden (Jump nach check()), gefolgt von dem Jump in den originalen Code der ursprünglichen Funktion (Code von output()). Dadurch kann man in die gehookte Funktion gehen, um die Parameter zu überprüfen, dann die Originalfunktion aufrufen, und dort den Rückgabewert kontrollieren.
Und so wird’s gemacht: Zunächst werden in einer For Schleife x Bytes von output() eingelesen (auf die Größe von x wird später eingegangen). Diese Bytes schreibt man an den Anfang des NOP-Arrays und ersetzen die Original-Bytes durch einen NOP (hierher, in diese NOPS, kommt dann der Jump zu check()). Danach schreiben wir den Jump-Befehl zu check() in die gerade geschaffenen NOPS. Nun schreiben wir hinter den gerade gesicherten Bytes (also im NOP-Array) einen Jumpbefehl zur Originalfunktion + 5 Bytes (diese 5 Bytes sind ja vom Jump besetzt und wenn wir direkt zur Orignialfunktion springen würden, würde dort der Jump erneut ausgeführt werden - also springen wir hinter diese Jumpbefehl). Jetzt können wir aus check heraus output() aufrufen, indem wir das NOP-Array nutzen. Dort werden dann
eben die gesicherten Befehle ausgeführt und dann durch den Sprung auch der Rest der Funktion.
Let's go!
So erstmal schaffen wir ein "aufrufbares" Nop-Array, das Platz für die x bytes und den Sprung zurück in die originalFunktion bietet. Die NOP-Funktion sollte dieselben Parameter wie die zu Hookende Funktion haben!
void new_adress(int i)
{
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
}
Der Hookprozess wird am besten in eine Funktion verpackt, die folgendes Grundgerüst aufweist:
/*
unsigned source_function = Adresse der zu hookenden Funktion
unsigned bytes_to_save = Bytes die gespeichert werden,vorher mit einem Debugger anschauen um keine Opcodes zu zerreisen
unsigned new_adress = Adresse des NOP-Arrays
unsigned instead_call = Diese Funktion wird aufgerufen statt der alten
DWORD process_id = Gibt an in welchem Prozess die Hooks gemacht werden
*/
void hook_function(unsigned source_function, unsigned bytes_to_save, unsigned new_adress, unsigned instead_call, DWORD process_id)
{
Zunächst muss ein Handle auf den geöffneten Prozess angefordert und alle verwendeten Variablen initialisiert werden. Die 2 Funktionsadressen werden gespeichert:
unsigned char byte; //Für Opcodes DWORD rw = 0; HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id); unsigned temp_adress[10]; //Um Adressen zwischenzuspeichern temp_adress[0] = source_function; temp_adress[1] = new_adress;
Jetzt geht es mit der For-Schleife weiter, die die Bytes sichert und durch NOPs ersetzt (0x90 ist übrigens die NOP Anweisung):
/* Liest x Bytes von source_function ein, überschreibt sie mit NOPs und sichert sie in new_adress */
for(int i = 0; i < bytes_to_save; i++)
{
//Liest von der SourceFunktion ein Byte nach "byte"
ReadProcessMemory(hproc, (LPCVOID)source_function, &byte, sizeof(byte), &rw);
//Schreibt das Byte an die neue Adresse im Array und inkrementiert diese
WriteProcessMemory(hproc, (LPVOID)new_adress++, &byte, sizeof(byte), &rw);
//Schreibt den NOP-Opcode nach "byte"
byte = 0x90;
//Schreibt das NOP an die stelle des alten Bytes und inkrementiert die Adresse
WriteProcessMemory(hproc, (LPVOID)source_function++, &byte, sizeof(byte), &rw);
}
Nachdem alles gesichert wurde, muss jetzt an den Anfang der zu hookenden Funktion der Jump zu unserer Funktion geschrieben werden.
/* Schreibt in die vorher geschaffenen NOPs einen JMP zu instead_call um alle aufrufe umzuleiten */ source_function = temp_adress[0]; //Stellt die Adresse von source_function wieder her byte = 0xE9; //schiebt den Opcode für einen JMP nach "byte" /* Schreibt an den Anfang der zu Hookenden Funktion den Jump zu unserer Funktion, das Gleiche haben wir im ersten Beispiel schon gemacht ;) */ WriteProcessMemory(hproc, (LPVOID)source_function, &byte, sizeof(byte), &rw); temp_adress[3] = sprungoffset(source_function,instead_call); WriteProcessMemory(hproc, (LPVOID)++source_function, &temp_adress[3], sizeof(temp_adress[3]), &rw);
Jetzt muss hinter den gesicherten Bytes noch ein Jump eingefügt werden, der zum Code der Originalfunktion springt:
/* Schreibt hinter die vorher gesicherten Bytes einen JMP zum restlichen Code der Originalfunktion */ byte = 0xE9; //Schreibt nach 'byte' wieder den Jump-Befehl new_adress = temp_adress[1]; //Stellt die Adresse zum Noparray wieder her new_adress += bytes_to_save; //Erhöht die Adresse damit sie hinter die gespeicherten Bytes zeigt /* Schreibt den Opcode für den Jump hinter die gesicherten Bytes */ WriteProcessMemory(hproc,(LPVOID)new_adress++,&byte,sizeof(byte),&rw); /* Rechnet den Sprungweg von "nach den gespeicherten Bytes" nach "origninalfunktion +5 also nach dem Jump" aus */ temp_adress[4] = sprungoffset(temp_adress[1]+bytes_to_save, temp_adress[0]+5); /* Schreibt den Weg nach dem "Jump-Opcode" E9 in den Speicher */ WriteProcessMemory(hproc, (LPVOID)new_adress, &temp_adress[4], sizeof(temp_adress[4]), &rw); CloseHandle(hproc); }
Die Funktion ist nun fertig. Bleibt noch zu klären, wie man die Größe von x (bytes_to_save) ermittelt: Dazu lädt man das Programm in den Debugger (z.B. Olly-Debugger) und geht zu der Funktion, die gehookt werden soll (die Adresse kann man sich im Zweifelsfalle ausgeben lassen: cout << (unsigned)&funktion << endl;). Interessant ist der Anfang der Funktion:
00401412 /$ 55 PUSH EBP 00401413 |. 89E5 MOV EBP,ESP 00401415 |. 83EC 08 SUB ESP,8
Bei den Opcodes sieht man nun, dass x 6 ist, da wir ja mindestens 5 Bytes brauchen (ein Jump ist so lang), aber 83EC 08 nicht zerreisen dürfen! Das fertige Programm:
#include <iostream> #include <windows.h> using namespace std; void output(int ); void check(int ); unsigned sprungoffset(unsigned ,unsigned ); void hook_function(unsigned ,unsigned ,unsigned ,unsigned ,DWORD ); void new_adress(int );
int main(void)
{
hook_function((unsigned)&output, 6, (unsigned)&new_adress, (unsigned)&check, GetCurrentProcessId());
output(4);
output(5);
system("PAUSE");
return 0;
}
void output(int i)
{
if(i == 5)
cout << "5 - DAS war wohl nichts!\n";
else
cout << i << " ist eine gueltige Zahl!\n";
}
void check(int i)
{
if (i != 5) new_adress(i);
}
unsigned sprungoffset(unsigned absprung, unsigned ziel)
{
unsigned weg;
absprung += 5;
weg = ziel - absprung;
return weg;
}
/*
unsigned source_function = Adresse der zu hookenden Funktion
unsigned bytes_to_save = Bytes die gespeichert werden,vorher mit einem Debugger anschauen um keine Opcodes zu zerreisen
unsigned new_adress = Adresse des NOP-Arrays
unsigned instead_call = Diese Funktion wird aufgerufen statt der alten
DWORD process_id = Gibt an in welchem Prozess die Hooks gemacht werden
*/
void hook_function(unsigned source_function, unsigned bytes_to_save, unsigned new_adress, unsigned instead_call, DWORD process_id)
{
unsigned char byte; //Für Opcodes
DWORD rw = 0;
HANDLE hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
unsigned temp_adress[10]; //Um Adressen zwischenzuspeichern
temp_adress[0] = source_function;
temp_adress[1] = new_adress;
/* Liest x Bytes von source_function ein,überschreibt sie mit NOPs und sichert sie in new_adress */
for(int i = 0; i < bytes_to_save; i++)
{
//Liest von der SourceFunktion ein Byte nach "byte"
ReadProcessMemory(hproc, (LPCVOID)source_function, &byte, sizeof(byte), &rw);
//Schreibt das Byte an die neue Adresse und inkrementiert sie
WriteProcessMemory(hproc, (LPVOID)new_adress++, &byte, sizeof(byte), &rw);
//Schreibt den NOP-Opcode in Byte
byte = 0x90;
//Schreibt das NOP an die Source-Funktion und inkrementiert die Adresse
WriteProcessMemory(hproc, (LPVOID)source_function++, &byte, sizeof(byte), &rw);
}
/* Schreibt in die vorher geschaffenen NOPs einen JMP zu instead_call um alle aufrufe umzuleiten */
source_function = temp_adress[0]; //Stellt die Adresse von source_function wieder her
byte = 0xE9; //schiebt den Opcode für einen JMP nach "byte"
/* Schreibt an den Anfang der zu Hookenden Funktion den Jump zu unserer Funktion, das gleiche haben wir im ersten Beispiel schon gemacht ;) */
WriteProcessMemory(hproc, (LPVOID)source_function, &byte, sizeof(byte), &rw);
temp_adress[3] = sprungoffset(source_function,instead_call);
WriteProcessMemory(hproc, (LPVOID)++source_function, &temp_adress[3], sizeof(temp_adress[3]), &rw);
/* Schreibt hinter die vorher gesicherten Bytes einen JMP zur Originalfunktion zurück */
byte = 0xE9; //Schreibt nach 'byte' wieder den Jump-Befehl
new_adress = temp_adress[1]; //Stellt die Adresse zum Noparray wieder her
new_adress += bytes_to_save; //Erhöht die Adresse damit sie hinter die gespeicherten Bytes zeigt
/* Schreibt den Opcode für den Jump hinter die gesicherten Bytes */
WriteProcessMemory(hproc, (LPVOID)new_adress++, &byte, sizeof(byte), &rw);
/* Rechnet den Sprungweg von "nach den gespeicherten Bytes" nach "origninalfunktion +5 also nach dem Jump" aus */
temp_adress[4] = sprungoffset(temp_adress[1]+bytes_to_save, temp_adress[0]+5);
/* Schreibt den Weg nach dem "Jump-Opcode" E9 in den Speicher */
WriteProcessMemory(hproc, (LPVOID)new_adress, &temp_adress[4], sizeof(temp_adress[4]), &rw);
CloseHandle(hproc);
}
void new_adress(int i)
{
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
asm("NOP");
}
Visual C++ 2008 Inline Assembler Befehl für nop
_asm nop;
Inline API-Hooking prozessintern
Für das Hooken der Windows API können die Funktionen von oben verwendet werden, weshalb dieser Abschnitt auf den bereits bekannten Quellcode aufbaut. Der unveränderte Quellcode wird der Übersichtlichkeit halber durch [...] gekürzt. Hier das Grundgerüst für das API-Beispiel:
#include <iostream> #include <windows.h> using namespace std; void check(int ); unsigned sprungoffset(unsigned ,unsigned ); void hook_function(unsigned ,unsigned ,unsigned ,unsigned ,DWORD ); void new_adress(int );
int main(void)
{
hook_function((unsigned)&output, /*noch zu ermittelnde Größe in Byte*/ x, (unsigned)&new_adress, (unsigned)&check, GetCurrentProcessId());
system("PAUSE");
return 0;
}
void check(int i)
{
cout << "Da wollte jemand die API aufrufen ;)" << endl;
}
unsigned sprungoffset(unsigned absprung,unsigned ziel)
{
[...]
}
void hook_function(unsigned source_function, unsigned bytes_to_save, unsigned new_adress, unsigned instead_call, DWORD process_id)
{
[...]
}
void new_adress(int i)
{
[...]
}
Als Beispiel wird hier die MessageBoxA() gehookt, welche laut MSDN folgende Parameter hat:
int MessageBoxA(HWND, LPCTSTR, LPCTSTR, UINT);
Daher muss new_adress() und check() entsprechend angepasst werden:
int check(HWND ,LPCTSTR ,LPCTSTR ,UINT ) int new_adress(HWND ,LPCTSTR ,LPCTSTR ,UINT )
Nun muss noch die Adresse von MessageBoxA() herausgefunden werden. Dazu lädt man die DLL, in der sich die Funktion befindet, und liest deren Adresse aus:
HINSTANCE hDLL = LoadLibrary("user32.dll");
unsigned msgbox_addr = (unsigned) GetProcAddress(hDLL, "MessageBoxA");
Für die hook_function() wird noch die Angabe von x (bytes_to_save) benötigt. Um den korrekten Wert zu ermitteln, kompiliert man zunächst ein Programm, dass MessageBoxA() aufruft, lädt es in den Debugger und steppt in den Call zu MessageBoxA()hinein (mit F7 beim Olly-Debugger). So gelangt man an:
00410C60 $- FF25 2C834400 JMP DWORD PTR DS:[<&USER32.MessageBoxA>] ; USER32.MessageBoxA
Das sieht noch nicht nach dem gewollten Ergebniss aus... Folgt man aber diesem JMP, kommt man tatsächlich bei der Funktion raus, genauer bei
77D36476 > 833D D0C3D677 00 CMP DWORD PTR DS:[77D6C3D0],0
Der erste Befehl ist also schon größer als 5 Bytes, nämlich 7 Byte, x ist also 7! Mit diesen Daten könnte man theoretisch anfangen, MessageBoxA() zu hooken; praktisch fehlen dem eigenen Programm aber die Rechte, in der user32.dll zu patchen. Diese Rechte lassen sich mithilfe von VirtualQuery() und VirtualProtect() erlangen. VirtualQuery() gibt die Basisadresse eines Speicherbereichs zurück, welche man benötigt, um sich mit VirtualProtect() die Rechte zu holen. Die Parameter von VirtualQuery():
- Der 1. Parameter ist eine Adresse im Speicherbereich von dem wir die Daten wollen,hier also die von MessageBoxA
- Der 2. Parameter ist ein Pointer auf eine Struktur der Typs MEMORY_BASIC_INFORMATION die man vorher initalisieren sollte
- Der 3. Parameter ist sizeof(MEMORY_BASIC_INFORMATION)
Die Parameter von VirtualProtect:
- Der 1. Parameter gibt die Basisadresse der Bereichs an
- Der 2. Parameter gibt die Größe des Bereichs an
- Der 3. Parameter gibt die verlangten Rechte an
- Der 4. Parameter ist ein Pointer auf eine DWORD Variable in die die alten Rechte geschrieben werden
Der fertige Code lautet demnach:
#include <iostream> #include <windows.h> using namespace std; int check(HWND ,LPCTSTR ,LPCTSTR ,UINT ); int new_adress(HWND ,LPCTSTR ,LPCTSTR ,UINT ); unsigned sprungoffset(unsigned ,unsigned ); void hook_function(unsigned ,unsigned ,unsigned ,unsigned ,DWORD );
int main(void)
{
HINSTANCE hDLL = LoadLibrary("user32.dll");
unsigned msgbox_addr = (unsigned)GetProcAddress(hDLL, "MessageBoxA");
MEMORY_BASIC_INFORMATION mbi;
DWORD dwOldProtect;
VirtualQuery((void*)msgbox_addr,&mbi,sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect((PDWORD)mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);
hook_function((unsigned)msgbox_addr, 7, (unsigned)&new_adress, (unsigned)&check, GetCurrentProcessId());
MessageBox(GetForegroundWindow(), "Mich solltest du nicht sehen können!", "Weg!", MB_OK);
system("PAUSE");
return 0;
}
int check(HWND ,LPCTSTR ,LPCTSTR ,UINT )
{
cout << "Da wollte jemand die API aufrufen ;)" << endl;
}
unsigned sprungoffset(unsigned absprung,unsigned ziel)
{
[...]
}
void hook_function(unsigned source_function, unsigned bytes_to_save, unsigned new_adress, unsigned instead_call, DWORD process_id)
{
[...]
}
int new_adress(HWND ,LPCTSTR ,LPCTSTR ,UINT )
{
[...]
}
Inline API-Hooking global
Dieser Abschnitt wurde bislang nur angedacht, aber noch nicht fertig gestellt. Die Grundlage der Methode des globalen „Inline API-Hooking“ ist es, sich z.B. mit Hilfe eines MessageBox-Hooks eine DLL-Injection zu basteln, welche die DLL, in der sich der Hook befindet, in jede Anwendung lädt.
Quellen
- Dieser Text wurde in seiner ursprünglichen Form mit freundlicher Genehmigung von Xalon, dem Autor des Tutorials (die Seite ist momentan leider nicht erreichbar) in unser Wiki übernommen. Es ist ausdrücklich erwünscht, dass der Text von anderen Autoren überarbeitet und erweitert wird! Neben der Diskussionsseite hier im Wiki befindet sich eine weitere Diskussionsseite zum TUT im Forum des Hackerboards.
WebLinks (externe Links)
|