[C++] Eigene Detours Teil1

04/05/2010 20:40 xNopex#1
Ein dickes Hallo an alle :D,

Ich möchte euch mit diesem Tutorial den Umgang mit Detours zeigen. Zu allererst klären wir die spannende Frage, was ein Detour ist:

Ein Detour führt eine bestimmte Funktion von dir aus, sobald eine andere Funktion, die von dir bestimmt wird, aufgerufen wird. So ruft man bei Trainer mit einem D3D-Menü immer dann die eigene Methode zum Zeichnen des Menüs auf, wenn die Funktion EndScene() aus der D3D.dll aufgerufen wird. Also brauchst du für ein Ingame-Menü ein Detour :)

Okay damit sollte hoffentlich klar sein, was ein Detour ist, wenn nicht, fragt einfach nochmal nach. So jetzt kommen wir zu der Theorie, wie der Detour funktionieren soll. Erstmal geh ich grob auf nötiges Backgroundwissen ein, damit du verstehst, warum wir was machen. Jede Funktion nimmt im Speicher eines Prozesses eine bestimmte Adresse und Größe ein. Zum Beispiel kann eine Funktion an der Adresse 0x0040CCE1 beginnen und an der Adresse 0x0040CE00 enden. Weiterhin solltest du wissen, dass jede Adresse ein Byte speichert. Ein Byte nimmt die Werte von 0x00 bis 0xFF ein. Die Bytes werden auch OP-Codes genannt. Jetzt haben einige OP-Codes ein spezielle Bedeutung. Zum Beispiel bedeutet 0x90 NOP, d.h. die Adresse ist ohne Funktion. Das Byte 0xE8 ist ein CALL. Ein CALL ruft eine andere Funktion auf. Das Byte 0xE9 ist ein JMP (Jump). Ein Jump springt an eine gewisse Adresse im Adressenraum. Natürlich habe ich nicht zufällig diese drei OP-Codes näher erläutert ;) Diese sind besonders für unseren Detour später interessant, also solltest du dir deren Bedeutung gut merken. So nachdem du dir jetzt im schnell-Exkurs wichtiges Backgroundwissen angeeignet hast, können wir zur Theorie eines Detours übergehen. Zuallererst brauchen wir die Start-Adresse der Funktion, die wir mit unserem Detour versehen wollen. Diese bekommen wir mithilfe eines Debuggers, zum Beispiel OllyDbg. Nun werden wir die Funktion so verändern, dass sie unsere Funktion aufruft und dann erst ihren Ablauf abarbeitet. Und zwar ersetzen wir das erste Byte der Funktion durch 0xE8. Wie oben bereits erwähnt steht 0xE8 für den CALL Befehl. Nach dem CALL-Befehl folgen vier Bytes, die die Adresse der Funktion angeben, die durch CALL aufgerufen werden soll. Das klingt nicht nur einfach, das ist auch einfach ;) Aber leider etwas zu einfach, denn es gibt ein Problem: Wenn wir die ersten fünf Bytes der Funktion überschreiben, dann geht der alte Code verloren. Wenn das jetzt wichtiger Code war, dann stürzt das Programm ab. Darum müssen wir da Vorkehrungen treffen. Und zwar speichern wir zuvor die OP-Codes, die überschrieben werden. Dann fordern wir neuen Speicher an und schreiben die überschriebenen Bytes dorthin. Danach springen ( JMP ;] ) wir aus der alten Funktion zu den angeforderten Speicherblock, wo der ursprüngliche (wichtige) Code ausgeführt wird und anschließend zurück zur alten Funktion. Ich hoffe du kommst noch mit :D

Zusammenfassend:

1. Adresse der Ur-Funktion, die umgeleitet werden soll herausfinden
2. Neuen Speicher anfordern
3. Alte OP-Codes im neuen Speicher sichern
4. Bytes der Ur-Funktion mit CALL auf neue Funktion überschreiben
5. Bytes der Ur-Funktion mit JMP auf Sicherung überschreiben
6. Ende des Speicherblocks mit JMP auf Ur-Funktion überschreiben


