Unbekanntes Prozessor-Reverse Engineering in einem einzigen Programm


TL; DR: Wir haben ein Programm fĂŒr eine völlig unbekannte CPU-Architektur ohne Dokumentation auf der CPU (ohne Emulator, ohne ISA, ohne alles) in nur zehn Stunden rĂŒckentwickelt. Aus dem Artikel erfahren Sie, wie wir es gemacht haben ...

Letztes Wochenende haben das CMU PPP-Team und ich am Dragon Sector Teaser CTF 2019-Team teilgenommen, um uns zu entspannen und von der harten Frist von CHI 2020 abzuweichen . Dragon Sector ist ein angesehenes polnisches Team mit einer Geschichte interessanter CTFs . Ich habe mich gefragt, was sie auf Lager haben.

Nachdem ich „ummmfpu“ gelöst hatte, eine Aufgabe, die das Reverse Engineering des Bytecodes fĂŒr den Micromega uM-FPU-Gleitkomma-Coprozessor beinhaltete, entschied ich mich, an einem Wettbewerb zur Lösung des CPU Adventure-Problems teilzunehmen, das zu diesem Zeitpunkt von keinem der Teams (als Ergebnis) gelöst wurde Wir waren die einzigen, die die Aufgabe erledigt haben.

Hier ist eine Beschreibung der CPU Adventure-Aufgabe:

Mein Großvater war in den 60er Jahren mit der Entwicklung von Computern beschĂ€ftigt. Als ich die Dinge auf seinem Dachboden in Ordnung brachte, fand ich ein seltsames Auto. Neben der Maschine befand sich ein Stapel Lochkarten mit der Aufschrift „Dragon Adventure Game“. Nach einiger Zeit habe ich es geschafft, es an moderne GerĂ€te anzuschließen, aber das Spiel ist zu kompliziert und ich kann nicht bis zum Ende kommen, ohne zu schummeln. Kannst du mir helfen? Ich lege eine Transkription der in der Maschine verwendeten Lochkarten bei. Es wird behauptet, dass die Maschine 4 Allzweckregister, 1 Kibibyte Datenspeicher und 32 Kibibyte Befehlsspeicher hat. Um das Spiel zu spielen, stellen Sie wie folgt eine Verbindung zum Server her: socat tcp4-connect:cpuadventure.hackable.software:1234 fd:0,rawer Hinweis: Der Prozessor des Computers ist eindeutig. Versuchen Sie nicht, Informationen darĂŒber zu googeln.

game.bin

Nachdem wir uns mit dem Server verbunden haben, haben wir die folgenden Informationen erhalten:

THERE IS A TAVERN HERE. INSIDE THE TAVERN, YOU SEE VALIS.

SELECT AN OPTION:

- GO (N)ORTH
- GO (E)AST
- (T)ALK TO VALIS
- (D)RINK
- SHOW (I)NVENTORY

YOUR CHOICE:

Großartig. Dies ist ein Abenteuerspiel der alten Schule. Nachdem wir es ein wenig gespielt hatten, stellten wir fest, dass Sie gegen Feinde kĂ€mpfen und eine Flagge von diesem Valis-Charakter erhalten können, wenn wir ihm gefallen:

YOUR CHOICE: T

YOU ENTER THE TAVERN AND APPROACH VALIS.

- HEY, I WAS WONDERING IF YOU COULD HELP ME FIND THE FLAG?
- THE FLAG? MAYBE, BUT FIRST, I NEED A REDBULL.
- I... I DON'T HAVE A REDBULL.
- WELL THEN, MAKE YOURSELF USEFUL AND FIND ONE.

THERE IS A TAVERN HERE. INSIDE THE TAVERN, YOU SEE VALIS.

SELECT AN OPTION:

- GO (N)ORTH
- GO (E)AST
- (T)ALK TO VALIS
- (D)RINK
- SHOW (I)NVENTORY

YOUR CHOICE:

Erste Schritte


