Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
gdbfuzz — 使用硬件断点对嵌入式系统进行模糊测试 | Kitploit
工具/GitHubGitHub/boschresearch/gdbfuzz
嵌入式系统安全漏洞分析调试器模糊测试硬件安全二进制分析论文与研究学习与教育固件分析Archived
GitHubboschresearch/gdbfuzz

gdbfuzz

使用硬件断点对嵌入式系统进行模糊测试

19420112年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
查看仓库

GDBFuzz:调试器驱动的模糊测试

本文是论文《使用调试器接口模糊测试嵌入式系统》的配套代码。 论文预印本可在此处获取:https://publications.cispa.saarland/3950/。代码允许用户 复现并扩展论文中报告的结果。在报告、复现或扩展结果时,请引用上述论文。

文件夹结构

root@kitploit:~
.
    ├── benchmark               # 用于构建 Google 模糊测试套件并运行实验的脚本
    ├── dependencies            # 包含安装 GDBFuzz 依赖项的 Makefile
    ├── evaluation              # 论文中呈现的原始实验数据
    ├── example_firmware        # 用于评估的嵌入式示例应用程序
    ├── example_programs        # 包含一个编译好的示例程序及配置,用于测试 GDBFuzz
    ├── src                     # 包含 GDBFuzz 的实现代码
    ├── Dockerfile              # 用于创建安装了全部 GDBFuzz 依赖项的 Docker 镜像
    ├── LICENSE                 # 许可证
    ├── Makefile                # 用于创建 Docker 镜像或在本地安装 GDBFuzz 的 Makefile
    └── README.md               # 本 README 文件

项目目的

GDBFuzz 的思想是利用微控制器的硬件断点作为覆盖引导模糊测试的反馈。因此,使用 GDB 作为通用接口以实现广泛的适用性。对于固件的二进制分析,使用 Ghidra。代码包含一个用于评估该方法的基准测试设置。此外,还附带了示例固件文件。

开始使用

GDBFuzz 能够对嵌入式系统进行覆盖引导模糊测试,但出于评估目的,也可以模糊测试任意的用户应用程序。对于微控制器上的模糊测试,我们建议在本地安装 GDBFuzz,以便能够顺畅地将模糊数据发送到被测设备。

本地安装

GDBFuzz 已在 Ubuntu 20.04 LTS 和 Raspberry Pi OS 32 位版本上测试通过。 前提条件是 java 和 python3。首先,创建一个新的虚拟环境并安装所有依赖项。

root@kitploit:~
virtualenv .venv
source .venv/bin/activate
make
chmod a+x ./src/GDBFuzz/main.py

在示例程序上本地运行

GDBFuzz 从一个包含以下键的配置文件中读取设置。

root@kitploit:~
[SUT]
# 被测系统二进制文件的路径。
# 例如,可以是 .elf 文件或 .bin 文件。
binary_file_path = <路径>

# 控制流图根节点的地址。
# 断点将放置在该 CFG 的节点上。
# 例如 'LLVMFuzzerTestOneInput' 或 'main'
entrypoint = <入口点>

# 在没有命中断点的情况下,必须执行的输入数量,之后将轮换断点。
until_rotate_breakpoints = <数字>

# 任何时候最多可以放置的断点数量。
max_breakpoints = <数字>

# 要忽略的黑名单函数。
# ignore_functions 是一个空格分隔的函数名列表,例如 'malloc free'。
ignore_functions = <空格分隔的列表>

# 枚举值之一:{Hardware, QEMU, SUTRunsOnHost}
# Hardware: 外部组件启动 gdb 服务器,GDBFuzz 可以连接到该 gdb 服务器。
# QEMU: GDBFuzz 启动 QEMU。QEMU 模拟 binary_file_path 并启动 gdbserver。
# SUTRunsOnHost: GDBFuzz 在 GDB 内部启动目标程序。
target_mode = <模式>

# 如果希望手动启动 Ghidra、分析被测系统并启动 Ghidra 桥接服务器,请设置为 False。
start_ghidra = True

# 设置软件断点(用于错误处理代码)的地址列表,以空格分隔。
# 执行到这些地址被视为崩溃。
# 示例:software_breakpoint_addresses = 0x123 0x432
software_breakpoint_addresses = 

# 是否将所有触发的软件断点视为错误
consider_sw_breakpoint_as_error = False

