说明: 函数太常见了,这里只是对函数的调用过程进行的简单的描述,希望对刚接触的童鞋有点帮助,高手也望不吝指点。
简单函数的过程
代码:
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