前言

本篇文章以STM32F411CEU6开发板为例,同时又不局限于该型号,一步步从固件 Dump 开始逐步分析并获得全部工程源代码

0x1 提取固件

如果你已经有了目标设备的固件升级包, 则可以跳过此步

连接设备

找到目标设备的 J-TAG 或者 ST-LINK 接口, 连接电脑, STM32 通常使用四线 SWD 接口:

VCC GND DIO SCK

建议不要接 VCC, 接上剩余三线并插入设备电源即可

准备工作

新建一个名为stlink.cfg的文件, 并编辑:

# choose st-link/j-link/dap-link etc.
#adapter driver cmsis-dap
#transport select swd
source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32f4x.cfg]
# download speed = 10MHz
adapter speed 10000

这里的 interface/stlink.cfg target/stm32f4x.cfg 可以打开 OpenOCD安装目录\share\openocd\scripts 按你的实际情况, 选择对应配置

成功连接后输出如下

> openocd -s D:\OpenOCD\share\openocd\scripts -f stlink.cfg

Open On-Chip Debugger 0.11.0 (2021-07-29) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
libusb1 09e75e98b4d9ea7909e8837b7a3f00dda4589dc3
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 10000 kHz

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 10000 kHz
Info : STLINK V2J37S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.275331
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections

查看芯片手册, 并找到 Memory Mapping 部分, 可看到下面这样的图

Memory Mapping

image.png

其中 Flash memory 中就存着我们需要的固件, 从图中可以看出 Flash memory 的地址映射范围为 0x08000000 - 0x0807FFFF 相减得到 Flash 起始地址: 0x08000000、大小 0x7FFFF

Dump 固件

如图开启 Telnet

image.png

连接到 OpenOCD, 并输入一下命令 Dump 固件

> telnet 127.0.0.1 4444

> Open On-Chip Debugger
> help dump_image
dump_image filename address size
> dump_image out.img 0x08000000 0x7FFFF
dumped 524287 bytes in 3.494784s (146.504 KiB/s)

完事! 当前目录下 out.img 就是目标设备的固件!

0x2 基础分析

导入 IDA

image.png

选择 ARM Little-Endian ARM 小端, 并进入处理器选项菜单

image.png

如图单击编辑 ARM 架构选项, 在打开的新窗口中选择 ARMv7-M

image.png

此处建议勾上Create RAM section给内存区域建一个段, 方便接下来分析过程中对全局变量重命名与引用查找(以后在 Segments 窗口再创建也行)

至于RAM的地址跟大小可以参考Memory Mapping中的SRAM(128 KB aliased by bit-banding), 同理, ROM可参考Flash memory, Loading addressLoading sizeROM保持一致即可。

image.png

鼠标选中箭头指向地址, 重复按D将其转为Double Word大小。重复操作, 直至想下图这样
image.png

双击第二个地址(这里是0x80001B1), 由于 ARM 的特性, 需要选中跳转后地址的上一行(0x80001B0处), 按C键反编译

image.png

没有什么意外的话IDA已经识别出了所有被使用过的函数

image.png

image.png

image.png

image.png

如图连续跟进,即可找到主函数

image.png

此时按 TAB, 就可以看到 IDA 给主函数生成的伪代码

stm32idafakecode.png

环境搭建

STM32设备的开发常使用到CMSISHAL, 这些库帮我们实现了大部分对硬件底层的操作, 降低了开发门槛、提高了开发效率但同时也为我们逆向提供了方便。我们逆向使用这些库的固件, 就可以充分利用这些开源的库。

此时, 代码由于描述符的缺失, 还是很难阅读与深入分析, 因此, 我们可以搭建与目标设备一致的环境,并编译带有描述符的固件,与目标设备固件对比,快速识别库函数, 节省大量逆向花费的时间。

这里的目标固件使用HAL库开发。我们在STM32CubeMX新建一个Demo项目,

image.png

尽可能多的调用外设以便接下来比对时识别更多的函数

image.png

生成代码并用 Keil 打开, 编译并在项目目录下找到生成的axf文件

image.png

IDA 载入, 可以看到很清晰的逻辑, 然后关闭IDA, 关闭时请保持默认选择Pack数据库文件

快速比对函数

这里使用 BinDiff 工具对自行编译的固件目标设备固件进行函数级的比对

安装 BinDiff

官网下载 msi 安装包一路下一步即可

BinDiff 使用

安装好后新建个Workspace, 右键New Diff, 选择使用IDA打开目标设备固件生成的idb文件自己编译出来的axf生成的idb

image.png

这里有个小坑, IDA 7.6 的 idb 会报错, 换成 IDA 7.5 以下版本就一切正常

可以看到比中了很多函数, confidence 在 0.9 以上就基本可以确定是相同函数, 使用 IDA 打开目标固件, Ctrl+6 打开 BinDiff 插件的菜单:

image.png

Diff Database 选择自行编译固件生成的 idb 文件

image.png

打开 view->bindiff->matched functions 右键import symbols/comments 可以一键导入函数的描述符

导入设备头文件

image.png

选择 工程路径\Drivers\xxxxx_HAL_Driver\Inc 中的 xxxxx_hal.h

如果没有意外的话, 你会收获一个或多个错误, 大部分的错误都是缺少文件, 从其他地方找到并放入同一目录一般可以解决问题, 关于错误修正的详细过程, 这里不再赘述

0x3 深入了解

进一步分析库函数

tips注意观察函数名的规律, 有的编译器会按照字典序给函数名排序

外设基址计算

待补充...


参考资料

使用 ida 逆向分析 stm32 的 bin 固件文件
https://www.angelic47.com/archives/97/
IDA编译STM32 Hex\Bin文件成C代码
https://blog.csdn.net/daidi1989/article/details/86304843