针对SCTF2020的STM32门锁固件题目firmware.hex。题目相关信息:STM32F103C8T6 MCU密码锁,具有4个按键,分别为1、2、3、4,分别对应GPIO_PA1、GPIO_PA2、GPIO_PA3、GPIO_PA4。有两个flag。flag1:门锁密码; flag2:UART输出的信息。

stm32逆向入门

0x00 背景知识

 针对SCTF2020的STM32门锁固件题目firmware.hex。题目链接为:链接。题目相关信息:STM32F103C8T6 MCU密码锁,具有4个按键,分别为1、2、3、4,分别对应GPIO_PA1、GPIO_PA2、GPIO_PA3、GPIO_PA4。有两个flag。flag1:门锁密码; flag2:UART输出的信息。

intel hex文件格式

 这是一个intel hex的格式。Intel Hex文件是遵循Intel Hex文件格式的ASCII文本文件。在Intel Hex文件的每一行中都包含了一个hex记录,这些记录是由一些代表机器语言代码和常量的16进制数据组成。

 Intel Hex文件常用来传输要存储在ROM、EPROM或者Flash中的程序和数据。大部分的EPROM编程器都能使用Intel Hex文件。Intel Hex由任意数量的十六进制记录组成。每个记录包含6个域。

(1)ROM(Read-Only Memory)是只读存储器,它的内容在制造时被固定下来,无法被修改。ROM 通常用于存储程序代码、固件、启动代码和常量数据等不需要经常修改的信息。

(2)EPROM(Erasable Programmable Read-Only Memory)是可擦写可编程只读存储器,它的内容可以被擦除和重新编程。EPROM 需要使用紫外线擦除器擦除,因此擦除后再次编程需要重新安装到设备中。EPROM 通常用于开发过程中需要不断修改的程序代码

(3)Flash 是一种非易失性的电子存储器,它的内容可以被擦除和重新编程,但不需要使用紫外线擦除器,而是通过电子擦除方式实现。Flash 通常用于存储程序代码和数据,它的可擦除性使得它比 ROM 更加灵活,同时也比 EPROM 更加方便。

 在 STM32 开发中,Flash 是一个非常重要的存储器类型,它用于存储程序代码和数据。对于一些需要经常修改的程序代码,可以使用 Flash 存储器进行存储。此外,STM32 还具有一些专用的存储器,如EEPROM(Electrically Erasable Programmable Read-Only Memory)和OTP(One-Time Programmable)存储器等,它们用于存储一些关键的参数和配置信息,如设备 ID、序列号、加密密钥等。

 记录按以下格式排列:

image-20230617221142375

 例如:

image-20230617221204737

(1)Start Code(冒号): 每个 Intel HEX 记录都由冒号开头
(2)Byte count(本行数据长度):是数据长度域,它代表记录当中数据字节的数量
(3)Address(本行数据的起始地址):是地址域,它代表记录当中数据的起始地址
(4)Record type(数据类型): 是代表HEX记录类型的域,它可能是以下数据当中的一个:
  00-数据记录
  01-文件结束记录
  02-扩展段地址记录
  03-开始段地址记录
  04-扩展线性地址记录
  05-开始线性地址记录
(5)Data(数据): 是数据域,一个记录可以有许多数据字节。记录当中数据字节的数量必须和数据长度域中指定的数字相符
(6)Checksum(校验码): 是校验和域,它表示这个记录的校验和.校验和的计算是通过将记录当中所有十六进制编码数字对
的值相加,以256为模进行补足

 例如上述题目,其内容如下:

1
2
3
4
5
6
7
8
9
:02 0000 04 0800 F2
:10 0000 00 4004002001010008090100080B010008 5C
:10 0010 00 0D0100080F0100081101000800000000 98
:10 0020 00 00000000000000000000000013010008 B4
...
:10 0600 00 00000000000000000000000000000000 EA
:04 0610 00 00000000 E6
:04 0000 05 080000ED 02
:00 0000 01 FF

 上述代码解释如下:

(1)第一行记录了扩展线性地址,其实就是程序的加载基址:0x08000000

(2)倒数第二行记录了程序入口地址:0x080000ED

(3)最后一行标志文件结束

(4)其余行全部是文件数据

芯片信息查找

 手册更是STM32开发者必不可少的法宝。查找手册的网站(必须知道芯片型号):https://www.alldatasheet.com/。然后就找到了对应芯片的信息:https://www.st.com/resource/en/datasheet/stm32f103c8.pdf

image-20230617224139974

image-20230617224240509

 从内存映射可以得到此代码的信息:

  • 0x08000000-0x0801FFFF: Flash 128k,用于来存储程序代码,不用把程序拷贝到RAM中,而直接在Flash中执行,这种技术叫XIP
  • 0x20000000-0x20004FFF: SRAM 20k,用于程序运算时存放变量
  • 0x40000000-0x40023FFF: Peripherals 144k,外设寄存器的映射地址,通过读写这些内存地址实现对外围设备的控制

 在之前的hex文件中,我们知道程序的加载基址是0x08000000,也是Flash的起始地址,所以这里直接就是从Flash中执行程序

补充:NOR Flash与NAND Flash

(1)NOR Flash是一种串行存储器,它的读取和写入操作可以同时进行。NOR Flash的存储单元通常是按字节进行编址的,因此适合于存储代码和执行指令等需要随机访问的应用场景。NOR Flash的读取速度比较快,但写入速度和存储密度相对较低。

(2)NAND Flash是一种并行存储器,它的读取和写入操作不能同时进行。NAND Flash的存储单元通常是按页或块进行编址的,因此适合于存储大量数据的应用场景,如图像、视频和音频等。NAND Flash的存储密度比较高,但读取速度和可靠性相对较低。

IDA配置

 由上面分析可知,内存分为FlashSRAMPeripherals。Flash段中的程序除了包括代码,还包括中断向量表。用IDA加载这个hex文件,必须还要进行相关的配置。选用ida7.732位,由于CPU内核系列是Cortex M3,指令集是ARMv7-M,因此可以进行配置。

image-20230617230049663

image-20230617230136986

0x01 题目分析

入口地址

 上面分析过,入口地址为0x080000ED(从hex文件中分析出来的)。那如果没有这个信息怎么办?利用中断向量表来找到复位时的中断处理函数,跟着函数找就可以找到入口地址。

image-20230617231227940

 之前分析的,代码基地址为0x08000000(Flash起始地址),跟踪此地址,得到:

image-20230617231411071

image-20230617231434118

 在ARM里,如果跳转到一个奇数的地址上,则是切换处理器为THUMB模式(指令的长度为2个字节,原来是4个字节)。nullsub什么都没有做。之后跳入start函数,然后从start,往后跟一会就找到了main函数(sub_8000428),如下所示:

image-20230617233635696

补充:BLX与BX指令

(1)BLX(Branch with Link and Exchange)和BX(Branch and Exchange)是ARM处理器中的两种跳转指令,用于在不同的指令集(ARM指令集和Thumb指令集)之间进行跳转。

(2)BX指令用于在ARM指令集和Thumb指令集之间进行跳转。当CPU处于ARM状态下时,BX指令可以跳转到Thumb状态的代码段,而当CPU处于Thumb状态下时,BX指令则可以跳转到ARM状态的代码段。

(3)BLX指令除了可以实现BX指令的功能外,还可以将跳转地址存入链接寄存器(Link Register,LR)中,从而实现函数调用的功能,即BLX指令实现了跳转并链接(Branch with Link and Exchange)的功能。

补充:LR寄存器

(1)Link Register是ARM Cortex-M系列处理器中的一种寄存器,它主要用于存储函数返回地址。当一个函数被调用时,LR寄存器会自动保存调用该函数的指令的地址,当函数执行完毕时,程序会从LR寄存器中读取该地址,返回到调用该函数的指令处继续执行。

