科锐课堂笔记:2017/3/2 函数调用约定

  VC中标号表示地址,只能在内嵌汇编里访问其值。(如取标号NEXT值,VC内嵌汇编中应用lea eax,NEXT,假设用mov eax,NEXT编译后的结果是mov eax,dword ptr[NEXT]变成取NEXT指向内存单元里的数了并非我们所要)


  程序的设计方向:强内聚,低耦合。


  函数的调用约定应该有4个方面:
  1. 如何传参(使用内存堆栈?寄存器?)
  2. 参数的传递方向(从左往右?从右往左?)
  3. 返回值定义(放哪里?比如放EAX里)
  4. 平衡堆栈(谁清理平衡,调用方?被调用方?)


  4种调用约定:
  C调用约定(__cdecl):

  从右往左依法入栈传递参数,返回值放EAX寄存器中,调用方清理堆栈(所以C调用约定的函数可支持多参)。


  Windows标准约定(__stdcall,绝大多数Win32 API的函数约定):
  从右往左依法入栈传递参数,返回值放EAX寄存器中,被调用方清理堆栈。


  快速调用约定(__fastcall):
  函数的第1个、第2个参数分别使用ECX、EDX寄存器传递,其他参数(若多于2个的)从右往左依法入栈传递参数,返回值放EAX寄存器中,被调用方清理堆栈。


  C++成员函数调用约定(thiscall):
  注意thiscall不是关键字,不能自己声明一个thiscall调用约定,它是C++类成员函数的默认约定。分2种情况多参成员函数与非多参成员函数,如果是多参函数规则如C调用约定(只不过压栈的最后多压一个this指针,即类对象的基址 ),对于非多参数函数规则如__stdcall调用约定(此时this指针不通过栈传递,而是保存在ECX寄存器里)。


  作业:练习查看函数调用过程
  先上图:
 
  main函数中调用了test这个函数,test使用的是默认C调用约定,即int __cdecl text(int x,int y),调试到test调用前看一下如图:
 
  查看汇编代码得知,调用test(99,100)的过程,先是把参数从右往左入栈(push 64h;push 63h),然后跳转到test函数处(call 40100F),test返回后清理一下堆栈(add esp,8;2个参数嘛)。很明显是个C调用约定的函数。
  继续跟进40100F,发现是个跳转指令jmp 401020,401020正是我们test函数的代码地址见图:
 
  先别单步,看一下寄存器ESP值是18FEF0,call之前(这里就不截图了,嫌麻烦打字吧)值是18FEF4,发现什么了吗,call调用往栈里压了一个数,什么数呢?我们看一下18FEF0存的是啥:
 
  哦,401071看上去怎么感觉有点熟悉,不就是前面add esp,8这条指令的地址嘛,见可call XXXX等于先把下一条指令(可以理解成返回地址)入栈然后jmp XXXX。
  最后我们瞄一眼test函数内都干了些啥,首先将上一次函数的栈基址入栈保存,同时也保存了栈顶(间接),接着将当前ESP改成此函数的栈基址,再申请了40H个字节的局部变量空间,然后保存EBX、ESI、EDI三个寄存器的值,走一个循环将申请的40H字节的局部变量空间初始化为CC,往下执行函数具体功能代码,结束后依次返回之前寄存器的值,恢复上一次函数的栈基址和栈顶,最后跳转回返回地址。(注意这里返回前没有清理堆栈,从这也能看出是C约定)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值