Erschienen in 64'er Magazin, Ausgabe 05/1994 · Originaldatei: CRACKER.TXT
Hinweis: Dies ist das an die Redaktion eingereichte Manuskript, nicht der gedruckte Endtext. Layout, Bildunterschriften, Korrekturen und Kürzungen der Redaktion können in der veröffentlichten Fassung abweichen.
| Objekt | 64'er Magazin |
|---|---|
| Ausgabe | 5/94 |
| Rubrik | Assembler-Corner |
| Redakteur | Pit Klein PK |
| Autor | Nicki Heusler |
| Datum | 17. 3.94 |
| Thema | So werden Spiele geknackt |
Ein Cracker packt aus
Spielen auf dem Computer ist eine schöne Sache. Weniger schön ist es, wenn das Spiel zu schwierig ist. Wollten Sie bei dem einen oder anderen Spiel nicht auch schon mal mehr Leben haben? In diesem Artikel lernen Sie, wie man Spiele knackt.
von Nikolaus Heusler
Fast jeder Computerfan kennt die "Schummel-" oder "Trainerpokes", die es zu vielen Spielen gibt. Vor dem Start eingegeben, verhelfen sie dem geplagten Spieler zu unendlich vielen Leben, heben Zeitlimits auf, ermöglichen den Einstieg in ein höheres Level oder Nützliches dieser Art. Doch was tut man, wenn man zu seinem Lieblingsspiel keinen Poke hat? Dann versuchen Sie doch, ihn selbst herauszufinden! Es ist gar nicht schwer, ein Spiel so zu manipulieren, daß es beispielsweise keinen Tod mehr gibt. Man muß dabei allerdings einige Kniffe kennen, die dieser Artikel Ihnen beibringen wird. Die Kenntnisse, die Sie hier vermittelt bekommen, können Sie aber nicht nur nur zum Knacken von Spielen verwenden. Auch bei einem Anwenderprogramm ist die Vorgehensweise nicht viel anders, etwa wenn es darum geht, ein Textprogramm an den eigenen Drucker anzupassen oder auch nur die Bildschirmfarben zu verändern.
Voraussetzungen
Da die meisten professionellen Spiele in Maschinensprache programmiert sind, sollten Sie zum Verständnis dieses kleinen Kurses das notwendige Grundwissen und schon etwas Erfahrung in Assembler mitbringen. Außerdem benötigen Sie einen Maschinensprachemonitor. Auch andere "Standardprogramme", etwa eine RENEW-Routine, sollten Sie in Ihrer Programmsammlung haben.
Nützlich, jedoch nicht unbedingt notwendig, sind Hardware-Erweiterungen wie ein neues Betriebssystem, das einen Schnellader und eine Funktion zum Verlassen von Reset-geschützten Programmen bietet. Wichtig ist auch, daß Sie einen Reset-Taster an Ihren C64 angeschlossen haben. Apropos C64: Diese Anleitung bezieht sich hauptsächlich auf den Commodore 64, sie kann aber auch genau so auf andere Computer, wie den C128 oder C16/116, angewendet werden.
Als Monitor hat sich bei mir der SM-Kit bewährt, da man hier den Ausdruck des Disassemblers direkt überschreiben kann. Auch sonst ist dieser Monitor komfortabler als andere auf dem C64 - reine Geschmackssache.
"SUSAX" hilft beim Suchen
Praktisch jeder der später vorgestellten Tips zeigt Ihnen, wie Sie in einem Spiel die Fehlerroutine finden können. Dazu ist es fast immer notwendig, bestimmte Bytefolgen im Speicher zu suchen. Dies erlauben die meisten Monitore auch, einfacher und komfortabler geht es jedoch mit dem 871 Bytes kurzen Hilfsprogramm "SUSAX" (Listing), das Sie, bevor wir uns gemeinsam ins Knacken stürzen, mit dem MSE (Hinweise auf Seite ***) abtippen und auf Diskette speichern sollten.
Damit es später nicht mit Grundsätzlichem Schwierigkeiten gibt, sollten Sie zunächst den Umgang mit SUSAX erlernen. Das Programm wird mit dem Befehl
LOAD "SUSAX $C000",8,8 NEW
geladen und mit
SYS 49152
gestartet. Auf dem Bildschirm erscheint nun das Titelbild. Geben Sie zunächst bitte dezimal Anfangs- und Endadresse des zu durchsuchenden Bereiches ein. Die Eingaben werden mit RETURN abgeschlossen. Der zu durchsuchende Bereich darf aus programmtechnischen Gründen nicht bei 65535 enden!
Dann erscheint die Frage, ob Sie das RAM oder ROM durchsuchen lassen wollen. Diese Eingabe (A für RAM, O für ROM) bezieht sich auf die mehrfach belegten Bereiche $A000-$BFFF und $D000-$FFFF.
Ist das geschehen, wird die Anzahl der gesuchten Bytes eingegeben. Sie darf nicht 0 sein und 255 nicht überschreiten. Jetzt werden, wieder dezimal, die Bytes eingegeben. Nach jedem Byte (Bereich 0 bis 255) ist RETURN zu drücken.
Danach folgen noch zwei Fragen, die mit den Tasten J oder N zu beantworten sind. Die erste betrifft das Ausgabegerät. Die Liste der Fundstellen kann auf dem Drucker oder dem Bildschirm ausgegeben werden.
Nun müssen Sie noch entscheiden, ob die RUNSTOP-Taste beim Suchen verriegelt sein soll (dadurch wird die Suche etwas schneller) oder nicht.
Der definierte Bereich wird jetzt durchsucht. Die Speicherstellen, an denen die gesuchte Folge beginnt, werden dezimal ausgegeben. Am Ende der Suche gelangt man wieder in den Direktmodus.
Ein Beispiel zur Bedienung des Programmes: Wir wollen das RAM unter dem KERNAL nach dem Text "64'er" durchsuchen. Dies entspricht der Bytefolge 54, 52, 39, 69, 82. Die Eingaben lauten für die Ausgabe auf Drucker also:
57344 65534 A 5 54 52 39 69 82 J J
Falls Sie auch den $C Bereich durchsuchen lassen, wird Ihnen auffallen, daß SUSAX grundsätzlich den Bereich ab 50023 als Fundstelle ausgibt. Dies liegt daran, daß der Searcher hier die Tabelle mit der zu suchenden Folge ablegt. Soviel gibt es zur Bedienung von SUSAX zu sagen. Und nun wollen wir uns endlich auf den Weg in Richtung Unsterblichkeit machen.
Zunächst noch einige Bemerkungen, wie Sie SUSAX auf Spiele anwenden können. Angenommen, Sie wollen in einem Spiel den Maschinensprachebefehl zum Setzen der Rahmenfarbe finden:
STA $D020
Gehen Sie dazu bitte wie folgt vor: Laden und starten Sie das Spiel. Eventuell wird jetzt noch etwas nachgeladen oder entpackt. Wenn das Titelbild erscheint, lösen Sie einen RESET aus. Ist der RESET blockiert oder mit einer "Selbstmordroutine" (die den gesamten Speicher löscht) verbunden, brauchen Sie ein Spezialbetriebssysten (neues KERNAL-ROM), mit dem dieser Schutz ("CBM80"-Kennung) umgangen werden kann. Wenn Sie wieder im Direktmodus sind, laden und starten Sie wie oben beschrieben SUSAX. Welchen Speicher müssen Sie nun durchsuchen? Wir gehen davon aus, daß das Spiel bei 2048 (BASIC-RAM) beginnt und irgendwo vor $A000 endet. Es wird also der Speicher von 2048 bis 40960 durchsucht. Als Bytezahl geben Sie hier 3 ein, die Bytes lauten 141, 32, 208.
Bevor Sie nach dem Verlassen des Spieles irgend etwas anderes machen, müssen Sie zuerst herausfinden, wie man es wieder startet. Hier hilft nur probieren. Versuchen Sie es zunächst mit Standard-SYS-Befehlen, wie
SYS 49152 SYS 32768 SYS 36864 SYS 51200 SYS 4096 SYS 8192 SYS 16384 SYS 10000 SYS 20000 SYS 30000 SYS 40000 SYS 50000 SYS 2061
und so weiter. Nach jedem SYS Befehl, der das Spiel nicht startet, sondern des Computer aussteigen läßt, sollten Sie den RESET-Taster drücken, da der SYS das Innenleben des C64 durcheinandergebracht haben könnte und der korrekte SYS-Befehl deswegen nicht lauffähig wäre.
Kommen Sie auf diese Weise nicht zum Ziel, kann es auch sein, daß noch ein BASIC-Start vorhanden ist. Laden Sie eine RENEW-Routine und starten Sie sie. Bringt der Befehl LIST etwas Sinnvolles, geben Sie auf gut Glück einfach mal RUN ein. Startet das Spiel jetzt, steigen Sie wieder aus, RENEWen abermals, LISTen das Spiel und schreiben sich den SYS-Befehl ab, der zum Starten dient. Überhaupt ist es bei dieser Arbeit wichtig, daß Sie sich alle Schritte auf Papier dokumentieren.
Falls Ausprobieren nicht hilft, laden Sie einen Monitor und betrachten den Speicherbereich ab $800 (2048), und zwar als Text (Befehl .T oder ähnlich). Je mehr Zeichen pro Zeile der Monitor darstellt, desto deutlicher sehen Sie, daß es im Speicher Bereiche gibt, die ganz offensichtlich keine lauffähigen Programme sind, sondern Datentabellen, Grafiken oder ähnliches. Diese erkennen Sie daran, daß bestimmte Muster in der Darstellung mit starker Regelmäßigkeit oder Systematik auftauchen (z.B. übermäßig viele Nullbytes oder $FF). Diese Bereiche wechseln ab mit solchen, in denen ganz chaotischer Buchstaben- und Zeichensalatz steht. Das sind dann meistens Maschinenprogramme. Haben Sie nun beispielsweise einen Grafikbereich aufgespürt, der bei 18423 endet, ab 18424 folgt der Zeichensalat, sollten Sie den Bereich ab 18424 mal disassemblieren. Tauchen vernünftige Befehle auf, können Sie ja mal einen SYS 18424 riskieren (oder eine Stelle anspringen, die Ihrer Meinung nach der Anfang eines Maschinenprogrammes sein könnte).
Sollte es Ihnen nicht gelingen, die Startadresse des Spieles zu ermitteln, müssen Sie leider aufgeben: Dann hat die ganze restliche Arbeit nicht mehr viel Sinn. Erfahrungsgemäß schafft man es aber eigentlich sehr oft, die Startadresse durch Probieren zu finden.
Der Weg zur Unsterblichkeit
Die folgende Kniffe werden anhand eines authentischen Spieles besprochen. Eines werden Sie, wenn Sie dann Ihre eigene Spielesammlung durcharbeiten, schnell merken: Es gibt kein Geheimrezept! Oft gehört auch eine kleine Portion Glück oder etwas Phantasie dazu, zum Ziel zu kommen. Sie müssen sich in die Lage des Programmierers versetzen, sich vorstellen, wie er das eine oder andere wohl programmiert haben könnte.
Wichtig ist, daß Sie das Spiel, das auf den "Operationstisch" soll, gut kennen. Spielen Sie es mehrmals gründlich durch und achten Sie auf alle Details, vor allem auf diejenigen, die bei Fehlern auftreten. Sie werden sich wundern, wie viele Details Sie später im Programmtext wiederfinden.
Was kann man tun, um unendlich viele Leben zu bekommen? Bei praktisch jedem Spiel gibt es mindestens eine Speicherzelle, die die momentane Anzahl der verbleibenden Leben enthält. Diese wird am Anfang des Spieles beispielsweise auf 3 gesetzt und dann bei jedem Fehler um eins heruntergezählt. Ist der Wert bei 0 angekommen, wird in die Game-Over Routine verzweigt. Unsere Aufgabe ist also, das Erniedrigen dieser Zelle zu verhindern. Wie kann man aber herausfinden, um welche der 65536 Speicherzellen es sich handelt und wo das Dekrementieren erfolgt?
Die erste Methode funktioniert im Allgemeinen bei Spielen, die im Klartext irgendwo auf dem Bildschirm die Anzahl der Leben anzeigen (Bsp.: "6 Lifes" oder "pilots left: 102"). Sehen wir uns einmal an, wie die (authentische) Routine, die das erledigt, aussehen könnte:
@li:1000 LDA 1541 ;Anzahl der Leben 1003 CLC ;Addition vorbereiten 1004 ADC #48 ;Umrechnen in Bildschirmcode 1006 STA 1988 ;irgendeine Speicherzelle auf dem Bildschirm
Die Speicherzelle 1541 enthält bei diesem Spiel also die Anzahl der verbleibenden Leben (eine Ziffer von 0 bis 9). Auch die folgende Routine zeigt die Anzahl der Leben an:
@li:1000 LDA 1541 ;Anzahl der Leben 1003 ORA #48 ;wirkt hier wie ADC #48 1005 STA 1988 ;irgendeine Speicherzelle auf dem Bildschirm
Die zweite Routine findet in der Praxis sogar öfters Anwendung. Jetzt wäre es natürlich schön, wenn wir diese Routine in einem 20 KByte langen Spiel finden könnten. Dann hätten wir auch die Speicherzelle, die die Anzahl der Leben enthält. In einem Spiel, an dem mehrere Spieler teilnehmen, können es übrigens auch mehrere Zellen sein, die Routine sähe dann etwa wie folgt aus:
@li:1000 LDX 143 ;Nummer des Spielers, der an der Reihe ist 1002 LDA 1541,X ;als Index in Tabelle mit Leben
1004 weiter wie oben
Doch wie findet man diese Routine?
Hier setzen wir SUSAX das erste Mal ein: Wir suchen nach einem typischen Kennzeichen einer Anzeigeroutine, nämlich den Befehlen mit dem Parameter 48 (ORA #48 und ADC #48). Nun könnte man das gesamte Spiel nach dem Byte 48 abzusuchen. Das wäre aber wenig erfolgversprechend, da SUSAX dann eine meterlange Liste mit Fundstellen ausdruckt. Das Byte 48 kommt ja nicht nur zur Umrechnung in den Bildschirmcode, sondern zum Beispiel auch als Parameter von anderen Befehlen (z.B. in STA 3120) und in Datentabellen vor. Wir suchen also besser nach der Codefolge 9, 48 (ORA #48) und nach 105, 48 (ADC #48). SUSAX meldet nun alle Speicherzellen, in denen ein solcher Befehl steht. Doch was, wenn mehr als eine Fundstelle gemeldet wird? Nun, dann funktioniert eben auch die Routine zur Anzeige des Scores oder der Spielernummer (PLAYER 1) nach diesem Prinzip. Doch welcher ADC #48 ist nun für die Anzahl der Leben zuständig? Dies läßt sich problemlos ermitteln. Der Trick ist ebenso einfach wie genial: Wir ersetzen ORA #48 beziehungsweise ADC #48 einfach durch einen entsprechenden Befehl, aber mit jeweils anderem Parameter, z.B. ORA #64. In diesem Fall wird dann die Anzahl der Leben nicht mehr "2 PILOTS LEFT", sodern "B PILOTS LEFT" angezeigt. Zur Unterscheidung der Routine für die Anzeige der Leben ersetzen bei den verschiedenen Befehlen die 48'er durch verschiedene andere Parameter, hier bieten sich folgende Werte an:
64 (Zahl wird als Buchstabe angezeigt) 49 (Ziffern werden um 1 erhöht angezeigt) 176 (Ziffern werden revers angezeigt) 0 (Ziffern werden als Kleinbuchstaben angezeigt) 33 (Ziffern werden als Sonderzeichen angezeigt)
Das Ersetzen geschieht am besten mit einem Monitor, notfalls tut's auch ein POKE-Befehl. Starten Sie das Spiel jetzt (wie das geht, haben Sie ja schon herausgefunden). Während des Spielablaufes werden Sie verschiedene Änderungen beobachten. Nehmen wir einmal an, SUSAX hat fünfmal den Befehl ORA #48 und ADC #48 gefunden. Sie haben die Parameter durch die oben angegebenen Beispielwerte ersetzt. Das Spiel zeigt nun direkt nach dem Start Folgendes an:
SCORE: 111111 MEN LEFT: a LEVEL: !
Dann können wir schon sagen, daß der zweite veränderte Befehl für die Punktezahl zuständig ist, der vierte für die Anzahl der Leben (aha!) und der fünfte für die Nummer des Levels. Sollte sich bei der Anzeige der Leben nichts geändert haben, hat diese Methode leider versagt: Die Anzeigeroutine enthält leider kein ADC #48.
Im Falle eines Erfolgserlebnisses sollten Sie grundsätzlich das Spiel neu laden (Sie haben sich die Adressen ja aufgeschrieben). Nun disassemblieren Sie sich den Bereich, in dem Sie den "schuldigen" Befehl gefunden haben. Dabei stoßen Sie dann auf eine Routine in der Art wie oben dargestellt. Nun versuchen Sie, herauszubekommen, in welcher Speicherzelle die Anzahl der Leben gespeichert wird.
Routineangelegenheiten
Auch bei den anderen, weiter unten vorgestellten Kniffen, kommen Sie an dieser Stelle heraus. Die weitere Behandlung ist nun immer die selbe: Herauszufinden, wo die Anzahl der Leben um eins vermindert wird und diesen Befehl zu "kastrieren".
Angenommen, es war tatsächlich die Speicherzelle 1541. Ihre nächste Aufgabe ist nun, diese Zahl in High- und Low-byte umzurechnen. Dazu gibt es die Formel:
HIGH = INT (ZAHL / 256) LOW = ZAHL - HIGH * 256
In diesem Falle ergibt das die Werte 5 (Low) und 6 (High). Wir suchen nun nach Befehlen, die diese Speicherzelle manipulieren, also 5/6 als Parameter haben. Dazu benutzen wir natürlich wieder SUSAX. Als Suchwerte werden die errechneten Bytes 5 und 6 angegeben. Der Sucher meldet uns nun die Stellen, an denen diese beiden Bytes vorkommen. Nun sollten Sie als erstes mit einem Monitor herausfiltern, wo 5/6 tatsächlich für die Zelle 1541 steht und wo es sich um Unsinn handelt. Dort, wo Sie Erfolg haben, disassemblieren Sie sich wieder die Umgebung der angegebenen Speicherzelle, und werden auf diese Weise eine oder mehrere Stellen finden, wo die Anzahl der Leben
- (vor Spielbeginn) auf einen bestimmten Wert (zum Beispiel 3) gesetzt wird,
- (bei Fehlern) um eins erniedrigt wird und
- auf Null getestet wird (gewöhnlich direkt nach dem Erniedrigen).
Unter Umständen finden Sie noch besondere Manipulationen, wie das Hochzählen (Bonusleben) oder ähnliches.
Nehmen wir einmal an, bei unserem Spiel steht ab 2000 die Fehlerroutine:
2000 JSR .... ;Raumschiff explodiert 2003 DEC 1541 ;aha! hier wird 1 Leben abgezogen 2006 LDA 1541 ;Test, ob Null Leben 2009 BEQ 2014 ;Wenn Null 2011 JMP yyyy ;Spiel fortsetzen 2014 JMP zzzz ;Game over
In diesem Fall müssen wir also den DEC-Befehl in 2003 unwirkam machen. Eine Möglichkeit: Das Überschreiben der drei Bytes 2003 bis 2005 mit NOPs (234). Einfacher und eleganter ist aber, wenn man den Code für DEC durch den Code für LDA ersetzt:
POKE 2003,173
Und damit haben Sie den Trainerpoke für dieses Spiel gefunden! Doch wie wenden Sie ihn nun an? Zunächst sollten Sie den POKE einmal testen. Schalten Sie den Computer aus und wieder an, dann laden Sie das Spiel. Starten Sie es und steigen Sie wieder aus. Dann geben Sie den POKE ein und starten das Spiel wieder. Falls es nun wirklich beliebig viele Leben gibt, haben Sie gewonnen. Sonst haben Sie irgendwo einen Fehler gemacht, oder das Spiel ist selbstmodifizierend. Solche Spiele kann man ohne sehr große Erfahrung nicht knacken.
Hat das funktioniert, lösen Sie einen RESET aus und laden das Spiel noch einmal. Diesmal geben Sie den POKE vor dem ersten Start ein und starten dann. Haben Sie auch dann unendlich viele Leben, ist Ihre Arbeit beendet. Sonst durchsuchen Sie das Programm entweder auf eine Kopierroutine, und rechnen sich aus, an welcher Stelle die Routine, die Ihnen ein Leben abzieht, vor dem Start des Spieles steht, und passen den POKE entsprechend an, oder sind mit dem bisher Errungenen zufrieden.
Leben in der Zeropage
Es kann auch passieren, daß sich herausstellt, daß das Spiel eine Zeropagespeicherzelle verwendet, um die Anzahl der Leben zu speichern. Dann entfällt natürlich die Umrechnung in High/Low, Sie suchen direkt nach einer Speicherzelle. Haben Sie dann den DEC-Befehl gefunden, im Fall einer Zeropage-Adresse etwa
2003 DEC 253
darf er nicht mit POKE 2003,173 zerstört werden, denn es muß anstelle des absoluten LDAs nun ein LDA für die Zeropage verwendet werden: POKE 2003,165. Obwohl es mir persönlich unverständlich ist: Die meisten Spiele verwenden keine Zeropageadressen, um sich die Anzahl der Leben zu merken, sondern Adressen, die irgendwo im Programmtext stehen.
...dann muß der Filzstift ran
Falls Sie die Routine, die die Anzahl der Leben anzeigt, durch Suchen nach ADC/ORA #48 nicht gefunden haben, können Sie auch die sogenannte "Holzhackermethode" anwenden. Laden und starten Sie das Spiel wie gewohnt. Wenn es losgeht, markieren Sie sich ganz leicht mit einem feinen Filzstift auf dem Bildschirm (auf der Mattscheibe!) die Stelle, an der die Zahl der Leben ausgegeben wird. Nun unterbrechen Sie das Spiel (Computer aus und wieder an - der Fleck auf dem Bildschirm bleibt) und zählen mit dem Cursor, in welcher Spalte und Zeile auf dem Schirm der Fleck sich befindet. Dann führen Sie folgende Rechnung durch:
ADRESSE = 1024 + SPALTE + 40 * ZEILE
Sie erhalten eine Zahl von 1024 bis 2023. Geben Sie zur Probe ein:
POKE ADRESSE,1 : POKE ADRESSE+54272,1
Hinter dem Fleck sollte nun ein weißes 'A' erscheinen. Stimmt das, lassen Sie sich durch
PRINT ADRESSE
den errechneten Wert ausgeben. Dies ist also die Speicherzelle des Bildschirmspeichers, in die die Ausgaberoutine die Leben unterbringen muß. Sie untersuchen nun also den Speicher nach dieser Adresse, die Sie dazu vorher wieder ins Low/Highbyteformat umrechnen. Vorher sollten Sie allerdingt mit leicht angefeuchtetem Finger die Mattscheibe wieder reinigen. Schäden sind dabei nicht zu befürchten.
Wenn SUSAX tatsächlich etwas findet, untersuchen Sie die Stelle, ob es sich tatsächlich um die Ausgabe der Leben handeln könnte. Wenn ja, machen Sie wie oben beschrieben weiter (interessehalber sollten Sie sich auch ansehen, wie das Spiel ohne ADC #48 oder ähnliches die Anzahl der Leben ausgibt). Sonst kann es sein, daß der Bildschirm im Spiel nicht in Bank 0, sondern in einer anderen Bank des VICs liegt. Um das herauszufinden, addieren Sie zu ADRESSE die Werte 16384, 32768, 49152 (die Startadressen der Banks) und lassen dann nach diesen Werten suchen. Diese Methode führt sehr oft zum Erfolg, versagt aber natürlich bei hochauflösender Grafik und bei Spielen, die die Anzahl der Leben nicht einstellig dezimal ausgeben, sondern beispielsweise mit Zehnerstelle.
Verräterische Zusammenstöße
Eine andere Methode führt vor allem bei Spielen zum Erfolg, bei denen ein Sprite nicht mit einem anderen Sprite oder bestimmten Zeichen zusammenstoßen, also kollidieren darf. Ich habe auf diese Weise beispielsweise "H.E.R.O." geknackt.
Der Videochip enthält bekanntlich zwei Register, die zur Abfrage verwendet werden können, ob Kollisionen eines Sprites mit einem anderen Sprite oder Hintergrund stattgefunden haben. Diese Register haben die Adressen 53278 (Sprite-Sprite) und 53279 (Sprite-Hintergrund). Sehen wir doch mal nach, ob das Spiel diese Zellen bemüht, um Kollisionen zu erkennen! Dazu werden die beiden Register in das Low/Highbyteformat umgerechnet. Das ergibt 30/208 bzw. 31/208, suchen Sie also mit SUSAX nach diesen Bytepaaren. Wird etwas gefunden, untersuchen Sie wieder die nährere Umgebung der Fundstelle, ob es sich wirklich um eine Maschinenspracheabfrage der Register handelt. Wenn ja, wird es kompliziert. Sie dürfen ja nicht alle Kollisionen aus dem Spiel entfernen, da es sonst auch nicht mehr auf die Berührung mit irgendwelchen Schatztruhen reagieren würde. Sie werden in unmittelbarer Umgebung der Befehle, die die Register auslesen, auch Maskierbefehle der Form AND# finden. Diese dienen dazu, die Bits, die den zu testenden Sprites entsprechen, zu isolieren (zu "maskieren") und zu testen. Hier hilft wieder nur probieren: Setzen Sie ein AND-Argument nach dem anderen auf Null, starten Sie das Spiel und sehen Sie, ob es nun auf bestimmte Kollisionen nicht mehr reagiert. Wenn Sie bei einem AND Befehl Erfolg haben, sehen Sie sich an, wohin das Programm springt, wenn die relevanten Bits gesetzt sind. Dort treffen Sie, wenn alles glattgeht, auch wieder eine Routine wie oben beschrieben an, die die Anzahl der Leben vermindert. Hier können Sie dann eingreifen.
Eine (erdachte) Kollisionsroutine könnte beispielsweise so aussehen:
4000 LDA 53278 ;Kollisionsregister 4003 STA 181 ;merken 4005 AND #3 ;Bits 1 (Spieler) und 2 (Gegner) isolieren 4007 CMP #3 ;beide 1? 4009 BNE 4014 ;nein, weiter 4011 JMP ..... ;Fehler: Zusammenstoß mit Gegner 4014 LDA 181 ;Kollisionsreg., Sicherheitskopie 4016 AND #5 ;Bits 1 (Spieler) und 3 (Schatz) isolieren 4018 CMP #5 ;beide gesetzt? 4020 BEQ 4025 ;ja, Schatz gefunden 4022 JMP ..... ;Spiel fortsetzen 4025 JMP ..... ;auf Schatz reagieren
Sämtliche bisher vorgestellte Methoden werden allerdings dann versagen, wenn das Spiel insgesamt nur ein Leben bereitstellt. Der Spieler hat zum Beispiel zu Beginn 100% Energie. Wenn diese bei Null angekommen ist, ist das Spiel sofort beendet. Derartige Programme zu knacken erfordert bereits ein sehr großes Fachwissen, das in einem Kurs sicher nicht vermittelt werden kann. Es ergibt sich aber mit der Zeit, wenn Sie öfters Spiele "in die Mangel" nahmen.
In dem Falle mit 100% Energie zu Beginn könnten Sie sich unter Umständen noch damit behelfen, daß Sie die Routine suchen, die zu Beginn des Spieles den Energiespeicher auf 100 setzt, indem Sie die Zahl 100 suchen. Dazu gehen Sie am besten wie weiter unten (wie erreicht man das X. Level) beschrieben vor. Sie müssen dann den Befehl "vernichten", der beispielsweise bei Kollisionen etwas von der Energie abzieht.
Eine Bemerkung zwischendurch noch: Hier ist immer von "dem Befehl", der die Anzahl der Leben oder die Energie erniedrigt die Rede. Es können aber durchaus mehrere (meistens natürlich identische) Befehle an ganz verschiedenen Stellen im Speicher dafür sorgen, daß unser Held nicht zu alt wird: Ein Befehl zieht ein Leben ab, wenn eine Uhr bei Null angekommen ist, ein anderer vielleicht, wenn die Spielfigur ins Wasser stürzt. Wenn Sie aber erst einmal die Speicherzelle, die die Anzahl der Leben enthält, gefunden haben, ist es auch nicht mehr problematisch, alle Befehle zu finden, die diese Zelle manipulieren.
Rasterfahndung
Wenn Ihnen keine der oben beschriebenen Methoden zum Erfolg verhilft, kommen Sie vielleicht mit dem folgenden Kniff weiter: Die meisten Spiele geben, wenn der Spieler einen Fehler macht, ganz typische "Signale" von sich, beispielsweise flimmert der Bildrahmen, oder ein besonderer Soundeffekt ertönt. Hier lohnt es sich, im Speicher nach der Routine zu suchen, die den Effekt erzeugt. Da er meistens durch Verändern besonderer Speicherzellen (meistens Register der I/O Chips, beim Flimmern beispielsweise 53280) erreicht wird, lohnt es sich, nach diesen zu suchen.
Beim schon etwas älteren Spiel "Alligata Blagger" beispielsweise färbt sich der Rahmen in Falle eines Fehlers rot. Dieses Spiel ließ sich knacken, indem nach Befehlen gesucht wurde, die die Speicherzelle 53280 auf 2 (rot) setzen. Die Routine könnte so aussehen:
4000 LDA #2 ;rot 4002 STA 53280 ;Rahmenfarbe 4005 JSR ..... ;Routine, die auf den Feuerknopf wartet 4008 LDA #6 ;Rahmen wieder blau 4010 STA 53280 ;setzen 4013 DEC 1541 ;ein Leben weg 4016 BMI 4021 ;bei Unterlauf (Null) 4018 JMP ..... ;sonst Spiel fortsetzen 4021 JMP ..... ;bei Unterlauf Game Over
Probieren wir bei einem solchen Spiel einmal, mit SUSAX nach dem Befehl STA 53280 (141, 32, 208) zu suchen. Nehmen wir einmal an, der Searcher gibt fünf Fundstellen aus, darunter die korrekten Adressen 4002 und 4010. Diassemblieren Sie wieder die nähere Umgebung der Fundstellen und stellen Sie zunächst fest, wo SUSAX überhaupt auf einen "echten" STA 53280 Befehl gestoßen ist, und wo die drei Bytes nur zufälligerweise Teil einer Datentabelle waren. Jetzt bleiben vielleicht noch drei Adressen übrig, nehmen wir einmal an, 1584, 4002 und 4010. Per Disassembler erkennen Sie nun, daß die Zelle bei 4010 (wie oben gesehen) und bei 1584 mit 6 gefüllt wird, während sie bei 4002 mit 2 beschrieben wird. Es gibt also offenbar in der Region von 1584 eine Routine, die den Rahmen auf blau setzt. Und wann, außer nach einem Fehler, braucht ein Spiel eine solche Routine? Richtig, vor Spielbeginn. Die Fehlerroutine muß also in der Gegend von 4002 liegen. Sie disassemblieren sich also diese Speicherregion, stoßen auf den DEC-Befehl in 4013 - und haben einen Ansatzpunkt für einen Trainerpoke gefunden:
POKE 4013,173
Dadurch wird der 3-Byte DEC Befehl zu einem LDA Befehl, der uns nicht mehr stört. Jetzt probieren Sie aus, ob es nun unendlich viele Leben gibt, oder ob der DEC Befehl etwas anderes dekrementiert hat.
Diese Methode funktioniert natürlich am besten bei Spielen, die nicht besonders oft die Rahmenfarbe verändern, da SUSAX sonst eine lange Liste mit STA 53280 Befehlen ausgibt. Und wer hat schon Lust, herauszufinden, an welcher von 25 Stellen die Rahmenfarbe im Falle eines Fehlers auf rot gesetzt wird?
Es muß natürlich nicht die Rahmenfarbe sein. Wenn etwa bei einem Fehler die Spielfigur blinkt, suchen wir eben nach Stellen, an denen das Sprite-Select-Register 53269 (Register 21) periodisch verändert wird. Flimmert unser Held, wenn er einen Fehler macht, suchen wir nach Stellen, an denen eines der acht Sprite-Farbregister laufend erhöht (INC) oder erniedrigt (DEC) wird. Sie merken schon: Diese Technik eignet sich vor allem für grafische Effekte!
Der Trick mit dem Totenkopf
Einen besonderen grafischen Effekt, der Ihnen beim "Zerlegen" des Spieles weiterhelfen kann, gibt es praktisch in jedem Spiel: Das Aussehen des Figur wird, wenn sie in eine Falle tappt, verändert. Beispielsweise verwandelt sich die Spielfigur für kurze Zeit in einen Totenkopf, oder Ihr Raumgleiter explodiert.
Die meisten dieser Figuren sind Sprites. Bekanntlich existiert für jedes Sprite ein Zeiger, der auf die Aussehensdaten des Objektes zeigt. Dieser Zeiger muß aber, wenn sich die Figur etwa in einen Totenkopf verwandeln soll, für die Zeit, die der Schädel zu sehen sein soll, auf die Stelle im Speicher zeigen, an der die entsprechenden Spritedaten stehen.
Um herauszufinden, wo im Speicher der Totenkopf steht, benötigen Sie einen sogenannten "Sprite-Finder", der nach dem Ausstieg aus dem Spiel geladen und gestartet wird und dann den Speicher auf Spritedaten durchsucht. Haben Sie auf diese Weise den Totenkopf gefunden, schreiben Sie sich die Adresse auf, an der die Spritedaten liegen (diese zeigt der Finder an) und teilen sie durch 64. Jetzt bekommen Sie einen Wert zwischen Null (theoretisch) und 1023 (1024 = 256 mal 4, der VIC hat ja 4 Banks, die je maximal 256 Spritedaten fassen). Liegt der Wert über 255, geben Sie ein:
PRINT WERT AND 255
Für WERT setzen Sie das Ergebnis der Division durch 64 ein. Dadurch wird das Highbyte der Zahl auf Null gesetzt. Jetzt erhalten Sie einen Wert zwischen Null und 255, der in das Spriteregister geschreiben werden muß. Dies kann beispielsweise eine Routine folgenden Aussehens erledigen:
5300 LDA #56 ;Sprite steht ab 52736 (Bank 3) im Speicher 5302 STA 51192 ;Spritepointer in Bank 3 5305 JSR ..... ;Toneffekt 5308 DEC 1541 ;ein Leben abziehen 5311 weiter wie oben
Wundern Sie sich nicht über die seltsame Adresse 51192, sie ergibt sich aus 2040 (relative Adresse des Spritepointers in einer Bank) + 49152 (Startadresse der Bank 3, die dieses Spiel offenbar benutzt). Übrigens muß der Pointer gar nicht ab der Adresse 2040 der Bank liegen, nämlich dann nicht, wenn der Bildschirmspeicher verschoben ist.
Nun müssen wir diese Routine wieder im Spiel finden. Das typische Kennzeichen, nach dem wir diesmal suchen, ist natürlich der Wert des neuen Spritepointers, in diesem Fall 56. Wir haben ja gelernt, daß man möglichst nicht große Speicherbereiche (wie ein Spiel) nach einem einzelnen Byte absuchen soll, also suchen wir nach LDA #56 und zur Sicherheit auch nach LDX #56 und LDY #56. Jede Fundstelle wird wieder disassembliert, und die Adressen aller Routinen, die in Frage kommen, aufgeschrieben. Falls es mehrere Routinen gibt, die die gesuchte sein könnten, wenden wir wieder den Trick an, den wir schon weiter oben kennengelernt haben (diesmal eine Schritt für Schritt Anleitung):
- ersetzen Sie die 56 im ersten gefundenen LDA #56 (bzw. LDX oder LDY) durch eine 0 (Monitor oder Poke)
- starten Sie das Spiel wie oben ermittelt neu
- wenn nun bei einem Fehler immer noch der Totenkopf erscheint, machen Sie mit dem ersten Schritt weiter, ersetzen diesmal aber das nächste LDA #56.
- Mit etwas Glück wird dann bei irgend einem Versuch anstelle des Schädels grafischer Unsinn erscheinen. Dann haben Sie es geschafft: Sie haben die Routine gefunden.
Nehmen Sie sie nun genau unter die Lupe, untersuchen alle Befehle, die vielleicht dazu dienen könnten, ein Leben abzuziehen. Dann geht es weiter wie gehabt: Trainerpoke erstellen, ausprobieren, aufschreiben, fertig.
Der Vorstoß in neue Level
Jetzt wollen wir uns einer anderen Tätigkeit zuwenden. Vielleicht haben Sie es ja geschafft, den Spieler in Ihrem Lieblingsspiel mit unendlich vielen Leben zu versorgen. Aber trotzdem scheitern Sie immer wieder im 5. Level, es ist einfach zu schwer. Warum kann man bei diesem Spiel nicht einstellen, in welchem Level man starten will?
Wir sind ja inzwischen fortgeschritten auf dem Gebiet des Spieleknackens, also sollte es uns nicht schwerfallen, eine solche Funktion selbst einzubauen! Auch hierzu gibt es wieder mehrere Methoden. Beispielsweise können Sie die "Ausgabemethode" anwenden: Sie versuchen ähnlich wie oben, die Routine zu finden, die auf dem Bildschirm die Nummer des aktuellen Levels ausgibt, und finden auf diese Weise die Speicherzelle, die die Levelnummer enthält. Nun suchen Sie wieder per SUSAX alle Stellen, an denen diese Zelle verändert wird. Dabei werden Sie mit etwas Glück auch auf die Initialisierung stoßen, bei der am Anfang des Spiels diese Zelle auf eins (man startet im ersten Level) gesetzt wird. Beispielsweise könnte dies so aussehen:
1500 lda #1 ;Level eins 1502 sta 154 ;enthält die Nummer des aktuellen Levels 1504 lda #4 ;vier Leben 1506 sta 1541 ;auch diese Zelle (siehe oben) definieren
In diesem Beispiel wird also in der Zelle 154 die Nummer gespeichert. Wollen Sie nun gern beispielsweise mit Level 6 beginnen, manipulieren Sie einfach den LDA# Befehl in 1500:
POKE 1501,6
Wenn Sie bis dahin alles richtig gemacht haben, können Sie nun zum ersten Mal das sechste Level besuchen und das schwierige fünfte Level vergessen. Hoffen wir, daß es es im 6. Level leichter wird...
Was aber tun Sie, wenn die Levelnummer gar nicht angezeigt wird? Dann gibt es noch einen anderen Trick: Nehmen wir einmal an, das Spiel hat 168 Level (so was gibt's!). Wie könnte die Routine aussehen, die das prüft?
1400 inc 154 ;ins nächste Level 1402 lda 154 ;welches nun? 1404 cmp #169 ;schon letztes Level vorbei? 1406 bcs 1411 ;wenn größer oder gleich 85, Ende 1408 jmp .... ;ansonsten Spiel im neuen Level fortsetzen 1411 jmp .... ;Spiel mit Glamour bestanden!
Sie werden sich schon denken, worauf es hinauslaufen wird. Das Problem ist ja immer, daß wir wissen müssen, in welcher Speicherzelle (in diesem Fall 154) das Spiel die Levelnummer speichert. Dann können wir den Speicher gezielt nach dieser Zelle absuchen, wo sie manipuliert wird, und die entsprechende Routine wie oben beschrieben modifizieren.
Eine Routine, die die Speicherzelle 154 verändert, ist die oben gelistete. Es würde also auch genügen, wenn wir diese finden würden. Wir müssen also wieder ein typisches Kennzeichen dieser Routine aufspüren. In diesem Fall ist es der CMP #169 Befehl: Er kommt im gesamten Spiel sicher nicht so oft vor.
Sie versuchen also, herauszufinden, wieviele Level das Spiel enthält (manchmal genügt da ein Blick in die Anleitung), und suchen per SUSAX nach dieser Zahl. Wie Sie oben gesehen haben, kann es auch nötig sein, nach der um 1 erhöhten Maximal-Levelzahl zu suchen, wenn der Programmierer des Spieles das so vorgesehen hat. Hier hilft, wie so oft, nur Probieren.
Sie suchen also nach nur einem Byte, in diesem Fall 168 beziehungsweise 169. Leider ist beim Suchen nach einem Byte die Wahrscheinlichkeit sehr hoch, daß SUSAX auch völlig unsinnige Stellen listet, besonders in diesem Fall, da 169 ja auch der Code für LDA# ist. Falls Sie keine Lust haben, mit einem Monitor alle angegebenen Stellen abzusuchen, ob die 168/169 tatsächlich das Argument eines CMP# Befehles ist, können Sie das auch SUSAX überlassen. Sie suchen jetzt nach den Bytefolgen 201,168 und 201,169. Jetzt werden es sicher schon weniger Fundstellen sein, allerdings kann es passieren, daß der relevante Befehl übersehen wird, beispielsweise, wenn die Maximalzahl nicht mit CMP# getestet wird, sondern mit CPY#.
Sie können nun natürlich nach allen Möglichkeiten suchen, einen unmittelbaren Vergleich mit 168 bzw. 169 vorzunehmen. Dies ist aber sehr umständlich, zumal der Test, ob das Spielende erreicht ist, ja auch auf ganz andere Weise durchgeführt werden kann. Daher eignet sich diese Methode eigentlich nur bei Spielen, die als Maximalzahl an Levels eine Zahl haben, die in einem Maschinenspracheprogramm relativ selten vorkommt, beispielsweise 123 oder 83.
Fazit
Sie haben nun einen ganzen Packen Möglichkeiten kennengelernt, wie man ein Spiel vereinfachen kann. Es gäbe natürlich noch einiges zu sagen, man könnte ein ganzes Heft mit diesem Thema füllen. Da wäre das Entfernen von Zeitlimits, von gegnerischen Sprites oder Autostarts zu nennen, das Entfernen der Kollisionen, "Hintertürchen" der Programmierer (Schummelmodus) zu finden und ähnliches. Aber wir wollen Sie mit diesem Thema auch nicht überstrapazieren. Wie schon gesagt gibt es kein Geheimrezept, um ein Spiel zu knacken. Diese sehr handwerkliche Arbeit ist zu einem sehr großen Teil Übungssache.
Leider muß gesagt werden, daß das "Tunen" von Spielen vor allem bei Raubkopien sehr effektiv ist. Spiele, die man gekauft hat, sind oft so geschützt, daß man sie entweder gar nicht verlassen oder nicht mehr starten kann. Außerdem laden sie nach, was die Arbeit nicht unwesentlich erschwert, während Raubkopien gewöhnlich zu einem File zusammengepackt sind.
Es bleibt noch, Ihnen viel Erfolg bei dieser Beschäftigung zu wünschen. Wenn Sie bei Ihrem Lieblingsspiel das erste Mal das Spielende sehen, werden Sie mit einem Lächeln an die Schwierigkeiten zurückdenken, die Sie hatten, als Sie das erste Mal ein Spiel auseinandernahmen.
(Nikolaus Heusler)
Eine Tabelle mit POKEs, die der Autor auf die beschriebene Weise herausgefunden hatSpiel POKE Skramble 8609,173 House of Usher 2735, 10 Hunch Back I 5440,100 Styx 2835,173 Squish'em 8310,165 Aztek Challenge 2521, 6 Kid Grid 9970,173 Dino Eggs 6261,165 Save New York 6904,180 H.e.r.o.Mauern 15924,100 H.e.r.o.Figuren 18623,255 H.e.r.o.Leben 13856, 0 und 19131, 0 Cyclons 5616,173 O'Riley's Mine 3633,173 und 3544,173 Kongo Bongo 3443,181 und 3456,181 Hard Hat Mack 16877,173 Donkey Kong 30624,173 Drelbs 14087,173 Spy's Demise 7485,165 Crystal Castles 5643,255
Listing. SUSAX hilft beim Aufspüren von Programmteilen (bitte mit dem MSE eingeben)