Donnerstag, 2024-11-21, 3:44 PM Willkommen Interessent

Video Game Developers Forum

Suche
Menü
Short News
SNpowered by ShortNews.de
News Verzeichnis
[ Neue Beiträge · Teilnehmer · Forumregeln · Suche · RSS ]
  • Seite 1 von 1
  • 1
Moderator in diesem Forum: Goblin1405  
Realtime-Manipulation von Spielen
Goblin1405Datum: Dienstag, 2010-06-08, 7:54 PM | Nachricht # 1
Coder
Gruppe: Administratoren
Nachrichten: 89
Auszeichnungen: 0
Status: Offline
Realtime-Manipulation von Spielen

Spätestens seit Einführung von Online-Shootern ist die Sicherheit bei solchen oder ähnlichen Anwendungen ein riesiges Problem geworden. Viele Spiele verlieren große Teile ihrer aktiven Community nur durch das überhöhte Auftreten von Cheatern, welche das faire Spiel ruinieren. Dieses Tutorial soll nicht dazu verleiten Hacks zu schreiben um anderen zu schaden, sondern vielmehr einen Anreiz geben Cheating in Spielen zu verhinden.

Einführung
Seit Anfang des letzten Jahres treten vermehrt große Online-Communities zutage, in denen man gegen Gebühren Zugriff auf komplette Hack-Pakete hat. Dies führt teilweise dazu, dass die Cheat-Programmierung regelrecht kommerzialisiert wird und einzelne Personen mit relativ wenig Aufwand mehrere Millionen Dollar Umsatz pro Jahr erwirtschaften. Auf der anderen Seite gibt es aber auch spezielle Firmen, die sich nur auf die Entwicklung bestimmter AntiCheat-Programme spezialisiert haben (z.B. EvenBalance).
Dieser Artikel soll einen kleinen Einblick in die Grundlagen der Manipulation von Spielen (also der Hackentwicklung) geben. In gegebenenfalls folgenden Artikeln werde ich dann auf die einzelnen Teilbereiche genauer eingehen und auch etwas über die konkrete Erkennung von Hacks schreiben.
Da hier wirklich nur die einfachsten und grundlegendsten Techniken vorgestellt werden, richtet sich dieser Artikel in erster Linie an Neulinge der Spielentwicklung. Alle anderen, die sich schon intensiver mit der Entwicklung von Spielen oder sonstigen Applikationen unter Windows befasst haben, sollten das meiste, was ich hier vorstelle, schon kennen.

Die Idee
Das Ziel ist eine geeignete Manipulation des reservierten Speichers (hier in diesem ersten Tutorial erstmal nur die Funktionen im Speicher) eines laufenden Spiele-Prozesses, um so einen Vorteil gegenüber anderen virtuellen Mitspielern zu haben. Der Speicher eines Prozesses ist adressierbar in dem Bereich zwischen 0x00000000 und 0x7FFFE000, was ungefähr 2 Gigabyte entspricht. Diese Werte entsprechen natürlich nicht den absoluten, physikalischen Adressen, mit denen später auf den RAM zugegriffen wird. Einerseits können niemals für jeden Prozess 2 Gigabyte Speicher reserviert werden, da die begrenzten Ressourcen heutiger Computer schnell verbraucht wären. Andererseits könnte man auch keinen absoluten Speicherbereich für jeden Prozess festlegen, da zwei Prozesse nicht gleichzeitig den Speicherbereich 0x00000000-0x7FFFE000 benutzen könnten.
Windows gibt daher jedem Prozess einen virtuellen Speicherbereich von 0x00000000 bis 0x7FFFE000. Bei jedem Speicherzugriff wird dann die Adresse intern von Windows in die zugehörige physikalische Adresse umgewandelt. Dieser Vorgang ist intern relativ komplex, da Teile des Speichers natürlich auch extern ausgelagert werden (virtueller Speicher in pagefiles) und dann, bei Zugriff, in den physikalischen Speicher kopiert werden müssen.
Dies ist u.a. auch ein Teil der Königsdisziplin des Game-Hacking, dem so genannten memory-cloaking. Durch sehr komplizierte Methoden ist es möglich, diesen Prozess (Übersetzung virtuelle in physikalische Adresse bzw. genauer den Page-Fault Handler) anzuzapfen und zu manipulieren, so dass Speicher, welcher ausgeführt wird, umgeleitet wird, was dazu führt, dass man anderen Code ausführen kann, ohne dass diese 'Änderung' durch traditionelles Auslesen des Speichers erkannt werden kann. Mehr dazu in einem gegebenenfalls folgenden Artikel.