[SUTConnection]
# 文件 'SUT_connection_path' 中的类 'SUT_connection_class' 实现了
# 如何将输入发送到被测系统。
# 例如,可以通过 Wi-Fi、串口、蓝牙等方式发送输入。
# 该类必须继承自 ./connections/SUTConnection.py。
# 更多信息请参阅 ./connections/SUTConnection.py。
SUT_connection_file = FIFOConnection.py

[GDB]
path_to_gdb = gdb-multiarch
# 格式为 address:port
gdb_server_address = localhost:4242

[Fuzzer]
# 以字节为单位
maximum_input_length = 100000
# 以秒为单位
single_run_timeout = 20
# 以秒为单位
total_runtime = 3600

# 可选
# 一个目录的路径,其中每个文件包含一个种子。如果不希望使用种子,请将值留空。
seeds_directory = 

[BreakpointStrategy]
# 选择基本块的策略位于 'src/GDBFuzz/breakpoint_strategies/' 中
# 在论文中,我们使用了以下策略:
# 'RandomBasicBlockStrategy.py' - 随机选择未到达的基本块
# 'RandomBasicBlockNoDomStrategy.py' - 与上一策略类似,但不使用支配关系导出传递到达的节点。
# 'RandomBasicBlockNoCorpusStrategy.py' - 与第一策略类似,但阻止输入语料库增长,因此表现为带有覆盖率测量的黑盒模糊测试。
# 'BlackboxStrategy.py', - 不设置任何断点
breakpoint_strategy_file = RandomBasicBlockStrategy.py

[Dependencies]
path_to_qemu = dependencies/qemu/build/x86_64-linux-user/qemu-x86_64
path_to_ghidra = dependencies/ghidra

[LogsAndVisualizations]
# 枚举值之一:{DEBUG, INFO, WARNING, ERROR, CRITICAL}
loglevel = INFO

# 存储输出文件(如图表、日志文件)的目录路径。
output_directory = ./output

# 如果设置为 True,MQTT 客户端将发送 UI 元素(如图表)
enable_UI = False

示例配置文件位于 ./example_programs/ 中,同时还有一个示例程序,该程序是使用 benchmark/benchSUTs/GDBFuzz_wrapper/common/ 中的模糊测试工具链编译的。 使用以下命令启动一小时的模糊测试:

root@kitploit:~
chmod a+x ./example_programs/json-2017-02-12
./src/GDBFuzz/main.py --config ./example_programs/fuzz_json.cfg

我们首先看到 Ghidra 分析二进制可执行文件的输出,随后是断点重定位或命中时的消息。

模糊测试输出

根据配置文件中指定的 output_directory,现在应该有一个 trial-0 文件夹,其结构如下:

root@kitploit:~
.
    ├── corpus            # 包含输入语料库的文件夹。
    ├── crashes           # 包含崩溃输入的文件夹(如果有)。
    ├── cfg               # 以邻接表形式表示的控制流图。
    ├── fuzzer_stats      # 模糊测试活动的统计信息。
    ├── plot_data         # 显示在模糊测试活动中哪个基本块在哪个相对时间被到达的表。
    ├── reverse_cfg       # 反向控制流图。

在 GUI 模式下使用 Ghidra

通过在配置文件中设置 start_ghidra = False,GDBFuzz 会连接到以 GUI 模式运行的 Ghidra 实例。因此,需要从脚本管理器手动启动 ghidra_bridge 插件。在模糊测试期间,到达的程序块会以绿色高亮显示。

GDBFuzz 在 Linux 用户程序上的使用

对于 Linux 用户应用程序的模糊测试,GDBFuzz 利用几乎所有模糊器(如 AFL、AFL++、libFuzzer 等)都使用的标准 LLVMFuzzOneInput 入口点。 在 benchmark/benchSUTs/GDBFuzz_wrapper/common 中有一个包装器,可以将任何兼容的模糊测试工具链编译成一个独立的程序,该程序通过位于 /tmp/fromGDBFuzz 的命名管道获取输入。 这允许模拟一个通过明确定义的输入接口消费数据的嵌入式设备,从而在任意应用程序上运行 GDBFuzz。为了方便起见,我们在 benchmark/benchSUTs 中创建了一个脚本,该脚本可以按照稍后说明的方式,使用我们的包装器编译来自我们评估的所有程序。