Ich habe das Spiel nicht lange gespielt und festgestellt, dass es höchstwahrscheinlich wichtiger ist, die Datei game.bin . Ich habe es in einem Hex-Editor geöffnet und erwartet, dass BinĂ€rwerte angezeigt werden. Stellen Sie sich meine Überraschung vor, als ich das sah:

  1100111111010000001111001100100011100000110011010000000000001100100111010100000011010011111000011111001100111000000011 ... 


Dies ist buchstĂ€blich eine BinĂ€rdatei - eine Textdatei, die nichts anderes als die ASCII-Zeichen 1 und 0 enthĂ€lt. Wir wissen, dass dies höchstwahrscheinlich der Maschinencode des Prozessors ist, aber zusĂ€tzlich zu 4 Registern, 1 KByte Datenspeicher und 32 Kibibyte des Befehlsspeichers, nichts ist darĂŒber bekannt. Daher wird unsere erste ernsthafte Aufgabe darin bestehen, die EinheitsgrĂ¶ĂŸe dieser BinĂ€rdatei zu bestimmen (hat sie beispielsweise eine 8-Bit-Dimension? Oder vielleicht eine 12-Bit- oder 18-Bit-Dimension, wie in einigen alten Architekturen ?).

Um die GrĂ¶ĂŸe einer unbekannten Datei herauszufinden, habe ich einen alten Trick verwendet: Ich habe die GrĂ¶ĂŸe des Textfelds geĂ€ndert, bis die ZeilenumbruchlĂ€nge der Ausrichtung entsprach. Diese Methode eignet sich hervorragend fĂŒr Dinge wie mehrere XOR-verschlĂŒsselte Texte, unbekannte (unkomprimierte) Dateiformate und Code von einer unbekannten CPU:


Ändern Sie die GrĂ¶ĂŸe des Textfelds

Bei dieser schnellen ÜberprĂŒfung habe ich herausgefunden, dass die EinheitsgrĂ¶ĂŸe dieser Datei ein Teiler von 20 sein sollte (die Breite des ausgerichteten Fensters). Um die genaue GrĂ¶ĂŸe herauszufinden, habe ich schnell ein Skript geschrieben, das nach langen sich wiederholenden Zeilen sucht (vorausgesetzt, jeder Code hat sich wiederholende stereotype Sequenzen). Die lĂ€ngste sich wiederholende Zeile war der folgende 425-Bit-Block, der bei 43625 und 44510 gefunden wurde:

  10000011111110000001010100011111110100000101100010111000001001000101000100001000100001010001011000101000000001111111111100010000011110010100100001010100111100000110000010100000101000101000011110001111001101111001010100001010000111110100001010000110010011011110011111000000111011101000000001100000110000111101011010111011000100100010100000111000100011100011000000000101010101100010111000001010000001101010010000000011000001100 

Da der Abstand zwischen den Wiederholungen 885 betrÀgt, kamen wir zu dem Schluss, dass die Dimension 5 Bits betragen sollte, d. H. Eine unbekannte CPU muss 5-Bit-Bytes haben. Fortschritt!

Wir suchten nach 5-Bit-Lochkartencodierungen und entschieden uns schnell fĂŒr die alte Codierung mit dem Baudot-Code . Und tatsĂ€chlich - als wir den Online-Decoder zum Decodieren einiger Fragmente verwendeten, erhielten wir lesbaren Text!

⇩DRAGON⇩HERE⇧; ⇧⇧⇩SHE⇩APPEARS⇩TO⇩BE⇩GUARDING⇩ EINIGE ART VON ⇩A⇩BOTTLE⇧; &. &. ⇩␀THERE⇩IS⇩A⇩B

LSB Baudot ITA-erweiterter 425-Bit-Code

