Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

TcH

Frischling

  • »TcH« ist der Autor dieses Themas

Beiträge: 25

Beruf: Abiturient & Nebenberuflich Softwareentwickler

  • Private Nachricht senden

1

17.05.2008, 18:49

HowTo: Erstellen eines Trainers II

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 :D) 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… :D

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

tnecniv

Treue Seele

Beiträge: 164

Wohnort: BERLIN

  • Private Nachricht senden

2

17.05.2008, 19:07

cool, ich finds gut. :)

was mir so aufgefallen ist (von wegen fehlerchen :D):

Zitat von »"TcH"«


Man kann mithilfe der Win API alle Fenster ermitteln, dazu nutzt man, wie es der Funktionsname bereits sagt EnumWindowsProc

das Proc ist doch zu viel, oder?
Sagt der igel zu dem dachs:
willst du größer sein, dann wachs!
- alte Fritz weisheit

TcH

Frischling

  • »TcH« ist der Autor dieses Themas

Beiträge: 25

Beruf: Abiturient & Nebenberuflich Softwareentwickler

  • Private Nachricht senden

3

17.05.2008, 19:08

Thx, stimmt!
Dummheit, die sich mit Arroganz und Besserwisserei profilieren will.

tnecniv

Treue Seele

Beiträge: 164

Wohnort: BERLIN

  • Private Nachricht senden

4

17.05.2008, 19:14

uh, außerdem ist deine "aufräumaktion" für die katz, wenn ich das richtig sehe..

Zitat von »"TcH"«

C-/C++-Quelltext

1
2
3
// Beenden

            else if(choice == 3)
                return 0; 
Sagt der igel zu dem dachs:
willst du größer sein, dann wachs!
- alte Fritz weisheit

TcH

Frischling

  • »TcH« ist der Autor dieses Themas

Beiträge: 25

Beruf: Abiturient & Nebenberuflich Softwareentwickler

  • Private Nachricht senden

5

17.05.2008, 19:23

Da hast du allerdings Recht!
Dummheit, die sich mit Arroganz und Besserwisserei profilieren will.

Toa

Alter Hase

Beiträge: 944

Beruf: Research associate

  • Private Nachricht senden

6

18.05.2008, 12:10

Cool wollte schon immer mal wissen wie soetwas funktioniert..Big Thx

TcH

Frischling

  • »TcH« ist der Autor dieses Themas

Beiträge: 25

Beruf: Abiturient & Nebenberuflich Softwareentwickler

  • Private Nachricht senden

7

18.05.2008, 13:11

Übrigens, bevor ich es vergesse: Man kann mit dem Tool auch andere Programme anzapfen und seinen Unfug treiben ;).
Ein großes Problem gibt es aber noch: die Gesundheit lässt sich mit dem Programm in GTA Vice City nicht manipulieren, liegt möglicherweise daran, dass diese als 1 Byte oder 2 Byte Variable gespeichert wird.
Somit müsste man das Programm auch noch um eine flexiblere datengrößenungebundene Auslesefunktion erweitern.
Dummheit, die sich mit Arroganz und Besserwisserei profilieren will.

drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

8

18.05.2008, 13:33

He. Nettes Tutorial.

Wir müssen jetzt aber schauen, was wir noch für Tutorials schreiben. ;) - Langsam geht es in den grauen Bereich über und könnte unter umständen nicht so gut ankommen..

Hoffe natürlich nicht, aber bevor jetzt noch mehr Leute in was grössereres Arbeit stecken, würde ich zuerst mal überlegen, ob es ev. schon zu weit geht.

Fred

Supermoderator

Beiträge: 2 121

Beruf: Softwareentwickler

  • Private Nachricht senden

9

18.05.2008, 13:57

Wieso, was ist schlimm daran, wenn ich ViceCity mit 999Raketen spiele?
Den einzigen den ich betrüge bin ich selbst. Ich finde da ist nichts grau dran.

drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

10

18.05.2008, 14:05

Zitat von »"Fred"«

Wieso, was ist schlimm daran, wenn ich ViceCity mit 999Raketen spiele?
Den einzigen den ich betrüge bin ich selbst. Ich finde da ist nichts grau dran.


Ne, NOCH nicht. Aber Was hindert dich jetzt daran das wissen zu nutzen für z.B ein Online Game, wo es auch um Kohle geht?
Oder gar ein Tutorial zu schreiben, wie man einen Aimbot schreibt? Ich sags ja nur. Mir ist das eigentlich egal,wenns jemand macht. ;)

Werbeanzeige