Zum Inhalt springen

Auto-Assembler-Grundlagen: AOB-Injection-Skripte in Cheat Engine verstehen

Cheat-Engine.net Von Cheat-Engine.net 8 Min. Lesezeit 34 Aufrufe

Was ein Skript-Eintrag ist, wie [ENABLE] und [DISABLE] funktionieren und eine Zeile-für-Zeile-Erklärung der AOB-Injection-Vorlage von Cheat Engine: aobscanmodule, alloc, Labels, Symbole und Wildcards.

Die meisten nützlichen Einträge in geteilten Cheat Tables sind keine einfachen Werte, sondern Skripte: unendlich Gesundheit, kein Nachladen, kostenloses Craften. Sie sind im Auto Assembler von Cheat Engine geschrieben, einem kleinen Skriptformat, das x86-Assembler mit Befehlen zum Finden und Patchen von Code kombiniert. Dieser Guide erklärt, was so ein Skript macht, geht die AOB-Injection-Vorlage durch, die Cheat Engine für dich erzeugt, und behandelt die Regeln, die ein Skript zuverlässig machen.

Du hast mehr davon, wenn du bereits weißt, wie man einen Wert findet und was ein Pointer ist; der Pointer-Scan-Guide liefert diesen Hintergrund. Skripte verändern den Code eines laufenden Spiels, also nutze sie nur in Einzelspieler- und Offline-Spielen, speichere vor dem Testen und lege Backups deiner Spielstände an.

Was ein Skript-Eintrag ist

Ein Skript-Eintrag ist ein Element in der Adressliste, dessen Wert-Spalte <script> anzeigt. Statt auf einen Wert zu zeigen, enthält er Auto-Assembler-Code. Wenn du das Kästchen „Aktiv“ des Eintrags anhakst, läuft der Abschnitt [ENABLE] des Skripts; wenn du den Haken entfernst, läuft der Abschnitt [DISABLE]. Die Anleitung So benutzt du eine Cheat Table behandelt das aus Sicht der Anwender.

Das typische Skript ändert einen Wert nicht nur einmal. Es ändert den spieleigenen Befehl, der den Wert schreibt, sodass der Effekt dauerhaft ist, solange das Skript aktiv ist. Zum Beispiel kann der Befehl, der nach Schaden deine neue Gesundheit speichert, auf Code umgeleitet werden, der stattdessen das Maximum speichert.

Skripte haben einen großen Vorteil gegenüber einfachen Adressen: Sie finden ihr Ziel, indem sie im Code des Spiels nach einem Bytemuster suchen, und funktionieren daher auch nach Neustarts ohne Pointer. Der Nachteil ist, dass sie davon abhängen, dass der Code gleich bleibt, was sie an eine Spielversion bindet.

[ENABLE] und [DISABLE]

Jedes ein- und ausschaltbare Skript hat beide Abschnitte. [ENABLE] findet den Code, reserviert Speicher für neuen Code und schreibt einen Sprung vom Befehl des Spiels zum neuen Code. [DISABLE] macht alles rückgängig: Es schreibt die ursprünglichen Bytes zurück, entfernt die registrierten Namen und gibt den Speicher frei. Ein gut geschriebenes [DISABLE] lässt das Spiel genau so, wie es war. Fehlt es oder ist es fehlerhaft, bleibt das Spiel nach dem Abwählen des Eintrags gepatcht oder kaputt, bis du es neu startest.

Den Befehl finden und die Vorlage erzeugen

  1. Finde den Wert, den du kontrollieren willst, und füge ihn zur Adressliste hinzu.
  2. Klicke ihn mit rechts an und wähle Find out what writes to this address. Lass den Wert im Spiel sich ändern. Ein oder mehrere Befehle erscheinen in der Liste.
  3. Wähle den Befehl aus und öffne ihn im Disassembler (Memory Viewer).
  4. Öffne mit diesem im Memory Viewer ausgewählten Befehl Tools → Auto Assemble.
  5. Wähle im Auto-Assemble-Fenster Template → AOB Injection. Bestätige die Adresse und übernimm oder ändere den Symbolnamen, der standardmäßig INJECT lautet.
  6. Bearbeite das Skript und weise es dann über das Datei-Menü des Fensters der aktuellen Cheat Table zu. Es erscheint als neuer Skript-Eintrag in der Adressliste.