Dann haben wir versucht, die gesamte Datei mit Bodo-Code zu dekodieren, aber in den ersten 20.000 Bits haben wir MĂŒll bekommen, wonach es einen perfekt lesbaren Text gab. Dies machte uns klar, dass der erste Teil der Datei zum Abschnitt „Code“ gehört, gefolgt vom Abschnitt „Daten“, der konstante Zeilen enthĂ€lt. Wir gingen davon aus, dass die Maschine wahrscheinlich Bodo-Code fĂŒr E / A verwendet und daher konstante Zeilen in der Bodo-Codierung auch im Speicher speichert. Um das Codesegment besser lesbar zu machen, habe ich beschlossen, es mit der Base-32-Codierung zu codieren, Ă€hnlich der hexadezimalen Codierung, aber mit dem Alphabet 0-9a-v zu erweitern. So sieht die Datei game.bin aus, wobei der erste Teil von base-32 und der zweite Teil von Bodo codiert werden (die vollstĂ€ndige Datei finden Sie hier: game.b32 ):

pv83pi70pk00p7a0qfgvpjg3f0kf13f28p5f3pv10pk40pn60f0sf1sf24p5f3r9c11qad0f0sf1df26p5f39c21qad0f05f1ff26p5f39c41qad0f08f1df26p5f39c81qad0f0hf1ef26p5f3r1c00qaq15c20qcl0f01f1of27p5f3p3g3psf35c10qal0f02f1nf27p5f3p3g3psf3rf0hf1nf27p5f3f05f16f27p5f3rf84f95101311fl0f510f84907qa40b518447qa40b514f84f95k9m0k9m0k9m0907qa40b511447qa40b512ruougf10f20g0i9g0i910931b320u2u1u0ro9f0o9f0ojh0o9f0o9f0o9f0olj0o9f0o9f0o9f0o9f0o9f0o9f0o9f0o9k1onp0o9f0o9f0o9f0o9f0onf0ot82odi0o9f0o9f0o9f0o9f0o9f0o9f0o9f0olg0o9f0f0gf1df24p5f3r9c11qa835548755
[...]
93e9n59ka8fo87r85g8ui8ml8ed87b9h89u291u82333333333456789abcdb01234567892)%c3BOTTLE OF SOPLICA PIGWOWAENEMY HEALTH:

YOU ATTACK

YOU APPROACH REDFORD.



YOU ENTER THE TAVERN AND APPROACH VALIS.
[...]

Der Einfachheit halber werde ich unten FĂŒnf-Bit-Einheiten "Bytes" nennen. Im Team haben wir uns andere Namen fĂŒr sie ausgedacht - ich nannte sie Knabbereien und Zak-Hacks.

Reverse Engineering unbekannter Prozessorarchitektur


Jetzt kommen wir zum schwierigen Teil - Reverse Engineering von 4000 Byte Code, der auf einer völlig unbekannten, einzigartigen CPU-Architektur ausgefĂŒhrt wird. Aus dem Code geht hervor, dass dies eine Reihe von Anweisungen variabler LĂ€nge sein sollte , da darin kein erkennbares stabiles Wiederholungsmuster zu finden ist. Ich verbrachte ein paar Stunden damit, spĂ€ter begann mein Teammitglied Zachary Wade (@ zwad3) mir zu helfen. ZunĂ€chst suchte ich nach doppelten Codeteilen, was darauf hindeutete, dass sie hĂ€ufig eine kleine Anzahl von Anweisungen verwenden wĂŒrden. Ich begann den Code in kĂŒrzere sich wiederholende Sequenzen aufzuteilen, die fĂŒr die Analyse bequemer waren. Ich möchte sagen, dass es ein strenger Prozess war, aber tatsĂ€chlich wurde hauptsĂ€chlich der folgende vage Algorithmus verwendet:

  • Wir schauen uns den Code an und prĂŒfen, ob sich darin sehr oft etwas wiederholt
  • FĂŒhren Sie die Such- und Ersetzungsprozedur durch, um neben dieser Wiederholung eine neue Zeile einzufĂŒgen
  • Untersuchen Sie die Ähnlichkeiten / Unterschiede zwischen den resultierenden Trennlinien.
  • Wiederholen Sie diesen Vorgang etwa eine Stunde lang ...