So dann fangen wir mal an die nötige Funktion dafür zu programmieren:

Als erstes sollten wir überlegen, welche Parameter wir brauchen. Zum einen brauchen wir die Adresse der Ur-Funktion, dann die Adresse der neuen Funktion und zu guter letzt die Anzahl an Bytes, die gesichert werden sollen:

Code:
#include <windows.h>

bool DetourFunc( ( BYTE* )oldFunc, ( BYTE* )newFunc, DWORD len )
{
    return true;
}
Was war schritt 1 nochmal? *Oben nachgucken* Okay, für das Programmieren brauchen wir vorerst die Adresse der Ur-Funktion nicht (wird später als Parameter übergeben), also fangen wir mit Schritt 2 an, das Anfordern von neuem Speicher. Dafür gibt es die schöne Funktion malloc(). Sie erwartet als Parameter die Größe des angeforderten Speicherblocks in Bytes und liefert als return-Wert einen void-Zeiger auf den Adressraum. An dieser Stelle sollten wir uns überlegen: Wie groß muss der Speicherblock sein? Dazu sollten wir uns überlegen, was in den Speicherblock "kommt". Zum einen die gesicherten OP-Codes aus der Ur-Funktion, also muss der Speicherblock schonmal mindestens die Anzahl Bytes fassen können, die wir als Parameter len an die DetourFunc() übergeben haben. Zum anderen müssen wir von dem Speicherblock wieder zurück zur Ur-Funktion springen können. Dazu muss der Speicherblock noch einen zusätzlichen Byte für den Spring-Befehl aufnehmen können (0xE9) und nochmals vier weitere für die Zieladresse. Macht insgesamt fünf Bytes. Die Gesamtgröße ist also 5+len Bytes:

Code:
#include <windows.h>

bool DetourFunc( ( BYTE* )oldFunc, ( BYTE* )newFunc, DWORD len )
{
    BYTE* newMem4base = NULL;
    
    newMem4base = ( BYTE* )malloc( 5+len );
    if( newMem4Base == NULL )
        return false;

    for( DWORD i = 0; i < ( len+5 ); i++ )
        newMem4base[i] = 0x90;

    return true;
}
newMem4base enthält also den Zeiger auf den neuen Speicherblock. Wenn die Funktion fehlschlägt, der Zeiger also NULL ist, schlägt die Funktion fehl und gibt false zurück. Danach belegen wir jeder Speicheradresse mit einem NOP ( entspricht nichts machen).
Der nächste Schritt wäre das Speichern der alten Bytes im neuen Speicherblock. Dazu müssen wir uns erstmal die nötigen Rechte verschaffen, um auf den Speicher zuzugreifen. Das geschieht mittels VirtualProtect(). MSDN liefert nähere Informationen zu der Funktion. Danach kopieren wir mithilfe von memcpy() die Bytes. memcpy() erwartet als ersten Parameter die Ziel-Adresse, als zweiten die Quelle und als dritten die Anzahl Bytes, die kopiert werden sollen:

Code:
#include <windows.h>

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 );

    return true;
}
Langsam kommen wir zum Ende :) Nachdem wir also die Bytes gesichert haben, müssen wir den CALL auf unsere neue Funktion einrichten. Dazu überschrieben wir das erste Byte der Ur-Funktion mit dem OP-Code für CALL (0xE8):

Code:
#include <windows.h>

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;

    return true;
}
So unmittelbar nach dem CALL-Byte folgen vier Bytes, die die Adresse der Funktion angeben, die aufgerufen werden soll. Diese Adresse muss relativ zu der aktuellen Adresse sein. Wir haben als Parameter jedoch die absolute Adresse unserer Funktion angegeben, also müssen wir erst an die relative Adresse kommen. Dazu gibt es zum Glück eine gültige Regel:

