Tutorial Speichermanipulation
Wir werden heute einen simplen Trainer für das Windows Standart Spiel Pinball schreiben. Dafür brauchen wir zusätzlich zu einer IDE noch ein Programm wie z.B. T-Search um den Speicher unseres Zielprogramms nach der Speicheradresse der Variable zu durchsuchen, die wir manipulieren wollen. Das ihr wisst wie ihr mit dem Programm arbeiten müsst setze ich voraus. Das könnte in diesem Fall die Anzahl der verbleibenden Kugeln oder auch die erreichten Punkte sein.
Wir erstellen nun ein neues Win32-Konsolenprogramm und inkludieren iostream und windows.h. Nun legen wir auch gleich den Namespace std fest.
Der Code müsste dann ungefähr so aussehen
Code:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
return 0;
}
Nun brauchen wir als erstes einen Handle auf das Fenster. Den holen wir uns mit der Funktion FindWindow, die als 2ten Parameter den Namen des Window braucht. Der ersten Parameter ist in unserem Fall 0 da uns die Klasse egal ist.
Code:
hWnd = FindWindow(0, TEXT("3D-Pinball für Windows - Space Cadet"));
Nun holen wir uns mithilfe dieses WindowHandles die Prozess Id unseres Opefer Programms.
Dies machen wir mit GetWindowThreadProcessId, das als ersten Parameter den WindowHandle haben möchte und als 2tes einen Pointer auf ein DWORD in dem die Prozess Id gespeichert werden soll.
Code:
GetWindowThreadProcessId(hWnd, &processId);
Jetzt können wir schon fast in den Prozess schreiben, aber eines fehlt noch: wir brauchen einen Handle auf den Prozess mit den Rechten zum Schreiben und Lesen.
OpenProcess verlangt als 1ten Parameter den gewünschten Zugriffrechte, das ist in unserem Fall PROCESS_ALL_ACCESS. Der 2te Parameter ist in unserem Fall nicht sehr wichtig, den er gibt an ob Prozesse die von unserem Prozess gestartet wurden das Handle erben sollen oder nicht. Wir „sagen“ einfach false. Als letzten Parameter muss die Prozess Id übergeben werden.
Jetzt sollte unser Code in etwa so aussehen
Code:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
HWND hWnd;
DWORD processId;
HANDLE hGame;
hWnd = FindWindow(0, TEXT("3D-Pinball für Windows - Space Cadet"));
if(!hWnd)
{
cout << "Pinball nicht gefunden" << endl;
system("pause");
return 1;
}
GetWindowThreadProcessId(hWnd, &processId); //ProcessId über das WindowHandle bekommen
hGame = OpenProcess(PROCESS_ALL_ACCESS, false, processId); //Handle auf den Process(das Spiel) bekommen
if(!hGame)
{
cout << "Konnte Handle nicht erstellen: " << GetLastError() << endl;
system("pause");
return 1;
}
return 0;
}
So jetzt ist es soweit wir haben Zugriff auf den Speicher und können nach belieben darin herumwerkeln. Aber Vorsicht wenn ihr aus Versehen oder evtl. auch beabsichtig Programmcode überschreibt kann der Zielprozess abstürzen. Nun aber genug geredet, suchen wir nach der Speicheradresse wo im Code der Punktestand gespeichert wird. Bei mir konnte ich die Variable an der Adresse bereits nach dem 2ten Durchlauf bei 0xBBAEBA lokalisieren.
Nun soll der Trainer in Spee bei jedem Druck auf F5 den Punktestand um 1.000.000 erhöhen.
Dafür brauchen wir die GetAsyncKeyState Funktion die als Parameter den virtuellen KeyCode der taste haben möchte.
Eine Liste der virtuellen KeyCodes findet ihr
.
Wenn das LSB des Rückgabewertes gesetzt ist, dann wurde die Taste seit dem letzten Aufruf von GetAsyncKeyState gedrückt wurde.
Also schreiben wir:
Code:
if(GetAsyncKeyState(VK_F5) & 1)
{
Nun lesen wir jedes Mal als erstes den aktuellen Score aus um ihn anschließend um 10^6
zu erhöhen.
ReadProcessMemory und WriteProcessMemory verlangen beide exakt die gleichen Parameter.
Als 1ten muss ein Handle auf den Prozess übergeben werden, danach die Speicheradresse die nach LPVOID gecastet werden muss. Jetzt noch ein Pointer auf die Variable die den Wert enthält/enthalten soll, die Größe dieser Variable und als allerletzten Parameter noch ein Pointer auf ein DWORD das die tatsächlich gelesen Bytes enthält. Der letzte Parameter kann 0 enthalten.
Code:
if(GetAsyncKeyState(VK_F5) & 1)
{
ReadProcessMemory(hGame, (LPVOID) 0xBBAEBA, &value, sizeof(value), 0);
value += 1000000;
WriteProcessMemory(hGame, (LPVOID) 0xBBAEBA, &value, sizeof(value), 0);
}
Nun setzen wir diesen Code in eine Schleife damit wir ihn mehr als 1 mal benutzen können.
Ein Sleep(100); sollten wir ebenfalls in die Schleife setzten damit das Programm nicht den Rechner vollkommen auslastet.
Kleine Anmerkung:
Wenn ein Handle nicht mehr benötigt wird sollte man ihn immer mit CloseHandle(hHandle);
freigeben. In diesem Fall ist das nicht so unbedingt nötig da es sich um ein kleines Programm handelt bei dem es nicht eine so große Rolle spielt.
Nun ist der Trainer in seinen Grundzügen fertig. Jetzt könnt ihr noch zusätzliche Verbesserungen/Funktionen einbauen wie z.B. die Möglichkeit unbegrenzt Bälle zu haben.
Abschließend noch einmal der gesamte Code:
Code:
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
HWND hWnd;
DWORD processId;
HANDLE hGame;
int value = 0;
hWnd = FindWindow(0, TEXT("3D-Pinball für Windows - Space Cadet"));
if(!hWnd)
{
cout << "Pinball nicht gefunden" << endl;
system("pause");
return 1;
}
GetWindowThreadProcessId(hWnd, &processId); //ProcessId über das Window Handle bekommen
hGame = OpenProcess(PROCESS_ALL_ACCESS, false, processId); //Handle auf den Process(das Spiel) bekommen
if(hGame == 0)
{
cout << "Konnte Handle nicht erstellen: " << GetLastError() << endl;
system("pause");
return 1;
}
cout << "Bitte F5 druecken" << endl;
while(1)
{
if(GetAsyncKeyState(VK_F5) & 1) //das LSB ist gesetzt wenn seit dem letzen Aufruf die Taste gedrückt wurde
{
ReadProcessMemory(hGame, (LPVOID) 0xBBAEBA, &value, sizeof(value), 0);
value += 1000000;
WriteProcessMemory(hGame, (LPVOID) 0xBBAEBA, &value, sizeof(value), 0);
}
}
return 0;
}
PS.
Falls ihr häufiger folgende Ausgabe bekommen solltet: „Konnte Handle nicht erstellen: 5“, meldet euch

.
Edit:
der Übersichtlichkeit wegen hab ich mal die Links zum Fehler 5 beheben nach vorne geholt:
Tutorial by UnlimitedPower 4
ddl-board.com