一起读反汇编代码之函数的调用

不知道小白有没有这样的困惑,就是单独一条的汇编语句能读懂,但是好几条组合到一起就感觉云里雾里了
那你就适合跟我们一起来读反汇编代码了,把基础指令的反汇编代码读懂才能逆向出一些更难的算法,今天我们一起来读的就是反汇编的基础部分———函数的调用


这里主要讲解的是函数调用的三方面知识:栈帧的形成、调用方式、参数以及EBP/ESP寻址
你可能有时候读反汇编代码的时候遇到一些问题例如:
经常在函数开头看见 push ebp ;mov ebp ,esp ;sub  esp,xxx这三行代码不知道做什么用的(分号只做分割用)
经常看见一些mov ecx,ss:[ebp+0x8];mov ecx,ss:[ebp-0x4]这样的赋值语句,却不知道赋的是什么值
经常在call 之后看见add esp,xxxx也不知道有什么用


那你应该能从我的帖子中找到答案
用的IDE是VC++6.0,程序有debug版本的也有release版本的,调试器就是论坛的OD



一.关于栈帧
说到函数的调用,不得不提的就是栈帧,你可能会经常在函数的开头看见这么几行代码:
push ebp
mov  ebp esp
sub   esp  xxxx
这三行代码其实就是用来形成栈帧的
那么这个栈帧到底是什么东西呢,我对它的定义就是:用于存放每次函数调用信息的专属栈空间
包括在被调用函数内部声明的局部变量

上面三行代码执行后,现在栈就变成来了这样

QQ拼音截图20190917204500.png

下面来解释下什么叫做栈平衡:
当我们调用一个函数的时候,就会针对这个开辟出其所需要的栈空间(因为要存放局部变量),形成属于这个函数的栈帧,而当调用结束之后,需要清除掉这个栈空间(关闭栈帧),这一过程就叫做栈平衡。
所以为什么局部变量有自己的生命周期就是因为函数执行完之后用于存放局部变量的栈帧被清除掉了,当然还是存在于内存中,只是无法再访问它了

而如果栈不平衡会怎样呢,我们知道调用函数时,需要先将函数所需的参数压入栈中,最后压入的是函数的返回地址,如果栈不平衡,会导致函数无法正确返回,而导致程序的崩溃。
所以!在退出函数时,会进行ESP与EBP的比较,用来检查栈帧是否被正确的关闭。

QQ拼音截图20190917180941.png

在函数的末尾处开始检查栈平衡

QQ拼音截图20190917181228.png

我们跟到cmp之后的call中看看发生了什么

QQ拼音截图20190917181506.png

一个不等于则跳转,手动修改标志位看看会怎样

QQ拼音截图20190917181919.png

呃,程序报错了,这就是栈空间溢出的后果


二.函数的调用方式
这里的函数调用方式其实是关于其参数的传递与栈空间的平衡的,看到这可能有人会说了,前面不是已经讲了栈空间的平衡里吗,为什么这里又要说呢?
前面说的栈帧是栈空间的一部分,是用来记录被调用函数的相关信息的,那么函数参数的传递大部分也是通过栈传递的,我们调用完函数后,这些参数也就没有其存在的价值里,我们就应该清除它们
C++中函数的调用方式主要有以下三种:
1._cdecl:是c++中默认的调用方式,所用参数从右到左入栈,这些参数由调用者清除
2._stdcall:入栈方向一致,参数由被调用者清除
3._fastcall:是快速调用方式,一般是前两个参数由寄存器传递,其他参数还是用栈传递,参数由被调用者清除
这里的东西比较简单,其实就是add  esp,xxxx 是放到被调用函数内部还是放在调用语句之后的区别
如果你有时在call之后看到add  esp,xxxx就知道是消除参数用的了

QQ拼音截图20190917185250.png



三.函数的参数以及EBP/ESP寻址
被调用函数执行时需要从栈空间中取出函数参数以及局部变量,现在的ESP作为栈顶指针EBP作为栈底指针,现在是通过哪个寄存器寻址呢,答案是EBP

QQ拼音截图20190917192922.png

上面的反汇编代码中我们即将调用一个printf函数,用来输出两个整数
一个整数是定义的局部变量而另一个整数是这个被调用函数的参数,可以看见OD已经帮我们做了分析
为了观察参数与局部变量的寻址方式我们可以先取消勾选“显示函数中的参数及局部变量”

QQ拼音截图20190917193238.png

取消勾选后OD就会直接显示出变量的地址

QQ拼音截图20190917193530.png

我们发现局部变量与参数的地址对于EBP的偏移量的符号不同,我们知道栈帧是在真正开始调用函数时才开始形成的,而参数却是在栈帧形成前就已经被压入栈中了。
所以偏移量为正数的理应是被调用函数的参数,是负数的自然就是被调用函数的局部变量了。


这里我们想一下如果我们传递了两个参数,而又在函数内部声明了两个局部变量,那么它们对于ESP的偏移又是多少呢?
而[esp]和[esp+4]中又存放的是什么值呢?
我最后再来解答

接下来我们说说ESP寻址,你有没有一个问题就是明明有ESP作为栈顶指针可以用来寻址,根本不需要EBP来做个中间量,是为了检查栈帧平衡吗,有时候或许完全不需要EBP,只要我们按规定进行栈平衡,我们也就不需要对栈平衡进行检查
事实上还真是这样,如果我们选择release版本,优化中勾选最快速度,那么我们就不再使用EBP,而是直接使用ESP进行寻址

QQ拼音截图20190917200019.png

这时候较小地址处放的应该是局部变量,较大地址处放的是参数,因为压栈时栈顶指针会减小

好了现在说说上面问题的答案了:

QQ拼音截图20190917201057.png

QQ拼音截图20190917201141.png

[EBP]中存放的应该是栈帧生成前的EBP的值,因为被调函数的开始有个push ebp
[EBP+4]中存放的应该是函数的返回地址,因为调用函数时压入参数之后还会把下一条语句的地址压入栈中

QQ拼音截图20190917204452.png

最后再附上源码跟程序:https://www.lanzous.com/i681dzi
好了,到这里今天的学习就结束了,能力有限,可能会有疏漏之处,欢迎在评论区指出。
感谢各位看官!
如果觉得这篇帖子对你有一点帮助的话,希望能动动小手帮忙给下免费的评分,谢谢大家了!



D:/%E6%9C%89%E9%81%93%E4%BA%91/christy_wjc@163.com/a13e3e238173428092cd46edbf8b7782/qq%E6%8B%BC%E9%9F%B3%E6%88%AA%E5%9B%BE20190917185250.png

D:/%E6%9C%89%E9%81%93%E4%BA%91/christy_wjc@163.com/a13e3e238173428092cd46edbf8b7782/qq%E6%8B%BC%E9%9F%B3%E6%88%AA%E5%9B%BE20190917185250.png

THE END
喜欢就支持以下吧
点赞0
分享
评论 抢沙发
  • 管埋员

    昵称

  • 取消
    昵称