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

STM32固件逆向

[复制链接]
STMCU小助手 发布时间:2022-12-1 18:00
一、概括6 A* o! r1 ?. @, W* y8 \
用的很久之前做的一个电风扇的课设来做实验。4 u5 c  w9 C- W# f. M
2 l9 E+ ^. t5 }- P
微信图片_20221129184848.png
5 U. L- n- Y! y- ^
. R; Q0 r/ l3 C0 U, Q5 t  K
9 m. d0 g# H% m2 {# c) ~用到了外部中断,定时器中断,pwm。MCU是STM32F103ZET6。
, m' g& L- \3 [, ]. L+ G* b
5 a- E, E  M# I5 f2 o 微信图片_20221129184844.png
# I4 ^9 }5 S: B5 z1 M! G* F& o2 p3 R6 t" i* C( s0 M7 R

' ~8 {  c) `) ^4 V% u3 ^在keil的设置中可以看到固件的起始地址,默认情况下为0x8000000。另外,keil生成的是hex文件,hex文件是带有基地址信息的bin文件,如果我们要生成bin文件,需要进行如下设置:
! h# s- i  ?1 [  Z3 t1 u. w! U( b, F" v' r! }. V) j
- |( }% _& }+ G2 Q9 @3 A  r
微信图片_20221129184839.png
' m! O' n. L& t, w# b
' A. ]- ^' U/ h( y& y- m7 s7 B! e另外,烧录到mcu中的固件是不带符号表等调试信息的,即hex和bin文件都不带调试信息,因为mcu的内存有限,但keil会单独生成一个带有调试信息的axf文件,我们可以以这个axf文件为基准,对照着恢复bin文件或者hex文件,下面我们来观察一下这三个文件的区别:& H# I7 Q  X. `8 J3 ?% p
首先是axf文件
4 ?. I1 R3 O+ i7 n2 o) E* b- h5 D  H  `  r1 X& Z
微信图片_20221129184836.png
1 r+ h0 v* \( ^# y 微信图片_20221129184833.png
2 T2 s; [' [0 m, m" h8 [9 ]" R4 x+ K& @% D  `
几乎和源代码一摸一样,除了一些外设名用地址代替了。) E6 K. \2 C7 ?" u5 W' g
) L, `) d$ F% Z$ b" H! `8 X
再看到hex文件:7 B, }+ l) Y0 p/ s4 c  e