Zugriff auf Speicher eines anderen Prozesses
Direkter Zugriff fällt also aufgrund der oben genannten Tatsachen weg. Glücklicherweise bietet Windows verschiedene andere Möglichkeiten, den Speicher eines anderen Prozesses auszulesen und zu verändern. Auf der einen Seite wären da die Funktionen ReadProcessMemory und WriteProcessMemory aus der WinAPI. Diese Funktionen sind aber leider in den meisten Fällen zu unhandlich (wirklich jeder Zugriff muss über diese Funktionen erfolgen) und zusätzlich erlauben diese Funktionen nicht so einfach, eigenen Code innerhalb des Ziel-Prozesses auszuführen.
Eine andere Möglichkeit wäre es, eine eigene DLL in den Ziel-Prozess zu injizieren. Diese würde dann in dem Adressbereich des Ziel-Prozesses laufen und so kann man Programmcode ausführen sowie direkt Zugriffe auf den Speicher ausführen. Wie bekommt man nun aber eine DLL in einen Prozess, welcher schon läuft? Hier gibt es wieder verschiedene Techniken welche alle Vor- und Nachteile haben. Zur Einfachheit halber will ich hier die CreateRemoteThread-Möglichkeit vorstellen. Diese hat den Vorteil, dass sie extrem einfach zu implementieren ist und dass sie bei ausnahmslos allen lokalen, ungeschützten Prozessen funktioniert.
Die Idee hinter dieser Methode ist, dass man per CreateRemoteThread einen Thread in dem Ziel-Prozess erstellt, dessen Einstiegspunkt die LoadLibraryA-Funktion ist und dessen Parameter ein Zeiger auf einen neu erstellen char-String (VirtualAllocEx, WriteProcessMemory) ist, welcher den DLL-Dateinamen enthält.
Wenn nun dieser Thread gestartet wird, wird die Funktion LoadLibraryA aufgerufen, welche die DLL nun lädt. Wenn der Ladevorgang beendet ist, d.h. Wenn LoadLibraryA zurückgegeben hat, wird dann auch der neue Thread beendet. Der Prozess, welcher CreateRemoteThread aufgerufen hat, muss also nur auf das Ende des neuen Threads warten dann sollte die neue DLL im anderen Prozesses geladen sein.

code

bool SimpleInjection(HANDLE Process, const string& sModuleFileName)
{
LPVOID pFileName = VirtualAllocEx(Process,
NULL,
sModuleFileName.length()+1,
MEM_COMMIT,
PAGE_READWRITE);

if(!pFileName)
{
return true;
}
else if(!WriteProcessMemory(Process,
pFileName,
sModuleFileName.c_str(),
sModuleFileName.length()+1,
NULL))
{
VirtualFre eEx(Process, pFileName, 0, MEM_RELEASE);
return true;
}

HANDLE Thread=CreateRemoteThread(Process,
NULL,
0,
reinterpret_cast<LPTHREAD_START_ROUTINE>(LoadLibraryA),
pFileName,
0 ,
NULL);
if(!Thread)
{
VirtualFreeEx(Process, pFileName, 0, MEM_RELEASE);
return true;
}

WaitForSingleObject(Thread, INFINITE);
CloseHandle(Thread);
VirtualFreeEx(Process, pFileName, 0, MEM_RELEASE);
return false;
}

Der Code, so wie er da steht, kann theoretisch nicht funktionieren. Nach der MSDN muss die Einstiegsadresse des neuen Threads im Adressbereich des neuen Prozesses sein. Eigentlich müssten man erst einmal die Adresse von LoadLibraryA im Ziel-Prozess herausfinden und diese dann als Einstiegspunkt übergeben, anstatt die Adresse von LoadLibraryA aus dem eigenen Prozess zu nehmen.
Glücklicherweise wird aber die kernel32.dll, in der LoadLibraryA zu finden ist, praktisch immer an der gleichen virtuellen Adresse geladen. Das heißt, dass praktisch die einzelnen 'Sections' der DLL in jedem Prozess die gleiche virtuelle Adresse haben und so ist praktisch die virtuelle Adresse von LoadLibraryA in jedem Prozess gleich.

Detours
Die einfachste Möglichkeit, ein Spiel in der Hinsicht zu Manipulieren, dass man selbst einen Vorteil hat, ist das Umschreiben bestimmter Funktionen. So könnte man z.B. die GetTickCount()-Funktion so ändern, dass sie immer das doppelte des eigentlichen Wertes zurückgibt, so dass das Spiel denkt, dass schon die doppelte Zeit vergangen wäre, was praktisch einen Speedhack zur folge hätte. (Das Spiel ruft dann alle Update/Move/etc. Funktionen mit einem Wert doppelt so groß wie der Originalwert auf).
In der Manipulation von Spielen (und auch in vielen anderen Bereichen) wird daher eine Technik namens Funktion-Detouring benutzt. Die Idee dahinter ist relativ einfach.

