Checksum FAQ
-----------------

Der Begriff CHECKSUM setzt sich zusammen aus SUMmation CHECK und
bedeutet übersetzt Scheck- oder Prüfsumme. Da man durch Prüfsummen-
verfahren feststellen kann, ob Daten modifiziert wurden, findet diese
Kennzahl in vielen Bereichen Verwendung: Anti-Viren-Software setzt
genauso auf diese Technik, wie Kompressionsprogramme,
Kopierschutzmechanismen usw.

Die Standard-Prüfsumme ist einfach die Quersumme aller Bytes einer Datei.
Dieses Verfahren ist aber nicht besonders sicher, wenn man bedenkt,
dass die Schecksumme nach einer ByteFlip Operation unverändert bleibt.

Mehr Schutz bieten da schon die verschiedenen CRC-Algorithmen (Cyclic
Redundancy Code)
, deren Prüfsumme das Ergebnis einer Division von Daten
durch ein Generatorpolynom ist.

Neben den genannten existieren natürlich noch unzählige weitere Algorithmen,
aber da dieses FAQ nicht erklären soll, wie man Prüfsummen knackt, sondern
vielmehr, wie man diese umgeht, spielt der Checksum-Typ sowieso keine Rolle.

Kommen wir nach dem theoretischen Gelabere endlich zur Praxis.

Wer kennt das nicht? Da will man sich mal eben ein paar Tausend Mark
(meinetwegen auch €uro) dazu schummeln, indem man den Spielstand mittels
Hex-Editor manipuliert, doch was ist nun los? - Der modifizierte Spielstand wird
erbarmungslos abgewiesen; selbst nach einer geringfügigen 8-Bit-Änderung.

Folgendes spielt sich ab: Das Spiel bildet beim Speichern der Daten eine
Prüfsumme, welche direkt im Spielstand abgelegt wird. Wenn man nun die Datei
modifiziert, stimmt beim Laden die errechnete Prüfsumme nicht mehr mit der
abgelegten überein.

Was man dagegen machen kann, wird nun anhand zweier Spiele beschrieben.


"Final Fantasy VII Deutsch SCES00869"

Versucht man, einen manipulierten Spielstand zu laden,
erscheint folgende Fehlermeldung:
"Datei ist beschädigt."
Von wegen beschädigt, das stinkt geradezu nach Checksum...

Aber was tun? Wir wissen nicht, wo die Prüfsumme abgelegt wird, geschweige
denn, wie der Algorithmus arbeitet. Doch das brauchen wir auch gar nicht...

Als erstes hacken wir mit XLink den Menü-Modifizierer.
Dazu springen wir zwischen den Menüs (Hauptmenü, "Steckplatz auswählen" &
"Datei auswählen") hin u. her, und verwenden dabei die Suchoperationen GLEICH
bzw. UNGLEICH, bis wir den richtigen Code erhalten:

Menü-Modifizierer
$801E2D8C 00??

00 - Steckplatz auswählen
01 - Datei auswählen
02 - Suche nach Datei... (1)
03 - Suche nach Datei... (2)
04 - Ladevorgang. Karte nicht entfernen.
05 - ???
06 - Karte formatieren
07 - Hauptmenü

Will man einen Spielstand laden, erscheint zunächst: "Ladevorgang. Karte nicht entfernen."
An diesem Punkt wird die Prüfsumme errechnet u. verglichen. Es erfolgt eine Auswahl:
FALLS Prüfsummen übereinstimmen,
 DANN lade Spiel,
SONST Ausgabe d. Fehlermeldung u.
      Sprung zum Menü "Datei auswählen".
Um rauszukriegen, wo die Auswahl im Speicher erfolgt, benutzen wir den Breakpoint Editor von XLinkWIN.
Wir setzen einen BREAK ON WRITE (Schreibzugriff) auf die Menü-Modifizierer-Adresse ($801E2D8C)
u. aktivieren den BP, sobald die Meldung "Ladevorgang. Karte nicht entfernen." erscheint;
wenn der Breakpoint anspricht, JUMP RA.

