190 likes | 307 Views
开始进入 Main 函数 esp =0x12FF84 ebp =0x12FFC0. 框 表示栈或是寄存器 框内数值是值 , 不是自身地址. 0x12FF84. 0x12FFC0. ebp. esp. 0x12FFC0. 0x12FF84. p ush ebp. p ush 指令会自动地影响 esp 减 4, 因为 esp 永 远指向栈顶. 0x12FFC0. 0x12FF84. 0x12FFC0. 0x12FF80. ebp. esp. 0x12FFC0. 0x12FF80. mov ebp , esp.
E N D
开始进入Main函数esp=0x12FF84 ebp=0x12FFC0 框 表示栈或是寄存器 框内数值是值, 不是自身地址 . . . 0x12FF84 0x12FFC0 ebp esp 0x12FFC0 0x12FF84
push ebp push 指令会自动地影响 esp减4, 因为esp永远指向栈顶 . . . 0x12FFC0 0x12FF84 0x12FFC0 0x12FF80 ebp esp 0x12FFC0 0x12FF80
movebp, esp 将esp的值 copy 给 ebp, 这样 ebp和esp就都指向了栈顶 . . . 0x12FFC0 0x12FF84 0x12FFC0 0x12FF80 ebp esp 0x12FF80 0x12FF80
sub esp, 40h 函数使用的堆栈,默认64个字节 . . . 0x12FFC0 . . . 0x12FF84 0x12FFC0 0x12FF80 0x12FF40 ebp esp 0x12FF80 0x12FF40
push ebx push esi push edi 函数使用的堆栈,默认64个字节 esi ebx . . . . . . 0x12FFC0 edi 0x12FF34 0x12FF84 0x12FFC0 0x12FF3C 0x12FF40 0x12FF38 0x12FF80 esp ebp 0x12FF80 0x12FF34
lea edi,[ebp-40h] //相当于 edi = ebp – 0x40 mov ecx,10h mov eax,0CCCCCCCCh rep stosdwordptr [edi] 初始化用于该函数的栈空间为0XCCCCCCCC 即从0x12FF40~0x12FF80所有的值均为0xCCCCCCCC edi esi ebx . . . 0xCCCCCC 0xCCCCCC . . . 0x12FF34 0x12FF38 0x12FF3C 0x12FFC0 0x12FF40 0x12FF80 0x12FF84 edi ebp esp 0x12FF80 0x12FF34 0x12FF40
printf("%d",test(10,90)); push 5Ah //参数入栈 从右至左 先90 后10push0Ah 可见 是: 调用者(main函数) 负责将被调用函数的实参 入栈的 0x5A edi esi ebx 0x0A 0xCCCCCC 0xCCCCCC . . . . . . 0x12FF2C 0x12FF30 0x12FF34 0x12FF38 0x12FFC0 0x12FF3C 0x12FF40 0x12FF80 0x12FF84 esp edi ebp 0x12FF40 0x12FF80 0x12FF2C
call @ILT+0(test) (00401005) CALL指令内部其实在调用函数之前, 还会先将下一条指令的地址00401091入栈, 下一条指令是: 00401091: add esp,8 被调用指今地址 的 指令是: 00401005: jmptest (00401020) 0x0A 0x5A edi esi 0x401091 . . . 0xCCCCCC ebx 0xCCCCCC . . . 0x12FF28 0x12FF38 0x12FF40 0x12FF3C 0x12FF30 0x12FFC0 0x12FF80 0x12FF2C 0x12FF34 esp edi ebp 0x12FF80 0x12FF40 0x12FF28
00401020: push ebp 一切都好象回到了开始, 不同的是这次是子函数, 调用 push ebp, 所以从被调用的子函数来看, 此处ebp的值就是, 它的调用者所处 栈侦的 base地址, ebp因此而得名: base pointer 0x12FF38 edi 0xCCCCCC 0x401091 . . . 0x5A 0x0A . . . ebx 0x12FF80 esi 0xCCCCCC 0x12FFC0 0x12FF24 0x12FF40 0x12FF80 0x12FF30 0x12FF28 0x12FF2C 0x12FF34 0x12FF3C edi esp ebp 0x12FF24 0x12FF40 0x12FF80
00401020 push ebp00401021 movebp,esp00401023 sub esp,40h00401026 push ebx00401027 push esi00401028 push edi00401029 lea edi,[ebp-40h]0040102C mov ecx,10h00401031 mov eax,0CCCCCCCCh00401036 rep stosdwordptr [edi] //这些指令和main函数做的是一样, //于是栈一直往下涨 0x12FF38 . . . 0x12FFC0 0xCCCCCC edi edi ebx esi . . . 0x5A 0x0A 0x401091 0x12FF80 . . . ebx esi 0x12FF3C 0x12FF40 0x12FF80 0x12FF34 0x12FF30 0x12FF28 0x12FFC0 0x12FF24 0x12FEE4 0x12FF2C esp edi ebp 0x12FF24
10: a = a + 3; //ebp=0x12FF24 加8 [0x12FF30]即取到了参数10moveax,dwordptr [ebp+8] add eax,3movdwordptr [ebp+8],eax11: b = b + 5;movecx,dwordptr [ebp+0Ch]add ecx,5movdwordptr [ebp+0Ch],ecx12: return a + b;moveax,dwordptr [ebp+8] //最后的结果保存在eax, 结果得以返回addeax,dwordptr [ebp+0Ch] 0x12FF38 edi . . . 0x12FFC0 0xCCCCCC . . . . . . esi ebx ebx 0x5A 0x0A 0x401091 0x12FF80 edi esi 0x12FF34 0x12FF80 0x12FF40 0x12FEE4 0x12FF30 0x12FF24 0x12FF28 0x12FFC0 0x12FF3C 0x12FF2C eax ecx ecx eax esp ebp 最终值 0x12FF24 0x5A 0x0A
开始回溯 pop edi pop esipop ebx 0x12FF38 0x5A . . . . . . 0x12FF80 0x401091 0x12FFC0 . . . ebx esi edi 0x0A 0xCCCCCC 0x12FF3C 0x12FFC0 0x12FF80 0x12FF40 0x12FF34 0x12FF24 0x12FEE4 0x12FF2C 0x12FF30 0x12FF28 eax esp ebp ecx eax ecx 0x5A 最终值 0x12FF24 0x0A
movesp,ebp //esp指向0x12FF24, test函数的堆栈空间被放弃, 从当前函数栈顶返回到栈底 0x12FF38 0x5A 0x12FFC0 . . . . . . 0xCCCCCC 0x12FF80 . . . ebx 0x0A esi edi 0x401091 0x12FF3C 0x12FFC0 0x12FF80 0x12FF40 0x12FF34 0x12FF24 0x12FF28 0x12FF2C 0x12FF30 0x12FEE4 eax esp ebp ecx eax ecx 0x5A 最终值 0x12FF24 0x12FF24 0x0A
popebp //此时ebp=0x12FF80, esp因为pop指令自动增加4, 指向0x12FF28 0x12FF38 0x5A 0x12FFC0 . . . . . . 0xCCCCCC 0x12FF80 . . . ebx 0x0A esi edi 0x401091 0x12FF3C 0x12FFC0 0x12FF80 0x12FF40 0x12FF34 0x12FF24 0x12FF28 0x12FF2C 0x12FF30 0x12FEE4 eax esp ebp ecx eax ecx 0x5A 最终值 0x12FF28 0x12FF80 0x0A
ret // ret负责栈顶0x12FF28之值00401091弹出到 指令寄存器(eip) 中,并jmp到这个地址, 继续执行程序. 下面我们再看下个指令 0x12FF38 0x5A 0x12FFC0 . . . . . . 0xCCCCCC 0x12FF80 . . . ebx 0x0A esi edi 0x401091 0x12FF3C 0x12FFC0 0x12FF80 0x12FF40 0x12FF34 0x12FF24 0x12FF28 0x12FF2C 0x12FF30 0x12FEE4 eax esp ebp ecx eax ecx 0x5A 最终值 0x12FF30 0x12FF80 0x0A
上个页面的ret指令后, 会继续执行 00401091: add esp,8 所以 esp就指向了0x12FF30 这也是清除之前压入栈中的两个实参. 可见实参的清除也是由调用者main负责 (所谓__cdecl调用由调用者负责恢复栈,调用者负责清理的只是入栈的参数,test函数自己的堆栈空间自己返回时自己已经清除 如果函数调用方式是__stdcall不同之处在于main函数call 后面没有了 add esp, 8test函数最后一句 是 ret 8 (由test函数清栈, ret 8意思是执行ret后,esp+8)) 0x12FF38 0x5A 0x12FFC0 . . . . . . 0xCCCCCC 0x12FF80 . . . ebx 0x0A esi edi 0x401091 0x12FF3C 0x12FFC0 0x12FF80 0x12FF40 0x12FF34 0x12FF24 0x12FF28 0x12FF2C 0x12FF30 0x12FEE4 eax esp ebp ecx eax ecx 0x5A 最终值 0x12FF30 0x12FF80 0x0A
push eax //入栈,计算结果108入栈,即printf函数的参数之一入栈 0x12FF38 0x6C 0x5A edi ebx . . . 0xCCCCCC esi 0x12FFC0 . . . 0x12FF30 0x12FF80 0x12FF34 0x12FFC0 0x12FF2C 0x12FF3C 0x12FF40 eax ecx ebp eax esp ecx 0x0A 最终值 0x5A 0x12FF2C 0x12FF80
//入栈,参数 "%d" 当然其实是%d的地址 push offset string "%d" (0042201c) //函数调用 printf("%d",108) callprintf (004010d0) //清栈,清除参数 ("%d", 108)add esp,8 19: return 0; //eax清零xoreax,eax 0x12FF38 0x6C 0x5A edi ebx . . . 0xCCCCCC esi 0x12FFC0 . . . 0x12FF30 0x12FF80 0x12FF34 0x12FFC0 0x12FF2C 0x12FF3C 0x12FF40 eax ecx ebp eax esp ecx 0x0A 最终值 0x5A 0x12FF2C 0x12FF80
popedipopesipopebx//为啥不用movesp, ebp? 是为了下面的比较 add esp,40h //较,若不同则调用chkesp抛出异常 cmpebp,espcall __chkesp (00401150) movesp,ebp popebp //至此, esp=0x12FF84ebp=0x12FFC0 尘归尘 土归土 一切都恢复最初的平静了 :) . . . 0x12FF84 0x12FFC0 ebp esp 0x12FFC0 0x12FF84