1. Man kopiert die ersten 5 Bytes der Originalfunktion in einen neuen unsigned char-Buffer.
2. Nach diesen 5 Bytes wird in dem neuen unsigned char-Buffer ein Sprung zur Originalfunktion+5 gesetzt.
3. Man überschreibt die ersten 5 Bytes der Originalfunktion mit einem Sprung (1 Byte Opcode + 4 Bytes neue Adresse) auf die neuen Funktion, welche die Originalfunktion 'ersetzen' soll.

Die hätte zur Folge, dass immer dann, wenn das Programm die Originalfunktion aufruft, zur neuen Funktion gesprungen wird, mit der die Originalfunktion 'ersetzt' wurde. Wenn nun doch aus der neuen Funktion die Originalfunktion aufgerufen werden soll, so wird einfach nur der unsigned char-Buffer als die Original-Funktion interpretiert (reinterpret_cast!).

Beispiel: Ein Detour auf die CreateFileA-Funktion

Originale Funktion:
code

.text:77E48CA4 mov edi, edi
.text:77E48CA6 push ebp
.text:77E48CA7 mov ebp, esp
.text:77E48CA9 push [ebp+lpFileName]
.text:77E48CAC call _Basep8BitStringToStaticUnicodeString@4
.text:77E48CB1 test eax, eax
.text:77E48CB3 jz loc_77E5BFD3
//...

Die ersten fünf Bytes werden in einen unsigned char-Buffer kopiert, ein Jump auf die originale Funktion+5 wird angehängt und ein Jump auf die neuen Funktion wird am Anfang der originalen Funktion eingesetzt.

unsigned char-Buffer:
code

0x0: mov edi, edi
0x2: push ebp
0x3: mov ebp, esp
0x5: jmp MyCreateFileA+5

Originale Funktion:
code

.text:77E48CA4 jmp NeueFunktion
.text:77E48CA9 push [ebp+lpFileName]
.text:77E48CAC call _Basep8BitStringToStaticUnicodeString@4
.text:77E48CB1 test eax, eax
.text:77E48CB3 jz loc_77E5BFD3
//...

Neue Funktion:
code

HANDLE WINAPI MyCreateFileA(LPCSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
if(!strcmp(lpFileName,”C:\\Unsere1337Datei.txt”))
{
SetLastError(ERROR_FILE_NOT_FOUND);
return 0;
}
else
{
//manche Compiler zicken bei der unsigned char*-Funktion-Konvertierung rum; daher erst zu
void* return reinterpret_cast<WINBASEAPI HANDLE (WINAPI*)(LPCSTR,
DWORD,
DWORD,
LPSECURITY_ATTRIBUTES,
DWORD,
DWORD,
HANDLE)(reinterpret_cast<void*>(acC reateFileADetourBuffer))
(lpFileName,dwDesiredAccess,dwShareMode,lpSecurityAttributes,
dwCreationDisposition,dwFlagsAndAttribute s,hTemplateFile);
}
}

Detour Probleme
Es kann bei der Technik, so wie oben beschrieben, zu Problemen führen, wenn man einfach nur blind die ersten 5 Bytes der originalen Funktion in den unsigned char-Buffer kopiert und dann einen Jump zu der originalen Funktion+5 einfügt, da es durchaus sein kann, dass eine Instruktion in der Mitte aufgebrochen wird.
Das wäre z.B. der Fall, wenn eine Instruktion an Position 4 (relativ zum Funktionsbeginn) beginnt und an Position 7 aufhört. Wenn nun die ersten 5 Bytes überschrieben werden und dann später zu Originalfunktion+5 gesprungen wird, kann so gut wie alles passieren.
Die einzige Lösung für dieses Problem ist, dass, bevor die originale Funktion geändert wird, geguckt wird, wie viele Instruktionen in den neuen Buffer kopiert werden müssen, bis es mindestens 5 Bytes Platz für den Jump gibt. Da dies relativ kompliziert ist, benutzen die meisten Programmierer fertige Bibliotheken, welche diese Arbeit übernehmen. Die bekannteste ist wohl die Detour-Bibliothek von Microsoft (link), welche so gut wie bei jedem Hack zum Einsatz kommt.

Das Beispiel
Das Beispielprogramm zu diesem ersten Artikel ist ein sehr, sehr einfacher Hack für den kostenlosen Onlineshooter America's Army. Mit diesem Hack ist es möglich, Nebel zu entfernen und alle Partikel im Spiel als Drahtmodelle anzeigen zu lassen.
Der C++ Code wurde mit VC++ 8.0 erstellt und kompiliert. Als DLL Injector habe ich meinen eigenen genommen (DLLInjector), wobei auch jeder andere funktionieren würde.

Quelltext: bei Interesse bitte klicken

 
  • Seite 1 von 1
  • 1
Suche:

Our Age
Mini Profil


Statistik
Bookmarks
Copyright by GamesArmyLabs © 2024