C反汇编实例(详细注解版)(三)

上次我分析了一下,debug模式下反汇编后的算法部分代码,天才的您可能觉得不算糟,想再搞点花样,那么本文就能满足你的需求。天书夜读上其实还贴出来了release模式下的代码,它经过vc编译器O2的优化,我初次看到反汇编代码时,还真汗了一把。不过定下心来细细品位还是可以看懂的,尽管正如原书所说,连语句的对应顺序也已经不见了。。。

         废话不多说,先贴出代码大伙“饱饱眼福”。。。

00401000 mov eax,dword ptr [esp+4]

00401004 mov edx,dword ptr [esp+0Ch]

00401008 mov ecx,dword ptr [esp+8]

0040100C push ebx

0040100D push esi

0040100E add eax,4

00401011 push edi

00401012 add edx,8

00401015 mov esi,3

0040101A lea ebx,[ebx]

00401020 mov ebx,dword ptr [eax]

00401022 imul ebx,dword ptr [ecx+0Ch]

00401026 mov edi,dword ptr [ecx+18h]

00401029 imul edi,dword ptr [eax+4]

0040102D add edi,ebx

0040102F mov ebx,dword ptr [eax-4]

00401032 imul ebx,dword ptr [ecx]

00401035 add edi,ebx

00401037 mov dword ptr [edx-8],edi

0040103A mov ebx,dword ptr [eax]

0040103C imul ebx,dword ptr [ecx+10h]

00401040 mov edi,dword ptr [ecx+1Ch]

00401043 imul edi,dword ptr [eax+4]

00401047 add edi,ebx

00401049 mov ebx,dword ptr [eax-4]

0040104C imul ebx,dword ptr [ecx+4]

00401050 add edi,ebx

00401052 mov dword ptr [edx-4],edi

00401055 mov ebx,dword ptr [eax+4]

00401058 imul ebx,dword ptr [ecx+20h]

0040105C mov edi,dword ptr [ecx+14h]

0040105F imul edi,dword ptr [eax]

00401062 add edi,ebx

00401064 mov ebx,dword ptr [eax-4]

00401067 imul ebx,dword ptr [ecx+8]

0040106B add edi,ebx

0040106D mov dword ptr [edx],edi

0040106F add eax,0Ch

00401072 add edx,0Ch

00401075 dec esi

00401076 jne myfunction+20h (401020h)

00401078 pop edi

00401079 pop esi

0040107A xor eax,eax

0040107C pop ebx

代码确实精简了不少,也精简地一点对应关系也找不到了。。。这份汇编代码,比(二)中其实要完整,因为它把整个函数调用到返回的代码都列出来了,尽管也早精简得不是标准的函数汇编代码了,由此可见VC编译器的优化能力之强令人叹为观止。。。

老规矩,对上述汇编代码的部分内容先作一简析。

第一.         细心的读者一定已经发现,(二)中的[](间接寻址符)包含的都是活生生的变量名,而在release中已经变为寄存器的表达式,当然我们可以根据参数入栈的顺序来推导出哪个是哪个,这会在后续有所说明。

第二.         正常的函数调用过程开始都会先将ebp入栈,而后将ebp用作esp的暂存寄存器,而在这里,从头至尾就没有出现过ebp,当然主要是因为esp并没有被函数用做特殊用途,再追溯根源一点,是因为循环变量i被更高速的寄存器esi所替代,而j这个循环。。。很神奇的由于只有3次,居然被编译器展开成三个等效计算过程给代替了。

第三.         最后一点也不是很重要,就是寄存器压栈的指令原本是放在最前的,现在又很神奇地被嵌在了数据计算语句之间。这点更加体现了经过优化的代码比较不符合人的常识,但更契合高性能的需要。

现在我们从头开始,一段段看:

; 随着c, b, a顺序入栈,堆栈向地址减小的方向发展,故而可以容易推断出

00401000 mov eax,dword ptr [esp+4] ; 对应a

00401004 mov edx,dword ptr [esp+0Ch] ; 对应c

00401008 mov ecx,dword ptr [esp+8] ; 对应b

; 40100C, 40100D, 401011 三个指令是常规的寄存器保存工作

0040100C push ebx

0040100D push esi

; 这里给eaxedx分别加上一个值的目的,后面会得知

0040100E add eax,4

00401011 push edi

00401012 add edx,8

; 使用esi寄存器作为循环变量,提高执行速度

00401015 mov esi,3

