7
Hallo zusammen,
bei meinem neuesten Projekt hatte ich eine spannende Reise in die Vergangenheit und wollte euch nun, nachdem ich es geschafft habe, darauf mitnehmen.
Es kann hier und da ein bisschen trocken werden aber das liegt in der Natur der Sache
Worum geht es eigentlich
Nun, viele von euch dürften ja das hervorragende "Monkey Island 2 Ultimate Talkie" Projekt von u.A. @Andi kennen. Er schrieb mich eines Tages in Discord an und fragte mich, ob ich da nicht so ein paar Dinge für ihn bei dem Projekt "beheben" könnte die schon lange auf seiner Agenda stehen. Unter anderem:
In der DOS-Version:
Akt 1: Die Fühler ausstrecken
Da die ganze Sache mit Disassembling noch ziemlich neu für mich ist, musste ich mich an die Tools und das Prozedere erstmal gewöhnen und Informationen sowie Tools sammeln.
Also einfach erstmal die "MONKEY2.EXE" in Ghidra (ein ziemlich guter OpenSource Disassembler von der NSA) geworfen - Ernüchterung.
Nur eine Funktion, ganz viel Daten - die Datei ist gepackt! Durch Zufall stieß ich auf ein Programm namens "SOURCER" welches einen generischen Unpacker mitbringt und die Datei entpacken konnte, sodass ich danach die echte EXE-Datei erhielt.
Ganz viel Code, ganz viele Funktionen und die gesuchten unübersetzten Texte. Punkt 1 kann man dann schon mal erledigen:
Danach machte ich mich auf die Suche nach Punkt 2.
Akt 2 - Die Suche nach Big Whoop ähh. der Stelle wo der Textmodus gesetzt wird
Der Punkt sollte sich als sehr schwierig herausstellen - ich wusste nämlich nicht wo ich anfangen soll. Erstmal zur Theorie.
So, wo fange ich an. Schon seit je her arbeitete ich mit Cheat Engine für diverse Dinge und dachte, versuch ich es einfach mal. Also habe ich DosBox-X (wegen integriertem Debugger) angeworfen und einfach mal den Speicher der DosBox nach Veränderungen durchsucht.
Was kann man in dem Video sehen?
Akt 3: Debuggen per DosBox
Nun ging es darum die Stelle im Code zu finden die dafür verantwortlich ist, den Text-Modus auf "Nur Sprache" zu setzen. Hierfür nutze ich den Debugger von DosBox-X und steppe so lange durch den Code bis der andere Debugger (von Cheat Engine) den Breakpoint auslöst.
Wie der Debugger aussieht, seht ihr hier:
Ich hatte nun die Stelle gefunden, der Code sieht wie folgt aus:
CALLF FUN_28e0_0002 -> Initialisiert den SoundBlaster (ich konnte die Werte 220, 7 und 1 erkennen was die Adresse des SoundBlasters in DosBox ist)
ADD SP,0x2 -> Unwichtig
OR AX,AX -> Unwichtig, aber: AX enthält den Wert 0 wenn der SoundBlaster erfolgreich initialisiert wird
JNZ LAB_26a4_031f -> Wenn AX den Wert > 0 enthält, springt er weg (dann war irgendwas kaputt)
MOV word ptr [0x23f2],0x1 -> Unwichtig
MOV [0x414c],AX -> Bingo - hier wird 0 in den Speicher geschrieben
So nun muss man das ganze noch irgendwie patchen.
Akt 4 - DOS und seine Tücken
Der schwierigste Part überhaupt ist das Speichermodell von DOS. Bei Windows haben wir durchgängig einen riesigen Block Speicher, wir können überall hinspringen, alles tutti. In Zeiten von 16GB+ Ram auch kein Problem.
Aber damals musste man sich was überlegen (wie ihr ja bestimmt alle wisst) da ein Programm häufig nicht ganz in den Speicher passt. Also gibt es unter DOS "Segmente" sodass man das Programm häppchenweise laden kann.
Das große Problem hierbei ist dass eine Adresse wie 19FE:2345 sich nicht so leicht auf eine phyische Adresse in der EXE-Datei übertragen lässt. Und am Ende des Tages muss ich ja die Bits und Bytes in der MONKEY2.EXE verändern.
Der Punkt hat mitunter am meisten Zeit gekostet und so ganz verstanden habe ich es immernoch nicht
Akt 5 - Assembler und seine Tücken
Kurzer Exkurs: Ein sogenannter OpCode hat eine feste Länge, manche sind 2 Byte, manche 4, manche 6 oder gar 12 Byte lang.
Möchte man das Programm verändern muss man penibel darauf achten dass man das einhält. Wenn man irgendwo Bytes einfügt (und das Programm vergrößert) verschieben sich alle Bytes dahinter und das ist eine Katastrophe da die ganzen Adressen nicht mehr stimmen.
Nun der Code den ich verändern will ist dieser:
MOV [0x414c],AX
Was ich brauche wäre sowas:
MOV AX,0x1 <- Schreib 1 in das Register (= Text + Sprache)
MOV [0x414c],AX <- Schreib 1 vom Register in den Speicher
Das blöde ist nur: Was ich benötige ist länger (6+6 Byte) als das was vorher da war (6 Byte). Also muss ich einen Trick anwenden. Dieser nennt sich: Code Cave.
Was ist ein Code Cave? Ein Code Cave ist "freier" (sprich leerer) Speicherbereich in den ich munter schreiben kann. Dort kann auch wesentlich mehr Code stehen als vorher da war. Viele Loader/Trainer etc. benutzen diese Technik.
Das Programm springt dann kurz mal woanders hin, tut Dinge die ich möchte, und springt dann wieder zurück:
Als ich dann eine passende Stelle gefunden und das mit der Sprungadresse korrekt gemacht hatte (siehe oben) funktionierte das mit Text + Sprache auch vom Start weg:
Akt 6 - Lessons learned
Das Speichermanagement unter DOS ist nach heutigen Maßstäben bestenfalls überholt, schlimmstenfalls eine Katastrophe aber mit den damaligen Mitteln ging es halt nicht anders.
Es war auf jeden Fall spannend sich da hinein zu fuchsen, zu sehen was die Interrupts machen (z.B. ganz am Anfang wird die DOS-Version überprüft), und und und.
Ich hoffe der Beitrag war nicht allzu trocken und ich habe viele Details rausgekürzt die vieeeeeeeeel zu theoretisch wären.
Jetzt gilt es den Patch zu testen und reifen zu lassen. Nur weil es in einer DosBox läuft heißt es nicht dass es überall funktioniert.
Akt 7 - Appendix
- Zum Cursor-Problem: Ich habe hierfür auch mal den Code von ScummVM angeschaut und es sieht so aus dass es eine Endlosschleife gibt die sich um die Aktualisierung des Cursor-States kümmert. Die Blinkrate hängt von der Geschwindigkeit des PCs ab, das lässt sich leider nur schwer beheben. Würde man es verlangsamen (das habe ich getestet) laufen die Animationen auch langsamer ab.
- Warum schreibt er erst "2" in den Speicher und danach "0": 2 (= nur Text) ist der Default und wenn keine Soundkarte vorhanden ist, wird eben nur Text angezeigt. Ganz einfach
Euer GBuster
bei meinem neuesten Projekt hatte ich eine spannende Reise in die Vergangenheit und wollte euch nun, nachdem ich es geschafft habe, darauf mitnehmen.
Es kann hier und da ein bisschen trocken werden aber das liegt in der Natur der Sache
Worum geht es eigentlich
Nun, viele von euch dürften ja das hervorragende "Monkey Island 2 Ultimate Talkie" Projekt von u.A. @Andi kennen. Er schrieb mich eines Tages in Discord an und fragte mich, ob ich da nicht so ein paar Dinge für ihn bei dem Projekt "beheben" könnte die schon lange auf seiner Agenda stehen. Unter anderem:
In der DOS-Version:
- Ein paar Texte bspw. wenn man die Lautstärke erhöht oder verringert sind unübersetzt
- Beim Start hört man nur die Sprache und muss zuerst STRG+T drücken um das ganze auf deutsche Untertitel + Sprache zu stellen
- Der Cursor blinkt zu schnell
Akt 1: Die Fühler ausstrecken
Da die ganze Sache mit Disassembling noch ziemlich neu für mich ist, musste ich mich an die Tools und das Prozedere erstmal gewöhnen und Informationen sowie Tools sammeln.
Also einfach erstmal die "MONKEY2.EXE" in Ghidra (ein ziemlich guter OpenSource Disassembler von der NSA) geworfen - Ernüchterung.
Nur eine Funktion, ganz viel Daten - die Datei ist gepackt! Durch Zufall stieß ich auf ein Programm namens "SOURCER" welches einen generischen Unpacker mitbringt und die Datei entpacken konnte, sodass ich danach die echte EXE-Datei erhielt.
Ganz viel Code, ganz viele Funktionen und die gesuchten unübersetzten Texte. Punkt 1 kann man dann schon mal erledigen:
Danach machte ich mich auf die Suche nach Punkt 2.
Akt 2 - Die Suche nach Big Whoop ähh. der Stelle wo der Textmodus gesetzt wird
Der Punkt sollte sich als sehr schwierig herausstellen - ich wusste nämlich nicht wo ich anfangen soll. Erstmal zur Theorie.
- Im Spiel kann man zwischen "Nur Sprache", "Sprache und Text" sowie "Nur Text" umstellen
- Das Spiel startet immer im Modus "Nur Sprache"
So, wo fange ich an. Schon seit je her arbeitete ich mit Cheat Engine für diverse Dinge und dachte, versuch ich es einfach mal. Also habe ich DosBox-X (wegen integriertem Debugger) angeworfen und einfach mal den Speicher der DosBox nach Veränderungen durchsucht.
Was kann man in dem Video sehen?
- Die Theorie dass der Textmodus im Speicher liegt war richtig, und der Wert dafür ist 0 (=Sprache), 1 (=Text und Sprache) oder 2 (= Nur Text) (die Suche danach hat aber wesentlich länger gedauert in der Praxis )
- Danach habe ich gesucht welcher Code an diese Stelle im Speicher schreibt
- Wichtiges Learning hierbei: Der CPU Core muss auf "normal" stehen sonst ist das ganze Suchen und Debuggen sinnlos
- Nach ein wenig try and error (das Video zeigt nur letztendlich die richtige Stelle) hab ich gesehen dass zweimal an die Stelle beim Start geschrieben wird
- Zuerst die 2 (= nur Text) und danach die 0 (= nur Sprache)
- Ich habe dann einen Breakpoint an die Stelle gesetzt damit die Ausführung pausiert und zusätzlich noch eine Bedingung gesetzt dass der Breakpoint nur halten soll wenn das SI-Register den Wert 0x2743C enthält (das ist ein Zeiger auf den Speicher wohin der Wert geschrieben werden soll)
- Die Methode die ich gefunden hatte stammt nämlich von DosBox und ist nicht der echte Code, dazu später mehr
- Beim zweiten Halten an der Stelle (wo er 0 gerne in den Speicher schreiben möchte) habe ich ihm den Wert "1" untergejubelt und siehe da: Das Spiel startet mit Text + Sprache!
Akt 3: Debuggen per DosBox
Nun ging es darum die Stelle im Code zu finden die dafür verantwortlich ist, den Text-Modus auf "Nur Sprache" zu setzen. Hierfür nutze ich den Debugger von DosBox-X und steppe so lange durch den Code bis der andere Debugger (von Cheat Engine) den Breakpoint auslöst.
Wie der Debugger aussieht, seht ihr hier:
Ich hatte nun die Stelle gefunden, der Code sieht wie folgt aus:
CALLF FUN_28e0_0002 -> Initialisiert den SoundBlaster (ich konnte die Werte 220, 7 und 1 erkennen was die Adresse des SoundBlasters in DosBox ist)
ADD SP,0x2 -> Unwichtig
OR AX,AX -> Unwichtig, aber: AX enthält den Wert 0 wenn der SoundBlaster erfolgreich initialisiert wird
JNZ LAB_26a4_031f -> Wenn AX den Wert > 0 enthält, springt er weg (dann war irgendwas kaputt)
MOV word ptr [0x23f2],0x1 -> Unwichtig
MOV [0x414c],AX -> Bingo - hier wird 0 in den Speicher geschrieben
So nun muss man das ganze noch irgendwie patchen.
Akt 4 - DOS und seine Tücken
Der schwierigste Part überhaupt ist das Speichermodell von DOS. Bei Windows haben wir durchgängig einen riesigen Block Speicher, wir können überall hinspringen, alles tutti. In Zeiten von 16GB+ Ram auch kein Problem.
Aber damals musste man sich was überlegen (wie ihr ja bestimmt alle wisst) da ein Programm häufig nicht ganz in den Speicher passt. Also gibt es unter DOS "Segmente" sodass man das Programm häppchenweise laden kann.
Das große Problem hierbei ist dass eine Adresse wie 19FE:2345 sich nicht so leicht auf eine phyische Adresse in der EXE-Datei übertragen lässt. Und am Ende des Tages muss ich ja die Bits und Bytes in der MONKEY2.EXE verändern.
Der Punkt hat mitunter am meisten Zeit gekostet und so ganz verstanden habe ich es immernoch nicht
Akt 5 - Assembler und seine Tücken
Kurzer Exkurs: Ein sogenannter OpCode hat eine feste Länge, manche sind 2 Byte, manche 4, manche 6 oder gar 12 Byte lang.
Möchte man das Programm verändern muss man penibel darauf achten dass man das einhält. Wenn man irgendwo Bytes einfügt (und das Programm vergrößert) verschieben sich alle Bytes dahinter und das ist eine Katastrophe da die ganzen Adressen nicht mehr stimmen.
Nun der Code den ich verändern will ist dieser:
MOV [0x414c],AX
Was ich brauche wäre sowas:
MOV AX,0x1 <- Schreib 1 in das Register (= Text + Sprache)
MOV [0x414c],AX <- Schreib 1 vom Register in den Speicher
Das blöde ist nur: Was ich benötige ist länger (6+6 Byte) als das was vorher da war (6 Byte). Also muss ich einen Trick anwenden. Dieser nennt sich: Code Cave.
Was ist ein Code Cave? Ein Code Cave ist "freier" (sprich leerer) Speicherbereich in den ich munter schreiben kann. Dort kann auch wesentlich mehr Code stehen als vorher da war. Viele Loader/Trainer etc. benutzen diese Technik.
Das Programm springt dann kurz mal woanders hin, tut Dinge die ich möchte, und springt dann wieder zurück:
Als ich dann eine passende Stelle gefunden und das mit der Sprungadresse korrekt gemacht hatte (siehe oben) funktionierte das mit Text + Sprache auch vom Start weg:
Akt 6 - Lessons learned
Das Speichermanagement unter DOS ist nach heutigen Maßstäben bestenfalls überholt, schlimmstenfalls eine Katastrophe aber mit den damaligen Mitteln ging es halt nicht anders.
Es war auf jeden Fall spannend sich da hinein zu fuchsen, zu sehen was die Interrupts machen (z.B. ganz am Anfang wird die DOS-Version überprüft), und und und.
Ich hoffe der Beitrag war nicht allzu trocken und ich habe viele Details rausgekürzt die vieeeeeeeeel zu theoretisch wären.
Jetzt gilt es den Patch zu testen und reifen zu lassen. Nur weil es in einer DosBox läuft heißt es nicht dass es überall funktioniert.
Akt 7 - Appendix
- Zum Cursor-Problem: Ich habe hierfür auch mal den Code von ScummVM angeschaut und es sieht so aus dass es eine Endlosschleife gibt die sich um die Aktualisierung des Cursor-States kümmert. Die Blinkrate hängt von der Geschwindigkeit des PCs ab, das lässt sich leider nur schwer beheben. Würde man es verlangsamen (das habe ich getestet) laufen die Animationen auch langsamer ab.
- Warum schreibt er erst "2" in den Speicher und danach "0": 2 (= nur Text) ist der Default und wenn keine Soundkarte vorhanden ist, wird eben nur Text angezeigt. Ganz einfach
Euer GBuster