Ein zweites dickes Hallo an Alle,
Nachdem wir uns in Teil 1 des Tutorials darum gekümmert haben, die Detour Funktion zu programmieren, geht’s in Teil 2 darum, diese Funktion richtig anzuwenden. Dazu schreiben wir uns zuerst ein „Opfer“-Programm:
Es sollte klar sein, was das Programm macht: Es wird in einer Endlosschleife die Funktion NormaleFunc() aufgerufen, die den Text „Von EXE“ in die Konsole schreibt. Genau diese Funktion wollen wir jetzt mit einem Detour versehen. Damit unsere Detour-Funktion korrekt arbeitet, müssen wir sie in Form einer DLL in den Zielprozess injezieren. Also erstellen wir nun diese DLL:
Viel ist noch nicht passiert. Wir erstellen in der DLL-Main einen Thread, der sich aber auch gleich wieder schließt, nachdem er gestartet wurde. In diesem Thread werden wir später unsere Detour-Funktion aufrufen. Wenn wir sie aufrufen wollen, müssen wir sie ja auch erstmal definieren:
So nun müssen wir uns so langsam mal Gedanken über die Parameter unserer Detour-Funktion machen. Als erstes benötigen wir die Adresse der Funktion, die mit unserem Detour versehen werden soll. Also die Adresse der Funktion aus unserem Opfer-Programm, die nichts weiter macht, als den Text „Von EXE“ in das Konsolenfenster zu schreiben. Erinnerst du dich? Frage: Wie kommen wir an diese Adresse? Antwort: OllyDbg Diesen tollen Debugger wirst du dir jetzt herunterladen. Wenn er startbereit ist, starte Olly.
Drücke jetzt F3. Es öffnet sich ein kleines Fenster mittels dem du unsere Opfer-Exe öfnen musst. Hast du das Programm in Olly geöffnet, sollte sich das Fenster mit vielen Zahlen und Buchstaben gefüllt habe ;) Es sieht ungefähr so aus:
[Only registered and activated users can see links. Click Here To Register...]
Klicke nun mit der rechten Maustaste auf das obere linke Fenster, das ich im Bild (s.o.) als „Für uns interssanter Teil“ gekennzeichnet habe. In dem sich öffnenden Dialog gehst du auf „Search For“ und dort auf „All referenced text strings“. Das Fenster sollte sich ein bisschen verändern. Lass uns nun kurz daran erinnern, was unsere Ur-Funktion macht. *Überleg* Genau, sie schreibt den Text „Von EXE“ in unsere Konsole. Also sollte die Startadresse unserer Funktion in der Nähe dieses Strings sein. Wir haben nun das Programm nach Strings absuchen lassen. Gefundene Zeichenketten zeigt uns Olly in dem neuen Fenster an. Und was ist denn das?
[Only registered and activated users can see links. Click Here To Register...]
Der rot-Umrahmte String ist genau der, den wir gesucht haben. Klick doppelt auf ihn drauf und wir gelangen zurück zu dem Startfenster an die Adresse, an der der String steht. Wir haben bereits festgestellt, dass in der Nähe des Strings die Startadresse unserere Funktion liegen muss, also srollen wir ein bisschen hoch. Olly macht es uns insgesamt noch einfacher und setzt eine dicke, schwarze Klammer um den Adressenbereich, der zu einer Funktion gehört. Also schauen wir, wo diese Klammer beginnt und das ist exakt drei Zeilen weiter oben der Fall, bei mir die Adresse 0x004013EE. Bei dir muss es nicht zwangsläufig die gleiche Adresse sein:
[Only registered and activated users can see links. Click Here To Register...]
Damit haben wir nun die Startadresse. Lass Olly aber nochmal offen, wir werden ihn gleich nochmal um Hilfe fragen müssen ;) Aber erstmal lass uns weiter programmieren:
Ich habe mal angefangen die Detour-Funktion aus unserem Thread aufzurufen. Bis jetzt haben wir ja nur die Adresse der Ur-Funktion. Der zweite Parameter ist die Adresse der neuen Funktion. Dazu müssen wir jetzt erstmal die neue Funktion definieren:
Direkt über unseren Thread habe ich die neue Funktion definiert. Sie macht auch nicht viel mehr als unsere Ur-Funktion. Auch sie schreibt nur einen Text in unser Konsolenfenster. Spannende Frage: Wie kommen wir an die Adresse der neuen Funktion? Brauchen wir dafür wieder Olly? Antwort: Nein. An die Adresse kommen wir viel leichter :)
Ist äquivalent wie die Adressierung eines Zeigers auf eine Variable :D Zu guter letzt brauchen wir noch die Anzahl der zu sichernden Bytes. Diese Größe kann auf keinsten Fall beliebig gewählt werden. Wenn du Glück hast läuft u.U. alles Glatt aber mit hoher Wahrscheinlichkeit wird es einige Probleme geben, wenn du die Größe beliebig festlegst! Zu allererst beträgt die Mindestgröße 10Bytes. Diese setzt sich zusammen aus dem CALL Befehl+Adresse (=5Bytes) und dem JMP Befehl+Adresse (=5Bytes). Beide zusammen machen eben 10Bytes. Jetzt kannst du aber auch nicht einfach immer die mindestgröße wählen, sondern das muss von Fall zu Fall angepasst werden. Warum? Das hat einen einfachen Grund. Ein paar Fakten: Der PUSH EBP (Das Ablegen eines Registers auf den Stack) nimmt genau die Größe von einem Byte ein. JMP und CALL nehmen jeweils 5Bytes ein. So haben wir z.B. folgende Befehlsreihe in unserer Funktion am Anfang:
PUSH EBP
JMP 32000000
CALL 4400000000
Dann machen alle drei Befehle insgesamt eine Größe von 11Bytes.Würden wir jetzt nur die ersten 10Bytes sichern und überschreiben, dann bleibt in der Ur-Funktion das letzte Byte übrig (0x00). Wir sichern ja die alten Bytes auf einem neuen Speicherblock. Und nach der Sicherung springen wir zurück zur Ur-Funktion. Gehen wir das mal durch. PUSH EBP braucht ein Byte. Bleiben noch 9 aus unserer Sicherung. JMP braucht 5Bytes. Bleiben noch 4Bytes aus der Sicherung. CALL braucht aber auch 5Bytes! Es sind aber nur noch 4übrig, da wir das letzte nicht mit gesichert haben. Problem! CALL nimmt sich aber 5Bytes. Was kam nach der Sicherung? Richtig der Jump zurück. Also nimmt sich CALL einfach das E9-Byte aus unserem Jump. Nicht gut. Damit kehren wir von unserem Speicherblock nichtmehr zurück zu unserer Ur-Funktion und landen irgendwo im Speicher-Nirvana… Ich hoffe du hast das bis jetzt einigermaßen verstanden :) Ich fasse es mal zusammen:
Regeln für die Anzahl Bytes, die gesichert werden sollen:
1. Es müssen mindestens 10Bytes sein
2. Es müssen alle zu einem Befehl gehörigen Bytes gesichert werden
Schauen wir uns in Olly mal unsere Ur-Funktion an. Der erste Befehl ist PUSH EBP. Links neben dem Befehl können wir die Bytes zu dem Befehl sehen. Und es ist ein Byte. Zu wenig, wir brauchen mind. 10Bytes. Der nächste Befehl ist MOV EBP, ESP. Dieser ist 2Bytes groß. Insgesamt haben wir 3Bytes, immer noch zu wenig. Der nächste Befehl ist SUB ESP, 8. Dieser ist 3Bytes groß. Nun haben wir schon 6Bytes. Immer noch zu wenig. Ui der nächste Befehl ist lang :) MOV blabla, blub. Dieser nimmt insgesamt 8Bytes ein. Wow. Mit den 6Bytes von vorhin haben wir 14Bytes. Das ist größer als 10. Damit hätten wir die Größe und wir könne sie als Parameter unserer Funktion übergeben:
Hui endlich fertig mit der DLL :) Jetzt müssen wir diese nur noch in unser Zielprozess injezieren. Wie man einen Injektor programmiert, findest du [Only registered and activated users can see links. Click Here To Register...] beschrieben.
[Only registered and activated users can see links. Click Here To Register...]
Ich wünsche euch viel Spaß beim Programmieren :) Wie immer wenn ihr Fehler findet, melden, ansonsten viel Spaß damit, würde mich über Feedback freuen.
Credits:
Nope
[Only registered and activated users can see links. Click Here To Register...]
Nachdem wir uns in Teil 1 des Tutorials darum gekümmert haben, die Detour Funktion zu programmieren, geht’s in Teil 2 darum, diese Funktion richtig anzuwenden. Dazu schreiben wir uns zuerst ein „Opfer“-Programm:
Code:
#include <iostream>
#include <windows.h>
using namespace std;
void NormaleFunc()
{
cout << "Von EXE\n";
}
int main( int argc, char** argv )
{
while( true )
{
NormaleFunc();
Sleep( 250 );
}
return 0;
}
Code:
#include <windows.h>
#include <iostream>
DWORD WINAPI Thread( LPVOID lpParam )
{
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
if( ul_reason_for_call == DLL_PROCESS_ATTACH )
{
HANDLE hThread = CreateThread( NULL, 0, Thread, NULL, 0, NULL );
CloseHandle( hThread );
}
return TRUE;
}
Code:
#include <windows.h>
#include <iostream>
bool DetourFunc( BYTE* oldFunc, BYTE* newFunc, DWORD len )
{
BYTE* newMem4base = NULL;
DWORD dwOld;
newMem4base = ( BYTE* )malloc( 5+len );
if( newMem4base == NULL )
return false;
for( DWORD i = 0; i < ( len+5 ); i++ )
newMem4base[i] = 0x90;
VirtualProtect( oldFunc, len, PAGE_READWRITE, &dwOld );
memcpy( newMem4base, oldFunc, len );
oldFunc[0] = 0xE8;
*( DWORD* )( oldFunc+0x01 ) = DWORD( newFunc-oldFunc-5 );
oldFunc[5] = 0xE9;
*( DWORD* )( oldFunc+0x06 ) = DWORD( newMem4base-( oldFunc+0x5 )-5 );
newMem4base += len;
newMem4base[0] = 0xE9;
*( DWORD* )( newMem4base+0x01 ) = DWORD( ( oldFunc+10 )-newMem4base-5 );
for( DWORD i = 10; i <len; i++ )
oldFunc[i] = 0x90;
return true;
}
DWORD WINAPI Thread( LPVOID lpParam )
{
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
if( ul_reason_for_call == DLL_PROCESS_ATTACH )
{
HANDLE hThread = CreateThread( NULL, 0, Thread, NULL, 0, NULL );
CloseHandle( hThread );
}
return TRUE;
}
Drücke jetzt F3. Es öffnet sich ein kleines Fenster mittels dem du unsere Opfer-Exe öfnen musst. Hast du das Programm in Olly geöffnet, sollte sich das Fenster mit vielen Zahlen und Buchstaben gefüllt habe ;) Es sieht ungefähr so aus:
[Only registered and activated users can see links. Click Here To Register...]
Klicke nun mit der rechten Maustaste auf das obere linke Fenster, das ich im Bild (s.o.) als „Für uns interssanter Teil“ gekennzeichnet habe. In dem sich öffnenden Dialog gehst du auf „Search For“ und dort auf „All referenced text strings“. Das Fenster sollte sich ein bisschen verändern. Lass uns nun kurz daran erinnern, was unsere Ur-Funktion macht. *Überleg* Genau, sie schreibt den Text „Von EXE“ in unsere Konsole. Also sollte die Startadresse unserer Funktion in der Nähe dieses Strings sein. Wir haben nun das Programm nach Strings absuchen lassen. Gefundene Zeichenketten zeigt uns Olly in dem neuen Fenster an. Und was ist denn das?
[Only registered and activated users can see links. Click Here To Register...]
Der rot-Umrahmte String ist genau der, den wir gesucht haben. Klick doppelt auf ihn drauf und wir gelangen zurück zu dem Startfenster an die Adresse, an der der String steht. Wir haben bereits festgestellt, dass in der Nähe des Strings die Startadresse unserere Funktion liegen muss, also srollen wir ein bisschen hoch. Olly macht es uns insgesamt noch einfacher und setzt eine dicke, schwarze Klammer um den Adressenbereich, der zu einer Funktion gehört. Also schauen wir, wo diese Klammer beginnt und das ist exakt drei Zeilen weiter oben der Fall, bei mir die Adresse 0x004013EE. Bei dir muss es nicht zwangsläufig die gleiche Adresse sein:
[Only registered and activated users can see links. Click Here To Register...]
Damit haben wir nun die Startadresse. Lass Olly aber nochmal offen, wir werden ihn gleich nochmal um Hilfe fragen müssen ;) Aber erstmal lass uns weiter programmieren:
Code:
#include <windows.h>
#include <iostream>
bool DetourFunc( BYTE* oldFunc, BYTE* newFunc, DWORD len )
{
BYTE* newMem4base = NULL;
DWORD dwOld;
newMem4base = ( BYTE* )malloc( 5+len );
if( newMem4base == NULL )
return false;
for( DWORD i = 0; i < ( len+5 ); i++ )
newMem4base[i] = 0x90;
VirtualProtect( oldFunc, len, PAGE_READWRITE, &dwOld );
memcpy( newMem4base, oldFunc, len );
oldFunc[0] = 0xE8;
*( DWORD* )( oldFunc+0x01 ) = DWORD( newFunc-oldFunc-5 );
oldFunc[5] = 0xE9;
*( DWORD* )( oldFunc+0x06 ) = DWORD( newMem4base-( oldFunc+0x5 )-5 );
newMem4base += len;
newMem4base[0] = 0xE9;
*( DWORD* )( newMem4base+0x01 ) = DWORD( ( oldFunc+10 )-newMem4base-5 );
for( DWORD i = 10; i <len; i++ )
oldFunc[i] = 0x90;
return true;
}
DWORD WINAPI Thread( LPVOID lpParam )
{
DetourFunc( ( BYTE* )0x004013EE,
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
if( ul_reason_for_call == DLL_PROCESS_ATTACH )
{
HANDLE hThread = CreateThread( NULL, 0, Thread, NULL, 0, NULL );
CloseHandle( hThread );
}
return TRUE;
}
Code:
#include <windows.h>
#include <iostream>
bool DetourFunc( BYTE* oldFunc, BYTE* newFunc, DWORD len )
{
BYTE* newMem4base = NULL;
DWORD dwOld;
newMem4base = ( BYTE* )malloc( 5+len );
if( newMem4base == NULL )
return false;
for( DWORD i = 0; i < ( len+5 ); i++ )
newMem4base[i] = 0x90;
VirtualProtect( oldFunc, len, PAGE_READWRITE, &dwOld );
memcpy( newMem4base, oldFunc, len );
oldFunc[0] = 0xE8;
*( DWORD* )( oldFunc+0x01 ) = DWORD( newFunc-oldFunc-5 );
oldFunc[5] = 0xE9;
*( DWORD* )( oldFunc+0x06 ) = DWORD( newMem4base-( oldFunc+0x5 )-5 );
newMem4base += len;
newMem4base[0] = 0xE9;
*( DWORD* )( newMem4base+0x01 ) = DWORD( ( oldFunc+10 )-newMem4base-5 );
for( DWORD i = 10; i <len; i++ )
oldFunc[i] = 0x90;
return true;
}
void MyFunction()
{
std::cout << "Von DLL. HGW dein Detour funktioniert :)\n";
}
DWORD WINAPI Thread( LPVOID lpParam )
{
DetourFunc( ( BYTE* )0x004013EE,
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
if( ul_reason_for_call == DLL_PROCESS_ATTACH )
{
HANDLE hThread = CreateThread( NULL, 0, Thread, NULL, 0, NULL );
CloseHandle( hThread );
}
return TRUE;
}
Code:
#include <windows.h>
#include <iostream>
bool DetourFunc( BYTE* oldFunc, BYTE* newFunc, DWORD len )
{
BYTE* newMem4base = NULL;
DWORD dwOld;
newMem4base = ( BYTE* )malloc( 5+len );
if( newMem4base == NULL )
return false;
for( DWORD i = 0; i < ( len+5 ); i++ )
newMem4base[i] = 0x90;
VirtualProtect( oldFunc, len, PAGE_READWRITE, &dwOld );
memcpy( newMem4base, oldFunc, len );
oldFunc[0] = 0xE8;
*( DWORD* )( oldFunc+0x01 ) = DWORD( newFunc-oldFunc-5 );
oldFunc[5] = 0xE9;
*( DWORD* )( oldFunc+0x06 ) = DWORD( newMem4base-( oldFunc+0x5 )-5 );
newMem4base += len;
newMem4base[0] = 0xE9;
*( DWORD* )( newMem4base+0x01 ) = DWORD( ( oldFunc+10 )-newMem4base-5 );
for( DWORD i = 10; i <len; i++ )
oldFunc[i] = 0x90;
return true;
}
void MyFunction()
{
std::cout << "Von DLL. HGW dein Detour funktioniert :)\n";
}
DWORD WINAPI Thread( LPVOID lpParam )
{
DetourFunc( ( BYTE* )0x004013EE, ( BYTE* )&MyFunction
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
if( ul_reason_for_call == DLL_PROCESS_ATTACH )
{
HANDLE hThread = CreateThread( NULL, 0, Thread, NULL, 0, NULL );
CloseHandle( hThread );
}
return TRUE;
}
PUSH EBP
JMP 32000000
CALL 4400000000
Dann machen alle drei Befehle insgesamt eine Größe von 11Bytes.Würden wir jetzt nur die ersten 10Bytes sichern und überschreiben, dann bleibt in der Ur-Funktion das letzte Byte übrig (0x00). Wir sichern ja die alten Bytes auf einem neuen Speicherblock. Und nach der Sicherung springen wir zurück zur Ur-Funktion. Gehen wir das mal durch. PUSH EBP braucht ein Byte. Bleiben noch 9 aus unserer Sicherung. JMP braucht 5Bytes. Bleiben noch 4Bytes aus der Sicherung. CALL braucht aber auch 5Bytes! Es sind aber nur noch 4übrig, da wir das letzte nicht mit gesichert haben. Problem! CALL nimmt sich aber 5Bytes. Was kam nach der Sicherung? Richtig der Jump zurück. Also nimmt sich CALL einfach das E9-Byte aus unserem Jump. Nicht gut. Damit kehren wir von unserem Speicherblock nichtmehr zurück zu unserer Ur-Funktion und landen irgendwo im Speicher-Nirvana… Ich hoffe du hast das bis jetzt einigermaßen verstanden :) Ich fasse es mal zusammen:
Regeln für die Anzahl Bytes, die gesichert werden sollen:
1. Es müssen mindestens 10Bytes sein
2. Es müssen alle zu einem Befehl gehörigen Bytes gesichert werden
Schauen wir uns in Olly mal unsere Ur-Funktion an. Der erste Befehl ist PUSH EBP. Links neben dem Befehl können wir die Bytes zu dem Befehl sehen. Und es ist ein Byte. Zu wenig, wir brauchen mind. 10Bytes. Der nächste Befehl ist MOV EBP, ESP. Dieser ist 2Bytes groß. Insgesamt haben wir 3Bytes, immer noch zu wenig. Der nächste Befehl ist SUB ESP, 8. Dieser ist 3Bytes groß. Nun haben wir schon 6Bytes. Immer noch zu wenig. Ui der nächste Befehl ist lang :) MOV blabla, blub. Dieser nimmt insgesamt 8Bytes ein. Wow. Mit den 6Bytes von vorhin haben wir 14Bytes. Das ist größer als 10. Damit hätten wir die Größe und wir könne sie als Parameter unserer Funktion übergeben:
Code:
#include <windows.h>
#include <iostream>
bool DetourFunc( BYTE* oldFunc, BYTE* newFunc, DWORD len )
{
BYTE* newMem4base = NULL;
DWORD dwOld;
newMem4base = ( BYTE* )malloc( 5+len );
if( newMem4base == NULL )
return false;
for( DWORD i = 0; i < ( len+5 ); i++ )
newMem4base[i] = 0x90;
VirtualProtect( oldFunc, len, PAGE_READWRITE, &dwOld );
memcpy( newMem4base, oldFunc, len );
oldFunc[0] = 0xE8;
*( DWORD* )( oldFunc+0x01 ) = DWORD( newFunc-oldFunc-5 );
oldFunc[5] = 0xE9;
*( DWORD* )( oldFunc+0x06 ) = DWORD( newMem4base-( oldFunc+0x5 )-5 );
newMem4base += len;
newMem4base[0] = 0xE9;
*( DWORD* )( newMem4base+0x01 ) = DWORD( ( oldFunc+10 )-newMem4base-5 );
for( DWORD i = 10; i <len; i++ )
oldFunc[i] = 0x90;
return true;
}
void MyFunction()
{
std::cout << "Von DLL. HGW dein Detour funktioniert :)\n";
}
DWORD WINAPI Thread( LPVOID lpParam )
{
DetourFunc( ( BYTE* )0x004013EE, ( BYTE* )&MyFunction, 14 );
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
if( ul_reason_for_call == DLL_PROCESS_ATTACH )
{
HANDLE hThread = CreateThread( NULL, 0, Thread, NULL, 0, NULL );
CloseHandle( hThread );
}
return TRUE;
}
[Only registered and activated users can see links. Click Here To Register...]
Ich wünsche euch viel Spaß beim Programmieren :) Wie immer wenn ihr Fehler findet, melden, ansonsten viel Spaß damit, würde mich über Feedback freuen.
Credits:
Nope
[Only registered and activated users can see links. Click Here To Register...]