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

STM32固件逆向

[复制链接]
STMCU小助手 发布时间:2022-12-1 18:00
一、概括. f& W' Z2 T# b
用的很久之前做的一个电风扇的课设来做实验。
0 Y* G% w' `# O3 y7 b' X8 c/ r+ l' c
微信图片_20221129184848.png ' }3 G! U2 x! o0 q& S% [4 L
3 |% w2 \& F  D' _! b
5 F, I5 g6 p' e3 F. v; H# _. m. z2 \
用到了外部中断,定时器中断,pwm。MCU是STM32F103ZET6。
( a6 j0 I% [6 P8 ~# {9 G$ q, c# x" z, s) D, `8 H0 N
微信图片_20221129184844.png . }2 a( b- B8 z3 h8 g) n( x
& K: F, W# ^- W  A0 U
4 q: c: @& ^- ]! I+ v, ^+ H
在keil的设置中可以看到固件的起始地址,默认情况下为0x8000000。另外,keil生成的是hex文件,hex文件是带有基地址信息的bin文件,如果我们要生成bin文件,需要进行如下设置:$ ?+ |" {9 X9 o8 E$ |( l: J

9 f; f: b9 e" g7 R) |0 B0 h) v5 T
微信图片_20221129184839.png
7 h; e0 A- g( a( t9 m
/ p* Z, V1 I$ V  A! z" h% V1 D$ F另外,烧录到mcu中的固件是不带符号表等调试信息的,即hex和bin文件都不带调试信息,因为mcu的内存有限,但keil会单独生成一个带有调试信息的axf文件,我们可以以这个axf文件为基准,对照着恢复bin文件或者hex文件,下面我们来观察一下这三个文件的区别:  V7 @1 U, V) u0 v
首先是axf文件
) n, |  P1 F* a. |8 {# S- E7 f  R+ t, x8 O) {- u. b3 E/ K8 ~
微信图片_20221129184836.png ' t4 X; S+ Y) G( H; B3 e0 ?1 b
微信图片_20221129184833.png
9 ]. ~# ?# r, ]4 U/ d6 G2 r$ c  t$ ~
& L' ]' @+ |6 W; h9 y: R几乎和源代码一摸一样,除了一些外设名用地址代替了。1 H/ {& j" a6 K6 ^; d

, D  X0 f& Z" f6 H2 }再看到hex文件:
- h/ I; J7 N- g) J  y* C0 h  {7 A# ~8 b& P) z7 v- `, f
微信图片_20221129184829.png ( F5 t# _0 e$ y+ X8 J8 Q
- }8 Z; M2 X3 f% ]
首先需要将架构修改为arm小端序,再修改一下架构版本,stm32f103zet6是cortex-m3内核的,armv7架构。
. O9 _$ A( a" _# r  z- U6 Q6 w8 Q, B7 P; m9 ^

! _8 o8 n4 w5 n$ l 微信图片_20221129184826.png 6 O( D# L( x# c$ X, d/ w

, c. Y% N7 F5 P) ~0 q8 j! d& F  [3 A4 ~, v! w/ H1 K
修改完后加载hex文件。# N8 @6 M* k6 V& I+ O
3 u6 u2 S+ J4 C+ r" X2 _8 {
0 r0 b4 A" {( D; q* N
微信图片_20221129184824.png
& u* ?4 S: H% M1 Q  L
6 K4 v* }. y  N+ R3 |4 C7 A& S9 I4 u1 w& u( S
IDA会自动识别出基地址,能识别出不少函数了,但伪代码是真的难看。
" T6 z: i6 \% h2 ]# |  x" @  J% e- X( s) g
微信图片_20221129184821.png
4 d( E9 t+ L# D  P  f/ c9 Y- R) a" h9 `5 B, l8 w8 X
5 W) |/ q4 v5 w1 V4 ~8 l( Q7 l
基本上是看不出个什么东西来。5 u0 R: A; }+ V2 p! M5 u, D

' ~7 M# |+ @& i8 g/ g最后是bin文件。' a# e/ C0 T% K* E/ o3 U

) z! v6 S' S& J0 q
0 ~, Q0 |4 A" v/ k, D2 t9 e# k 微信图片_20221129184818.png
6 {. \0 x* l" G
  L! y7 [4 l; c( v* z3 p: nbin文件去除了地址信息的hex文件,需要手动设置基地址。9 u, v0 |# Y" H% ^+ k" @' V

( l1 E4 @/ I; \5 D+ n. ~! R& Y
# b7 s3 T: F8 W' y0 U; X; ]! z0 t# E, O3 L+ N" s
二、手动设置固件基地址

  w6 N# {; H, Q. Z: H. G! |4 k4 Y1 ]7 B
我们的分析将以bin文件为目标。7 N/ U2 [, V, O- J; B

% o. x6 J; i6 t- P) F7 |将基地址重设为0x8000000,这个地址怎么来的?可以看stm32f103zet6的datasheet。4 ~1 R! l% U4 I/ i& }  ^9 @! q
5 O% r$ l3 q1 G3 G5 k
微信图片_20221129184815.png
$ ]2 B  L6 l; N0 g% C' o4 S3 t7 F2 o9 S( N0 I

* Z5 C/ Y0 C. b8 ]1 E- ?9 q0 Y" Q# p从0x8000000开始就是flash的空间。3 M* M5 g4 u% k$ Y- A& d% _

* m3 q0 ]& B% x0 D+ j重新设置加载地址之后再将其按照代码解析就变成了hex文件的样子。
; z& T# C+ B+ Q8 o( G) y  z" y+ }6 y& K& v& Y
( |9 A+ S0 ]/ z7 o
微信图片_20221129184812.png - ]7 c! Y% L% S6 ]" k5 q0 i

* z  l$ d+ P; [) o! G& ]1 k
1 {% R0 ]9 ~$ @1 y' H2 @  F但此时存在很多标红的地址,这是因为这些地址在IDA中并没有设置,因此IDA将其解析为非法地址,标红了,我们现在要做的就是手动添加一些段。# [8 Z) r1 z& ~0 x0 M" k  m

) @+ k) |( x1 R9 A) g3 t. {  }. K8 z* a
三、添加SRAM

