OS:XP SP3
编译器: DEV C++ 4.9.9.2
工具:
OD (ollydbg)
大家可以去先查询一下 组合语言(32位元)方面的知识,然后至少要了解 stack区常用的指令 PUSH 跟 POP,与EIP寄存器 不用了解全部章节。
实验目的:了解函数的进入与退出
#include <stdio.h>#include <stdlib.h>void func(void);//全域变数 int g1=0;int g2=1;int main(){ //区域变数 int a=2; int b=3; func(); system("pause"); return 0; }void func(void){ //区域变数 int x=7; int y=8; }
关于组合语言的知识一偏文肯定讲不完。所以这次给大家简单的方式说明。
stack 区域是一个公共空间,任何函数都可以使用,但是每个函数都有自己的空间,所以使用前,要先规定自己的基底,这个基底的标记就是 寄存器(EBP)。
函数的基底 EBP 规划好后 可以开始往上佔领其他空间了。
而这个工作需要交给寄存器 (ESP)
打开OD观察 stack 区
你可以先在 main 函数 里面随便找个地方 按F2设中断 然后F9执行 接下来一直F7走过每一条指令透过观察发现,esp 一直在变动。 ebp 则不会。
1.Ctrl + F2 重新载入
2.找到 main 函数 调用 func函数(call)那行按下F2
3.找到 func 函数 LEAVE 那行按下F2
4.按下F9 跑到 main函数的中断 观察 stack 区
可以看到 EBP 到 ESP 的空间 就是目前函数正在使用的空间
5.在按下F9就会跑到 func函数的中断 观察 stack 区
比对一下可以发现 main函数 呼叫进入 func函数 EBP会变动(前面说过 每个函数都要先规划自己的基底)
但是执行完 func函数 还要回到 main函数,慢慢F7,执行 leave RETN
leave = mov esp,ebp ### pop ebp
retn = pop eip
这两条指令又称为 Function Epilogue 也就是函数的结尾,目的是在释放自己的领土,然后归还给 main 函数
与之对应的 Function Prologue 函数的开头被跳过去了,给大家看一下长什么样子
push ebp # 储存 main数的 ebp到堆叠
mov ebp,esp # 规划自己的基底
最后了解一下 call(调用函数) 这个指令: 他会把 call 下一行的地址给推入堆叠。
call(调用) 加上 (Function Prologue) 加上 (Function Epilogue) 一起看
call 推入下一行(004012CD)到堆叠
push ebp # 储存 main数的 ebp到堆叠
mov ebp,esp # 规划自己的基底
leave = mov esp,ebp (释放自己的领土回到基底) ### pop ebp (归还 main的ebp)
retn = pop eip (回到004012CD)
透过观察可以发现在一个函数规划好自己的基底后堆叠上面通常储存着区域变数,例如本例的 x=7 , y=8。
这边没有列出归还的流程图,大家有兴趣可以手动调适一下应该不会很难。
如果有任何问题欢迎指教,谢谢。