GDB調試匯編堆棧過程分析
分析過程
-
這是我的C源文件:click here
-
使用
gcc - g example.c -o example -m32
指令在64位的機器上產生32位匯編,然后使用gdb example
指令進入gdb調試器:
-
進入之后先在main函數處設置一個斷點,再run一下,使用
disassemble
指令獲取匯編代碼,用i(info) r(registers)
指令查看各寄存器的值:
-
可見此時主函數的棧基址為0xffffd068,用
x(examine)
指令查看內存地址中的值,但目前%esp所指堆棧內容為0,%ebp所指內容也為0
-
首先,結合display命令和寄存器或pc內部變量,做如下設置:
display /i $pc
,這樣在每次執行下一條匯編語句時,都會顯示出當前執行的語句。下面展示每一步時%esp、%ebp和堆棧內容的變化:
-
call指令將下一條指令的地址入棧,此時%esp,%ebp和堆棧的值為:
-
將上一個函數的基址入棧,從當前%esp開始作為新基址:
-
先為傳參做准備:
-
實參的計算在%eax中進行:
-
f函數的匯編代碼:
-
實參入棧:
-
call指令將下一條指令的地址入棧:
-
計算short+int:
-
pop %ebp指令將棧頂彈到%ebp中,同時%esp增加4字節:
-
ret指令將棧頂彈給%eip:
-
因為函數f修改了%esp,所以用leave指令恢復。leave指令先將%esp對其到%ebp,然后把棧頂彈給%ebp:
-
主函數匯編代碼:
指令 | %esp | %ebp | 堆棧 |
---|---|---|---|
push $0x8 | 0xffffd068 | 0xffffd068 | 0x0 |
call 0x80483ef
|
0xffffd064 | 0xffffd068 | 0x8 0x0 |
push %ebp | 0xffffd060 | 0xffffd068 | 0x8048412 0x8 0x0 |
mov %esp,%ebp | 0xffffd05c | 0xffffd068 | 0xffffd068 0x8048412 0x8 0x0 |
mov 0x804a01c,%edx | 0xffffd05c | 0xffffd05c | 0xffffd068 0x8048412 0x8 0x0 |
call 0x80483db
|
0xffffd058 | 0xffffd05c | 0xa 0xffffd068 0x8048412 0x8 0x0 |
push %ebp | 0xffffd054 | 0xffffd05c | 0x8048403 0xa 0xffffd068 0x8048412 0x8 0x0 |
mov %esp,%ebp | 0xffffd050 | 0xffffd05c | 0xffffd05c 0x8048403 0xa 0xffffd068 0x8048412 0x8 0x0 |
movzwl 0x804a018,%eax | 0xffffd050 | 0xffffd050 | 0xffffd05c 0x8048403 0xa 0xffffd068 0x8048412 0x8 0x0 |
ret | 0xffffd054 | 0xffffd05c | 0x8048403 0xa 0xffffd068 0x8048412 0x8 0x0 |
leave | 0xffffd05c | 0xffffd05c | 0xffffd068 0x8048412 0x8 0x0 |
ret | 0xffffd060 | 0xffffd068 | 0x8048412 0x8 0x0 |
add $0x4,%esp | 0xffffd064 | 0xffffd068 | 0x8 0x0 |
mov $0x3,%edx | 0xffffd068 | 0xffffd068 | 0x0 |
ret | 0xffffd06c | 0x0 |
遇見的問題
-
使用-m32指令報錯:
-
這是因為編譯64位Linux版本32位的二進制文件,需要安裝一個庫,使用指令
sudo apt-get install libc6-dev-i386
即可