Reverse Engineering: Wo anfangen?



  • Hallo,

    Ich habe mal eine Frage bezüglich Reverse Engineering. Und zwar frage ich mich, wie man anfängt ein Target zu analysieren. Dazu möchte ich ein Beispiel vorgeben. Dies ist nur zur Übung und Überlegung gedacht. Wenn man jetzt ein Spiel hat, und möchte z.b. an bestimmte Werte kommen. Dies seien hier mal z.b. Spielerpositionen und Lebenspunkte. Diese sollten in irgendeiner Spielerdatenstruktur gesprichert/referenziert werden. Die Frage ist: Wie fängt man da am besten an, um ans Ziel zu kommen? Gibt es da bestimmte Vorgehensweisen? Sollte man erstmal generell das Verständnis haben, wie Spiele im Prinzip aufgebaut sind, bzw. die Spielerdaten verwalten? Ich denke nicht, dass man beim Einsprungspunkt anfängt... und dann von da an so lange das Spiel auseinandernimmt, bis man ans Ziel kommt. Oder doch? Das würde doch extrem lange dauern. Manche benutzen ja Cheat-Engine. Das Programm kenne ich nur vom Hören. In meinem Fall möchte ich aber nur das Spiel und den Disassembler (ich verwende OllyDbg) benutzen. Auf ein Spiele-SDK soll ebenfalls verzichtet werden.

    Ich weiß einfach nicht, wo ich am besten anfangen sollte, wenn ich nun das Spiel analysiere.

    Über Hinweise/Links und Anregungen bzw. Hilfe würde ich mich sehr freuen. 👍

    Hoffe, dass mein Text jetzt auch nicht zu lang war. ^^

    In den Augen der Pros klingt das Geschriebene vllt dämlich, aber ich bitte um Nachsicht, denn jeder hat ja mal klein angefangen.

    Vielen dank.



  • Kommt ganz auf das Spiel an und was du machen willst.
    In Olly kannst du auch memory search machen, aber mit CheatEngine ist es komfortabler!
    Beim entry point anfangen kann durchaus nützlich/nötig sein.
    Nen Wert im Speicher finden hat übrigens eher wenig mit RE zu tun 😛
    Leg einfach los, setz dir einfache Ziele etc, die Erfahrung kommt mit der Zeit...



  • Also ich überlege mir immer WAS ich GENAU machen will und ob eventuelle standard API Funktionen aufgerufen werden müssen.

    Wenn ich ein neues Ziel Analysieren will dann schaue ich immer erst ob ich aufällige Anti Debug Routinen finde ( z.b. IsDebuggerPresent(); ist immer sehr aufällig wenn es direkt über die API aufgerufen wird^^ [JA OK ich verwende ein Olly Plugin was den passenden Wert im PEB setzt^^] )

    Für so Sachen wie Werte im Speicher finden verwende ich auch Cheat Engine stell dir mal vor du sollst deine Spielerposition ermitteln in dem du das ganze im Debugger Analyisierst ... also das würde ich nur im absoluten Notfall machen wobei nein ich würde es gar nicht machen weil mir der Zeitaufwand für ein Game Hack da dann doch dann zugroß wäre. Da fehlt mir dann die Motivation für sowas ...

    Ich hab mal schnell was zusammen geschustert das kannst du ja einfach mal Compilieren und anschließend versuchen die Punkte manuell auf 2000 zu setzen oder so.

    #include <iostream>
    #include <conio>
    #include <windows>
    
    #define KEY_RIGHT 100
    #define KEY_LEFT 97
    #define KEY_UP 119
    
    using namespace std;
    
    void SpawnEnemy()
    {
       gotoxy(10,10);
       cout<<"\x3";
    }
    
    int main()
    {
    int x=1,y=20;
    int SavedX,SavedY;
    
    char p='\x1';
    int key;
    int points =0;
    
    char projektil='\x4';
    
    srand(time(0));
    
    while(1)
    {
            key = getch();
    
            if ( key == KEY_RIGHT)
            {
               clrscr();
               x++;
               gotoxy(x,y);
               cout<< p;
            }
    
            if ( key == KEY_LEFT )
            {
               clrscr();
               x--;
               gotoxy(x,y);
               cout<< p;
            }
    
            if ( key == KEY_UP )
            {
               SavedX = x;
               SavedY = y;
               for(unsigned i=0;i<10;i++)
               {
                  y--;
                  gotoxy(x,y);
                  cout<< projektil;
                  Sleep(20);
               }
    
               if ( x == 10 && y == 10 ) // x=10 , y=10 an der position ist der gegner
               {
                    points++;
               }
    
               clrscr();
               x = SavedX;
               y = SavedY;
               gotoxy(x,y);
               cout<<p;
            }
    
            SpawnEnemy();
    
            gotoxy(1,8);
            cout<<"Punkte: " << points;
    }
    
    }
    

    Nochmal kurz ein Beispiel zu API Funktionen:
    Es gibt ja z.b. Programme die nur Zeitbegrenzt und um die Zeitbegrenzung zu entfernen muss man erst mal wissen wie sowas ungefährt implementiert worden sein könnte.

    In so einem Fall konzentriere ich mich immer erst auf Funktionen die dafür verwendet werden so Sachen wie Datum/Uhrzeit abzufragen.

    Diese Funktionen kann man in der Regel sehr schnell mit Olly finden sollte im Zielmodul nichts zu finden sein dann könntest du auch direkt in der entsprechenden DLL einen Breakpoint auf die Funktion setzen.

    Beispiel für die Funktion: GetSystemTime
    GetSystemTime ist in der Kernel32.dll zu finden. (Das steht auch in der MSDN http://msdn.microsoft.com/de-de/library/windows/desktop/ms724390(v=vs.85).aspx )

    Jetzt drückst du in Olly einfach strg + g und gibst im Suchfeld ein: GetSystemTime

    Dann landest du im Kernel32 Module (kernel32.dll) dann scrollst du bis zum Funktionsende und setzt dort einen Breakpoint, jetzt einfach die Zielanwendung normal mit F9 ausführen. Dann wenn die Zeit abgefragt wird landest du beim Breakpoint jetzt einmal F7 drücken und schon siehst du von wo genau die API Funktion aufgerufen wurde. (Nach dem Drücken von F7 landest du wieder im Module deiner Zielanwendung).

    💡

    Man findet nicht immer mit der "All intermodular calls" Funktion von Olly bestimmte API Funktionen obwohl die Zielanwendung diese Aufruft.



  • Hallo,

    danke für eure Beiträge.

    Nen Wert im Speicher finden hat übrigens eher wenig mit RE zu tun 😛

    Ich möchte ja nicht einfach nur den Wert finden, sondern hauptsächlich einen Weg finden, um immer dynamisch zu der Adresse, in der der Wert abgelegt ist zu gelangen.

    Mein Problem ist schon mal folgendes. Ich habe jetzt z.b. bei Counter-Strike 1.6 die Lebenspunkte mit CE gefunden (Server Programm), aber wie ich nun einen dynamischen Weg zur Adresse finde, das habe ich nicht geschafft.

    Wobei die vorgehensweise eigentlich einfach sein sollte: Ich schaue wo die Adresse verwendet wird und versuche eine Signatur im Zusammenhang mit der Adresse zu finden, die im Prozess einzigartig ist. Diese kann dann verwendet werden, damit jeder der den "Hack" nutzen würde, auch sicher sein kann, dass immer die Lebenspunkte gemanaged werden. Da ja oftmals Adressen von System zu System unterschiedlich sind, ist ein dynamischer Offset-Finding-Algorithmus unumgänglich.

    Hierbei scheitere ich jetzt daran, zu sehen, wo die betreffende Adresse verwendet wird. Wenn ich jetzt die Adresse von CE bekommen habe, wie gehe ich nun am besten mit OllyDbg vor, um zu finden, wo die Adresse im Codesegment verwendet wird? Immerhin gibt es zig Möglichkeiten, wie die Adresse vom Spielecode gehandelt werden könnte.

    Ich brauch einfach das betreffende Wissen, wie man vorgehen sollte, damit ich das dann auch immer alleine ohne Hilfe machen kann.

    So, der Text ist mal wieder lang geworden, hoffe, dass das in Ordnung ist.

    Für Antworten bin ich sehr dankbar. 👍

    Viele Grüße 🙂



  • reinterest schrieb:

    Ich brauch einfach das betreffende Wissen, wie man vorgehen sollte, damit ich das dann auch immer alleine ohne Hilfe machen kann.

    o du musst gut Programmier können (C,C++,Assembler,...)
    o die WinAPI kennen
    o viele Jahre Erfahrung mit vorher Genanntem haben

    sofern dann überhaupt noch Interesse dran besteht, ergeben sich die "RE-Fähigkeiten" ganz von selbst 😉



  • @ reinterest

    Für mich beschriebst du das ganze komplizierter als es eigentlich ist. (Oder ich hab nicht verstanden was du meinst.^^)

    Naja folgendes habe ich verstanden:

    - Du suchst die Lebenspunktem mit CE.
    - Du findest die Lebenspunkt Adresse.

    Jetzt startest du z.b. dein Spiel neu und stellst fest das deine eben ermittelte Adresse nicht mehr die Lebenspunkte beeinhalten.

    So kann man das Problem lösen [Falls es überhaupt gerade dein Problem ist^^]:

    - Basis Adresse der DLL finden in welcher sich die Adresse befindet.

    Jetzt nur noch kurz rechnen:
    Aktuelle Lebenspunkt Adresse - Basis Adresse der DLL = RVA Adresse

    Jetzt machst du einfach folgendes:
    Starte dein Spiel einfach mal neu und rechne folgendes.
    RVA Adresse + Basis Adresse der DLL = Lebenspunkte Adresse

    Beispiel mit WinExec (WinExec ist in der user32.dll enthalten):

    Basis Adresse der user32.dll ist bei mir: 77bd0000
    Adresse der WinExec Funktion ist bei mir: 7C862585

    7C862585(WinExec Adresse) - 77bd0000(user32.dll Basis Adresse) = 4C92585

    4C92585 + 77bd0000(user32.dll Basis Adresse) = 7C862585 ( WinExec Adresse )

    So kannst du die Basis Adresse einer DLL ermitteln:

    #include <windows>
    #include <iostream>
    #include <Tlhelp32>
    #include <Psapi.h>
    #include <string>
    #include <conio>
    using namespace std;
    
    unsigned GetProcessId( string ProcessName );
    unsigned GetDllBaseAddress(unsigned id, string ProcessName, string DllName );
    
    int main(int argc, char* argv[])
    {
    
    	unsigned id = GetProcessId( "DeinSpiel.exe"  );
    	if ( id == 0 )
    	{
    				cout<<"Error, the process does not exist"<<endl;
    				getch();
    				return 0;
    	}
    
    	unsigned Addr = GetDllBaseAddress( id, "DeinSpiel.exe" , "user32.dll" );
    	if( Addr == 0 )
    	{
    		cout<<"GetDllBaseAddress error"<<endl;
    		getch();
    		return 0;
    	}
        cout<<"Addr: " << hex<< Addr << endl;
    
    return 0;
    }
    //---------------------------------------------------------------------------
    unsigned GetDllBaseAddress( unsigned id, string ProcessName, string DllName )
    {
    HANDLE snap;
    MODULEENTRY32 lppe;
    lppe.dwSize = sizeof(MODULEENTRY32);
    
    		snap =  CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, id );
    
    		if ( snap == INVALID_HANDLE_VALUE )
    		{
    			cout<< "CreateToolhelp32Snapshot error: " << GetLastError() << endl;
    			getch();
    			return 0;
    		}
    
    		Module32First(  snap, &lppe );
    		do
    		{
    				if ( !strcmp( lppe.szModule,DllName.c_str()) )
    				{
    						cout<<lppe.szModule<< "     "<<hex<<lppe.hModule<<endl;
    						break;
    				}
    	   }
    		while ( Module32Next ( snap , &lppe ) == 1 ) ;
    
    	   return unsigned(lppe.hModule);
    }
    
    unsigned GetProcessId( string ProcessName )
    {
    bool ProcessExists = false;
    HANDLE snap;
    PROCESSENTRY32 lppe;
    lppe.dwSize = sizeof(PROCESSENTRY32);
    
    ZeroMemory( &lppe,sizeof(lppe)) ;
    
    snap =  CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    
    		if ( snap == INVALID_HANDLE_VALUE )
    		{
    			cout<<"CreateToolhelp32Snapshot error: " << GetLastError() << endl;
    			CloseHandle(snap);
    			getch();
    			return 0;
    		}
    
    		lppe.dwSize = sizeof(PROCESSENTRY32);
    		Process32First(  snap, &lppe );
    
    		do
    		{
    				if( ! strcmp(lppe.szExeFile, ProcessName.c_str() ) )
    				{
    				   cout<<lppe.szExeFile << "  Process ID: "  << lppe.th32ProcessID << endl;
    				   ProcessExists = true;
    				   CloseHandle(snap);
    				   break;
    				}
    		}
    		while ( Process32Next ( snap , &lppe ) == 1 ) ;
    
    		if ( ProcessExists == false )
    		{
    			  CloseHandle(snap);
    			  return 0;
                    }
    
    return lppe.th32ProcessID;
    }
    


  • Bassmaster schrieb:

    @ reinterest

    Für mich beschriebst du das ganze komplizierter als es eigentlich ist. (Oder ich hab nicht verstanden was du meinst.^^)

    Naja folgendes habe ich verstanden:

    - Du suchst die Lebenspunktem mit CE.
    - Du findest die Lebenspunkt Adresse.

    Jetzt startest du z.b. dein Spiel neu und stellst fest das deine eben ermittelte Adresse nicht mehr die Lebenspunkte beeinhalten.

    So kann man das Problem lösen [Falls es überhaupt gerade dein Problem ist^^]:

    - Basis Adresse der DLL finden in welcher sich die Adresse befindet.

    Jetzt nur noch kurz rechnen:
    Aktuelle Lebenspunkt Adresse - Basis Adresse der DLL = RVA Adresse

    Jetzt machst du einfach folgendes:
    Starte dein Spiel einfach mal neu und rechne folgendes.
    RVA Adresse + Basis Adresse der DLL = Lebenspunkte Adresse

    Beispiel mit WinExec (WinExec ist in der user32.dll enthalten):

    Basis Adresse der user32.dll ist bei mir: 77bd0000
    Adresse der WinExec Funktion ist bei mir: 7C862585

    7C862585(WinExec Adresse) - 77bd0000(user32.dll Basis Adresse) = 4C92585

    4C92585 + 77bd0000(user32.dll Basis Adresse) = 7C862585 ( WinExec Adresse )

    So kannst du die Basis Adresse einer DLL ermitteln:

    #include <windows>
    #include <iostream>
    #include <Tlhelp32>
    #include <Psapi.h>
    #include <string>
    #include <conio>
    using namespace std;
    
    unsigned GetProcessId( string ProcessName );
    unsigned GetDllBaseAddress(unsigned id, string ProcessName, string DllName );
    
    int main(int argc, char* argv[])
    {
    
    	unsigned id = GetProcessId( "DeinSpiel.exe"  );
    	if ( id == 0 )
    	{
    				cout<<"Error, the process does not exist"<<endl;
    				getch();
    				return 0;
    	}
    
    	unsigned Addr = GetDllBaseAddress( id, "DeinSpiel.exe" , "user32.dll" );
    	if( Addr == 0 )
    	{
    		cout<<"GetDllBaseAddress error"<<endl;
    		getch();
    		return 0;
    	}
        cout<<"Addr: " << hex<< Addr << endl;
    
    return 0;
    }
    //---------------------------------------------------------------------------
    unsigned GetDllBaseAddress( unsigned id, string ProcessName, string DllName )
    {
    HANDLE snap;
    MODULEENTRY32 lppe;
    lppe.dwSize = sizeof(MODULEENTRY32);
    
    		snap =  CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, id );
    
    		if ( snap == INVALID_HANDLE_VALUE )
    		{
    			cout<< "CreateToolhelp32Snapshot error: " << GetLastError() << endl;
    			getch();
    			return 0;
    		}
    
             
    
    		Module32First(  snap, &lppe );
    		do
    		{
    				if ( !strcmp( lppe.szModule,DllName.c_str()) )
    				{
    						cout<<lppe.szModule<< "     "<<hex<<lppe.hModule<<endl;
    						break;
    				}
    	   }
    		while ( Module32Next ( snap , &lppe ) == 1 ) ;
    
    	   return unsigned(lppe.hModule);
    }
    
    unsigned GetProcessId( string ProcessName )
    {
    bool ProcessExists = false;
    HANDLE snap;
    PROCESSENTRY32 lppe;
    lppe.dwSize = sizeof(PROCESSENTRY32);
    
    ZeroMemory( &lppe,sizeof(lppe)) ;
    
    snap =  CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    
    		if ( snap == INVALID_HANDLE_VALUE )
    		{
    			cout<<"CreateToolhelp32Snapshot error: " << GetLastError() << endl;
    			CloseHandle(snap);
    			getch();
    			return 0;
    		}
    
    		lppe.dwSize = sizeof(PROCESSENTRY32);
    		Process32First(  snap, &lppe );
    
    		do
    		{
    				if( ! strcmp(lppe.szExeFile, ProcessName.c_str() ) )
    				{
    				   cout<<lppe.szExeFile << "  Process ID: "  << lppe.th32ProcessID << endl;
    				   ProcessExists = true;
    				   CloseHandle(snap);
    				   break;
    				}
    		}
    		while ( Process32Next ( snap , &lppe ) == 1 ) ;
    
    		if ( ProcessExists == false )
    		{
    			  CloseHandle(snap);
    			  return 0;
                    }
    
    return lppe.th32ProcessID;
    }
    

    Hätte auch mal ne Frage dazu .Sagen wir mal ich finde mit Ce eine adresse,und suche immer weiter was auf die Adresse zugreift .Am ende bekomme ich dann etwas wie solitaire.exe+BAFA8 raus und 2 Offsets um zu dieser Adresse zu gelangen ( 0x14 und 0x50) muss ich dann erst mit der Create tool help snapshot funktion die Base Adresse meines Spieles auslesen( von solitair.exe) , und danach nurnoch BAFA8 und 0x14 + 0x50 draufrechnen um zu aktuellen Addresse der Punkte zu gelangen?Oder hab ich da noch was falsch verstnanden?



  • @Bassmaster: Wie machst du das dann mit Spielerpositionen in einem Multiplayer Match? Immerhin bräuchte man ja für jeden Spieler die Positionen. Auch wäre es gut, wenn man die komplette Spielerdatenstruktur auslesen könnte, aber wie man vorgeht, um dorthin zu gelangen (Spielerdatenstruktur eines jeden Spielers) ist mir ein Rätsel... Wie machst du das denn?



  • Was hat das alles mit Assembler zu tun?



  • gigg schrieb:

    Was hat das alles mit Assembler zu tun?

    Weil man vielleicht Mnemonics lesen muss?

    Hier mal ein Beispiel, wie OllyDbg aussehen kann:
    http://www.joestewart.org/morphine-dll/ollydbg0.png



  • Mnemonics schrieb:

    Weil man vielleicht Mnemonics lesen muss?

    Ich sehe hier nicht eine Zeile Assembler-Code, nur C++.
    Für mich gehört das ins Spiele-Forum.



  • gigg schrieb:

    Was hat das alles mit Assembler zu tun?

    Gar nichts.

    Vielleicht könnte ein Mod das mal verschieben ...

    reinterste schrieb:

    Immerhin bräuchte man ja für jeden Spieler die Positionen.

    So findest man z.b. seine eigene Spielerposition:

    Zu erst suchst du nach: Unitialised value
    Datentyp: float

    Dann lauf z.b. ein paar Schritte und such nach "changed value" dann suchst du ein paar mal nach "unchanged value" dann wieder laufen "change value" suchen wieder laufen "changed value" suchen dann wieder ein paar mal "unchanged value" suchen.

    Irgendwann bleiben nur noch 3 oder über 100 Adressen über. 😃

    Dann musst du halt die Werte der Adressen ändern und nachschauen ob sich deine Spielerposition verändert. Mir ist auch bekannt das du in manchen Spielen deine Spielfigur "syncen" musst, also einfach mal irgendeine Aktion ausführen wie z.b. schiessen oder springen.

    Ich hab mal eine Open source Anwendung rausgesucht wo du das ganze mal probieren kannst.

    Die Version hab ich runtergeladen:
    http://nehe.gamedev.net/data/lessons/bcb6/lesson10_bcb6.zip

    Bei mir sind am Ende 155 Adressen übergeblieben xD
    http://www7.pic-upload.de/10.01.13/98sped7nf59.jpg

    Ich hab mir mal die mühe gemacht ... ^^ Ganz unten die letzten 3 Adressen habe ich mal blau makiert.

    Bei diesen Adressen handelt es sich um Pointer, in diesem Fall ist es aber ganz leicht die Base Adresse zu ermitteln.

    Rechtsklick auf eine der Adresse "what writes to this address" anklicken dann ein paar Schritte im Spiel wieder laufen und dann sieht man schon was man wissen muss.

    Aber wofür willst du den die Positionen der anderen Spieler.🙄 😃
    Da will wohl jemand einen Aimbot programmieren.^^

    Schau dir auf www.guidedhacking.com mal die Tutorials von Fleep an da wirst du auf jedenfall fündig. ( Aber nicht in Ligen oder bei Tunieren Cheaten sonst kommt Biggi Smalls ! Auch darf man sich nicht vor einen Spiegel stellen und 3 mal seinen Namen sagen dann kommt er auch und dann rappelts im Karton.^^ [Kennt ihr die South Park Folge mit Biggi Smalls? xD] )

    Ach ja, wenn du z.b. die X-Achse deiner Spielerposition findest dann kannst du zu der Adresse meistens einfach +4 dazurechnen um die Y Achse zu bekommen.

    Du kannst bei google auch mal nach "home of gamehacking" suchen, auf der Seite gibt es auch ein Tutorial bezüglich Spielerpositionen.


Anmelden zum Antworten