说明: 函数太常见了,这里只是对函数的调用过程进行的简单的描述,希望对刚接触的童鞋有点帮助,高手也望不吝指点。
简单函数的过程
代码:
void TestFun() { printf("I'm comming!\r\n"); } int _tmain(int argc, _TCHAR* argv[]) { TestFun(); return 0; }
代码:
int _tmain(int argc, _TCHAR* argv[]) { //保存当前的栈信息,以便函数返回时平衡自身栈 01182140 push ebp 01182141 mov ebp,esp TestFun();//函数调用 0118215E call TestFun (118100Ah) return 0; 01182163 xor eax,eax } //Debug版这里有个跳转指令 0118100A jmp TestFun (11815E0h) void TestFun() { 011815E0 push ebp 011815E1 mov ebp,esp char* lpTest = "I'm comming!"; 011815FE mov dword ptr [lpTest],offset string "I'm comming!" (11879A0h) } 01181608 mov esp,ebp 0118160A pop ebp 0118160B ret
1)当执行TestFun时,将其下一条指令地址压入栈中,以便函数调用结束时可以回来继续执行
2)进入函数(Debug会多一个JMP),保存当前的栈信息,以便函数出去时还原到进来时状态
3)执行函数功能
4)用2保存的栈信息还原到进来时栈状态
5)跳转到1保存的下一条要执行的指令处去执行。
实际调试看下具体情况
执行到TestFun函数调用处时栈信息

执行函数调用时栈,将函数的下一条指令压栈(函数执行完的返回地址,有参数时会先压参数在调函数,过程一样)
0118100A > /E9 D1050000 JMP First.011815E0 (DEBUG有个跳转不会影响栈)

保存寄存器信息,执行函数功能,恢复寄存器信息
常用的就这些指令(push< -- > pop / sub esp, xx < -- > add esp xx / mov xx, esp < -- > move sp, xx)
准备返回Ret

此时在回头看下栈调用的简单描述会有多一点的理解
调用TestFun栈
Esp > Ret:1182163
进入TestFun后
Esp 值保存到 ebp中
Esp -- > 局部变量
环境信息 (各种寄存器值)
Ret:1182163
函数功能完成后,清理局变量和恢复环境信息,esp ebp,这是esp还是执行Ret
Esp -- > Ret : 1182163
主要涉及的知识在下面简单介绍
栈的结构, 老大发一篇 "逆向基础知识学习 " 很不错,有个栈的图比较亮

这里直接copy过来了,哈哈

栈有关的操作,这些在汇编书籍上都会有比较清晰的讲解。
Push xx,
抬高栈 esp = esp -4 (从低到高的排列顺序)
Mov [esp] = xx
Pop xx
Mov xx, [esp]
降低栈 esp = esp + 4
Call MyFun
NextCode:
Push offset NextCode
JMP MyFun
补充 只要理解了,操作的实质就可以了,这里在后面有使用,补充下,push xx ret == JMP XX
call MyFun 的其他方式
push offset NextCode
push MyFun
Ret
Ret
Pop eip
JMP EIP
在使用局部变量时也会通过push, sub esp来抬高栈留空足够使用的空间,在使用完毕后要pop,add esp来将使用的栈还原。
有了上面的东西下面就可以简单模拟无参数函数调用过程
代码:
int _tmain(int argc, _TCHAR* argv[]) { _asm JMP Test1 MyTestFun: _asm { push ebp mov ebp, esp ;环境变量保存 } //申请栈作局变量,执行具体函数功能 printf("This is my TestFun %s\r\n", lpBuf); _asm { ;恢复环境变量 mov esp, ebp pop ebp } _asm { ret } Test1: _asm { mov eax , MyTestFun call eax } NextCode: printf("这里是返回地址\r\n"); return 0; }
代码:
int _tmain(int argc, _TCHAR* argv[]) { char* lpBuf = NULL; _asm JMP Test1 MyTestFun: //保存环境部分 _asm { push ebp mov ebp, esp ;环境变量保存 } //申请局部变量栈空间,此外也有push,xx的方式来申请少量的空间 _asm { sub esp, 0x110 } //局部变量的使用,通常见到的是EBP做标杆偏移的定位方式 _asm { mov [esp], 'a' ;优化才会用esp,正常用ebp,不过自己算晕了, mov [esp+1], 'b' mov [esp+2], '\0' mov lpBuf, esp } //申请栈作局变量,执行具体函数功能 printf("This is my TestFun %s\r\n", lpBuf); //释放申请的局变量空间 _asm { add esp, 0x10 } //恢复环境 _asm { ;恢复环境变量 mov esp, ebp pop ebp } //o,要回去喽! _asm { ;ret pop eax ; 获取返回地址并清栈,这里假设eax没有用到 JMP eax } Test1: _asm { ;mov eax , MyTestFun ;call eax push Test2 push MyTestFun ret } Test2: _asm { push NextCode JMP MyTestFun } NextCode: printf("这里是返回地址\r\n"); return 0; }

五边形
论坛资源:
标 题: 【原创】学什么编程语言?汇编+C似乎是个不错的选择。(99%YY篇)
作 者: vshhw
时 间: 2010-11-18,23:01:09
链 接: http://bbs.pediy.com/showthread.php?t=125062
标 题: 【注意】逆向基础知识学习
作 者: kanxue
时 间: 2007-09-01,10:45
链 接: http://bbs.pediy.com/showthread.php?t=50879
标 题: 献给汇编初学者-函数调用堆栈变化分析
作 者: 堕落天才
时 间: 2007-01-19,19:20:09
链 接:http://bbs.pediy.com/showthread.php?t=38234