Die AOB-Injection-Vorlage, Zeile für Zeile

Unten siehst du ein Beispiel für die Struktur, die die Vorlage erzeugt. Prozessname, Symbol und Bytes sind zur Veranschaulichung erfunden; das ist kein Code für ein echtes Spiel.

{ EXAMPLE STRUCTURE ONLY - not code for a real game.
  Process: game.exe   Symbol: INJECT   Bytes: made up }

[ENABLE]
aobscanmodule(INJECT,game.exe,89 83 F0 00 00 00 48 8B 5C 24 ??) // should be unique
alloc(newmem,$1000,INJECT)

label(code)
label(return)

newmem:
  mov eax,#999              // your change: store 999 instead

code:
  mov [rbx+000000F0],eax    // the original instruction
  jmp return

INJECT:
  jmp newmem
  nop
return:
registersymbol(INJECT)

[DISABLE]
INJECT:
  db 89 83 F0 00 00 00      // original bytes: mov [rbx+000000F0],eax

unregistersymbol(INJECT)
dealloc(newmem)

aobscanmodule

aobscanmodule(INJECT,game.exe,...) durchsucht den Speicher des Moduls game.exe nach dem Bytemuster und definiert, falls es gefunden wird, den Namen INJECT als die Adresse, an der es beginnt. Die Suche auf ein Modul zu beschränken ist schneller und vermeidet falsche Treffer an anderer Stelle. Es gibt verwandte Befehle: aobscan durchsucht den gesamten Speicher und aobscanregion sucht zwischen zwei Adressen. Wird das Muster nicht gefunden, lässt sich das Skript nicht aktivieren und zeigt einen Fehler an, das typische Symptom nach einem Spiel-Update.

alloc

alloc(newmem,$1000,INJECT) reserviert 0x1000 Bytes Speicher für deinen Code und nennt ihn newmem. Der dritte Parameter weist Cheat Engine an, ihn nahe bei INJECT zu reservieren. In 64-Bit-Spielen ist das wichtig: Ein kurzer 5-Byte-jmp reicht in beide Richtungen nur etwa 2 GB weit, und eine Reservierung in der Nähe hält den Sprung klein genug, um anstelle des ursprünglichen Befehls zu passen.

label

label(code) und label(return) deklarieren Namen für Positionen, die weiter unten definiert werden. code markiert, wo der ursprüngliche Befehl erneut ausgeführt wird; return markiert den Befehl direkt nach dem gepatchten, an dem die Ausführung weitergeht.

newmem und code

Alles unter newmem: ist dein Code, gefolgt vom ursprünglichen Befehl unter code:. Im Beispiel setzt mov eax,#999 das Register auf 999 (das # kennzeichnet eine Dezimalzahl), und das ursprüngliche mov [rbx+000000F0],eax speichert es dann, sodass der Wert immer als 999 geschrieben wird. Schließlich springt jmp return zurück in den Code des Spiels.

INJECT: jmp newmem

An der gefundenen Adresse überschreibt das Skript den ursprünglichen Befehl mit jmp newmem. Der Sprung braucht 5 Bytes, der ursprüngliche Befehl war hier aber 6 Bytes lang, also füllt ein nop das übrige Byte; sonst würde der Prozessor versuchen, einen halben Befehl auszuführen. Die Vorlage zählt das für dich mit. return: steht direkt danach und markiert somit den nächsten unberührten Befehl.

registersymbol und unregistersymbol