: z0 v  z" F* u$ S3 u) I段在axf文件中,IDA解析出了如下几个段:3 S! |* l7 T* I  B% Q: F$ n

7 Y" X( ~6 b+ G+ E( s 微信图片_20221129184805.png
2 k4 m% j/ E2 t4 N3 N/ Q# i) v# O9 \- S
- z# K4 H& e% O& F2 g单片机内存被总分为flash(rom)和sram(ram),flash里面的数据掉电可保存,sram中的数据掉电就丢失,sram的执行速度要快于flash,flash容量大于sram上方的最低内存地址,最高地址,都是在flash和sram中。* Q' ^* |- m2 M; @/ o6 ]6 E

5 p9 B; E& x! G我们正常下载程序都是下载存储进flash里面,这也是为什么断电可保存的原因。
# J+ B+ B  W# y' g. d: Y
/ n. H. q7 G) g1 h单片机的程序存储分为code(代码存储区)、RO-data(只读数据存储区)、RW-data(读写数据存储区) 和 ZI-data(零初始化数据区)
0 f# T0 c/ Z; _9 dFlash 存储 code和RO-data8 Z8 h  W0 x/ a, e* z' M/ t
Sram 存储 RW-data 和ZI-data
  s' E+ o! z# M7 X& r. ?8 m
+ q8 m. g& _, a4 x+ o* Y
& k  b: E8 F: l" K在datasheet中一样可以找到sram的起始地址,为0x20000000,结束地址为0x2000ffff,我们自己添加段不必像IDA自动分析的那样详细,一个sram的段即可。
+ V, J/ x" `7 m; w0 u( n! Y, L0 C% b7 z
也可以在IDA启动时设置好固件加载地址和sram的开始以及结束地址。/ _) ?( N3 u: b7 F
添加了sram段之后,红色的非法地址消失了,如下:/ y& D7 H7 K$ b) V/ Q

