Inhaltsverzeichnis
1. Einleitung
2. Erläuterung des Programmcodes
2.1 Fenster auflisten
2.2 Prozess anzapfen
2.3 Adressbereiche ermitteln
2.4 Adressbereiche durchsuchen
2.5 Adressbereiche wiederholt durchsuchen
2.6 Werte ersetzen
2.7 Aufräumen
3. gesamter Quellcode
4. Anwendungsbeispiel - GTA Vice City
5. Schlusswort
6. Downloads
7. weiterführende Links
1. Einleitung
Dieses Tutorial baut auf unsigned long seinem Tutorial auf und soll zeigen, wie man sich einen Trainer programmiert, der selbstständig anhand eines Filters erkennt, auf welcher Adresse sich beispielsweise die Munition oder die Geldbestände befinden.
2. Erläuterung des Programmcodes
Man kann mithilfe der Win API alle Fenster ermitteln, dazu nutzt man, wie es der Funktionsname bereits sagt EnumWindows. Dieser Funktion übergibt man die Rückruf-Funktion und der letzte Wert ist mit 0 zu ignorieren.
2.1 Fenster auflisten
Die Callback-Funktion ist folgendermaßen aufgebaut:
|
C-/C++-Quelltext
|
1
|
BOOL CALLBACK EnumWindowsProc (::HWND hwnd, ::LPARAM lParam)
|
Im Durchlauf dieser Funktion bekommen wir eine Fensterhandle übergeben. Mit FALSE als Rückgabewert lässt sich die Auflistung jederzeit unterbrechen.
|
C-/C++-Quelltext
|
1
2
3
4
|
{
// Kein Fenster
if (!::IsWindow (hwnd))
return (TRUE);
|
Da uns nur sichtbare Fenster interessieren (die Anderen können uns egal sein, da das Spiel mit sehr hoher Wahrscheinlichkeit sichtbar sein wird

) wollen wir auch nur diese Auflisten lassen.
|
C-/C++-Quelltext
|
1
2
3
|
// nur sichtbare Fenster auflisten
if (!::IsWindowVisible (hwnd))
return (TRUE);
|
Jetzt interessiert uns die Fensterüberschrift und ob das Fenster überhaupt eine Überschrift hat.
Mithilfe der Überschrift erkennen wir unsere Zielanwendung wieder.
|
C-/C++-Quelltext
|
1
2
3
4
|
// Fensterberschrift ermitteln
::TCHAR title[256];
if (!::GetWindowText (hwnd, title, sizeof (title) / sizeof (title[0])))
return (TRUE);
|
Nachdem das Fenster all unsere Kriterien erfüllt hat, kommt es in unser Fenster Array, damit es später aufgelistet werden kann.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
|
// neues Fenster hinzufgen...
Window wnd;
wnd.handle = hwnd;
wnd.title = title;
windows.push_back (wnd);
return (TRUE);
}
|
Es ist nicht wirklich lohnenswert diese Funktion genauer zu erläutern… Aber der Verständlichkeit halber…
|
C-/C++-Quelltext
|
1
2
3
4
|
::HWND select_window (void)
{
// Alle Fenster auflisten
::EnumWindows (EnumWindowsProc, 0);
|
Nachdem alle Fenster aufgelistet wurden, iteriert man das gesamte Array durch und gibt die Fenster sowie den zugehörigen Index aus.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// Liste aller Fenster ausgeben.
std::vector<Window>::iterator window_it = windows.begin ();
for (int i = 0; window_it != windows.end (); ++ window_it, ++ i)
::_tprintf (_T ("%d : %s\n"), i, window_it->title.c_str ());
// Auswahl des Fensters & Prüfung, ob Auswahl im Gültigskeitsbereich liegt
unsigned int choice = 0;
do
{
::_tprintf (_T ("\nWelches Fenster soll gescannt werden: "));
::_tscanf (_T ("%d"), &choice);
} while (choice < 0 || choice >= windows.size ());
// ausgewählte Fensterhandle zurückgeben
return (windows[choice].handle);
}
|
2.2 Prozess anzapfen
In der Hauptfunktion sieht es folgendermaßen aus:
|
C-/C++-Quelltext
|
1
|
int _tmain (int argc, ::TCHAR* argv[])
|
Gewünschte Fensterhandle wird ermittelt und der zugehörige Prozess angezapft.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
|
// Ausgabe der Überschrift
::_tprintf (_T ("MemScan v1.3 by TcH\n\n"));
// Fenster auswählen
::HWND hwnd = select_window ();
// Scann starten...
::DWORD process_ID;
// Zum Fenster zugehörigen Prozess ermitteln
::DWORD thread_ID = ::GetWindowThreadProcessId (hwnd, &process_ID);
// In den Prozess einklinken
::HANDLE process = ::OpenProcess (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF, FALSE, process_ID);
|
2.3 Adressbereiche ermitteln
Wie erwähnt soll dieses Tutorial auf unsigned long seinem aufbauen. Ziel war es das Programm so zu gestalten, das es automatisch nach den Adressen scant. Hierfür brauchen wir folgende Informationen.:
1. Den Anfangsbereich des Arbeitsspeichers, der für Anwendungen genutzt werden kann.
2. und natürlich den maximalen Bereich, der von Anwendungen genutzt werden kann.
|
C-/C++-Quelltext
|
1
2
|
::SYSTEM_INFO sys_info;
::GetSystemInfo (&sys_info);
|
Jetzt suchen wir innerhalb des oben ermittelten Intervalls alle Bereiche des Arbeitsspeichers ab, die von unserer Ziel- bzw. Opferanwendung genutzt werden. VirtualQueryEx macht seinen Job hier sehr gut.
2.4 Adressbereiche durchsuchen
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
|
// Anfangsadresse für Durchsuchung festlegen
::DWORD adress = (::DWORD) sys_info.lpMinimumApplicationAddress;
do
{
// Arbeitsspeicherbereiche ermitteln
::VirtualQueryEx (process,
reinterpret_cast<void*>(adress),
&mbi,
sizeof (::MEMORY_BASIC_INFORMATION));
|
Folglich erhalten wir eine Memory Basic Information, welcher wir ihren Start- und Endwert entnehmen und diesen Zwischenbereich einem tieferen Scan unterziehen.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
|
// Anfang & Ende des aktuellen Bereiches ermitteln
::DWORD start = (::DWORD) mbi.BaseAddress;
::DWORD end = (::DWORD) mbi.BaseAddress + mbi.RegionSize;
// Bereich einem "tiefen" Scan unterziehen
if (mbi.State == MEM_COMMIT)
|
Diese Funktion scannt den Bereich nach dem gesuchten Wert.
|
C-/C++-Quelltext
|
1
2
3
|
scan_memory (process, start, end, value, adresses);
adress += mbi.RegionSize;
|
Der ganze Prozess wird schließlich so oft wiederholt, bis der ganze Arbeitsspeicher durchforstet ist.
|
C-/C++-Quelltext
|
1
2
|
// Durchsuche solange nach Bereichen, bis Ende im RAM
} while (adress < (::DWORD) sys_info.lpMaximumApplicationAddress);
|
Die unten gezeigte Routine übernimmt den "tieferen Scan"
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
|
void scan_memory (::HANDLE process, ::DWORD start, ::DWORD end, unsigned int value, std::vector<::DWORD>& adresses)
{
Wir wollen jede einzelne Adresse dieses Bereiches durchsuchen
for (::DWORD i = start; i < end; ++ i)
{
unsigned int read_buffer = 0;
Und lessen dazu den Wert jeder Adresse aus
// Wert auslesen
::ReadProcessMemory (process, reinterpret_cast<void*>(i), &read_buffer, sizeof (unsigned int), 0);
|
Vergleichen ihn mit unserem gesuchten Wert und fügen gegebenenfalls die Adresse zu unserem Array hinzu (diese werden später nochmals überprüft).
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
|
// Wert stimmt mit gesuchten Wert Überein
if (read_buffer == value)
{
::_tprintf (_T ("Wert (%d) an der Stelle 0x%x gefunden\n"), value, i);
adresses.push_back (i);
}
}
}
|
2.5 Adressbereiche wiederholt durchsuchen
Diese Funktion ist auch weitestgehend sebsterklärend. Erst wird gefragt, nach welchem Wert gesucht werden soll und dann werden alle bereits gesammelten Adressen aus dem Array nochmals durchsucht.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
void rescan_memory (::HANDLE process, std::vector<::DWORD>& adresses)
{
if (adresses.size () > 0)
{
unsigned int value = 0;
::_tprintf (_T ("Nach welchem Wert soll gescannt werden: "));
::_tscanf (_T ("%d"), &value);
::_tprintf (_T ("starte Scanvorgang nach %d\n"), value);
// temporäres Array, indas die neuen Adressen kopiert werden
std::vector<::DWORD> temp;
std::vector<::DWORD>::iterator i = adresses.begin ();
for (i; i != adresses.end (); ++ i)
{
unsigned int read_buffer = 0;
// Wert auslesen
::ReadProcessMemory (process, reinterpret_cast<void*>(*i), &read_buffer, sizeof (unsigned int), 0);
// Wenn gesuchter Wert vorhanden, Adresse speichern
if (read_buffer == value)
{
::_tprintf (_T ("Wert (%d) an der Stelle 0x%x gefunden\n"), value, *i);
temp.push_back (*i);
}
}
// Altes Array lschen
adresses.clear ();
// Altes Array durch neues ersetzen
adresses = temp;
::_tprintf (_T ("%d Uebereinstimmungen gefunden\n"), adresses.size());
}
else
{
::_tprintf (_T ("Keine Werte vorhanden\n"));
}
}
|
Das ganze wird dann folgendermaßen in der _tmain aufgerufen
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
// nochmaliger Scan oder Ersetzen kann nur stattfinden, wenn Adressen gefunden wurden
if (adresses.size () != 0)
{
while (true)
{
// Ausgabe des Menus
unsigned int choice;
::_tprintf (_T ("Gefilterte Adressen Scannen (1)\n")
_T ("Werte aus vorhandenem Scan ersetzten (2)\n")
_T ("Beenden (3)\n")
_T ("Auswahl: "));
::_tscanf (_T ("%u"), &choice);
// Vorhandene Adressen nochmals durchsuchen
if (choice == 1)
rescan_memory (process, adresses);
// Werte der Adressen ersetzen
else if (choice == 2)
repleace_values (process, adresses);
// Beenden
else if (choice == 3)
{
// Aufräumen - Handle wieder freigeben
::CloseHandle (process);
return 0;
}
// Ungültige Eingabe
else
::_tprintf (_T ("Fehler - ungueltige Eingabe\n"));
}
}
|
2.6 Werte ersetzten
Diese Funktion iteriert alle gesammelten Adressen durch und versucht den vorhandenen Wert durch einen neuen, welcher vorher abgefragt wird zu ersetzten.
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
void repleace_values (::HANDLE process, std::vector<::DWORD>& adresses)
{
// Abfragem durch welchen Wert ersetzt werden soll
unsigned int write_buffer = 0;
::_tprintf (_T ("Mit welchem Wert soll ersetzt werden: "));
::_tscanf (_T ("%d"), &write_buffer);
// Adresse fr Adresse den Wert ersetzen
std::vector<::DWORD>::iterator i = adresses.begin ();
for (i; i != adresses.end (); ++ i)
{
if (::WriteProcessMemory (process, reinterpret_cast<void*>(*i), &write_buffer, sizeof (unsigned int), 0))
::_tprintf (_T ("Erfolg - Wert wurde ueberschrieben\n"));
else
::_tprintf (_T ("Fehler - Wert konnte nicht ueberschrieben werden\n"));
}
}
|
2.7 Aufräumen
Am Ende wird noch alles aufgeräumt.
|
C-/C++-Quelltext
|
1
2
|
// Aufräumen - Handle wieder freigeben
::CloseHandle (process);
|
3. gesamter Quellcode
Zum Abschluss nochmal den gesamten Quellcode sowie ein praktisches Anwendungsbeispiel:
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
#include <Windows.h>
#include <Tchar.h>
#include <vector>
// Struktur für Fensterelement
struct Window
{
std::basic_string<::TCHAR> title;
::HWND handle;
};
// Variablen
std::vector<Window> windows;
// Callback fr EnumWindows. Wird aufgerufen fr jedes Fenster
BOOL CALLBACK EnumWindowsProc (::HWND hwnd, ::LPARAM l_param);
// Listet Fenster auf und regelt Auswahl eines Fensters
::HWND select_window (void);
// Werte aus dem Adresspuffer ersetzen
void repleace_values (::HANDLE process, std::vector<::DWORD>& adresses);
// Durchsucht Arbeitsspeicher nach gesuchtem Wert u. speichert alle Adressen mit diesem Wert
void scan_memory (::HANDLE process, ::DWORD start, ::DWORD end, unsigned int value, std::vector<::DWORD>& adresses);
// Dursucht die gespeicherten Adressen wiederholt
void rescan_memory (::HANDLE process, std::vector<::DWORD>& adresses);
// ///////////////////////////////////////////////////////////////////////////
// Programmeinsprungspunkt.
int _tmain (int argc, ::TCHAR* argv[])
{
// Ausgabe der Überschrift
::_tprintf (_T ("MemScan v1.3 by TcH\n\n"));
// Fenster auswählen
::HWND hwnd = select_window ();
// Scann starten...
::DWORD process_ID;
// Zum Fenster zugehörigen Prozess ermitteln
::DWORD thread_ID = ::GetWindowThreadProcessId (hwnd, &process_ID);
// In den Prozess einklinken
::HANDLE process = ::OpenProcess (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF, FALSE, process_ID);
::MEMORY_BASIC_INFORMATION mbi;
// Dient zur Ermittlung der Arbeitsspeichergröße, sowie Anfang und Ende
::SYSTEM_INFO sys_info;
::GetSystemInfo (&sys_info);
// Alle Adressen welche den gesuchten Wert werden hier gespeichert
std::vector<::DWORD> adresses;
unsigned int value = 0;
// Keine Adresswerte im Array, Scan starten
::_tprintf (_T ("Nach welchem Wert soll gescannt werden: "));
::_tscanf (_T ("%d"), &value);
::_tprintf (_T ("starte Scanvorgang nach %d\n"), value);
// Anfangsadresse für Durchsuchung festlegen
::DWORD adress = (::DWORD) sys_info.lpMinimumApplicationAddress;
do
{
// Arbeitsspeicherbereiche ermitteln
::VirtualQueryEx (process,
reinterpret_cast<void*>(adress),
&mbi,
sizeof (::MEMORY_BASIC_INFORMATION));
// Anfang & Ende des aktuellen Bereiches ermitteln
::DWORD start = (::DWORD) mbi.BaseAddress;
::DWORD end = (::DWORD) mbi.BaseAddress + mbi.RegionSize;
// Bereich einem "tiefen" Scan unterziehen
if (mbi.State == MEM_COMMIT)
scan_memory (process, start, end, value, adresses);
adress += mbi.RegionSize;
// Durchsuche solange nach Bereichen, bis Ende im RAM
} while (adress < (::DWORD) sys_info.lpMaximumApplicationAddress);
// Ausgabe der Anzahl der Übereinstimmungen
::_tprintf (_T ("%d Uebereinstimmungen gefunden\n"), adresses.size ());
// nochmaliger Scan oder Ersetzen kann nur stattfinden, wenn Adressen gefunden wurden
if (adresses.size () != 0)
{
while (true)
{
// Ausgabe des Menus
unsigned int choice;
::_tprintf (_T ("Gefilterte Adressen Scannen (1)\n")
_T ("Werte aus vorhandenem Scan ersetzten (2)\n")
_T ("Beenden (3)\n")
_T ("Auswahl: "));
::_tscanf (_T ("%u"), &choice);
// Vorhandene Adressen nochmals durchsuchen
if (choice == 1)
rescan_memory (process, adresses);
// Werte der Adressen ersetzen
else if (choice == 2)
repleace_values (process, adresses);
// Beenden
else if (choice == 3)
{
// Aufräumen - Handle wieder freigeben
::CloseHandle (process);
return 0;
}
// Ungültige Eingabe
else
::_tprintf (_T ("Fehler - ungueltige Eingabe\n"));
}
}
// Aufräumen - Handle wieder freigeben
::CloseHandle (process);
// Schließen des Fensters verhindern
::TCHAR a;
::_tscanf (_T ("%c"), &a);
return 0;
}
// ///////////////////////////////////////////////////////////////////////////
// Callback fr EnumWindows. Wird aufgerufen fr jedes Fenster
BOOL CALLBACK EnumWindowsProc (::HWND hwnd, ::LPARAM lParam)
{
// Kein Fenster
if (!::IsWindow (hwnd))
return (TRUE);
// nur sichtbare Fenster auflisten
if (!::IsWindowVisible (hwnd))
return (TRUE);
// Fensterberschrift ermitteln
::TCHAR title[256];
if (!::GetWindowText (hwnd, title, sizeof (title) / sizeof (title[0])))
return (TRUE);
// neues Fenster hinzufgen...
Window wnd;
wnd.handle = hwnd;
wnd.title = title;
windows.push_back (wnd);
return (TRUE);
}
// ///////////////////////////////////////////////////////////////////////////
// Listet Fenster auf und regelt Auswahl eines Fensters
::HWND select_window (void)
{
// Alle Fenster auflisten
::EnumWindows (EnumWindowsProc, 0);
// Liste aller Fenster ausgeben.
std::vector<Window>::iterator window_it = windows.begin ();
for (int i = 0; window_it != windows.end (); ++ window_it, ++ i)
::_tprintf (_T ("%d : %s\n"), i, window_it->title.c_str ());
// Auswahl des Fensters & Prüfung, ob Auswahl im Gültigskeitsbereich liegt
unsigned int choice = 0;
do
{
::_tprintf (_T ("\nWelches Fenster soll gescannt werden: "));
::_tscanf (_T ("%d"), &choice);
} while (choice < 0 || choice >= windows.size ());
// ausgewählte Fensterhandle zurckgeben
return (windows[choice].handle);
}
// ///////////////////////////////////////////////////////////////////////////
// Werte aus dem Adresspuffer ersetzen
void repleace_values (::HANDLE process, std::vector<::DWORD>& adresses)
{
// Abfragem durch welchen Wert ersetzt werden soll
unsigned int write_buffer = 0;
::_tprintf (_T ("Mit welchem Wert soll ersetzt werden: "));
::_tscanf (_T ("%d"), &write_buffer);
// Adresse fr Adresse den Wert ersetzen
std::vector<::DWORD>::iterator i = adresses.begin ();
for (i; i != adresses.end (); ++ i)
{
if (::WriteProcessMemory (process, reinterpret_cast<void*>(*i), &write_buffer, sizeof (unsigned int), 0))
::_tprintf (_T ("Erfolg - Wert wurde ueberschrieben\n"));
else
::_tprintf (_T ("Fehler - Wert konnte nicht ueberschrieben werden\n"));
}
}
// ///////////////////////////////////////////////////////////////////////////
// Durchsucht Arbeitsspeicher nach gesuchtem Wert u. speichert alle Adressen mit diesem Wert
void scan_memory (::HANDLE process, ::DWORD start, ::DWORD end, unsigned int value, std::vector<::DWORD>& adresses)
{
for (::DWORD i = start; i < end; ++ i)
{
unsigned int read_buffer = 0;
// Wert auslesen
::ReadProcessMemory (process, reinterpret_cast<void*>(i), &read_buffer, sizeof (unsigned int), 0);
// Wert stimmt mit gesuchten Wert berein
if (read_buffer == value)
{
::_tprintf (_T ("Wert (%d) an der Stelle 0x%x gefunden\n"), value, i);
adresses.push_back (i);
}
}
}
// ///////////////////////////////////////////////////////////////////////////
// Durchsucht die gespeicherten Adressen
void rescan_memory (::HANDLE process, std::vector<::DWORD>& adresses)
{
if (adresses.size () > 0)
{
unsigned int value = 0;
::_tprintf (_T ("Nach welchem Wert soll gescannt werden: "));
::_tscanf (_T ("%d"), &value);
::_tprintf (_T ("starte Scanvorgang nach %d\n"), value);
// temporäres Array, indas die neuen Adressen kopiert werden
std::vector<::DWORD> temp;
std::vector<::DWORD>::iterator i = adresses.begin();
for (i; i != adresses.end (); ++ i)
{
unsigned int read_buffer = 0;
// Wert auslesen
::ReadProcessMemory (process, reinterpret_cast<void*>(*i), &read_buffer, sizeof (unsigned int), 0);
// Wenn gesuchter Wert vorhanden, Adresse speichern
if (read_buffer == value)
{
::_tprintf (_T ("Wert (%d) an der Stelle 0x%x gefunden\n"), value, *i);
temp.push_back (*i);
}
}
// Altes Array löschen
adresses.clear ();
// Altes Array durch neues ersetzen
adresses = temp;
::_tprintf (_T ("%d Uebereinstimmungen gefunden\n"), adresses.size());
}
else
{
::_tprintf (_T ("Keine Werte vorhanden\n"));
}
}
|
4. Anwendungsbeispiel - GTA Vice City
Hier meine Screenshoots mit kurzen Erläuterungen:
Ich zeige euch hier anhand eines Beispiels, wie ich die Munition des Raketenwerfers RPG7 von 4 auf 999 erhöhe.
Als erstes wird die Anwendung GTA Vice City gestartet.
http://www.germangamedev.de/downloads/ar…rainer/2/s1.jpg
Sammel den RPG7 Raketenwerfer auf.
http://www.germangamedev.de/downloads/ar…rainer/2/s2.jpg
öffne das Tool.
http://www.germangamedev.de/downloads/ar…rainer/2/s3.jpg
Scanne den RAM nach 4 (da ich 4 Schuss besitze). Finde 62434 Adressen im RAM, die den gleichen Wert besitzen.
http://www.germangamedev.de/downloads/ar…rainer/2/s4.jpg
verschieße Rakete für Rakete und Scanne die vorhandenen Adressen nach den neuen Werten (3, 2, 1, 0...)
http://www.germangamedev.de/downloads/ar…rainer/2/s5.jpg
http://www.germangamedev.de/downloads/ar…rainer/2/s7.jpg
Schließlich bleibt nur noch eine Adresse übrig... Also, aktuellen Wert durch 999 ersetzen...
http://www.germangamedev.de/downloads/ar…rainer/2/s8.jpg
JEA!!!!! funktioniert
5. Schlusswort
Da es sich um mein erstes selbstgeschriebenes Tutorial handelt, würde ich mich sehr über Feedback erfreuen. Sicherlich befinden sich hier noch einige grammatische und Reschtschrebfehler. Das Eine oder Andere muss auch noch besser erklärt werden.
Bevor ich es Vergesse:
Ich möchte mich nochmals ausdrücklich bei unsigned long für sein sehr informatives Tutorial, was mir als Grundlage diente, bedanken!
Außerdem ist ein neues Programm in Planung, was anhand der (Stack) Adresswerte eine Exe-Datei erstellt, die dann Adresse und Fenstername enthält. Ein Vorteil besteht darin, das kein Compilieren mehr für die Trainererstellung benötigt wird (und das lästige Scannen fällt weg). Für die dynamisch generierten Trainer-Anwendungen soll Windows-Fenster-Oberfläche mit Buttons & Edit-Feldern aufgesetzt werden.
Und hoffe euch ein bisschen weiter geholfen zu haben... :lol:
6. Downloads
Anwendung:
http://www.germangamedev.de/downloads/ar…mscn_binary.rar
Quellcode und Visual Studio 2005 Projekt :
http://www.germangamedev.de/downloads/ar…mscn_source.rar
7. weiterführende Links
Microsoft Developer Network:
CloseHandle Function
Microsoft Developer Netowkr:
EnumWindows Function
Microsoft Developer Network:
EnumWindowsProc Function
Microsoft Developer Network:
GetSystemInfo Function
Microsoft Developer Network:
GetWindowText Function
Microsoft Developer Network:
GetWindowThreadProcessId Function
Microsoft Developer Network:
IsWindow Function
Microsoft Developer Network:
IsWindowVisible Function
Microsoft Developer Network:
OpenProcess Function
Microsoft Developer Network:
ReadProcessMemory Function
Microsoft Developer Network:
VirtualQueryEx Function
Microsoft Developer Network:
WriteProcessMemory Function
Microsoft Developer Network:
MEMORY_BASIC_INFORMATION Structure
Microsoft Developer Network:
SYSTEM_INFO Structure
mfG TcH