& b) p- D/ t, F& u4 j 微信图片_20221129184829.png 4 Y% H# Y7 o' o+ ]) h, Q8 i
1 J% Z: s) z* |4 W# Z2 q& |
首先需要将架构修改为arm小端序,再修改一下架构版本,stm32f103zet6是cortex-m3内核的,armv7架构。' |; F6 w! }. K! u+ n4 {$ h
$ A$ c- M, D( ?+ S$ p6 G7 y

( `7 @, C# s: t- |0 u 微信图片_20221129184826.png   y- ?% c+ n# o+ {! _
* S6 m9 }# f; {- Z  y% B
' M7 Y9 V6 K, J$ |4 j& H
修改完后加载hex文件。8 e& i; e& v* R- H3 D9 H

* H1 C- o; i: O4 c
6 k3 d# h# T# u# s9 N, M3 V( M 微信图片_20221129184824.png
3 e/ U) z  l5 B, \- k+ F  i+ p; \* A" L% m1 w9 z8 r  Z
  x- ?; P' {; }) l' c
IDA会自动识别出基地址,能识别出不少函数了,但伪代码是真的难看。# b( n* ^5 |5 {7 z* f/ U
" x# v0 @( b: f( g: e- E
微信图片_20221129184821.png
( i9 o- ]$ n8 b" W# q0 Q$ X7 X4 G' l. @3 l

) P$ W7 ?& H2 ^% k% E5 l  W基本上是看不出个什么东西来。# \/ l" U$ ]6 R3 p0 k; e
5 U1 ?% H4 r$ \) a
最后是bin文件。
9 V, s3 R$ j6 ^, i8 n. o" ?2 K4 @3 e' l9 H" T* y

( U: l' {& A4 E- z' a- N8 j9 Q 微信图片_20221129184818.png ) @3 F' c% ]* V- c, K

1 b5 j# ^. c: f; Q% g) a( \9 ^bin文件去除了地址信息的hex文件,需要手动设置基地址。
- U2 Q7 Z, t4 ^) m' p* S
- j# S0 Z2 b7 D: l( W% m* L9 Q
& L. C. b( V4 ^3 t% T7 {( Y' U0 b) _9 [5 s: j* k& [" @0 i
二、手动设置固件基地址
, v7 k# F& a1 N7 H4 n( ?

1 r* h4 ^7 \  z) V我们的分析将以bin文件为目标。) J9 c$ q$ Z  ]1 ^6 n

7 s$ |4 l  ]. o( ?$ f( ]将基地址重设为0x8000000,这个地址怎么来的?可以看stm32f103zet6的datasheet。
8 p* Q+ f% @( g- P8 J& B8 P) G9 q( P+ J- [  c
微信图片_20221129184815.png - |$ b6 P' i7 E/ }6 T& Y& l) U

2 o* @5 x! T  {/ H- s
, i4 p3 O6 f0 D4 p! X1 s从0x8000000开始就是flash的空间。$ }) m( l' A7 N* y- {3 J0 o2 ^7 q, `
2 f  n0 ~5 t" S2 b' U+ y+ t
重新设置加载地址之后再将其按照代码解析就变成了hex文件的样子。) N" F  a9 h3 B! z2 D' D( C% j2 i
  C3 f7 O$ @7 |: J% U  j6 p4 z1 u% T

9 t( d7 F% v2 l. F7 H! u, R6 ~ 微信图片_20221129184812.png
0 \, q5 {9 }3 r6 t
) x* s2 L! N) l2 m8 ]" T) d- U- ~7 {" T1 U0 S- [( t+ O. \
但此时存在很多标红的地址,这是因为这些地址在IDA中并没有设置,因此IDA将其解析为非法地址,标红了,我们现在要做的就是手动添加一些段。
. c5 d# w& {: @: n! s( x$ B; x9 \
0 f9 X# T1 ]7 d9 P8 r: U- I* W! P  i. z+ x
三、添加SRAM

1 d. f& f8 {' ]2 A% B1 }) n段在axf文件中,IDA解析出了如下几个段:
8 h3 z( d; z  `3 `, o. |7 K( Q: O2 j; t8 O
微信图片_20221129184805.png
+ P$ H/ u# @0 h1 W
6 G# A7 N" R* @8 g" n0 l单片机内存被总分为flash(rom)和sram(ram),flash里面的数据掉电可保存,sram中的数据掉电就丢失,sram的执行速度要快于flash,flash容量大于sram上方的最低内存地址,最高地址,都是在flash和sram中。
: W: Q! E, E4 X$ i. G* Q) n/ w3 X3 Y9 s) @. @
我们正常下载程序都是下载存储进flash里面,这也是为什么断电可保存的原因。
* P1 f. u1 o1 Y
0 Z! _7 I$ |, L, J4 ]单片机的程序存储分为code(代码存储区)、RO-data(只读数据存储区)、RW-data(读写数据存储区) 和 ZI-data(零初始化数据区)4 L  K) E& n+ Y% J$ b6 H9 Y) ~$ c
Flash 存储 code和RO-data6 V- G2 X3 A; t5 k( M' l# X( @" k
Sram 存储 RW-data 和ZI-data
' k0 |6 F& u: t2 q% z0 j$ [
  [0 J; Z& [8 O- T* u
  W4 O% E: i, g3 R! a& Y在datasheet中一样可以找到sram的起始地址,为0x20000000,结束地址为0x2000ffff,我们自己添加段不必像IDA自动分析的那样详细,一个sram的段即可。
' E4 m6 C* x! u" c+ d$ B
4 E3 _& B! i! v% b也可以在IDA启动时设置好固件加载地址和sram的开始以及结束地址。/ N+ h2 }# A# S" \  d
添加了sram段之后,红色的非法地址消失了,如下:
& A4 ]! v" E; z- {; I- }: N' D! y% X$ g0 a2 a2 Z
微信图片_20221129184756.png ' n; |6 [' f$ Q6 R- S

( E7 p, v5 M: b4 V
8 v5 _; D' R; \) q3 h7 }+ q+ J四、恢复符号表
4 [1 o; P. c' W下一步,我们需要恢复符号表,由于bin文件不存在调试信息,所以IDA生成的伪代码几乎没眼看,为了方便我们的逆向,我们需要恢复函数名以及导入一些结构体。在这里就需要用到axf文件。- Q' U; }* A$ ^

7 d  h( F. e* x在前面我们看到axf文件几乎和源代码是一样的效果,里面有丰富的调试信息,当然,我们不使用这个课设的axf文件来恢复bin,这样就太刻意了。
5 u1 y  V7 l9 w0 n) _3 d4 b8 q0 l6 N9 C
由于我这个课设使用的是库函数来开发的,所以就不能使用stm32cube生成的hal库来恢复。具体该怎么恢复?我们采用bindiff来恢复符号表。
0 N, {; v# q% g! z' }2 r
3 E9 V' c8 X! N如果有闲工夫或者是对stm32的开发非常上手,就可以自己写一个demo,尽可能多的使用到各种库函数,然后编译出一个axf文件。我这里的话,由于好久没有用stm32了,开发起来有些生疏,所以就不自己手写了,我选择捡现成的项目。我学习stm32的时候买的是xx原子家的开发板,他们提供的例程也是非常丰富的,有五十多个,由易到难,涵盖了stm32开发的方方面面,因此我就直接拿这些例程中的一部分,编译出axf文件。比如下面这个内存分配的例程:3 x" F" Y( A* B) K5 f& k2 V! d

" Q3 ^2 _5 v( ?+ z+ s& _" j1 S5 x7 C" l

: J3 r! \- N7 S( e5 o* o又比如这个pwm的例程:& w' A8 m# z/ Y' S% {" }9 q

9 T+ G3 [2 h, i. z0 z6 h 微信图片_20221129184753.png : y9 E' j' i7 X* d

2 p: u3 d- g/ ^) }/ d9 W& u可以多选几个例程,能涵盖更多的库函数,将这些axf文件用IDA打开,然后生成idb文件。然后在我们的目标bin文件中,使用bindiff加载idb文件。  n9 Z: O) u4 P/ N- \
2 E; ~, ]  |# g2 @" a4 m
微信图片_20221129184750.png
' R# D8 K; t" e! p' [- N2 O+ U* D2 g
7 F: E, M. v& w8 f+ c5 C
选择一个idb文件,然后会出现这样一个比较界面:
7 O7 p6 O1 U! e: ~  L! D
* c, H2 o% X$ D5 n8 W 微信图片_20221129184747.png
( M9 V2 K  }/ ^6 J- s. y3 T- w. b, i/ e) Q3 s" O
0 _7 b( T$ }" r3 i  k9 e  k
选取similarity大的函数导入到bin文件中。0 ?7 k0 _# }( j9 J1 S% R/ |

, ^6 v/ v) u6 U/ W( O 微信图片_20221129184743.png 9 M  A! P3 f4 _; a' t4 `

3 T# w. I) N; b1 d# c3 w. R, s0 h9 |( z: S3 l) R- q: [+ t
导入之后实际上就能恢复大部分的函数名了。% p- }+ m+ v& a

  x& R1 S: B' d" j
: H* F" P; C7 ~  F 微信图片_20221129184739.png ; x8 _1 `" a4 `, z6 I

& C: Z6 P$ r0 F! E; G- V3 P
: X1 K% p  ]4 k/ F2 N+ O2 M但实际上,由于我这个课设的开发中使用了一些xx原子封装好的初始化函数(相当于xx原子对原本的库函数做了二次开发),例如uart_init,delay_init这两个函数均是二次封装后的函数,所以恢复出来的函数会比一般情况下多一点。# t; a7 ]- G, [; i
7 G, h0 J9 M3 k+ \( o
多使用几个idb文件进行恢复后,已经可以看了。! f7 ^& B1 H1 f) G4 W

% C% g$ O& r6 Z) F2 B6 l- N
5 K0 b* |2 H6 r" d  n4 h 微信图片_20221129184735.png " o4 c/ ]2 h1 n% U) Z+ S' a
+ {% r% U) }+ l2 l2 {
. X7 Q' \0 |" S* _( M& h
五、导入SVD文件,恢复外设结构
  f2 f9 V# r* a

8 X  l* ?# R' J  e0 s* v但这还不够,我们还需要导入SVD文件,啥是SVD文件捏?, S( r: K( x  D) |5 `

8 s$ I7 M- I$ C0 I' m1 J) W3 [: x 微信图片_20221129184732.png
# P* F2 J$ e* c8 K9 h3 N
8 k. A8 ]& L8 A; [) u2 T8 @" u$ [% d) n, y) u
在IDA7.5以后,就自带SVD文件加载插件了,如下图:  h. E* i. I/ c3 C2 P6 p) D( K

3 H+ H+ _$ J- t: H% D4 n* ~9 C  K# b/ u% `
微信图片_20221129184728.png ! a7 Z( S# [6 z% I3 p
3 G; K9 d' v1 X1 Q0 V. m
打开之后如下:: }3 N( i) P/ K. {; K8 k

0 L5 }9 x( P3 g; k 微信图片_20221129184723.png
3 v. b& w. v2 L- l/ z
) Q3 c' K: z- }9 P7 n  E' `& d2 @
+ |: w2 q) F# M* ?% m我们可以自行下载相应的SVD文件,或者加载GitHub上的仓库,我这里选择自行下载然后在本地加载。下载链接是这个:cmsis-svd
) _8 i! z, O. p/ U) O7 o6 Q选中想要加载的svd文件之后,IDA就会自动恢复bin文件中的外设结构,体现在伪代码中就是这样:9 m( F! U9 N1 X* c. {; I
' N8 s; d" X) [; D, Q% y' S
微信图片_20221129184720.png & y" W# G" K2 U6 S
3 A$ w0 \) @  ]& ~

9 @* k7 @! b2 j5 e9 g/ C这样:
# J0 [8 y$ I: c# i, L
' a+ o/ p8 C1 G. i# n
9 Q' v  X9 C( x9 s1 j6 `+ @ 微信图片_20221129184710.png
8 B+ p* w" m' e5 o  ]/ l1 }% h1 m

6 C" Z1 ]7 V0 i当然,也不是所有的外设都能恢复,和axf文件中的肯定是有差别的,但对我们的逆向已经起到了很大的作用了。
9 l9 ?5 m) s' d# |1 {1 n- H+ `1 S1 R) ~" G* Z
到目前位置我们已经做了如下几步:加载bin文件->设置固件加载基地址->添加sram段->恢复符号表->恢复外设信息。程序还原工作已经接近尾声,还剩下最后一步,恢复结构体。
1 u: \% s+ P- y5 g, ?$ g
' n' |) y8 f1 F  W9 R六、恢复结构体9 _! u' g4 R: a) q1 y
打开某个例程的axf文件,可以查看下其中使用的结构体。
8 }5 ^1 D. d: D) {/ u: T% `# |2 c$ [; \3 t; Y. L
微信图片_20221129184705.png
$ \9 P5 i) n8 a; o+ c- I
  h! ^1 S0 ]1 `' z/ ^; l+ ^5 t$ z这些结构体中,有TIM_TimeBaseInitTypeDef,NVIC_InitTypeDef,GPIO_InitTypeDef,USART_InitTypeDef,GPIO_TypeDef等是我们经常使用的,用来进行定时器初始化,中断初始化,IO口初始化等等。
5 L  u- Y( e7 V/ O* V3 l6 m
  x% y7 z4 i4 \& _我们生成c header file。* w" I5 G% D' m8 f6 m+ F

: T/ e) x/ ~/ D) Z( X 微信图片_20221129184702.png
; M4 r3 F7 ?% Q# f3 C6 I9 H9 g+ L) ?0 q# E$ j& i
然后在bin文件中解析生成的头文件:
6 r0 V& H% e- j% U" [# v) V1 B+ u4 F$ g: r5 z
微信图片_20221129184659.png 0 J1 G. ?$ `4 E9 o; H9 ]8 f( h
+ Q# W9 z4 b( s8 w5 e4 ]
然后在local type中就能看到不少结构体了。
  K$ r, Y/ A6 J; t/ X  f2 ]3 J: N- i3 L
6 x' B% M" W3 n, M3 k 微信图片_20221129184654.png
, Q6 p, J; O- j. {5 g2 I1 `& q5 F
# O: V* H9 r, \3 X: R2 |( y" c光导入了结构体还不够,我们还得手动将IDA的伪代码中的变量重新设置类型,怎么确定某个变量的类型是什么呢?需要结合库函数的变量类型来设置。, B3 }+ d& M7 m5 A: f6 l' {
$ Z: m/ n, P7 q! ^; J2 s) f5 k- V
以GPIO_Init这个函数为例:
5 @; s% M% e% K( L3 e
* a9 C* d6 ?& C6 m" n, ~  \ 微信图片_20221129184650.png 9 T5 [+ S0 K! g9 b  L3 J3 ]
; J0 s$ o) y9 g
我们打开固件库使用手册,找到GPIO_Init的原型。( E1 F, }( S3 n4 j) x# }( y
% |2 d- Y# Z9 \$ C9 z- M
  O' X/ L+ `6 w8 ~% u- \" b
