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
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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.

Credits:
Nope