; 下面一行指令尽管看不出什么用处,但我倒不是很赞同原书上的说法,

; 如果有哪位大侠知道,请告知下大家,谢谢

0040101A lea ebx,[ebx]

{ esi循环体

; 原先j = 0时要执行的代码片段

; 最初eax保存了a数组的首地址,而对eax+4,则等于跳到01列这个元素

; 所以这时ebx = a[0][1]

00401020 mov ebx,dword ptr [eax]

; ecx对应b, 加了12,即跳跃过3int,指向b[1][0]

; ebx = a[0][1] * b[1][0]

00401022 imul ebx,dword ptr [ecx+0Ch]

; 同理edi = b[2][0]

00401026 mov edi,dword ptr [ecx+18h]

; edi = b[2][0] * a[0][2],注意这里的eax+4,实际上是a往后8个字节

00401029 imul edi,dword ptr [eax+4]

; edi = b[2][0] * a[0][2] + a[0][1] * b[1][0]

0040102D add edi,ebx

; ebx = a[0][0], eax-4就是a首地址,即00列这个元素

0040102F mov ebx,dword ptr [eax-4]

; ebx = a[0][0] * b[0][0]

00401032 imul ebx,dword ptr [ecx]

; edi = b[2][0] * a[0][2] + a[0][1] * b[1][0] + a[0][0] * b[0][0]

; 把上式中各项换下位置,稍微整理下,很容易就看出它就是当j = 0时,c[i][0]的表达式

; 大家可以对照C的源程序,一看就会明白

00401035 add edi,ebx

; 接着下面一句,由于前面edx被加过一次8,故而这里edx-8就指向c首地址,

; c[0][0] = edi = b[2][0] * a[0][2] + a[0][1] * b[1][0] + a[0][0] * b[0][0]

00401037 mov dword ptr [edx-8],edi

; 下面j = 1, 2的情况与j = 0的算法完全一样,大家完全可以自己看懂了

; 原先j = 1时要执行的代码片段

0040103A mov ebx,dword ptr [eax]

0040103C imul ebx,dword ptr [ecx+10h]

00401040 mov edi,dword ptr [ecx+1Ch]

00401043 imul edi,dword ptr [eax+4]

00401047 add edi,ebx

00401049 mov ebx,dword ptr [eax-4]

0040104C imul ebx,dword ptr [ecx+4]

00401050 add edi,ebx

00401052 mov dword ptr [edx-4],edi

; c[0][1] = edi = b[2][1] * a[0][2] + a[0][1] * b[1][1] + a[0][0] * b[0][1]

; 原先j = 2时要执行的代码片段

00401055 mov ebx,dword ptr [eax+4]

00401058 imul ebx,dword ptr [ecx+20h]

0040105C mov edi,dword ptr [ecx+14h]

0040105F imul edi,dword ptr [eax]

00401062 add edi,ebx

00401064 mov ebx,dword ptr [eax-4]

00401067 imul ebx,dword ptr [ecx+8]

0040106B add edi,ebx

0040106D mov dword ptr [edx],edi

; c[0][2] = edi = b[2][2] * a[0][2] + a[0][1] * b[1][2] + a[0][0] * b[0][2]

} esi 循环体

; 可以看到,上面这个循环体每次处理的是ac对应的一行数据,如上面的注解是针对第0

; 行的情况,而下面两个指令就是把ac分别下移一行,即下次将处理ac的第1行,只要

; 把上面注解中c[0][x]改为c[1][x]a[0][y]改为a[1][y],依次类推……

0040106F add eax,0Ch

00401072 add edx,0Ch

; 循环变量esi1,若还不为0,则循环未结束,跳转后接着循环

00401075 dec esi

00401076 jne myfunction+20h (401020h)

; 函数结束时常规的寄存器恢复工作,依次恢复edi, esi, ebx,和保存顺序相反

00401078 pop edi

00401079 pop esi

0040107A xor eax,eax ; 返回值在eax里,异或运算,把eax清零,即返回值是0

0040107C pop ebx

       其实对C反汇编的技术还有很多,这里只是举个例子给大家起一个抛砖引玉的作用,要完全领悟还得靠自己去学习去多多实践。

       本系列三篇文章就结束了,首先感谢《天书夜读》的作者,另外如果由于我语文或技术水平有限导致理解有些困难的话,那我先说声sorry哈,也欢迎大家在回复中提出各种问题共同探讨,共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值