欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]C语言最简main函数的反汇编代码解析

[原创]C语言最简main函数的反汇编代码解析

对于新手而言,汇编看着就头疼,更别提去OD什么东东了,我也正处于这个阶段。前两天看了《天书夜读》第一章关于main函数反汇编的解析,顿时有种茅塞顿开的感觉。再看起来汇编代码,虽然还是没有看C语言那么容易,但心情变得很轻松。这都是《天书夜读》的功劳。特此分享给菜菜同行。
目前我们写的最简单的Main函数如下:


#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])

{

	return 0;

}


利用VS编译器将运行时程序反汇编,结果如下:

int _tmain(int argc, _TCHAR* argv[])

{

010C13A0  push        ebp  

010C13A1  mov         ebp,esp  

010C13A3  sub         esp,0C0h  

010C13A9  push        ebx  

010C13AA  push        esi  

010C13AB  push        edi  

010C13AC  lea         edi,[ebp-0C0h]  

010C13B2  mov         ecx,30h  

010C13B7  mov         eax,0CCCCCCCCh  

010C13BC  rep stos    dword ptr es:[edi]  

	return 0;

010C13BE  xor         eax,eax  

}

010C13C0  pop         edi  

010C13C1  pop         esi  

010C13C2  pop         ebx  

010C13C3  mov         esp,ebp  

010C13C5  pop         ebp  

010C13C6  ret


看起来汇编很头疼吧,那么让我们来茅塞顿开吧。首先了解一下以前几个未知半懂的汇编指令的含义:

push        ebp

mov         ebp,esp


push:把一个32位操作数压入栈中,这个操作导致esp被减4。 ebp被用来保存这个函数执行前的esp的值,执行完毕后用ebp恢复esp。同时,调用此函数的上层函数也用ebp做同样的事情。所以先把ebp压入堆栈,返回之前弹出,避免ebp被我们改动。

xor         eax,eax

ret


xor eax,eax常用来代替mov eax,0。清零操作。在windows中,函数返回值都是放在eax中然后返回,外部从eax中得到返回值。这就代表return 0操作。

lea         edi,[ebp-0C0h]


lea取得第二个参数代表的地址放入第一个参数代表的寄存器中。

mov         ecx,30h  

mov         eax,0CCCCCCCCh  

rep stos    dword ptr es:[edi]  


stos是串存储指令,它的功能是将eax中的数据放入edi所指的地址中,同时edi会增加4个字节。Rep使指令重复执行ecx中填写的次数。
由于CPU的寄存器有限,而且操作寄存器会影响标志值,push作用是压栈,pop是退栈。即保存寄存器标志的值和寄存器本身的值,以便在函数调用完毕后恢复原有的标志值。这也是为什么我们见到在调用某个函数或者运行一个程序时,入口总是push一堆寄存器的东东,也就是这个原因。
通过上面的基础知识,我们来看看main函数的反汇编解释:

int _tmain(int argc, _TCHAR* argv[])

{

010C13A0  push        ebp  

;保存ebp,返回之前弹出,避免ebp被我们改动。push操作使esp减小,esp不变

010C13A1  mov         ebp,esp

; ebp被用来保存这个函数执行前的esp的值,执行完毕后用ebp恢复esp

;原ebp值已经被压栈(位于栈顶),而新的ebp又恰恰指向栈顶

;此时ebp寄存器就已经处于一个非常重要的地位,该寄存器中存储着栈中的一个地址(原ebp入栈后的栈顶)

;从该地址为基准,向上(栈底方向)能获取返回地址、参数值(假如main中有参数,“获取参数值”会比较容易理解

;向下(栈顶方向)能获取函数局部变量值,而该地址处又存储着上一层函数调用时的ebp值

010C13A3  sub         esp,0C0h 

;把esp往上移动一个范围,等于在栈中开辟一片空间存储main函数的局部变量

;由于冯诺依曼机是小端模式,所以sub操作可以理解为将esp栈顶减去,实际上是为栈增加空间

010C13A9  push        ebx  

010C13AA  push        esi  

010C13AB  push        edi  

;保存三个寄存器的值,待main结束恢复,原来的值被破坏有可能引起系统崩溃

010C13AC  lea         edi,[ebp-0C0h]  

;把ebp-0c0h加载到edi中,目的是保存局部变量的区域

010C13B2  mov         ecx,30h  

010C13B7  mov         eax,0CCCCCCCCh  

010C13BC  rep stos    dword ptr es:[edi]  

;从ebp-0c0h开始的局部变量空间区域初始化成全部0CCCCCCCCh(也就是int 3中断指令的机器码)

;局部变量不可能被执行,执行了就会出错。这样发生意外时执行堆栈里面的内容会引发调试中断提示开发者

	return 0;

010C13BE  xor         eax,eax

; 返回值将放在eax返回(这就是很多软件给秒杀爆破的原因,因为eax的返回值是可以改的) 

}

010C13C0  pop         edi  

010C13C1  pop         esi  

010C13C2  pop         ebx  

; 恢复原来寄存器的值,怎么“吃”进去,怎么“吐”出来

010C13C3  mov         esp,ebp 

;恢复ESP栈顶指针

010C13C5  pop         ebp

;恢复ebp,也就是恢复调用main函数之前各个寄存器的状态 

010C13C6  ret

;将返回地址存入eip,退出主函数


通过以上分析,大家应该对main函数的反汇编解析有一定的概念了吧。返回去看文章开头的反汇编代码,心里肯定比刚开始看的感觉舒服多了。在实战中,我们OD的汇编代码基本也就是这个套路,当然细节还得另外分析。有什么错误,敬请大伙指正,共同学习。
参考资料:

《天数夜读:从汇编语言到windows内核编程》第一章

反汇编代码分析--函数调用  http://blog.csdn.net/dolphin98629/article/details/6047665

C语言中一个最简main函数的反汇编代码的困惑  http://tieba.baidu.com/p/1184907086

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功