' u8 Z* w( {- O( R5 ] 微信图片_20221129184756.png # d7 \' L: b/ K; p8 w
) C5 \% @$ ]; t
; p4 W# Z  Z  ]1 v0 d
四、恢复符号表
/ y% i8 v. x( \) c下一步,我们需要恢复符号表,由于bin文件不存在调试信息,所以IDA生成的伪代码几乎没眼看,为了方便我们的逆向,我们需要恢复函数名以及导入一些结构体。在这里就需要用到axf文件。8 O+ _2 x& J' |5 o+ w
1 i8 K' g4 ^- b4 {' ?4 f& H
在前面我们看到axf文件几乎和源代码是一样的效果,里面有丰富的调试信息,当然,我们不使用这个课设的axf文件来恢复bin,这样就太刻意了。, `' @: v7 j& w. V. V; z  B

" _; P  R6 V7 v5 ]/ y由于我这个课设使用的是库函数来开发的,所以就不能使用stm32cube生成的hal库来恢复。具体该怎么恢复?我们采用bindiff来恢复符号表。! D5 s, }7 Y9 ~* p2 \2 j
/ @# \& M% }; p9 P4 ~3 Q0 o1 m; `
如果有闲工夫或者是对stm32的开发非常上手,就可以自己写一个demo,尽可能多的使用到各种库函数,然后编译出一个axf文件。我这里的话,由于好久没有用stm32了,开发起来有些生疏,所以就不自己手写了,我选择捡现成的项目。我学习stm32的时候买的是xx原子家的开发板,他们提供的例程也是非常丰富的,有五十多个,由易到难,涵盖了stm32开发的方方面面,因此我就直接拿这些例程中的一部分,编译出axf文件。比如下面这个内存分配的例程:4 \' j$ w$ h7 D  \8 v% X$ Y# n
) T+ g; J( ?! [+ T

- g, J- x0 n: l: P
* C+ ?6 t; v- _' Y0 z% y! f又比如这个pwm的例程:$ ]! u' `+ u: n4 L$ l8 \

/ Q  S* k; h  w  |. a* f' g8 Y 微信图片_20221129184753.png
8 d, ]3 E; V3 i) g( R+ U9 X9 |5 @
, S9 P9 b: b% `# D- I9 W: {6 u( N2 B可以多选几个例程,能涵盖更多的库函数,将这些axf文件用IDA打开,然后生成idb文件。然后在我们的目标bin文件中,使用bindiff加载idb文件。
4 t$ f6 o. L& n$ ]4 z0 ~& h# ~" B+ _7 E* |  z3 M
微信图片_20221129184750.png
7 `- g9 W1 V& H, U& d; H4 s+ d  R( L9 x1 B5 _

5 n# k$ J, a: M! _1 X( R) O选择一个idb文件,然后会出现这样一个比较界面:
# z2 z+ I) F) S3 g
/ Z5 w7 n5 h- Z" z0 v% z 微信图片_20221129184747.png
% g/ `( M' n. s5 R
% m7 D# _! m' v) j
; o, A" a0 s3 `' \选取similarity大的函数导入到bin文件中。. b  I, l; R. m+ P# {/ m# p

6 t' A# b# g5 c. H0 g" X 微信图片_20221129184743.png 7 v) r7 R7 o7 m
* S5 u: h) Q* t9 @

, i$ |" B. [; v& L, e4 N  Q% O% d导入之后实际上就能恢复大部分的函数名了。- K) l6 c! a+ _+ Y3 r
4 C9 K: {% Q5 L9 ]5 U$ J. Z
% a. w) B) f- l5 }
微信图片_20221129184739.png - I& d, `& v9 n" D4 b$ \

$ C( f! j$ g' v  l" i! X1 J- m, Y, t8 _( V7 d- x! _
但实际上,由于我这个课设的开发中使用了一些xx原子封装好的初始化函数(相当于xx原子对原本的库函数做了二次开发),例如uart_init,delay_init这两个函数均是二次封装后的函数,所以恢复出来的函数会比一般情况下多一点。
) N  |! v# T$ l- W! @' {8 Q6 b2 R0 ?  K# t/ [- P# W# J- L2 n
多使用几个idb文件进行恢复后,已经可以看了。
+ e, E; [# X# {) X( f8 t1 p& h. ~) a, v% a: m
- K3 ?6 }+ j# a) {/ S. j  i, |* _
微信图片_20221129184735.png : Z8 I, E% z, v% s
1 ]3 [( \9 _& N9 w: U+ e

' M, c2 u8 x$ j五、导入SVD文件,恢复外设结构
& p+ i* v, \7 x& J- r4 l

* j6 N& W3 ^1 D  f但这还不够,我们还需要导入SVD文件,啥是SVD文件捏?( \. x) @6 m, Q1 Y- I

- A$ ^- z9 F+ r5 [0 `0 L' ^ 微信图片_20221129184732.png $ i" t: U6 ]9 K6 |
9 ~( k/ B- A* w+ ]8 c

