blog 文章

2013/05/06

gcc -fstack-protector 的實作

這篇是重寫的版本, 由於我的疏忽, 第一版本不小心被我蓋掉了, 之前找的連結資料也都不見了, 這篇花了我不少心血, 就這樣沒了, 我的懊惱可想而知。

我一直很好奇 check stack 有沒爆掉是怎麼做的, 一位朋友為我解惑, 就在 stack 底端塞個值, 檢查他就好了, 我頓時茅塞頓開, 原來如此。 不過本著求證的心態, 來看看是不是真的。
stack_pro.c
 1 
 2 #include <stdio.h>
 3 #include <string.h>
 4 
 5 int main() 
 6 {
 7 
 8     char string[10];
 9 
10     strcpy(string, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
11 
12 }
gcc -m32 -g -static  -fno-stack-protector stack_pro.c -o stack_pro.nosp
gcc -m32 -g -static  -fstack-protector stack_pro.c -o stack_pro.sp
objdump -d stack_pro.sp
 1 080482d0 <main>:
 2  80482d0:       55                      push   %ebp
 3  80482d1:       89 e5                   mov    %esp,%ebp
 4  80482d3:       83 e4 f0                and    $0xfffffff0,%esp
 5  80482d6:       83 ec 20                sub    $0x20,%esp
 6  80482d9:       65 a1 14 00 00 00       mov    %gs:0x14,%eax
 7  80482df:       89 44 24 1c             mov    %eax,0x1c(%esp)
 8  80482e3:       31 c0                   xor    %eax,%eax
 9  80482e5:       b8 dc 02 0b 08          mov    $0x80b02dc,%eax
10  80482ea:       c7 44 24 08 23 00 00    movl   $0x23,0x8(%esp)
11  80482f1:       00 
12  80482f2:       89 44 24 04             mov    %eax,0x4(%esp)
13  80482f6:       8d 44 24 12             lea    0x12(%esp),%eax
14  80482fa:       89 04 24                mov    %eax,(%esp)
15  80482fd:       e8 ee 74 00 00          call   804f7f0 <memcpy>
16  8048302:       8b 54 24 1c             mov    0x1c(%esp),%edx
17  8048306:       65 33 15 14 00 00 00    xor    %gs:0x14,%edx
18  804830d:       74 05                   je     8048314 <main+0x44>
19  804830f:       e8 2c be 00 00          call   8054140 <__stack_chk_fail>
20  8048314:       c9                      leave  
21  8048315:       c3                      ret 
L6, 7, 16, 17 就是這些魔法施行的地方。string[10] 從 0x12(%esp) 開始, 長度 10, 所以這個魔法標記紀錄在 0x1c(%esp), 0x1c-0x12 = 0xa, 在做完 memcpy (為什麼 strcpy 變成 memcpy 就不用管他了) 之後, 再來檢查 0x1c(%esp) 值是不是之前保存 (從 %gs:0x14 得來的) 的即可, 如果不一樣, 就去執行 __stack_chk_fail。 在我測試時, 這個值是: %gs:0x14: 0x944bc400 不過似乎會一直變化, 每次執行都不一樣, 不是固定值。 這是把 0x1c(%esp) dump 出來, 本來應該要是 0x00 0xc4 0x4b 0x94 (little endian), 可是因為我們把 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 覆蓋超過了 string[10], 所以就變成 0x41 0x41 0x41 0x41 ... (gdb) x/32xb 0xffffd39c 0xffffd39c:     0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41 0xffffd3a4:     0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41 這是執行結果, 沒有 -fstack-protector 的版本, 自然是發出 Segmentation fault。
stack protector 執行結果
 1 *** stack smashing detected ***: ./stack_pro.sp terminated
 2 ======= Backtrace: =========
 3 [0x8054189]
 4 [0x805414d]
 5 [0x8048314]
 6 [0x41414141]
 7 ======= Memory map: ========
 8 08048000-080cf000 r-xp 00000000 08:05 18620900                           /home/descent/my-git/progs/stack_pro.sp
 9 080cf000-080d1000 rw-p 00086000 08:05 18620900                           /home/descent/my-git/progs/stack_pro.sp
10 080d1000-080d3000 rw-p 00000000 00:00 0 
11 08f7b000-08f9d000 rw-p 00000000 00:00 0                                  [heap]
12 f77c9000-f77ca000 r-xp 00000000 00:00 0                                  [vdso]
13 ffdc2000-ffdd7000 rw-p 00000000 00:00 0                                  [stack]
14 Aborted
Emit extra code to check for buffer overflows, such as stack
  smashing attacks.  This is done by adding a guard variable to
  functions with vulnerable objects.  This includes functions that
  call alloca, and functions with buffers larger than 8 bytes.  The
  guards are initialized when a function is entered and then checked
  when the function exits.  If a guard check fails, an error message
  is printed and the program exits.

  NOTE: In Ubuntu 6.10 and later versions this option is enabled by
  default for C, C++, ObjC, ObjC++, if neither -fno-stack-protector
  nor -nostdlib are found.
至於 man page 的 8 byte 我怎麼試都觀察不到, 就沒辦法分享這段的意思了。stack overflow 有篇類似的, 可惜我找不到了。

沒有留言:

張貼留言