GameTrainer / Memory-Hacks/ Hintergrundwissen

01/12/2013 23:38 Niamgel#1
Hallo alle zusammen,

ich beschäftige mich momentan mit der Programmierung von Game-Trainern und habe hier mal eine Grundsatzfrage in der Hoffnung, dass mir die hier irgendwer beantworten kann.

Und zwar habe ich folgende Überlegung:

1. Eine Spieleprogrammierer definiert sich ja Variablen, die irgendwo auf den Speicher zeigen um dort dann Werte abzulegen.

2. Welcher Speicher reseviert wird, entscheidet nicht das Spiel, sondern mein Betriebssystem.

Gut, angenommen beide Annahmen stimmen.

Wenn ich jetzt bspw. mit der CheatEngine nach den "Adressen" suche wo ein bestimmter Wert hinterlegt wurde, würde mich interessieren was eigentlich jetzt genau die "Adresse" ist:

Steht diese Adresse quasi für den Platzhalter der Variable, die vom Programmierer definiert wurde ? Oder ist das die konkrete Adresse des Speichers?

Weil wenn letzteres, wie kann es dann sein das eine bestimmte Adresse immer "gilt" ? Wenn doch mein Betriebssystem entscheidet, welcher Speicher gerade zur Verfügung steht.

Beispiel.

Der GamerTrainer-Programmeirer sieht in der Cheat Engine, das der Wert 20 in der Adresse 0x000AFCC4 hinterlegt wurde.

Jetzt greift über seinem Code darauf zu:

WriteProcessMemory(hProc, (LPVOID)0x000AFCC4, &newValue, (DWORD)sizeof(newValue), NULL);

______________________________

Was ist wenn sowohl Spiel und Game Trainer auf einen anderen PC gestartet werden? Kann ja sein das der Wert 20 eben nicht mehr an der Adresse 0x000AFCC4 hinterlegt wurde, sondern ganz woanders im Speicher. Der Trainer aber trotzdem auf die Adresse 0x000AFCC4 zugreift?


Ich hoffe, die Problematik die mir durch den Kopf schwirrt ist verständlich.

Viele Grüße
01/13/2013 00:39 Jeoni#2
1.) Ja, das, was CE als Adresse angibt, ist die Konkrete Adresse im Speicher des Programms.
2.) Das Betriebssystem entscheidet, in welchen Teil des Arbeitsspeichers das Programm geladen wird. Wenn du diesen Prozess aber nun mit CE öffnest (für WPM / RPM gilt das selbe), sind alle Adressen relativ zur Prozessbase (nicht verwechseln mit der Modulbase), also der Adresse im Speicher, die das Betriebsystem für das Programm alloziert hat.
3.) Wenn das Spiel dynamisch Speicher alloziert (new, malloc), ist dieser auch nicht immer an der gleichen Stelle vorzufinden. Soweit ich weiß, entscheidet auch hier das Betriebssystem über die Adresse.
4.) Diese statischen Adressen werden vom Spiel nicht dynamisch alloziert, sondern sind statisch (meist in der .data-Section des Spielmoduls). Hierrunter müssten z.B. globale Variablen fallen.

Ob das Spiel nun statische Daten nutzt oder dynamisch Speicher alloziert, hängt vom Bedürfnis ab. So muss z.B. die Schwerkraftskonstante nicht dynamisch alloziert werden, da sie immer da ist, immer gebraucht wird und ihre größe nicht verändert. Platz für Spielerklassen hingegen wird meist dynamisch alloziert, weil sich die größe ändert (verschiedene Spieleranzahlen / Spielerlimitierungen auf dem Server). So braucht man auf einem 8-Mann Server keinen Platz für 100 Spielerobjekte, sondern maximal nur 8, etc. Man will ja nichts verschwenden :D

Hoffe, dass ich helfen konnte und du in etwa verstehst, worauf ich hinaus wollte ;)
Jeoni

P.S.: Ich hoffe, dass das alles korrekt so ist und bitte um Belehrung bei Fehlern.
01/13/2013 11:21 marykillsjane#3
Quote:
Originally Posted by Niamgel View Post
Hallo alle zusammen,

ich beschäftige mich momentan mit der Programmierung von Game-Trainern und habe hier mal eine Grundsatzfrage in der Hoffnung, dass mir die hier irgendwer beantworten kann.

Und zwar habe ich folgende Überlegung:

1. Eine Spieleprogrammierer definiert sich ja Variablen, die irgendwo auf den Speicher zeigen um dort dann Werte abzulegen.

2. Welcher Speicher reseviert wird, entscheidet nicht das Spiel, sondern mein Betriebssystem.

Gut, angenommen beide Annahmen stimmen.

Wenn ich jetzt bspw. mit der CheatEngine nach den "Adressen" suche wo ein bestimmter Wert hinterlegt wurde, würde mich interessieren was eigentlich jetzt genau die "Adresse" ist:

Steht diese Adresse quasi für den Platzhalter der Variable, die vom Programmierer definiert wurde ? Oder ist das die konkrete Adresse des Speichers?

Weil wenn letzteres, wie kann es dann sein das eine bestimmte Adresse immer "gilt" ? Wenn doch mein Betriebssystem entscheidet, welcher Speicher gerade zur Verfügung steht.

Beispiel.

Der GamerTrainer-Programmeirer sieht in der Cheat Engine, das der Wert 20 in der Adresse 0x000AFCC4 hinterlegt wurde.

Jetzt greift über seinem Code darauf zu:

WriteProcessMemory(hProc, (LPVOID)0x000AFCC4, &newValue, (DWORD)sizeof(newValue), NULL);

______________________________

Was ist wenn sowohl Spiel und Game Trainer auf einen anderen PC gestartet werden? Kann ja sein das der Wert 20 eben nicht mehr an der Adresse 0x000AFCC4 hinterlegt wurde, sondern ganz woanders im Speicher. Der Trainer aber trotzdem auf die Adresse 0x000AFCC4 zugreift?


Ich hoffe, die Problematik die mir durch den Kopf schwirrt ist verständlich.

Viele Grüße
Also soweit ich es verstanden habe gibt es seit Xp halt keine statischen Adressen mehr ,welche immer gleich bleiben.
Mir wurde gesagt auf neuern systemen setzt sich die Adresse immer aus der Image Base adresse des Spiels + der Modul adresse + den Offsets zusammen ( sprich der Speicherplatz )wo die werte angelegt werden wird (so wie ich es verstanden habe) nicht von Windows iwohin verlegt ,sondern das Spiel "definiert " praktisch wo der Speicherort sein wird. Da sich die Image Base nach jedem neustart des Spieles ändert muss das Programm diese vorher jedes mal auslesen ,und dann mit meiner oben gennanten Rechnung auf die atuelle Speicheradresse kommen .
So habe ich das ganze verstanden sollte da was falsch dran sein korrigiert mich BITTE.
01/13/2013 11:50 Kosic#4
@TE

Du musst mit CE mal den Wert suchen, in deinem Fall ist dieser 20. Anschliesend so lande verändern und wieder neu suchen bis du nur noch eine Adresse mit dem entsprechendem Wert hast, in deinem Fall ist die Adresse 0x000AFCC4. Jetzt musst du die Adresse immer weiter zurückverfolgen, dies machst du mit "Find out what writes to this Adress".Notiere dir IMMER die Offsets (Auch nach einem Pointer noch weiter suchen! *hust* Multipointer) Nun wirst du höchst wahrscheinlich irrgentwann an die besagte Image Base mit derem Offset treffen. Die Image Base musst du, wie schon gesagt, jedes mal beim start deines Trainers als ERSTES Auslesen, danach den Offset hinauf addieren und mit wieder auslesen, so machst du das immer weiter bis du nichts mehr zum Auslesen hast, und benutzt die WriteProcessMemory() function. (C++)

Mfg,
Kosic
01/13/2013 12:13 marykillsjane#5
Mal als Beispiel du findest eine Adresse machst dann solange das mit Findout what writes/accesses to this adress ( das was mein vorposter schrieb) irgendwann wirst du zu keiner "normalen" Adresse mehr gelangen ,sondern es steht dann dort z.b. solitaire.exe+BAFA8 . Das ganze bedeutet man muss die ImageBaseAdresse von solitair.exe auslesen ( das geht z.B. mit der WinApi mit Getmodulehandle ) und auf diese BAFA8 hinaufaddieren .Anschließend musst du auf die ermittelte Adresse noch deine Offsets draufrechnen durch welche du zu der Adresse gelangt bist ( bei Solitair z.b. 0x14 und 0x50 ).
01/14/2013 01:00 MrSm!th#6
Quote:
Nun wirst du höchst wahrscheinlich irrgentwann an die besagte Image Base mit derem Offset treffen. Die Image Base musst du, wie schon gesagt, jedes mal beim start deines Trainers als ERSTES Auslesen, danach den Offset hinauf addieren und mit wieder auslesen, so machst du das immer weiter bis du nichts mehr zum Auslesen hast, und benutzt die WriteProcessMemory() function. (C++)
Nein, du findest als letzte Adresse einen statischen Pointer, der im Modul seinen festen Platz hat.