( Y3 E+ t/ A7 l在IDA7.5以后,就自带SVD文件加载插件了,如下图:
# k% h: N' N$ }$ ~7 g
! J% j. H8 i9 k' K
% M: ]7 x. r$ v% }4 Y. S" S6 } 微信图片_20221129184728.png
  Z: E0 m8 }: U/ s1 G
9 a& Z! _/ z* z0 }. d' u0 m打开之后如下:
$ V$ q. `0 @+ \& m4 |- x
" d& g0 _+ f  I+ s( g0 [  V7 l& H8 E 微信图片_20221129184723.png
, e; X- W2 `! E; Z9 _( Z3 A! _5 k3 {1 V

  O3 A' X6 J, L; [5 P1 ?1 P我们可以自行下载相应的SVD文件,或者加载GitHub上的仓库,我这里选择自行下载然后在本地加载。下载链接是这个:cmsis-svd
$ T) s% `. l- j" F选中想要加载的svd文件之后,IDA就会自动恢复bin文件中的外设结构,体现在伪代码中就是这样:
7 X0 H5 i" B& i. v- \$ U
( H# ~1 Z& J+ X% h& W! s 微信图片_20221129184720.png . V" |( b: k/ |8 R) ?) N, h. r8 U* m
7 J6 l7 A# V; j
/ j: T, U- H! G/ {( o0 [
这样:
+ K  Z+ S5 H( |6 w0 `
4 ~3 \+ M& H# W$ m
8 @& f) f9 A' z; x+ S/ l 微信图片_20221129184710.png # N: E+ s; W9 B! o# a: J- P

5 c- z/ C- G% @# ^$ v4 ~, E# n
$ [9 O. @& D, l: j当然,也不是所有的外设都能恢复,和axf文件中的肯定是有差别的,但对我们的逆向已经起到了很大的作用了。
% U3 O) `6 {( k8 k0 z* d6 p0 |7 j1 E& J6 k. `$ c9 j
到目前位置我们已经做了如下几步:加载bin文件->设置固件加载基地址->添加sram段->恢复符号表->恢复外设信息。程序还原工作已经接近尾声,还剩下最后一步,恢复结构体。# h# u8 v: \8 D4 X

