你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32固件逆向

[复制链接]
STMCU小助手 发布时间:2022-12-1 18:00
一、概括
% m. D# H3 P! O. \+ C4 f8 ^用的很久之前做的一个电风扇的课设来做实验。+ R# P+ `' ^: {2 i) q
( D: d8 l: o  _$ E0 O8 x
微信图片_20221129184848.png
2 W& @2 Q8 W* I3 A5 C, m' ^/ @+ V2 w9 r1 D% G% `5 p, j+ r
3 W2 D- ^. t  }
用到了外部中断,定时器中断,pwm。MCU是STM32F103ZET6。7 ]# l, J* R9 p" d/ r3 o* S- L* [, S
; y5 t; f! U) W- W  z5 g% I
微信图片_20221129184844.png
5 A& O# w5 n' c  L. [0 Y. {, j! v" V# G1 p& c/ G. N

5 z; L: Z/ f% j2 @. b在keil的设置中可以看到固件的起始地址,默认情况下为0x8000000。另外,keil生成的是hex文件,hex文件是带有基地址信息的bin文件,如果我们要生成bin文件,需要进行如下设置:& f$ [! a, L. k. v5 z
3 \& \3 X7 D/ ^/ i4 u! y' u( R2 ]
) L' S; }; M0 W4 D0 y
微信图片_20221129184839.png
1 k3 b- j+ s# I1 V% p  t* R) S) i! o# @
另外,烧录到mcu中的固件是不带符号表等调试信息的,即hex和bin文件都不带调试信息,因为mcu的内存有限,但keil会单独生成一个带有调试信息的axf文件,我们可以以这个axf文件为基准,对照着恢复bin文件或者hex文件,下面我们来观察一下这三个文件的区别:) Y' e1 R0 E  o' N0 x& d
首先是axf文件) b" z/ z3 t! P# e: m6 u) ~- |

# Z1 R* S/ L& C$ Q  B4 h' U! } 微信图片_20221129184836.png $ M6 ^9 Z( J7 @  V8 `0 D9 y
微信图片_20221129184833.png + Y. U: P6 q! [$ V& h0 e
+ D; C& M' k% N6 [8 E3 B
几乎和源代码一摸一样,除了一些外设名用地址代替了。5 i7 w" r  D+ \" i$ A0 X
. L1 c. ?- u; ?8 u- y/ p" t
再看到hex文件:
* V. H2 q2 ~3 {( W; @% }& A$ _& Z2 `: x+ U/ y5 I
微信图片_20221129184829.png ! C( E0 p& H' Z) O) A) ~# U: d
' F7 j5 J: q6 L- B9 H/ ~
首先需要将架构修改为arm小端序,再修改一下架构版本,stm32f103zet6是cortex-m3内核的,armv7架构。
6 N5 f6 W3 J" |- N
! ]0 D+ L# ?3 d( K$ C; f
  \; q* Z8 i! n- e; P# H7 ? 微信图片_20221129184826.png
7 L# p2 X( h2 W" o. d* X0 F
+ R+ C+ U1 w( ~
5 l1 K) l. l' k, ]5 p修改完后加载hex文件。
- e9 K  g, c. b  I+ T9 I, N; E+ L7 `/ c7 v
3 ]  ~, M5 r1 w( c& ?8 G, ^1 K
微信图片_20221129184824.png 3 N% H# A0 L+ a2 U% a8 x# O! a

* u& m$ i  \, i2 v& |1 y2 y
! d: F7 F" e+ j# [, r% iIDA会自动识别出基地址,能识别出不少函数了,但伪代码是真的难看。
# y! D$ P, x2 ]" O, f
, N' D( i7 H% m9 I+ P 微信图片_20221129184821.png # }% ?- y2 O- l8 y
, M3 P5 b. z: e" k2 O( I& D# O

* F' k& l* _8 o  d基本上是看不出个什么东西来。
; ]6 c/ o8 b) o3 M- f4 Q0 L6 _0 K% G" x) L4 D+ e: f) W
最后是bin文件。
+ H3 A3 x+ @6 X8 {
& S: L, F+ Y$ |6 h6 Z6 y
  M0 r% }+ j8 V/ E+ u 微信图片_20221129184818.png