(2)在STM32的汇编指令中,LR寄存器通常使用LR或者R14表示。在函数调用时,使用汇编指令BL(Branch with Link)跳转到子函数中执行,此时LR寄存器会自动保存调用该函数的指令的地址。在子函数执行完毕返回时,使用汇编指令BX LR(Branch and Exchange)跳转到LR寄存器中保存的地址,返回到调用该函数的指令处继续执行。

 接着分析,为什么上图会有很多标红?红色的位置其实就是IDA没有创建的内存段,因为加载程序hex的时候的地址以及程序大小只让IDA加载了0x8000000附近的内存,而我们之前获得SRAM以及Peripherals的地址信息都没有告诉IDA,所以接下来就创建内存段让这些红色消失。

segment建立

 根据上面分析的SRAM与Peripherals的地址,可以创建新的段。SRAM创建如下:

image-20230617234118824

修复中断向量表

 与其说是修复中断向量表,不如说是:配置ida,让ida可以识别出中断向量表的数据结构。

 程序入口之前都是中断向量表,即中断向量表的范围:0x08000000-0x080000EC

 IDApython脚本如下(其实手动也可以):

1
2
3
4
5
6
for i in range(0x8000000,0x80000eb,1): 
// 删除该地址处的标签、注释、函数定义、结构定义等所有命名信息
del_items(i)
for i in range(0x8000000,0x80000eb,4):
// 创建4字节
create_dword(i)

 得到如下:

image-20230617235008280

 其中,0x800016D;0x80001AD;0x80001E5;0x800022D;0x8000149对应的是有函数的,其对应的地址分别为:0800005C;08000060;08000064;08000068;08000078。查询手册,对应的中断为(此图是copy的链接):

image-20230617235629489

 对这些地址create_function创建函数。博客原话:分析这五个函数,对应到他们的功能,感觉前四个就是密码按键中断的对应的处理函数,最后一个DMA相关暂时不知道是干什么的。总之这一趟忙活下来,IDA基本就彻底看懂了这个题目的intel hex到底是个啥了。但是我们目前还不是很懂,接下来就是对着STM32的文档,研究程序是怎么用的外设寄存器,并分析程序函数并解题了。

0x03 解题

 由于之前选过嵌入式这门课,所以有一点点小了解。之后,用MDK加载题目中的hex文件。步骤具体在《CTF特训营》P460-P465。

 由于题目与GPIO_PA1、GPIO_PA2、GPIO_PA3、GPIO_PA4这几个引脚相关,首先先了解一下。而我们想让程序从USART1输出,因此也要用到PA9引脚。如下所示:

image-20230618125932091

补充:引脚

(1)PA、PB、PC、PD等每组都是16个GPIO引脚

(2)PA、PB、PC、PD等每组都可能有引脚和其他功能复用

(3)通过设置每个引脚的配置寄存器来决定是否复用

 我们需要设置PA1、PA2、PA3、PA4的输入,PA9设置复用为UART的输出。通过STM32F10xxx参考手册,可以配置程序中的寄存器名称。最终得到:

image-20230621103841549

 但是调试的时候一直卡在USART1_SEND的循环中,搜索了一下,可能有三个原因:

(1)确保在程序入口点 main() 中正确地初始化了 USART1串口,并且初始化后USART1的状态为可用状态。

(2)确保在发送数据之前USART1接收缓冲区为空。可以通过读取USART1_SR寄存器的RXNE位来检查是否有未处理的数据,如果有,则需要先读取USART1_DR寄存器中的数据。

(3)如果USART1串口的波特率设置不正确,可能会导致通信错误,也会导致卡在 while ((USART1_SR & 0x80) == 0)。请确保波特率设置正确。

 排查了一下错误,发现配置时应该这样:

image-20230621105615611

 之后点击debug,然后load firmware.hex,最后reset就好啦。结果如下图所示:

image-20230621105524795

参考链接:https://xuanxuanblingbling.github.io/iot/2020/07/08/stm32/

留言

2023-06-17

© 2024 wd-z711

⬆︎TOP