registersymbol(INJECT) macht den Namen INJECT in Cheat Engine global bekannt. Dadurch kann der Abschnitt [DISABLE], der später und getrennt läuft, dieselbe Adresse finden, ohne erneut zu scannen. Außerdem können andere Einträge darauf verweisen. unregistersymbol(INJECT) in [DISABLE] entfernt den Namen.

Ursprüngliche Bytes wiederherstellen und dealloc

In [DISABLE] schreibt INJECT: gefolgt von db 89 83 F0 00 00 00 die ursprünglichen Bytes über den Sprung zurück und stellt so den Befehl des Spiels exakt wieder her. Dann gibt dealloc(newmem) den Speicher frei, der deinen Code enthielt. Die Reihenfolge ist wichtig: Stelle zuerst den Sprung wieder her, damit das Spiel newmem nicht mehr benutzt, wenn es freigegeben wird.

Wildcards und warum eindeutige Signaturen wichtig sind

Das Bytemuster in aobscanmodule nennt man Signatur. Sie muss zwei Bedingungen erfüllen:

  • Sie muss eindeutig sein. Kommt das Muster mehr als einmal vor, patcht das Skript den ersten Treffer, der eine völlig andere Funktion sein kann. So bringen Skripte Spiele zum Absturz oder ändern das Falsche. Die Vorlage fügt zur Erinnerung den Kommentar „should be unique“ hinzu.
  • Sie sollte kleine Änderungen überstehen. Manche Bytes ändern sich leicht zwischen Spielversionen oder sogar zwischen Starts: Offsets in Befehlen, relative Adressen von Calls und Jumps sowie eingebettete Konstanten. Ersetzt du diese Bytes durch ??-Wildcards, passt das Muster weiterhin, wenn sich nur diese Details ändern.

Diese beiden Ziele widersprechen sich. Zu wenige Bytes, und das Muster passt an vielen Stellen; zu viele Wildcards, und es wird wieder allgemein. Ein praktischer Ansatz: Behalte die Opcode-Bytes, ersetze die veränderlichen Offsets und Adressen durch Wildcards und nimm ein paar Bytes der folgenden Befehle als Kontext dazu. Prüfe dann, ob das Muster eindeutig ist: Stelle im Hauptfenster den Wert-Typ auf Array of byte, füge das Muster ein und scanne mit Speicheroptionen, die ausführbaren Speicher einschließen. Genau ein Ergebnis ist das Ziel. Der AOB-Signatur-Generator hilft dir, kopierte Bytes in ein Wildcard-Muster zu verwandeln und zu erkennen, welche Bytes sich wahrscheinlich ändern.

Ein Skript testen

  • Speichere das Spiel und lege vor dem ersten Test ein Backup deines Spielstands an.
  • Aktiviere das Skript, prüfe den Effekt, deaktiviere es dann und stelle sicher, dass sich das Spiel wieder normal verhält.
  • Schalte es mehrmals an und aus. Ein Skript, das beim zweiten Aktivieren fehlschlägt, hat meist einen [DISABLE]-Abschnitt, der [ENABLE] nicht vollständig rückgängig macht.
  • Sei vorsichtig bei geteilten Befehlen. Der Befehl, der deine Gesundheit schreibt, schreibt vielleicht auch die der Gegner; dann betrifft deine Änderung alle, und du musst die Fälle unterscheiden, bevor du etwas änderst.

Sobald ein Skript funktioniert, gib dem Eintrag eine klare Beschreibung und speichere die Table. Wenn das Spiel ein Update bekommt und sich das Skript nicht mehr aktivieren lässt, ist die Signatur das Erste, was du prüfen solltest.

Kommentare

0
Antwort an

Wird nie öffentlich angezeigt.

Kommentare werden von einem Moderator geprüft, bevor sie erscheinen.

Noch keine Kommentare

Sei der Erste, der seine Meinung teilt.