|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
0x0 介绍& } i" ~; R) g& S" s
, W7 I) \' u0 Q3 |* `! I
本文记录软件安全课程一项实验内容,为”分析一款编译器的安全特性”,偷懒选了Linux下的gcc,网上有很多相关资料,这里做一实验总结,主要是测试该特性在当前版本Linux平台下是否工作,顺便比较和Windows平台的异同.
* S7 l% _. O6 v& D3 c2 b* F- i7 Z5 N1 \+ y" e9 F+ T% z
另:有更多关于Windows平台下的安全保护机制,但由于windows平台编译器众多(特别是vs开头的),十分依赖编译器和操作系统的配合(虽然在linux平台的实验表明这种程度的安全措施只能依靠编译器和OS的配合,甚至于CPU提供相关的指令集)3 o% h. h, U3 e
Q Z- ^8 _) R5 U, V4 ^7 G! j本文分析和实验内容如下: ?7 u" c' O$ p2 P! _5 G4 E
( a2 k! o) x, [( `+ i& j0 |6 N; qa) 分析gcc编译器对安全特性的支持。
, x# l/ o3 W. Q4 d6 c; [! l5 O; a8 e; x' {% [0 a0 m
b) 写程序观察生成可执行代码的差异。$ g1 }8 C. ~, D" m4 |. O/ a
( T* y7 i7 |0 p Jc) 分析这些安全特性和程序编写者、编译器、操作系统的关联。' z6 f: I P0 T5 k# h/ @3 G
5 S: R A8 @- o; m( |5 }; T
实验环境: ubuntu 18.04 + gcc 7.3
% m: d8 e9 D$ W; L
; O/ N" K M6 L4 Y" r经学习研究,Linux操作系统中gcc相关的安全保护机制有:栈Canaries保护、PIE机制、NX、fortity、relro机制。
# K0 J) [; n: e5 n' [; `
3 y) t9 N0 ^/ c* O- T0 }0x1 Canaries3 i) L, j" y# b
$ I6 P7 n F6 J& \& c在windows操作系统中,这一机制被称为gs机制。实现方式是类似的,即在函数的返回地址前加一个cookie检查,在linux中被称为Canaries。gcc在4.2版本中增加了-fstack-protector来使用这种保护方式,若要禁用栈保护,则应使用-fno-stack-protector。对栈空间的保护在gcc中是默认开启的。2 A9 e* r5 N7 z
2 h- N9 y$ w- P. rCanaries的产生方式是随机的,即从fs寄存器的0x28偏移处获取,而fs寄存器是被glibc定义存放tls信息的。如图是glibc定义的fs寄存器的数据结构:
" @% N! w' u' F% r4 f. o5 W* o% }3 p; |+ U1 ?. l: L
& V& x; p K/ H* j: T) f
% X# b1 l) n/ [0 s0 y+ |9 I
图1 fs寄存器的数据结构9 |( u/ i3 K" v8 r
* |2 h- q" f! L
可以发现,0x28处存放的即是stark_guard,canaries值。这个值是进程载入时初始化的,生成随机数的代码如下:
6 _/ _- D$ s! d/ y* e4 ^: s6 I5 y
: _5 b9 D/ l* B" f* i' L
+ ] j. n4 T$ C+ ~图2 canaries的值初始化方式
* l( r1 i" P- ~2 y5 R, P3 {; L" k+ ^4 ?4 `& s; X8 L2 A
可以看到,_dl_random是一个随机数,它由_dl_sysdep_start函数从内核获取的。_dl_setup_stack_chk_guard函数负责生成canary值,THREAD_SET_STACK_GUARD宏将canary设置到%fs:0x28位置。. D3 C! p( E: ]2 b' w; _
$ X1 x' H$ x6 w% _ f9 p在实验中,我们编写了代码,使用调试程序查看其栈结构和机器码。下图是开启了栈保护的栈结构和机器码:* D8 L+ d* C: c1 j. H
+ D1 j4 y: V5 D5 M( K. l
: ^+ p6 j* ]& j7 D3 L" U( O# `
7 ~* L$ I5 n" [' f# v图3 栈结构
( C& J: a7 x, X9 _" v* I9 K0 P
3 K8 F' }' ]+ G4 N* }3 G$ P' v
+ g+ a0 W0 j; t8 ]& i6 q7 k图4 开启栈保护函数的执行代码
& s7 ]- o& y' Z& t' }
8 i- x: u T' y" ~; B: [+ n可以看到,在初始化函数时。除了push rbp之外,在申请栈空间后又将fs[0x28]压入了栈中,即rbp-0x8。在函数结束前,又检查该处数值是否和fs[0x28]的大小相同,以此保证函数栈未被溢出。该检查如下图所示:
9 |1 ?- {9 q# Y( a+ G: D* `2 o; Y$ e1 S+ T
1 M& w5 j* @6 R; ^* U1 J: t. }; S, I7 f
|
|