Der Disassembler öffnet sich bei $801D4A10.
Schauen wir uns die unmittelbare Umgebung an:
[1]  801D4A10  8E030000  lw        $v1,0000($s0)	; Breakpoint
[2]  801D4A14  3042FFFF  andi      $v0,$v0,FFFFFFFF
[3]  801D4A18  10620009  beq       $v1,$v0,801D4A40	; Auswahl
[4]  801D4A1C  34020001  ori       $v0,$zero,0001
[5]  801D4A20  3C01801E  lui       $at,801E
[6]  801D4A24  AC222D8C  sw        $v0,2D8C($at)
[7]  801D4A28  0C074AEE  jal       801D2BB8
[8]  ...
Sofort sticht die Branch on Equal-Anweisung [3] ins Auge. Der Befehl vergleicht
den Inhalt zweier Register u. verzweigt, falls beide den gleichen Wert aufweisen.
Eventuell handelt es sich hierbei um die gesuchte Auswahl, denn es ist sehr gut
möglich, dass die Register die jeweiligen Prüfsummen enthalten.

Wir verwenden einen immerverzweigenden BRANCH und schauen, was passiert:
[3]  801D4A18  10000009  beq       $zero,$zero,801D4A40	; verzweige, wenn 0 = 0
Bingo!! Ohne Probleme akzeptiert das Spiel selbst manipulierte Spielstände.

Der fertige XploderCode sieht so aus:

//-----

"Final Fantasy VII Deutsch SCES00869"

Saved Game Checksum Bypass
$701D4A1A 1062
$801D4A1A 1000

//-----

Wer noch wissen will, wo die Prüfsumme im Spielstand abgelegt wird, macht bei
[3] einen BREAK ON EXECUTE, notiert sich den Wert aus Register $v1 und sucht
nach diesem in der Spielstandsdatei. Ergebnis: 0x236


"Silent Hill PAL SLES01514"

Bei SH werden alle Spielsituationen in nur einer Datei gespeichert.
Um das zu realisieren, unterteilt sich der Spielstand in mehrere "Sektoren".
Nach kurzem Herumexperimentieren stellen wir fest:
Diesmal gehen wir komplett anders vor u. finden heraus, wo die Prüfsummen abgelegt werden.

Zunächst brauchen wir einen nicht-modifizierten Spielstand, von dem wir eine Kopie auf
der Festplatte ablegen. Anschließend laden wir das SaveGame von der Memory Card,
nehmen geringfügige Änderungen vor (Spielzeit ist ausreichend), speichern ab u. machen
einen Byte-für-Byte Vergleich mit der Kopie von der Festplatte.

Dabei kommen rund ein Dutzend Unterschiede heraus (u.a. Checksum).
Wir notieren diese Werte + einige Bytes aus der jeweiligen Umgebung,
sodass wir mehrere Byteketten erhalten.

Bsp.: 40 01 97 97 DC DC

Nun erzeugen wir einen ungültigen Spielstand, indem wir z.B. den
Wert für die Munition auf [FF] setzen. Beim Laden erscheint:
"The data is damaged!"
An dieser Stelle durchsuchen wir den Speicher, Bytekette für Bytekette (siehe oben).
Ist die Suche erfolgreich, machen wir dort einen BREAK ON READ (Maske: 0FFF FFFF)
u. versuchen erneut den Spielstand zu laden. Wenn der Breakpoint anspricht, JUMP RA.

Diese Prozedur wiederholen wir solange, bis wir irgendwann zur "richtigen" Routine gelangen:
 [1]  8002F670  92230000  lbu       $v1,0000($s1)	; Breakpoint
 [2]  8002F674  304200FF  andi      $v0,$v0,00FF
 [3]  8002F678  14620006  bne       $v1,$v0,8002F694	; Auswahl
 [4]  8002F67C  02001021  addu      $v0,$s0,$zero
 [5]  8002F680  96220002  lhu       $v0,0002($s1)
 [6]  8002F684  00000000  nop
 [7]  8002F688  3842DCDC  xori      $v0,$v0,FFFFDCDC
 [8]  8002F68C  2C500001  sltiu     $s0,$v0,0001
 [9]  8002F690  02001021  addu      $v0,$s0,$zero
[10]  8002F694  8FBF0018  lw        $ra,0018($sp)
[11]  8002F698  8FB10014  lw        $s1,0014($sp)
[12]  8002F69C  8FB00010  lw        $s0,0010($sp)
[13]  8002F6A0  03E00008  jr        $ra
Die Branch on Not Equal-Anweisung [3] erzwingt die o.g. Fehlermeldung, falls die
Prüfsummen nicht übereinstimmen. Ein NOP (keine Operation) bewirkt hier Wunder:

//-----

"Silent Hill PAL SLES01514"

Saved Game Checksum Bypass
$7002F678 0006
$8002F67A 2400

//-----


*** Ich widme dieses Dokument meiner Katze Max, gest. im Januar 2002. ***

Dieses FAQ wurde geschrieben von misfire