Zum Beispiel war eines der Muster, die ich entdeckte, "0f0.f", wobei "." zeigt ein unbekanntes Zeichen an. Ich habe die Schnur nach diesem Muster gebrochen und Folgendes erhalten:

pv83pi70pk00p7a0qfgvpjg3f0kf13f28p5f3pv10pk40pn60f0sf
1sf24p5f3r9c11qad0f0sf
1df26p5f39c21qad0f05f
1ff26p5f39c41qad0f08f
1df26p5f39c81qad0f0hf
1ef26p5f3r1c00qaq15c20qcl0f01f
1of27p5f3p3g3psf35c10qal0f02f

Sehr hilfreich! In der zweiten und dritten Zeile sehen wir, dass es "... p5f3r9c ..." und "... p5f39c ..." gibt, und dies deutet darauf hin, dass "r" ein Einzelbyte-Opcode ist, dh "... 5f3" ist das Ende eines Opcodes und " 9c .. ”ist der Anfang eines anderen. In den letzten beiden Zeilen sehen wir "p5f3r1c ..." und dies bedeutet, dass "1c .." ein anderer Opcode ist und "p3 ..." auch ein anderer Opcode ist.

Ich fuhr fort, Anweisungen auf Ă€hnliche Weise immer wieder aufzuteilen, wobei ich die Ähnlichkeiten und Unterschiede Ă€hnlicher Blöcke verwendete, um wahrscheinliche Anweisungen zu finden. Am Ende habe ich so etwas bekommen:

pv83
pi70
pk00
p7a0
qfgv
pjg3
f0k
f13
f28
p5f3
pv10
pk40
pn60
f0s
f1s
f24
p5f3
r
9c11
qad0
f0s
f1d
f26
p5f3
9c21
qad0
f05
f1f

Ich nahm an, dass "p" und "q" Anweisungen mit drei Bytes von Operanden waren, "f0", "f1" und "f2" Anweisungen mit einem Operanden waren und "9c" eine Anweisung mit zwei Operanden war. Ich wusste jedoch nicht, was jede dieser Anweisungen ist.

Also wandte ich mich dem Verzeichnis aller von mir hervorgehobenen "p" -Anweisungen zu und stellte fest, dass im Moment die hĂ€ufigsten Anweisungen mit "p" "p5f3" waren. Als ich mir die Orte ansah, an denen es auftritt, stellte ich außerdem fest, dass immer die Anweisungen „f0“, „f1“ und „f2“ vorangestellt sind. Bei Betrachtung aller Operanden „f0“, „f1“ und „f2“ habe ich festgestellt, dass die Operanden f2 immer im Bereich von 4 bis 8 liegen. Als ich mich daran erinnerte, dass die CPU ĂŒber 32 KB Programmspeicher fĂŒr die Adressierung verfĂŒgt, was 15 Bit erfordert, nahm ich an, dass „f0“, „f1“ und „f2“ eine Adresse mit f2 als High-Byte geladen haben. Als ich einige dieser Adressen miteinander verband, stellte ich fest, dass sie alle genau auf den Anfang der konstanten Linien im Datenabschnitt zeigen. Ich habe die print ! Es folgte sofort, dass "p5f3" tatsĂ€chlich eine Art Anweisung zum Drucken einer Leitung oder eines Anrufs ist; Wenn Sie den Drei-Byte-Operanden berĂŒcksichtigen, handelt es sich höchstwahrscheinlich um einen „Aufruf“. Beim Betrachten der "p" -Anweisungen wurde mir wieder klar, dass die drei Bytes des Operanden die Adresse in direkter Reihenfolge (Little-Endian) angeben, dh das letzte Byte des Operanden ist das höchste Byte der Adresse.

Es war ein großer Durchbruch! Wir haben unsere erste Anweisung identifiziert. Nachdem ich gesehen hatte, dass "f0" und "f1" an einigen anderen Stellen verwendet werden, nahm ich an, dass sie nicht die Adressen laden, sondern eines der vier Register (zum Beispiel lĂ€dt f0 das Register 0) mit 5-Bit-Konstanten mit direkter Adressierung. Dies ist fĂŒr p5f3 logisch - es werden drei Registerargumente fĂŒr die 3f5-Funktion geladen („print_string“).