: w8 u+ X$ s2 {  |/ Y' T. H# D, J7 o% s8 x! p- R( z4 V
bin文件去除了地址信息的hex文件,需要手动设置基地址。! [3 w- G# I' R0 x( q+ K

5 e, e6 B" b. O/ R% W3 t$ |7 I9 n' [3 d7 {+ M3 p
+ q5 P# h" B/ o. U  _
二、手动设置固件基地址
9 w+ l) `% X# c/ v- V2 g0 [
# o; ^8 K  F, y# g2 Z- o- s; c- M
我们的分析将以bin文件为目标。5 O5 _  @6 G3 L) a7 N/ N+ E% x6 j

3 K( x! H% I. b- O将基地址重设为0x8000000,这个地址怎么来的?可以看stm32f103zet6的datasheet。
6 D+ J% A* [8 m5 r& J4 [: V! X7 M1 R! h4 w$ I4 i3 W2 k. g1 T
微信图片_20221129184815.png
& Y# L) S9 u& @0 a: S9 I
- L# T; ~5 V$ b/ A5 \' o  a/ z  n$ Y
从0x8000000开始就是flash的空间。
' A1 w* U# f" S  Y2 w4 U. W. b
' n0 B" u; v4 b5 X5 H( P' g9 ?" y) R重新设置加载地址之后再将其按照代码解析就变成了hex文件的样子。: _  |( s. `. l! T% N& b
% }( h, y( G; t2 ]' W. s$ i

: m' K! d% a2 w* I! Y; _% \ 微信图片_20221129184812.png
3 C- n% n- R3 {/ Y* @8 s, O0 Y$ z6 w* g$ }/ o% _& k6 j
* d: k% h3 q# u. h1 @* ]: H. L: D
但此时存在很多标红的地址,这是因为这些地址在IDA中并没有设置,因此IDA将其解析为非法地址,标红了,我们现在要做的就是手动添加一些段。+ C, b! o. C* _( K: B8 p$ v. i

( p8 D6 p$ x' ~. u! y" g  t5 P( Z- w
三、添加SRAM

2 c0 h$ }' X7 K1 |段在axf文件中,IDA解析出了如下几个段:6 X/ g0 j( I* N4 F  E

+ f  S8 r, [! F! k- a0 U! ? 微信图片_20221129184805.png 7 T; h" e' ?6 r4 ?: S

( Y9 ~8 u5 H4 L3 e; k3 P, F单片机内存被总分为flash(rom)和sram(ram),flash里面的数据掉电可保存,sram中的数据掉电就丢失,sram的执行速度要快于flash,flash容量大于sram上方的最低内存地址,最高地址,都是在flash和sram中。
7 T7 {3 c$ x% o+ i$ X
8 |0 |: h! p8 a- `3 L+ L2 E0 N我们正常下载程序都是下载存储进flash里面,这也是为什么断电可保存的原因。
8 {/ g" a  g. s" c8 d) T( `
: K" d6 W8 D+ t单片机的程序存储分为code(代码存储区)、RO-data(只读数据存储区)、RW-data(读写数据存储区) 和 ZI-data(零初始化数据区)
7 D. A, m+ Y; |/ X: v5 X. Z/ s& w: bFlash 存储 code和RO-data
; V* C# Z1 `" v% wSram 存储 RW-data 和ZI-data3 f2 B% C2 X3 ]

8 f7 n1 x& r/ W2 ]8 _) p) z- z4 q, h- Y2 J0 _
在datasheet中一样可以找到sram的起始地址,为0x20000000,结束地址为0x2000ffff,我们自己添加段不必像IDA自动分析的那样详细,一个sram的段即可。9 Q2 y7 [6 M& G' s& X4 w  ?+ |/ d

# x" ^# R9 T7 A9 o( l4 ~. R, o5 B& i+ l也可以在IDA启动时设置好固件加载地址和sram的开始以及结束地址。
! }# k0 C8 m# v- o添加了sram段之后,红色的非法地址消失了,如下:9 W6 [5 s' S' J& M( z% c
3 j7 ~) n1 _3 H3 H  `9 V
微信图片_20221129184756.png 6 S" t- Y0 k2 ?6 l. I& }; S
6 H8 ~4 @  n8 H! A$ v4 |7 X0 U
7 I9 C" d0 G0 c" b  ]
四、恢复符号表
( u, m  u, C) a* i  z下一步,我们需要恢复符号表,由于bin文件不存在调试信息,所以IDA生成的伪代码几乎没眼看,为了方便我们的逆向,我们需要恢复函数名以及导入一些结构体。在这里就需要用到axf文件。
, J& ~5 @. P8 }2 V5 F8 z6 k8 {. i
& u2 n+ T( e: }% |. T# ]- ^+ W在前面我们看到axf文件几乎和源代码是一样的效果,里面有丰富的调试信息,当然,我们不使用这个课设的axf文件来恢复bin,这样就太刻意了。
1 z9 {! W  r5 b7 ^! s9 x: j, B* \7 t9 S
由于我这个课设使用的是库函数来开发的,所以就不能使用stm32cube生成的hal库来恢复。具体该怎么恢复?我们采用bindiff来恢复符号表。
  A/ L/ j4 `$ N3 t# V. m+ Q/ T
6 X; [8 l9 M% S! U, |  \* P! c如果有闲工夫或者是对stm32的开发非常上手,就可以自己写一个demo,尽可能多的使用到各种库函数,然后编译出一个axf文件。我这里的话,由于好久没有用stm32了,开发起来有些生疏,所以就不自己手写了,我选择捡现成的项目。我学习stm32的时候买的是xx原子家的开发板,他们提供的例程也是非常丰富的,有五十多个,由易到难,涵盖了stm32开发的方方面面,因此我就直接拿这些例程中的一部分,编译出axf文件。比如下面这个内存分配的例程:( l7 Y; x+ Z2 i- c

+ e" B0 s' y6 P( Y6 j# N8 V) m- T3 m3 ^4 l5 F7 N: i; U. b  p, u
. M3 g( y$ R& }/ e( l6 ]/ U3 {
又比如这个pwm的例程:
- n' K0 n; h4 O, @, v
/ E& E9 R" w& b# }8 v. d8 D; B9 n 微信图片_20221129184753.png
0 w$ F/ H# p# y5 _% T! p( i* y- Y1 C) F$ x
可以多选几个例程,能涵盖更多的库函数,将这些axf文件用IDA打开,然后生成idb文件。然后在我们的目标bin文件中,使用bindiff加载idb文件。
! G& z4 u7 `1 i. a8 c" s" l; b# H; f, V0 @  E, R2 E) u$ ~
微信图片_20221129184750.png
1 x9 B" W; g" G  S5 t( E9 G5 c% j8 ]: f0 v% M5 q; j0 S. t# K$ ]( o
# M% X% k2 g0 m: M1 K
选择一个idb文件,然后会出现这样一个比较界面:
! ~: ?6 C$ e+ M$ N! a2 |0 z" P
9 E5 [% [- f$ _# F 微信图片_20221129184747.png 3 x3 k2 ^, [0 ]( r/ ]
4 D; X- C$ r4 Y- J4 g. J

8 S/ j4 h  E  B$ c3 z7 k$ G选取similarity大的函数导入到bin文件中。! W. T5 j, U7 [5 l( V2 I
0 I* \2 `' x+ l; W$ |$ v& T
微信图片_20221129184743.png
" P1 n- F* w- Z, f* I( J" S% c8 f3 n5 R& x

% z3 e5 j# G  ^* G1 L导入之后实际上就能恢复大部分的函数名了。
. ]; O% x9 H* I( Z* _5 a3 Q8 q# t7 a2 ^/ j. Y
/ N" d# P4 X9 E" \# L1 o3 ]
微信图片_20221129184739.png ( S" {# {! [1 v  J

8 z3 v0 ]# p. j. S+ d. ~3 ]/ u0 p" P3 z  }% i, h% Z" ]( k
但实际上,由于我这个课设的开发中使用了一些xx原子封装好的初始化函数(相当于xx原子对原本的库函数做了二次开发),例如uart_init,delay_init这两个函数均是二次封装后的函数,所以恢复出来的函数会比一般情况下多一点。) y+ L( v% L6 {, K2 H5 [6 v

: n) ]# ~2 u7 u% o( H# a多使用几个idb文件进行恢复后,已经可以看了。
% _, u8 R7 w8 V! i8 I0 s: h6 G9 h, E  o* Z. w

' C, G6 s. w, d" N+ G. d! c1 `! P 微信图片_20221129184735.png
$ q+ \3 ~; o: i( r& C! e+ i  \4 ?7 H5 P' v+ ]. l

3 H1 u8 P& n( k: x! W+ |2 |! r五、导入SVD文件,恢复外设结构
- m  I3 L  |2 W6 a0 Z) e

: X# x# j8 n& m6 a, T* `! Y" A但这还不够,我们还需要导入SVD文件,啥是SVD文件捏?
& q2 n/ f7 ]; I6 B# H/ l7 \7 @/ i# J9 U! Z* p0 P9 ^3 z
微信图片_20221129184732.png
7 y9 L0 t3 G8 Y$ F9 g, V& z" g" o$ q: m  I/ d+ h$ J. y

+ r2 T0 @8 Z4 L& e* y在IDA7.5以后,就自带SVD文件加载插件了,如下图:
8 b# [3 }. o$ _( `. t3 B5 C4 ^, Z. z+ n$ m7 P/ A/ x' ~

. ~4 K+ D6 h: n, L$ K$ A 微信图片_20221129184728.png
8 I" t: m3 M8 V, v1 Y% \- k$ k4 M) n0 x* g
打开之后如下:( S" p5 N5 H5 _3 r8 m% Z) g3 R% x9 R; }
3 i( \; H# |/ B7 I
微信图片_20221129184723.png
# K7 ?% O* v3 \  L$ {
; l9 f1 o5 X- Y- [/ V( _# }+ v; G" `: ?0 i* h# `) }
我们可以自行下载相应的SVD文件,或者加载GitHub上的仓库,我这里选择自行下载然后在本地加载。下载链接是这个:cmsis-svd
! \! L( L+ y% j/ D9 ^) E+ a3 C1 I选中想要加载的svd文件之后,IDA就会自动恢复bin文件中的外设结构,体现在伪代码中就是这样:! Z4 o0 J) {& x) y6 o
6 E* u' \6 L5 U# }0 U
微信图片_20221129184720.png
% f: Q& E' {9 z+ [( M& z0 Y2 u

7 n3 f7 a) ]% ?+ Q这样:
. X7 n$ q& [9 U' _4 S
, R( x- \. B3 D5 C+ U$ N! q; `  s
9 [1 X: g4 I6 L! X 微信图片_20221129184710.png 0 x! m$ ^5 }- w$ v; b0 F% c
1 j/ l# T; x7 N& p
) \0 _9 u2 G" y; K
当然,也不是所有的外设都能恢复,和axf文件中的肯定是有差别的,但对我们的逆向已经起到了很大的作用了。6 N$ S  B/ ?& V: E/ X8 J

* D/ N/ M5 k  @6 Q3 S! \  G2 t到目前位置我们已经做了如下几步:加载bin文件->设置固件加载基地址->添加sram段->恢复符号表->恢复外设信息。程序还原工作已经接近尾声,还剩下最后一步,恢复结构体。
$ H4 h! b% D6 _( s; I$ U) U1 ?: g( \9 `; ?! j( l' v% X( s* ^' w
六、恢复结构体
. B2 _, T! {' y2 ]4 {( F! R" I打开某个例程的axf文件,可以查看下其中使用的结构体。
8 k# o5 R& @0 r0 c  B
$ ~0 G. O0 G5 E+ m8 j6 Y 微信图片_20221129184705.png
: U" R) {, L! F+ v4 f( `0 x. I% \2 P( M
这些结构体中,有TIM_TimeBaseInitTypeDef,NVIC_InitTypeDef,GPIO_InitTypeDef,USART_InitTypeDef,GPIO_TypeDef等是我们经常使用的,用来进行定时器初始化,中断初始化,IO口初始化等等。/ `4 \2 _- w' a
2 }" O0 L) u+ x" ~4 F- R6 i
我们生成c header file。& O8 c9 N1 b# F  [9 D: L
0 i+ f, Q; e  C1 [1 K3 x
微信图片_20221129184702.png
$ r' |. M' u. Q$ B& {+ N
% j# S4 G+ Q/ \6 ~4 P& }& T% ~然后在bin文件中解析生成的头文件:
0 z; a  G9 M! Y: i- V8 m: w3 x9 _( T; D
/ x' j; ?) P1 `( `& S$ Y  ~5 m9 m 微信图片_20221129184659.png % U( W, _: G* |; i  W

- r; w0 a; f) D然后在local type中就能看到不少结构体了。
. _9 s8 J6 j6 l! |3 ^" a+ x6 V$ b8 @
微信图片_20221129184654.png . T8 {1 C1 t& ]! k$ Y- \' n
! S& `+ v: `) {$ d% j, Z
光导入了结构体还不够,我们还得手动将IDA的伪代码中的变量重新设置类型,怎么确定某个变量的类型是什么呢?需要结合库函数的变量类型来设置。0 O6 b, Y$ v! J, o7 {

/ F0 c3 i  L/ y5 C4 ~以GPIO_Init这个函数为例:3 C- \; Y1 r0 F, e+ Q3 q

9 \8 W7 ]5 z- c8 D% K 微信图片_20221129184650.png 7 C7 B5 M; V- l! I& M
7 \! q' O$ A8 l2 \# H1 C& i8 c& t
我们打开固件库使用手册,找到GPIO_Init的原型。" r- h* V# v& ]  G
! l7 P  r6 |7 U) }) r2 T/ E0 V
9 V: d$ A& I) m
微信图片_20221129184646.png , _% R4 \9 G* ~, A/ y2 ?! @
, m' q0 P9 R* w( X6 A: O2 [+ w
可以看到原型为:8 H: w4 |% u: }
$ y+ s2 t, S+ P# ?) m( W- F
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
0 |' c0 s9 F, ]9 O: \& N
: N. X: `/ Q2 _0 N4 K( @3 Q  D/ `第二个参数为GPIO_InitTypeDef类型的,因此我们将伪代码中的v10的类型修改为GPIO_InitTypeDef,剩下的结构体我们也一样可以使用这样的方法来恢复,只要有固件库函数,我们就能够恢复对应的结构体,效果如下:. K# t9 w) t9 I3 E+ c3 C; `. k! E
. s$ h; S8 V' z8 e- J

5 ~! z' x% n, Z6 _: \
) B* j- W9 N8 [9 y1 w( p- f! R4 w9 C% g* u3 x

+ p8 e8 d+ D- L  y5 W+ j8 P2 e" h$ u- G效果还不错。. c* Z' q# r- a' N! O

" q, V1 W% z- n4 `- c
4 f9 A' ~' d. E. \" ?" u) P七、总结- u  ^8 M+ o. h% z0 [9 N# a
此次逆向是基于我的一个小课设,功能不多,而且由于我的课设用到了xx原子的例程二次开发的一些库,而我用来恢复符号表时使用的axf文件也是xx原子的例程编译出来的,所以恢复出来的符号表的比例会偏高。如果能确定使用芯片型号,以及使用的是固件库还是hal库,那么按照此方法大致上能在一定程度上恢复固件,使其可读性大大增加。; ^5 k( I- Z& P; i3 F6 o) p
" e. |7 I2 h" T
转载自:Lpwn3 K# X8 [* [. e8 l  m* r

, v  E; N4 `$ H, a4 {- N( W* g9 N& Z7 o: }2 ]' ~( G- d4 E/ {7 k/ Z
微信图片_20221129184800.png
微信图片_20221129184404.png
收藏 评论0 发布时间:2022-12-1 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版