微信图片_20221129184646.png
9 t3 q6 D2 J+ Q8 e2 g& o' u9 ?+ \4 ]5 Z, o3 n, R  l% Z/ A0 l7 ]# \7 l
可以看到原型为:4 o' \$ m& x# K) l, A- A
( S9 f1 a" a! f* ~2 T' \
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); p5 v: G+ j4 f! t0 b

2 B, k% _1 y8 k7 S第二个参数为GPIO_InitTypeDef类型的,因此我们将伪代码中的v10的类型修改为GPIO_InitTypeDef,剩下的结构体我们也一样可以使用这样的方法来恢复,只要有固件库函数,我们就能够恢复对应的结构体,效果如下:
2 R! Q; {8 U' v' C$ W6 K# w
8 G8 n7 g8 C! m3 a4 J; S' ?6 |1 K8 A9 K
# r8 W, W2 Q6 C
6 F- n3 @3 ?2 M6 H# q5 Y
3 S' H; V0 F& V8 V4 Y& n/ }1 l
效果还不错。! D  [1 _, }; Y

$ t2 E; Z: R  s0 R( d, h0 H. z" ^5 ]9 K% s
七、总结3 t% L! l! |% `  P, I4 W
此次逆向是基于我的一个小课设,功能不多,而且由于我的课设用到了xx原子的例程二次开发的一些库,而我用来恢复符号表时使用的axf文件也是xx原子的例程编译出来的,所以恢复出来的符号表的比例会偏高。如果能确定使用芯片型号,以及使用的是固件库还是hal库,那么按照此方法大致上能在一定程度上恢复固件,使其可读性大大增加。
2 F# n. T; R" Z5 u" e
# \# K& u; ~- g4 E4 e  g2 g转载自:Lpwn
' ?; N4 b' ^# p* ^7 h  E  J
' J: t" U- s! C1 O3 E
& h" v& _1 L! I
微信图片_20221129184800.png
微信图片_20221129184404.png
赞 收藏 评论0 发布时间:2022-12-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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