Ich habe angefangen, einen Disassembler zu schreiben, der die Ausdrucksweise „Drucken“ (f0x, f1x, f2x, p5f3) erkennt und die Ersetzung der gedruckten Zeile in den disassemblierten Code einfĂŒgt. Aufgrund der großen Anzahl von Zeilen im Programm wurde der zerlegte Code schnell sehr gut lesbar und es wurde einfacher, die Position der Funktionsblöcke herauszufinden (der vollstĂ€ndige zerlegte Code ist hier ):

0: call 38v
4: call 7i
8: call k
c: call a7
g: q vgf

k: call 3gj
o: print 83k # 'SELECT AN OPTION\x0e:\r\n\r\n\x0f\x00'
15: call 1v
19: call 4k
1d: call 6n
1h: print 4ss # '\r\nYOUR CHOICE\x0e: \x0f\x00'
1u: ret

1v: unk 9
20: unk c
21: unk 1
22: unk 1
23: q 0da
27: print 6ds # '\x0e- \x0fGO \x0e(\x0fS\x0e)\x0fOUTH\r\n\x00'
2k: unk 9
2l: unk c
2m: unk 2
2n: unk 1
2o: q 0da
2s: print 6f5 # '\x0e- \x0fGO \x0e(\x0fN\x0e)\x0fORTH\r\n\x00'
39: unk 9
3a: unk c
3b: unk 4
3c: unk 1
3d: q 0da
3h: print 6d8 # '\x0e- \x0fGO \x0e(\x0fE\x0e)\x0fAST\r\n\x00'
3u: unk 9
3v: unk c
40: unk 8
41: unk 1
42: q 0da
46: print 6eh # '\x0e- \x0fGO \x0e(\x0fW\x0e)\x0fEST\r\n\x00'
4j: ret

Aus diesem kleinen Codefragment konnte ich verschiedene Aspekte herausfinden: Die Anweisung "q0" sollte eine bedingte Verzweigung anzeigen (da sie zum Überspringen unnötiger Anweisungen in Funktion 1v verwendet wird) und die Anweisungen "9c11", "9c21", "9c41", " 9c81 “sollte eine Art UND-Anweisung anzeigen - sie ĂŒberprĂŒfen die gesetzten Bits, um festzustellen, ob diese Anweisungen zulĂ€ssig sind (dies wird durch die Verwendung von„ 1 “,„ 2 “,„ 4 “und„ 8 “in diesen Anweisungen deutlich angedeutet).

In den nĂ€chsten zwei Stunden sortierten Zachary Wade (@ zwad3) und ich die verschiedenen Anweisungen aus und machten und verfeinerten unsere Annahmen darĂŒber, was sie taten. Viele lesbare Druckaussagen haben uns die Arbeit erheblich erleichtert. Wir beschlossen, dass jeder von uns einzeln seinen eigenen Disassembler schreibt, damit wir die Anweisungen in unserem eigenen Tempo prĂŒfen und unsere Ergebnisse teilen können.

Code Reverse Engineering


Nach ein paar Stunden machte ich große Fortschritte beim Zerlegen. Nachdem wir den Code untersucht hatten, der mit dem Inventar des Benutzers zusammenarbeitete (genauer gesagt die Funktion „drink“ und jeden damit verbundenen Handler), fanden wir Anweisungen zum Speichern und Laden aus dem Speicher (vergessen Sie nicht, dass die CPU ĂŒber 1 Kibibyte Datenspeicher verfĂŒgt). Dann fanden wir heraus, dass einige arithmetische / logische (ALU) Befehle Speicheroperanden verwenden (zum Beispiel bedeutet "9c41" "UND mit Wert an Datenadresse 1 mit Wert 4 ausfĂŒhren"). Auf diese Weise konnten wir die Variablen im Datenspeicher neu erstellen, z. B. in [0] wird die NPC-Kennung am aktuellen Speicherort gespeichert und in [6,7] wird der aktuelle Zustand des Spielers gespeichert (die unteren 5 Bits in [6], die Ă€ltesten 5 Bits in [7] ]). Zu diesem Zeitpunkt wechselte ich von den Anweisungen fĂŒr das Reverse Engineering zum Kommentieren meines zerlegten Codes und zum Reverse Engineering des Programms selbst. Unten ist meine lustige Notation fĂŒr 5-Bit-Werte:

_start:
call init

L4:
call check_moves
call print_menu
call handle_command
br 4

print_menu:
call print_itemname
print 83k # 'SELECT AN OPTION\x0e:\r\n\r\n\x0f\x00'
call print_moves
call print_npcmenu
call print_itemmenu
print 4ss # '\r\nYOUR CHOICE\x0e: \x0f\x00'
ret

print_moves:
and 0y1, [1]
brz 2k
print 6ds # '\x0e- \x0fGO \x0e(\x0fS\x0e)\x0fOUTH\r\n\x00'
2k: and 0y2, [1]
brz 39
print 6f5 # '\x0e- \x0fGO \x0e(\x0fN\x0e)\x0fORTH\r\n\x00'
39: and 0y4, [1]
brz 3u
print 6d8 # '\x0e- \x0fGO \x0e(\x0fE\x0e)\x0fAST\r\n\x00'
3u: and 0y8, [1]
brz 4j
print 6eh # '\x0e- \x0fGO \x0e(\x0fW\x0e)\x0fEST\r\n\x00'
4j: ret

print_npcmenu:
add 0y0, [0]
brz 6m
sub 0y2, [0]
br<c> 5p
print 7o1 # '\x0e- (\x0fT\x0e)\x0fALK TO \x00'
call print_npcname
call print_crlf
5p: sub 0y1, [0]
brz 6m
print 7n2 # '\x0e- (\x0fF\x0e)\x0fIGHT \x00'
call print_npcname
call print_crlf
6m: ret

print_itemmenu:
print 7nh # '\x0e- (\x0fD\x0e)\x0fRINK\r\n\x00'
print 765 # '\x0e- \x0fSHOW \x0e(\x0fI\x0e)\x0fNVENTORY\r\n\x00'
ret

Wir haben zum Beispiel immer noch viele unbekannte Opcodes, obwohl wir herausgefunden haben, dass „qa“ eine bedingte Verzweigung auf Null ist (Verzweigung auf Null, brz), konnten wir nicht verstehen, was „qc“ ist (oben angegeben als brc)). Aber das war genug, um die Logik des Programms zu verstehen.

TatsĂ€chlich erlaubt das Spiel dem Spieler, sich auf einer 8 × 8-Karte zu bewegen, auf der NPCs (Drachen, rote Stiere und Menschen) zufĂ€llig platziert sind. Du kannst mit jedem NPC kĂ€mpfen (sogar mit Valis, obwohl kein entsprechender MenĂŒpunkt vorhanden ist). WĂ€hrend des Kampfes können Sie den Feind angreifen und eine zufĂ€llige Menge an Schaden oder Fehlschlag verursachen. Danach greift der Feind den Spieler an und verursacht auch eine zufĂ€llige Menge an Schaden oder Fehlschlag. Sie können auch eine Schildsperre wĂ€hlen, dank der der Feind den Schild entweder verfehlt oder trifft, ohne Schaden zu verursachen. Schließlich können Sie betrĂŒgen, indem Sie Ihre Gesundheit auf 1000 erhöhen. In diesem Fall wird die versteckte Variable („betrogen“, Adresse 10) auf 1 gesetzt. Wenn der Spieler den Feind erfolgreich tötet, fĂ€llt ein Gegenstand aus ihm heraus, normalerweise eine Flasche Alkohol (offensichtlich) Dieses Spiel ist nicht fĂŒr Kinder.