@TE:

Der Programmierer definiert nur indirekt die Position im Speicher.
Wenn er programmiert, wird sein Code in Maschinen-Code übersetzt. Das resultierende Programm enthält dann lauter binäre Daten, die teilweise Code und teilweise Daten darstellen (z.B. konstante, globale Variablen).
Der Compiler entscheidet beim Übersetzen, was er wo hin schreibt und was er wie zusammenfasst. Der Programmierer hat darauf insofern Einfluss, als dass logisch zusammengehörige Daten (z.B. in Klassen) auch zusammenhängend im Speicher liegen.
IdR wird auch sein Code der Reihenfolge nach ins Modul geschrieben.
Aber ansonsten ist es alles Aufgabe des Compilers. Der Programmierer gibt nicht (mehr) an, welche Daten wie viele Bytes vom Anfang des Moduls entfernt liegen. In seinem Programm spricht er seine Variablen logischerweise mit Namen an und erst der Compiler ersetzt diese dann durch Adressen, vereinfacht gesagt.

So, normale Variablen haben also einen festen Abstand zum Anfang des Moduls (es sei denn, sie liegen auf dem Stack, weil sie lokal sind, aber dann findet man sie ohnehin selten mit CE, weil sie eine beschränkte Gültigkeit haben). Dieses wird wiederum vom System in den Speicher geladen und entsprechend ist die Adresse einer jeden Variable gleich der Summe aus der angesprochenen Image-Base und dem Offset innerhalb des Moduls.
Die Base kann statisch (oft 0x400000) sein, sie kann sich aber auch (was öfters passiert) verändern und dynamisch vergeben werden. Dann muss sie jedes Mal ausgelesen werden.

Dann gibt es auch noch dynamisch erzeugte Daten, die zur Laufzeit im Freispeicher abgelegt werden. Diese werden vom Programmierer dynamisch reserviert, aber auch da legt er ihre Adresse nicht fest, sondern bekommt sie lediglich zugewiesen.
Die Adresse speichert er in speziellen Varibalen, sogenannten Pointern. Diese verweisen halt auf den Speicherbereich, in dem die Daten liegen (z.B. ein zur Laufzeit geladens Player-Model). Deren Position ist willkürlich (bzw. wird vom System nach Regeln außerhalb des Einflusses des Programmierers festgelegt), d.h. man kann sie nicht ohne weiteres ermitteln.
Aber der Pointer wiederum, der die Adresse speichert, ist ebenso eine Variable, die irgendwo eine Position im Modul hat (es sei denn, er wurde selbst dynamisch erzeugt, aber im Grunde lässt sich eine Multi-Pointer-Kette ebenso bis zum letzten, statischen Pointer zurückverfolgen) und diese kann man wieder durch ImageBase+OffsetInnerhalbDesModuls ausdrücken.
D.h. die Position von dynamisch erzeugten Daten ermittelst du, indem du aus Image-Base und Offset innerhalb des Moduls die Adresse des Base-Pointers berechnest. Dessen Wert stellt wiederum die Adresse von Daten da (in denen sich selbst wieder ein Pointer verbergen kann). Man folgt also dieser und addiert ggf. ein Offset um die Adresse der Variablen zu erhalten, die man sucht (ggf. wiederholt man das eben, wenn sie selbst nur ein Pointer ist, der auf eine Varibable zeigt und immer so weiter).

Um die anfängliche Frage also zu beantworten:

Quote:
Steht diese Adresse quasi für den Platzhalter der Variable, die vom Programmierer definiert wurde ? Oder ist das die konkrete Adresse des Speichers?
Die Adresse ist die Position der Daten im Speicher, Punkt.
Woher sie kommt und wie sie berechnet wird, hat, wie gesagt, teilweise unterschiedliche Hintergründe, aber es gibt immer einen Weg, über X Ecken auf sie zu kommen.