relativeAdresse = absoluteAdresse - aktuelleAdresse -5;

Die absoluteAdresse ist in unserem Fall die Adresse der neuen Funktion, die aktuelleAdresse ist die Adresse der Ur-Funktion. Damit ergibt sich folgendes:

Code:
#include <windows.h>

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 );

    return true;
}
Damit wäre der CALL auf unsere Funktion fertig. Nachdem unsere Funktion aufgerufen wurde, müssen wir nun zu unserem neuen Speicherblock springen, an dem die alten OP-Codes gespeichert wurden, damit unsere Ur-Funktion auch noch ordnungsgemäß ausgeführt wird. Dazu setzen wir das Byte der nächsten Adresse auf 0xE9 und die nächsten 4Bytes wieder auf die Adresse, zu der gesprungen werden soll. Auch hier müssen wir erst aus den absoluten Adressen relative machen:

Code:
#include <windows.h>

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 );

    return true;
}
Kaum zu glauben, aber wir sind fast fertig! Nur noch zwei Schritte:

1. Wir müssen von unserem Speicherblock wieder zurück zu der Ur-Funktion springen

2. Wir müssen "Zu viel" gesicherte Bytes aus der Ur-Funktion mit NOP's versehen


Diese Punkte sollten keine Probleme mehr bereiten. Zum ersten Schritt: Zuerst erhöhen wir die Adresse des neuen Speicherblocks, um die Anzahl der gesicherten Bytes, damit die Adresse auf freien Speicher zeigt. Danach überschreiben wir wieder das erste Byte mit 0xE9 (JMP) und die nächsten vier Bytes mit der relativen Adresse, zu der gesprungen werden soll. Zum zweiten Schritt: Insgesamt nehmen der CALL und der JMP aus der Ur-Funktion 10Bytes ein. Sollte die Anzahl der zu sichernden Bytes, die als Parameter überegben wurde, größer als 10Bytes sein, müssen wir die restlichen Bytes aus der Ur-Funktion NOPen, damit diese nicht doppelt ausgeführt werden:

Code:
#include <windows.h>

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;
}
So damit ist die DetourFunc() fertig programmiert. Bleibt eine spannende Frage: Wie wende ich die Funktion denn jetzt an? Diese spannende Frage werde ich im zweiten Teil des Tutorials mithilfe eines Beispiels erklären ;) Also gedulded euch, bis es fertig ist :)


Wenn ihr Fragen habt, könnt ihr sie gerne stellen.

Und wenn ihr Fehler findet (sei es Formulierungsfehler oder Falschaussagen), bitte melden; ansonsten freue ich mich auch über Feedback.


[Only registered and activated users can see links. Click Here To Register...]

Credits:
Nope
[Only registered and activated users can see links. Click Here To Register...]
04/05/2010 21:01 MrSm!th#2
Auch hier ist zu beachten, dass man entweder den Code disassemblieren muss und dann die richtige Anzahl an Bytes sichern muss oder nur API Funktionen hooken darf.
Denn wenn dort eine Instruction ist, die mehr oder weniger als 5 Bytes belegt, dann machst du sie durch den Hook kaputt.
04/05/2010 21:06 Blackbirds#3
Das Tutorial kommt für mich genau richtig, da ich mich garde mit d3d9 und Hooking und den ganzen Kram beschäftige... thx :)
04/05/2010 21:15 schlurmann#4
Quote:
Originally Posted by xTheLast View Post
Das Tutorial kommt für mich genau richtig, da ich mich garde mit d3d9 und Hooking und den ganzen Kram beschäftige... thx :)
Lieber 'mal mit'm Dativ beschäftigen.
04/05/2010 21:23 Blackbirds#5
Quote:
Originally Posted by schlurmann View Post
Lieber 'mal mit'm Dativ beschäftigen.
|
v

Quote:
Originally Posted by schlurmann View Post
Und ich würde mich über Megan Fox auf meinem Penis freuen...