Der Haupt-NPC Valis, von dem der Spieler die Flagge erhalten muss, verfĂŒgt ĂŒber eine Zustandsmaschine, in der er den Spieler nach mehreren GegenstĂ€nden fragt - einer Reihe von Red Bull-GetrĂ€nken (offensichtlich erhalten, wenn er Red Bull-Feinde besiegt), verschiedenen MixgetrĂ€nken (zum Beispiel Gin Tonic). Um sie zu erhalten, musst du den blauen und den grauen Drachen besiegen und dann die von ihnen abgeworfenen Objekte und die Steckdosenleiste mischen, die du erhalten kannst, wenn du eine andere NPC-Person im Spiel besiegst (Redford) oder ihm hilfst. Wenn Sie all diese langen Reihen von Anfragen erfĂŒllen, gibt er Ihnen die Flagge, aber nur, wenn die Variable „betrogen“ nicht gleich 1 ist. Das heißt, unsere Aufgabe ist es, das Spiel ohne Betrug zu gewinnen. Da wir wie alle Feinde mit nur 100 HP beginnen, ist es mit der ĂŒblichen Passage unmöglich, alle Feinde zu besiegen (um alle notwendigen Dinge zu bekommen, die Sie benötigen, um etwa 20 Gegner zu besiegen). Es ist notwendig, das RNG irgendwie zu modifizieren, damit der Feind immer verfehlt.

Zufallszahlen werden von einer Funktion generiert, die einer Art PRNG (Adresse 37a) Ă€hnelt, jedoch eindeutige Anweisungen verwendet, die nirgendwo anders verwendet werden, sodass wir sie nicht zurĂŒckentwickeln können. Wir haben jedoch festgestellt, dass es seinen Zustandsvektor von drei Adressen im Speicher lĂ€dt ([11], [12] und [13]), dh sein vollstĂ€ndiger Zustand benötigt nur 15 Bit. Dies bedeutet, dass das RNG eine kurze Periode haben muss - nicht lĂ€nger als 2 ^ 15 = 32768.

WĂ€hrend wir (erfolglos) versuchten, die RNG-Implementierung umzukehren, implementierten Jay Bosamia (@ jay_f0xtr0t) und Matthew Savage (@thebluepichu) einen Exploit. Durch einfaches 100.000-maliges Senden des Befehls "Schild" in Folge konnten wir eine Folge von feindlichen "Treffern" und "FehlschlĂ€gen" erhalten, die der vom RNG ausgegebenen Bit entsprechen. Wir haben dafĂŒr gesorgt, dass sich diese Sequenz mit einem Zeitraum von 32767 wiederholt. Dank dessen konnten wir unseren Haupt-Exploit zusammenstellen. Beim Kampf mit dem ersten Feind, den wir getroffen haben, haben wir unsere Schilde 40 Mal geschlossen, um die Sequenz von Treffern und FehlschlĂ€gen wiederherzustellen, diese Sequenz in einer großen periodischen Sequenz gesucht und dann herausgefunden wann abgeschirmt und wann angegriffen werden muss, damit der Feind immer verfehlt. Dann haben wir einfach die ganze Karte im Mordhobo- Modus umrundet , alle getötet und ihre Beute gesammelt . Am Ende kehrten wir nach Valis zurĂŒck und fragten höflich nach unserer Flagge, die wir erhielten:

DrgnS{m4kin9-v4lis-happy-w1th-n4t1ve-b4ud0t-cpu}

Fuh! In der Tat ein echtes Abenteuer. Ich kann immer noch nicht ganz glauben, dass wir in weniger als 10 Stunden von einer binĂ€ren Zeichenfolge und einem völligen Mangel an Prozessordokumentation zu zwei fast vollstĂ€ndigen Disassemblern und einem sauberen disassemblierten Code gekommen sind! Der gesamte Code ist auf GitHub verfĂŒgbar: mein Disassembler , Zacharys Disassembler , mein roher disassemblierter Code , mein kommentierter disassemblierter Code , Matts Exploit-Client .

Source: https://habr.com/ru/post/de468849/


All Articles