9 l, f( q; N/ ?- N# U7 Z* }! p六、恢复结构体: ^  ^- t* U! B( }3 e( U" w, v
打开某个例程的axf文件,可以查看下其中使用的结构体。
0 a* Q" J6 v( [; \" x0 `4 H. E& [
( X- l6 F' b0 @0 H3 \ 微信图片_20221129184705.png 3 ~  O; P+ ^; T5 h* |
* q1 _, n8 m8 n( Z. O
这些结构体中,有TIM_TimeBaseInitTypeDef,NVIC_InitTypeDef,GPIO_InitTypeDef,USART_InitTypeDef,GPIO_TypeDef等是我们经常使用的,用来进行定时器初始化,中断初始化,IO口初始化等等。! ~* r: x+ a& S% T

# T( F* s' A: p% q6 n' w我们生成c header file。
& f8 Q9 V2 {$ ~, R) I
8 x3 [  K5 R. H7 P& L4 o% { 微信图片_20221129184702.png 6 I' g; t9 V$ \% _

! a7 t  r7 j( O" N8 f3 {" a然后在bin文件中解析生成的头文件:
* m. r- ?, S  Z  _& O6 G) m' n+ O) A" J
微信图片_20221129184659.png ' t5 V# {8 L9 ~
( K9 v/ J& e5 e' ^3 z. t! ]
然后在local type中就能看到不少结构体了。+ D3 j; T# j7 r, l/ M$ m& H$ C

5 x4 ^% g- B+ H% _/ b 微信图片_20221129184654.png " S/ H& I( B  h+ v: a, N  p% X2 ]
' y2 F, M1 _7 b* ]* Q: s5 h  ?* p6 U
光导入了结构体还不够,我们还得手动将IDA的伪代码中的变量重新设置类型,怎么确定某个变量的类型是什么呢?需要结合库函数的变量类型来设置。' A5 i" C8 i/ a/ Y# t
! [1 i% t0 I. r$ c$ c  F
以GPIO_Init这个函数为例:
/ O7 C* l# ?& y- a7 \0 L2 ^0 B2 e7 C9 @4 U) ^, n+ G) s3 E7 P+ W
微信图片_20221129184650.png
5 G8 I+ l2 V5 J
$ F6 B! ~3 J2 E1 l我们打开固件库使用手册,找到GPIO_Init的原型。
- g7 t& @* {4 x- x- }2 x* D. n2 u# ]0 y% O  x. d% M( M

# @: ~1 |; r. G* }) { 微信图片_20221129184646.png
0 f9 c# {. P; |0 m+ `1 f, h* \; a3 I# n3 l$ T% B7 W
可以看到原型为:
, @6 L6 R: C6 t. o+ U5 s( `5 @! r2 Y; F, F3 M2 E
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)3 m9 L8 F1 R8 O+ }$ ~4 N/ a

$ Z: e* h% v; F0 T第二个参数为GPIO_InitTypeDef类型的,因此我们将伪代码中的v10的类型修改为GPIO_InitTypeDef,剩下的结构体我们也一样可以使用这样的方法来恢复,只要有固件库函数,我们就能够恢复对应的结构体,效果如下:
3 c3 [# q  w' Z- X. F
+ Q1 F" o( U0 P" I/ I9 t: G" W& f. w: P+ Y2 O

& ~/ \. `6 O/ J8 d0 B) ?$ ]# \  a. ?( a, |
# c& Q- {1 H% Y1 x
效果还不错。1 h& @( h1 f: I1 B% l

# X; U" |0 s' P: Z$ C2 {! k& X: D4 \: u6 Z3 _+ Z( i
七、总结  H8 Z" }0 ^# k1 k- a
此次逆向是基于我的一个小课设,功能不多,而且由于我的课设用到了xx原子的例程二次开发的一些库,而我用来恢复符号表时使用的axf文件也是xx原子的例程编译出来的,所以恢复出来的符号表的比例会偏高。如果能确定使用芯片型号,以及使用的是固件库还是hal库,那么按照此方法大致上能在一定程度上恢复固件,使其可读性大大增加。
$ g8 K  ~- z( W( v; s
0 O0 |4 u. d: q. \# Z6 d: l5 U) Q* H2 \转载自:Lpwn
5 |" y1 c8 Z* U) H  i
, M$ g: _6 i+ i
' }" U* L+ j3 G- l" {2 p
微信图片_20221129184800.png
微信图片_20221129184404.png
赞 收藏 评论0 发布时间:2022-12-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版