test env: x86/linux
這是 func.c 的某部份反組譯, 透過反組譯來查看 c 如何使用 stack 來傳遞參數, 如何存取傳進來的參數。
34 func('a', 9, (char*)(0x1234));
從 L5, 10, 11 可以看到, 用 %epb 存取 func 傳入的 'a', 9, (char*)0x1234,
%epb + 8 -> 'a'
%epb + 0xc -> 9
%epb + 0x10 -> 0x1234
參考以下的 stack 位址 (這是我用 gdb 印出的位址, 每個系統可能會不同) 可以更清楚。
綠色是 main(), 藍色是 func()。
執行 func('a', 9, (char*)(0x1234));
'a', 9, (char*)(0x1234) 分別被複製到 stack 位址 (這就是所謂的 call by value)
0xffffd6e0 + 8
0xffffd6e0 + 4
0xffffd6e0
func('a', 9, (char*)(0x1234)); 的下一個指令會被 push 到 stack, 就是圖中的 ret addr, 而 main() 的 %epb 會被 push, func() epb 會被指定為 0xffffd6d8, 所以這就是
%epb + 8 -> 'a'
%epb + 0xc -> 9
%epb + 0x10 -> 0x1234
可以存取到傳進來的參數的方法。
20 c+=2;
21 i+=3;
22 ++ptr;
5 8048440: 8b 45 08 mov 0x8(%ebp),%eax // 'a'
6 8048443: 88 45 f4 mov %al,-0xc(%ebp)
7 8048446: 0f b6 45 f4 movzbl -0xc(%ebp),%eax
8 804844a: 83 c0 02 add $0x2,%eax
10 8048450: 83 45 0c 03 addl $0x3,0xc(%ebp) // 9 + 3
11 8048454: 83 45 10 01 addl $0x1,0x10(%ebp) // 0x1234 + 1
不過 'a' /* c+=2 */ 的處理方式比較複雜, 不像
%epb + 0xc 直接加 3 (9+3) /* i+=3) */
%epb + 0x10 直接加 1 (0x1234+1) /* ++ptr */
而是很複雜的方式, 也許是長度為 1byte 的關係。
我也不知道為什麼 %esp 要減掉 0x28
4 804843d: 83 ec 28 sub $0x28,%esp
下圖是我自己得到的結果可能有些錯誤, 我還不是很懂組合語言, 不過和主題無關, 是我自己想觀察的部份。
我要描述的主題應該是正確的。這不是很好懂, 得花腦力看一下組合語言, 也要思考一下。因為我被這部份困惑很久了, 決心用自己的方法來理解, 我已經儘量將東西簡化了, 一次只討論一個小部份。
stack frame
(main esp) 0xffffd6e0 + 8 | 0x1234 |
(main esp) 0xffffd6e0 + 4 | 9 |
(main esp) 0xffffd6e0 | 'a' |
0xffffd6dc calll func | ret addr |
0xffffd6d8 push %ebp; mov %esp,%ebp | caller ebp |
0xffffd6d4 | |
0xffffd6d0 | |
0xffffd6cc | 'a'+2 |
0xffffd6c8 | |
0xffffd6c4 | |
0xffffd6c0 | |
0xffffd6bc | 0x1234 + 1 |
0xffffd6b8 | 9+3 |
0xffffd6b4 | 'a'+2 |
(func esp)0xffffd6b0 | "%c %d %p\n" |
callee (func) epb = 0xffffd6d8
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。