注意: GDBFuzz 并非用于模糊测试 Linux 用户应用程序。请使用 AFL++ 或其他模糊器。该包装器仅用于评估目的,以便在规模上运行基准测试和比较!

在 Docker 容器中安装并运行

我们方法的总体效果在部署为 Docker 容器的大规模基准测试中得到了展示。

root@kitploit:~
make dockerimage

要在 Docker 容器中运行上述实验(按配置文件指定的一小时),请将 example_programs 和 output 文件夹映射为卷,并按如下方式启动 GDBFuzz:

root@kitploit:~
chmod a+x ./example_programs/json-2017-02-12
docker run -it --env CONFIG_FILE=/example_programs/fuzz_json_docker_qemu.cfg -v $(pwd)/example_programs:/example_programs -v $(pwd)/output:/output gdbfuzz:1.0

在当前工作目录中应该会出现一个 output 文件夹,其结构如上所述。

详细说明

我们的评估分为两部分。

  1. GDBFuzz 在其预期的设置上,直接在硬件上运行。
  2. GDBFuzz 在模拟环境中运行,以便对结果进行独立分析和比较。

GDBFuzz 可以配合任何 GDB 服务器工作,因此也适用于大多数微控制器的调试探针。

GDBFuzz 与黑盒模糊测试对比(研究问题1)

关于论文中的研究问题1,我们在不同的微控制器上使用位于 example_firmware 中的不同固件来执行 GDBFuzz。 对于每个实验,我们分别使用 RandomBasicBlock 策略和 RandomBasicBlockNoCorpus 策略运行 GDBFuzz。后者的行为类似于无反馈的模糊测试,但我们仍然可以测量达到的覆盖率。 为了回答研究问题1,我们比较了 RandomBasicBlock 和 RandomBasicBlockNoCorpus 策略所达到的覆盖率。 相应的配置文件位于相应的子文件夹中,下面我们解释如何在四个开发板上设置模糊测试。

GDBFuzz 在 STM32 B-L4S5I-IOT01A 板上的使用

GDBFuzz 需要访问 GDB 服务器。本例中使用 B-L4S5I-IOT01A 及其板载调试器。该板载调试器通过 'st-util' 程序设置 GDB 服务器,并允许通过 localhost:4242 访问该 GDB 服务器。

  • 安装 STLINK 驱动:链接
  • 通过 USB 连接 MCU 板和 PC(在 MCU 板上,连接到标有 'USB STLINK' 的 USB 接口)
root@kitploit:~
sudo apt-get install stlink-tools gdb-multiarch

为 STM32 B-L4S5I-IOT01A 构建并烧录一个固件,例如 arduinojson 项目。

前提条件:安装 platformio (pio)

root@kitploit:~
cd ./example_firmware/stm32_disco_arduinojson/
pio run --target upload

提示:platformio 将被测系统的一个 .elf 文件存储在此处:./example_firmware/stm32_disco_arduinojson/.pio/build/disco_l4s5i_iot01a/firmware.elf 该 .elf 文件稍后也会在 Ghidra 的用户配置中使用。

打开一个新的终端,并运行以下命令启动 GDB 服务器:

root@kitploit:~
st-util

使用 arduinojson 的用户配置运行 GDBFuzz。我们可以通过 USB 端口将数据发送到微控制器。微控制器通过串口将这些数据转发给被测系统。在我们的例子中,/dev/ttyACM0 是连接微控制器板的 USB 设备。如果您的系统将其他设备分配给微控制器板,请在配置文件中将 /dev/ttyACM0 更改为您的设备。

root@kitploit:~
./src/GDBFuzz/main.py --config ./example_firmware/stm32_disco_arduinojson/fuzz_serial_json.cfg

模糊测试统计信息和日志位于 ./output/... 目录中。

GDBFuzz 在 CY8CKIT-062-WiFi-BT 板上的使用

安装 pyocd:

root@kitploit:~
pip install --upgrade pip 'mbed-ls>=1.7.1' 'pyocd>=0.16'

确保设备上装有 'KitProg v3',并通过按相应按钮将板子切换到 'Arm DAPLink' 模式。 启动 GDB 服务器:

root@kitploit:~
pyocd gdbserver --persist

烧录一个固件并开始模糊测试,例如:

root@kitploit:~
gdb-multiarch
    target remote :3333
    load ./example_firmware/CY8CKIT_json/mtb-example-psoc6-uart-transmit-receive.elf
    monitor reset
./src/GDBFuzz/main.py --config ./example_firmware/CY8CKIT_json/fuzz_serial_json.cfg

GDBFuzz 在 ESP32 和 Segger J-Link 上的使用

  • 安装 ESP32 SDK

为 ESP32 构建并烧录一个固件,例如使用 platformio 的 arduinojson 示例。

root@kitploit:~
cd ./example_firmware/esp32_arduinojson/
pio run --target upload

向 J-Link 调试器的 openocd 配置文件中添加以下行:jlink.cfg

root@kitploit:~
adapter speed 10000

打开一个新的终端,并运行以下命令启动 GDB 服务器:

root@kitploit:~
get_idf
openocd -f interface/jlink.cfg -f target/esp32.cfg -c "telnet_port 7777" -c "gdb_port 8888"

使用 arduinojson 的用户配置运行 GDBFuzz。我们可以通过 USB 端口将数据发送到微控制器。微控制器通过串口将这些数据转发给被测系统。在我们的例子中,/dev/ttyUSB0 是连接微控制器板的 USB 设备。如果您的系统将其他设备分配给微控制器板,请在配置文件中将 /dev/ttyUSB0 更改为您的设备。

root@kitploit:~
./src/GDBFuzz/main.py --config ./example_firmware/esp32_arduinojson/fuzz_serial.cfg

模糊测试统计信息和日志位于 ./output/... 目录中。

GDBFuzz 在 MSP430F5529LP 上的使用

从 https://www.ti.com/tool/MSP430-GCC-OPENSOURCE 安装 TI MSP430 GCC

启动 GDB 服务器:

root@kitploit:~
./gdb_agent_console libmsp430.so

或者(更稳定)。从 https://github.com/dlbeer/mspdebug/ 构建 mspdebug,并使用:

root@kitploit:~
until mspdebug --fet-skip-close --force-reset tilib "opt gdb_loop True" gdb ; do sleep 1 ; done

Ghidra 无法直接分析 TI MSP430 控制器的二进制文件。为了解决这个问题,我们在 Ghidra GUI 中导入文件,选择 MSP430X 作为架构,并跳过自动分析。接下来,打开“符号表”,按名称排序,并删除所有名称类似 $C$L* 的符号。现在可以执行自动分析。分析完成后,从 Ghidra GUI 手动启动 ghidra 桥,然后启动 GDBFuzz。

root@kitploit:~
./src/GDBFuzz/main.py --config ./example_firmware/msp430_arduinojson/fuzz_serial.cfg

USB 模糊测试

为了以非 root 用户身份使用 pyusb 访问 USB 设备,我们在 udev 中添加适当的规则。将以下行粘贴到 /etc/udev/rules.d/50-myusb.rules 中:

root@kitploit:~
SUBSYSTEM=="usb", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678" GROUP="usbusers", MODE="666"

重新加载 udev:

root@kitploit:~
sudo udevadm control --reload
sudo udevadm trigger

与 Fuzzware 的比较(研究问题2)

在论文的研究问题2中,我们将 GDBFuzz 与基于模拟的方法 Fuzzware 进行比较。首先,我们按照之前所述,在提供的固件文件上分别运行 GDBFuzz 和 Fuzzware。 对于每个 GDBFuzz 实验,我们通过以下方式从控制流图文件中创建一个包含有效基本块的文件:

root@kitploit:~
cut -d " " -f1 ./cfg > valid_bbs.txt

现在,我们可以根据 fuzzware 结果回放覆盖率: fuzzware genstats --valid-bb-file valid_bbs.txt

发现错误(研究问题3)

当发现崩溃或挂起的输入时,它们会被存储在 crashes 文件夹中。在评估过程中,我们发现了以下三个错误:

  1. STM32 USB 设备栈中的无限循环,由在 for 循环中将一个 uint8_t 索引变量计数到一个攻击者可控制的 uint32_t 变量引起。
  2. Cypress JSON 解析器中的缓冲区溢出,由缺少对固定大小内部缓冲区的长度检查引起。
  3. Cypress JSON 解析器中的空指针解引用,由缺少验证检查引起。

GDBFuzz 在 Raspberry Pi 4 (8GB) 上的使用