viel sinnvolles kommt von dir irgendwie nicht, oder? leute wie dich, deren geistiger horizont niedriger ist als der von einem meter feldweg sollten eigentlich sofort gebannt werden

und btw: wer außer du achtet eig schon auf groß-und kleinschreibung?
04/05/2010 22:23 xNopex#6
Quote:
Originally Posted by MrSm!th View Post
Auch hier ist zu beachten, dass man entweder den Code disassemblieren muss und dann die richtige Anzahl an Bytes sichern muss oder nur API Funktionen hooken darf.
Denn wenn dort eine Instruction ist, die mehr oder weniger als 5 Bytes belegt, dann machst du sie durch den Hook kaputt.
Das kommt alles in Teil2 ;)
04/06/2010 16:22 MrSm!th#7
Quote:
Originally Posted by xNopex View Post
Das kommt alles in Teil2 ;)
ja, da schreibst du deine eigene disassemble lib? o.O
Quote:
Originally Posted by xTheLast View Post
|
v




viel sinnvolles kommt von dir irgendwie nicht, oder? leute wie dich, deren geistiger horizont niedriger ist als der von einem meter feldweg sollten eigentlich sofort gebannt werden

und btw: wer außer du achtet eig schon auf groß-und kleinschreibung?
i lol'd 'bout this fail :awesome:
05/27/2010 20:50 schlurmann#8
Quote:
Originally Posted by Blackbirds View Post

und btw: wer außer du achtet eig schon auf groß-und kleinschreibung?
Keine Ahnung. Bill Gates, Albert Einstein, Steven Hawking? Die Liste ist lang.

Der Dativ ist übrigens ein Fall und hat nichts mit Groß- und Kleinschreibung zu tun.
05/27/2010 21:04 Bl@ze!#9
Wieso nicht Microsoft Detours lib benutzen? Funktioniert doch. <.<
Außerdem glaube ich, dass ich genau diese DetourFunc() schonmal in einem Forum gesehen habe, vor einigen Monaten aber, bis auf die Variabel mem4base, die hieß anders.
05/27/2010 21:10 Ende!#10
Quote:
Originally Posted by Blackbirds View Post
|
und btw: wer außer du achtet eig schon auf groß-und kleinschreibung?
Ja, genau, wer außer du macht das schon? Ist ja total behindernd beim Lesen!

Damit das hier kein Spamm ist: An sich ein hübsches Tutorial, ich würde aber trotzdem lieber MS Detours benutzen, einfach weil du ein Problem hast, wenn zufällig genau in dem Moment der Code ausgeführt wird, den du überschreibst. Außerdem hab ich bei GameDeception so ziemlich genau diese Funktion schon mal gesehen. Für Anfänger ist dein Tutorial jedoch vielleich ganz hilfreich, weil sie so den Stoff so besser verstehen.
06/05/2010 20:09 RedKelly#11
ist eig autoit leichter zu programmieren oder C++ ?
07/13/2010 18:29 ●ROBBY●#12
Quote:
Originally Posted by RedKelly View Post
ist eig autoit leichter zu programmieren oder C++ ?
AutoIt aber das gehört hier nicht rein.

B2T. Nettes Tutorial.
03/27/2011 22:18 yihaaa#13
Schön und gut das ganze nur du solltest aufjeden Fall noch die Bytes die du überschreibst sicher und dann die Adresse des Arrays zurückgeben. In diesem Array muss aber noch darauf geachet werden, dass ein JMP zum alten Code vorhanden ist. Dann kannst du dir einen Functionpointer erstellen und dann läuft die Sache sicher.

MfG
04/01/2011 03:36 Flyff_Service#14
Die meisten Anti Hacks erkennen das leider schon, aber zum Glück gibts ja auch noch andere Methoden
04/03/2011 19:25 yihaaa#15
Ja BP's zum Beispiel. Und zum Thema detected kann man nur sagen, dann hast du nicht richtig reversed :P

MfG