測試環境:Red Hat Linux 7.2
注解 :
eip 寄存器內容式當前執行指令的下一條指令的地址;
mov eax, ebx 將寄存器eax內容移到ebx; 機器指令2字節。
leave 指令所做的操作相當於mov ebp, esp 然後 pop ebp; 機器指令1字節。
ret 指令所做的操作相當於pop eip; 機器指令1字節。
call addr 指令所做的操作相當於push eip 然後 jmp addr; 機器指令4字節。
jmp addr 執行所做的操作相當於 mov addr, eip; 機器指令2字節。
注意:我們所說的”相當於”是在功能上的等價,並不是實際機器指令的等價。
1. 編寫測試程序,如下:
//file name : cc.c
#include
int foo(int fi,int fj)
{
int fk;
fk=3;
return(0);
}
int main()
{
int mi;
int mj;
mi=1;
mj=2;
foo(mi,mj);
return(0);
}
2. 對代碼進行編譯:
gcc –g –o cc cc.c
3. 用gdb進行debug:gdb cc
(1) 查看源程序:
(gdb) list
4 {
5 int fk;
6 fk=3;
7 return(0);
8 }
9 int main()
10 {
11 int mi;
12 int mj;
13 mi=1;
(gdb)
14 mj=2;
15 foo(mi,mj);
16
17 return(0);
18 }
(2)查看匯編代碼:
(gdb) disass main
Dump of assembler code for function main:
0x8048444 : push %ebp
0x8048445 : mov %esp,%ebp
0x8048447 : sub $0x8,%esp
0x804844a : movl $0x1,0xfffffffc(%ebp)
0x8048451 : movl $0x2,0xfffffff8(%ebp)
0x8048458 : sub $0x8,%esp
0x804845b : pushl 0xfffffff8(%ebp)
0x804845e : pushl 0xfffffffc(%ebp)
0x8048461 : call 0x8048430
0x8048466 : add $0x10,%esp
0x8048469 : mov $0x0,%eax
0x804846e : leave
0x804846f : ret
End of assembler dump.
(gdb) disass foo
Dump of assembler code for function foo:
0x8048430 : push %ebp
0x8048431 : mov %esp,%ebp
0x8048433 : sub $0x4,%esp
0x8048436 : movl $0x3,0xfffffffc(%ebp)
0x804843d : mov $0x0,%eax
0x8048442 : leave
0x8048443 : ret
End of assembler dump.
(3)在主函數設置斷點,並執行程序,讓程序在main函數剛開始時暫停:
(gdb) break 9
Breakpoint 1 at 0x8048444: file c1.c, line 9.
(gdb) run
Starting program: /home/syf/p/cc
Breakpoint 1, main () at c1.c:10
10 {
(4)查看關鍵寄存器內容:
(gdb) i reg esp
esp 0xbffffaec 0xbffffaec
(gdb) i reg ebp
ebp 0xbffffb28 0xbffffb28
(gdb) i reg eip
eip 0x8048444 0x8048444
(5)查看棧空間內容:
(gdb) x/32xw 0xbffffae0
0xbffffae0: 0x080494e8 0x080495e4 0xbffffaf8 0x40046507
0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2
0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1
0xbffffb10: 0x00000000 0xbffffb5c 0x4015ec3c 0x40016300
0xbffffb20: 0x00000001 0x08048330 0x00000000 0x08048351
0xbffffb30: 0x08048444 0x00000001 0xbffffb54 0x080482bc
0xbffffb40: 0x080484b0 0x4000dc14 0xbffffb4c 0x40016944
0xbffffb50: 0x00000001 0xbffffc53 0x00000000 0xbffffc62
注意:ebp內容是:0xbffffb28,這個地址對應的內容是0x00000000;
(6)執行一條機器指令(0x8048444 : push %ebp),即將ebp壓棧:
(gdb) si
0x08048445 10 {
(gdb) i reg esp
esp 0xbffffae8 0xbffffae8
(gdb) i reg ebp
ebp 0xbffffb28 0xbffffb28
(gdb) i reg eip
eip 0x8048445 0x8048445
(gdb) x/12xw 0xbffffae0
0xbffffae0: 0x080494e8 0x080495e4 0xbffffb28 0x40046507
0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2
0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1
可以看到,ebp(內容是0xbffffb28)被壓到0xbffffae8處。
(7)執行一條機器指令(0x8048445 : mov %esp,%ebp),即將esp->ebp,查看寄存器內容和堆內容:
(gdb) si
0x08048447 in main () at c1.c:10
10 {
(gdb) i reg ebp
ebp 0xbffffae8 0xbffffae8
(gdb) i reg esp
esp 0xbffffae8 0xbffffae8
(gdb) i reg eip
eip 0x8048447 0x8048447
(gdb) x/12xw 0xbffffae0
0xbffffae0: 0x080494e8 0x080495e4 0xbffffb28 0x40046507
0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2
0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1
可以看到,棧空間內容沒變,只是寄存器ebp變成了esp的值。
(8) 執行三條機器指令,為mi,mj賦值。
三條指令是:
0x8048447 : sub $0x8,%esp
0x804844a : movl $0x1,0xfffffffc(%ebp)
0x8048451 : movl $0x2,0xfffffff8(%ebp)
這三條指令的作用是為mi,mi賦值。
(gdb) si
13 mi=1;
(gdb) si
14 mj=2;
(gdb) si
15 foo(mi,mj);
(gdb) i reg esp
esp 0xbffffae0 0xbffffae0
(gdb) i reg ebp
ebp 0xbffffae8 0xbffffae8
(gdb) i reg eip
eip 0x8048458 0x8048458
(gdb) x/12xw 0xbffffae0
0xbffffae0: 0x00000002 0x00000001 0xbffffb28 0x40046507
0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2
0xbffffb00: 0x080484b0 0x00000000 0xbffffb28 0x400464f1
可以看到,系統為mi,mj在棧內分配了空間(對應的地址分別是:0xbffffae4,0xbffffae0),並進行了賦值(分別是:0x00000001,0x00000002)。
(9)執行下一條指令(0x8048458 : sub $0x8,%esp),將堆棧棧空間加8個字節的空間,沒有意義,可以略去,在優化代碼時,將被略去。
注:優化代碼的方法是:gcc –O –o cc cc.c
(gdb) si
0x0804845b 15 foo(mi,mj);
(10)執行下兩條指令,堆將要調用的函數foo(int, int)的參數壓棧。兩條指令是:
0x804845b : pushl 0xfffffff8(%ebp)
0x804845e : pushl 0xfffffffc(%ebp)
這兩條指令的作用是對調用的函數foo(int fi, int fj)的參數壓棧。
(gdb) si
0x0804845e 15 foo(mi,mj);
(gdb) si
0x08048461 15 foo(mi,mj);
(gdb) i reg esp
esp 0xbffffad0 0xbffffad0
(gdb) i reg ebp
ebp 0xbffffae8 0xbffffae8
(gdb) i reg eip
eip 0x8048461 0x8048461
(gdb) x/12xw 0xbffffad0
0xbffffad0: 0x00000001 0x00000002 0xbffffaf8 0x08048411
0xbffffae0: 0x00000002 0x00000001 0xbffffb28 0x40046507
0xbffffaf0: 0x00000001 0xbffffb54 0xbffffb5c 0x080482d2
(11)執行下一條指令:0x8048461 : call 0x8048430 。
這條call指令可以分解成兩條指令:push eip;jmp 0x8048430。
其作用是保存main函數的foo(int, int)函數調用後的下一條指令的地址(0x8048466),以便foo(int ,int)函數調用返回後在main()函數繼續指令,同時還要跳轉到foo(int, int)的地址(0x8048430),以便執行函數foo(int ,int);
注意:此時的eip為0x8048466,對應的指令是:0x8048466 : add $0x10,%esp
(gdb) si
foo (fi=1, fj=-1073743020) at c1.c:4
4 {
(gdb) i reg esp
esp 0xbffffacc 0xbffffacc
(gdb) i reg ebp
ebp 0xbffffae8 0xbffffae8
(gdb) i reg eip
eip 0x8048430 0x8048430
(gdb) x/12xw 0xbffffacc
0xbffffacc: 0x08048466 0x00000001 0x00000002 0xbffffaf8
0xbffffadc: 0x08048411 0x00000002 0x00000001 0xbffffb28
0xbffffaec: 0x40046507 0x00000001 0xbffffb54 0xbffffb5c
(12)執行下兩條指令,並查看寄存器和堆內容:
這兩條指令是:
0x8048430 : push %ebp
0x8048431 : mov %esp,%ebp
這兩條指令的作用是對main函數中的 ebp進行壓棧,以便返回時用(和第15步對應),同時將esp賦值為ebp(ebp->esp),一般本函數返回(和第16步對應)。
(gdb) si
0x08048431 4 {
(gdb) si
0x08048433 in foo (fi=1, fj=2) at c1.c:4
4 {
(gdb) i reg esp
esp 0xbffffac8 0xbffffac8
(gdb) i reg ebp
eb
(gdb) si
0x08048431 4 {
(gdb) si
0x08048433 in foo (fi=1, fj=2) at c1.c:4
4 {
(gdb) i reg esp
esp 0xbffffac8 0xbffffac8
(gdb) i reg ebp
eb