GDBFuzz 也可以在 Raspberry Pi 主机上运行,只需稍作修改:

  1. 必须修改 Ghidra,使其能在 32 位操作系统上运行

在文件 ./dependencies/ghidra/support/launch.sh:125 中,JAVA_HOME 变量必须硬编码,例如设置为 JAVA_HOME="/usr/lib/jvm/default-java"

  1. STLink 必须为 1.7 或更高版本才能正常工作 -> 从源码构建

GDBFuzz 在其他板子上的使用

要在其他板子上模糊测试软件,GDBFuzz 需要:

  1. 一个带有硬件断点和符合 GDB 规范的调试探针的微控制器
  2. 固件文件
  3. 一个运行的 GDB 服务器和合适的 GDB 应用程序
  4. 一个入口点,模糊测试应从该点开始,例如解析器函数或一个地址
  5. 一个输入接口(参见 src/GDBFuzz/connections),用于触发入口点处代码的执行,例如串口连接

所有这些属性都需要在配置文件中指定。

运行完整的基准测试(研究问题4-8)

对于研究问题4至8,我们运行一个大规模的基准测试。 首先,如前所述构建 Docker 镜像,并使用 benchmark/benchSUTs/GDBFuzz_wrapper/common 中的模糊测试工具链编译来自 Google Fuzzer Test Suite 的应用程序。

root@kitploit:~
cd ./benchmark/benchSUTs
chmod a+x setup_benchmark_SUTs.py
make dockerbenchmarkimage

接下来,根据您的需求调整 benchmark/scripts/benchmark.py 和 benchmark/scripts/benchmark_aflpp.py 中的基准测试设置(尤其是 number_of_cores、trials 和 seconds_per_trial),然后使用以下命令启动基准测试:

root@kitploit:~
cd ./benchmark/scripts
./benchmark.py $(pwd)/../benchSUTs/SUTs/ SUTs.json
./benchmark_aflpp.py $(pwd)/../benchSUTs/SUTs/ SUTs.json

在 ./benchmark/scripts 中出现一个文件夹,其中包含每个实验的绘图文件(覆盖率随时间变化)、模糊器统计文件和控制流图文件,与 evaluation/fuzzer_test_suite_qemu_runs 中的类似。

[可选] 安装可视化及可视化示例

GDBFuzz 有一个可选功能,可以绘制已覆盖节点的控制流图。此功能默认禁用。您可以按照本节中的说明启用它,并在用户配置中将 'enable_UI' 设置为 'True'。

在主机上:

安装:

root@kitploit:~
sudo apt-get install graphviz

安装 较新版本的 node,例如来自此处的 选项 2。使用选项 2,不要使用选项 1。这应该同时安装 node 和 npm。作为参考,我们的版本号如下(但更新的版本也应该可以):

root@kitploit:~
➜ node --version
v16.9.1
➜ npm --version
7.21.1

安装 Web UI 依赖项:

root@kitploit:~
cd ./src/webui
npm install

安装 mosquitto MQTT 代理,例如参见此处

更新 mosquitto 代理配置:将文件 /etc/mosquitto/conf.d/mosquitto.conf 替换为以下内容:

root@kitploit:~
listener 1883
allow_anonymous true

listener 9001
protocol websockets

重启 mosquitto 代理:

root@kitploit:~
sudo service mosquitto restart

检查 mosquitto 代理是否正在运行:

root@kitploit:~
sudo service mosquitto status

输出应包含文本 'Active: active (running)'

启动 Web UI:

root@kitploit:~
cd ./src/webui
npm start

您的 Web 浏览器应自动打开 'http://localhost:3000/'。

启动 GDBFuzz,并使用一个将 enable_UI 设置为 True 的用户配置文件。您可以使用上面的 Docker 容器和 arduinojson SUT。但请确保将 'enable_UI' 设置为 'True'。

以 'blue' 颜色覆盖的节点是已覆盖的。白色节点是未覆盖的。我们只显示其父节点已覆盖的未覆盖节点(如果控制流图很大,绘制完整的控制流图会花费太多时间)。

许可证

GDBFuzz 在 AGPL-3.0 许可下开源。详情请参阅 LICENSE 文件。

有关 GDBFuzz 中包含的其他开源组件列表,请参阅 文件 3rd-party-licenses.txt。

下载工具