2013年5月16日 星期四

gcc -fstack-protector 的實作

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

以下正文開始

我一直很好奇 check stack 有沒爆掉是怎麼做的, 一位朋友為我解惑, 就在 stack 底端塞個值, 檢查他就好了, 我頓時茅塞頓開, 原來如此。

不過讓我們看看有支援 stack protector 的 c compiler, 以下是 gcc 的 fstack-protector 實作。

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 有篇類似的, 可惜我找不到了。

有人認為 c++ 的邪惡在於 c++ compiler 產生太多我們沒寫的程式碼, 類似於這樣的行為。c++ exception 也是用類似的方似塞了不少程式碼。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。