《简单反汇编阅读》是我学习了《天书夜读》后写的心得,并做成了动画, 也可以说是我看《天书夜读》的读书笔记吧。我贴出文本,然后提供动画的下载地址,希望对还没入门的兄弟有所帮助,也希望高手能指出我的不足。
大家好,我是 BoXer ,今天为大家做的动画是《简单反汇编阅读(一)》
这类的文章是不少,但是动画好像很少,那我做一个抛砖引玉吧。
由于水平有限,而且动画定位于初学者,所以动画会显得有点唠叨,请各位多多包涵
------------------------------------------------------------------------------
我们先启动VC++6.0,输入下面的程序
程序很短,大家看我输入一次,留意成对编码原则,防止对低级的错误
void boxer(int a,int b)
{
int c=a+b;
}
void main()
{
boxer(1,2);
}
一个简单的有2个形参的boxer()函数,接着在main()主函数调用boxer()函数
然后我们debug看看,F10单步
------------------------------------------------------------------------------
6: void main()
7: {
00401060 push ebp ;保存ebp, 执行这句前ESP = 0012FF84,EBP = 0012FFC0
;push的结果是esp总减少,执行后ESP = 0012FF80,EBP = 0012FFC0
00401061 mov ebp,esp ;将esp放入ebp中此时ebp和esp相同,即执行后ESP = EBP = 0012FF80
;原EBP值已经被压栈(位于栈顶),而新的EBP又恰恰指向栈顶。
;此时EBP寄存器就已经处于一个非常重要的地位,该寄存器中存储着栈中的一个地址(原EBP入栈后的栈顶),
;从该地址为基准,向上(栈底方向)能获取返回地址、参数值,向下(栈顶方向)能获取函数局部变量值,
;而该地址处又存储着上一层函数调用时的EBP值!
------------------------------------------------------------------------------
00401063 sub esp,40h ;把esp往上移动一个范围
;等于在栈中空出一片空间来存局部变量
;执行这句后ESP = 0012FF40
00401066 push ebx ;下面3句都是保存3个寄存器
00401067 push esi
00401068 push edi
00401069 lea edi,[ebp-40h] ;把ebp-40h加载到edi中,目的是保存局部变量的区域
0040106C mov ecx,10h
00401071 mov eax,0CCCCCCCCh ;从ebp-40h开始的区域初始化成全部0CCCCCCCCh,就是int3断点
00401076 rep stos dword ptr [edi] ;拷贝字符串,初始化局部变量空间
;以上的语句就是在栈中开辟一块空间放局部变量
;然后把这块空间都初始化为0CCCCCCCCh,就是int3断点,一个中断指令。
;因为局部变量不可能被执行,执行了就会出错,这时候发生中断提示开发者。
;到时候我们可以用od很直观的看到
------------------------------------------------------------------------------
8: boxer(1,2);
00401078 push 2 ;参数2入栈,执行前ESP = 0012FF34,执行后ESP = 0012FF30
0040107A push 1 ;参数1入栈,执行后ESP = 0012FF2C
0040107C call @ILT+0(boxer) (00401005) ;调用boxer()函数,可以按F11跟进
00401081 add esp,8 ;调用完函数后恢复/释放栈,执行后ESP = 0012FF34
;其实call 指令调用一个过程, 但它有一个小动作
;在参数入栈以后, 被调用函数执行 之前, 它会将当前函数的下一条指令地址, 即EIP的值压入
;
;在 c/c++ 中, 函数的默认调用约定为 cdecl, 它约定参数从右到左入栈,
;由调用者清理堆栈, 所谓清理, 即调整ESP的值, 使得原来的局部数据不再属于栈
------------------------------------------------------------------------------
9: }
00401084 pop edi ;下面3句都是恢复寄存器,上面怎样push,这里就要对应反过来pop
00401085 pop esi ;简单来说就是先进来最后才出去,最后进来的先出去
00401086 pop ebx
00401087 add esp,40h ;恢复esp,对应上面的sub esp,40h
0040108A cmp ebp,esp ;检查esp是否恢复正常,不正常就进入下面的call里面debug
0040108C call __chkesp (004010b0) ;处理可能出现的堆栈错误(如果出错,将陷入debug)。
00401091 mov esp,ebp ;将栈顶指针放回esp
00401093 pop ebp ;恢复原来的ebp和esp,让上一个调用的函数正常使用
00401094 ret ;将返回地址存入EIP, 转移流程
;以上那部分代码在vc的debug调试版才会有,主要检查栈是否被破坏了。这样就能及时在你出错的地方停下了告诉你。
;发行版自动消除这些代码(调试产生的)。
;如果函数有返回值,返回值将放在eax返回(这就是很多软件给秒杀爆破的原因了,因为eax的返回值是可以改的)
------------------------------------------------------------------------------
好了,到了这里我们基本弄清主函数的详细过程了,其实无论主函数还是其他的普通函数,过程都是基本是一样的
下面我们看看boxer()有那些不同就行了
------------------------------------------------------------------------------
1: void boxer(int a,int b)
2: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,44h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
;上面的代码跟前面介绍过的几乎一样了
------------------------------------------------------------------------------
3: int c=a+b;
00401038 mov eax,dword ptr [ebp+8] ;取第一个参数a放在eax里面
0040103B add eax,dword ptr [ebp+0Ch] ;取第二个参数b,加上a的值放在eax中
0040103E mov dword ptr [ebp-4],eax ;将最终结果放在c中
;一般而言,ss:[ebp+4]处为返回地址
;ss:[ebp+8]处为第一个参数值(这里是a),ss:[ebp+0Ch]处为第二个参数(这里是b,这里8+4=12=0Ch)
;ss:[ebp-4]处为第一个局部变量(这里是c),ss:[ebp]处为上一层EBP值
;ebp和函数返回值是32位,所以占4个字节
------------------------------------------------------------------------------
4: }
00401041 pop edi
00401042 pop esi
00401043 pop ebx
00401044 mov esp,ebp
00401046 pop ebp
00401047 ret
;恢复性的操作,只是比前面少了检查esp的语句而已
------------------------------------------------------------------------------
基本的函数反汇编阅读过程就这样的,下一次的动画我们将进入复杂一点的函数里面看看。
如果看动画的时候有什么疑问,可以通过我的blog留言或者直接发E-mail给我,期待你的交流
http://boxer.yo2.cn
《简单反汇编阅读(一)》动画下载地址:
http://3800hk.com/donghua/g/18945.html
Linux平台漏洞分析、利用和挖掘