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

来源:互联网 发布:网络增值服务登录 编辑:程序博客网 时间:2024/04/29 16:20

呵,好久没写CSDN文章了,来凑个热闹。最近我阅读了楚狂人,wowocock写的《天书夜读》试读本,对C反汇编感触颇深,书中有一例算法反汇编,其对汇编的阅读确实富有挑战,而该书中也未详解,在此,我谨将此例详细分析如下,帮助大家更好理解C反汇编代码,若有任何错误,请大家批评指正!

         该例的要求是求两个3x3矩阵的乘积,其C源代码如下:

int myfunction(int a[3][3], int b[3][3], int c[3][3])

{

         int i, j;

         for (i = 0; i < 3; ++i)

         {

                   for (j = 0; j < 3; ++j)

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

       }

       return 0;

}

相当简单吧?呵呵,那么希望你看过了下面的反汇编代码后,还能这样乐观。

如下的反汇编代码,据原书作者,在VC2003debug模式下得到,同时我在VC2008PRO下得到汇编代码完全相同,可以通用,但此处引用原书中的代码。

00411A3E mov dword ptr [i],0

00411A45 jmp myfunction+30h (411A50h)

00411A47 mov eax,dword ptr [i]

00411A4A add eax,1

00411A4D mov dword ptr [i],eax

00411A50 cmp dword ptr [i],3

00411A54 jge myfunction+0AEh (411ACEh)

00411A56 mov dword ptr [j],0

00411A5D jmp myfunction+48h (411A68h)

00411A5F mov eax,dword ptr [j]

00411A62 add eax,1

00411A65 mov dword ptr [j],eax

00411A68 cmp dword ptr [j],3

00411A6C jge myfunction+0A9h (411AC9h)

00411A6E mov eax,dword ptr [i]

00411A71 imul eax,eax,0Ch

00411A74 mov ecx,dword ptr [a]

00411A77 mov edx,dword ptr [j]

00411A7A mov esi,dword ptr [b]

00411A7D mov eax,dword ptr [ecx+eax]

00411A80 imul eax,dword ptr [esi+edx*4]

00411A84 mov ecx,dword ptr [i]

00411A87 imul ecx,ecx,0Ch

00411A8A mov edx,dword ptr [a]

00411A8D mov esi,dword ptr [j]

00411A90 mov edi,dword ptr [b]

00411A93 mov ecx,dword ptr [edx+ecx+4]

00411A97 imul ecx,dword ptr [edi+esi*4+0Ch]

00411A9C add eax,ecx

00411A9E mov edx,dword ptr [i]

00411AA1 imul edx,edx,0Ch

00411AA4 mov ecx,dword ptr [a]

00411AA7 mov esi,dword ptr [j]

00411AAA mov edi,dword ptr [b]

00411AAD mov edx,dword ptr [ecx+edx+8]

00411AB1 imul edx,dword ptr [edi+esi*4+18h]

00411AB6 add eax,edx

00411AB8 mov ecx,dword ptr [i]

00411ABB imul ecx,ecx,0Ch

00411ABE add ecx,dword ptr [c]

00411AC1 mov edx,dword ptr [j]

00411AC4 mov dword ptr [ecx+edx*4],eax

00411AC7 jmp myfunction+3Fh (411A5Fh)

00411AC9 jmp myfunction+27h (411A47h)

晕了?呵呵,如果你第一遍看就可以完全读通,那我只好对您Orz了= =,希望你们这些天才别来砸我场子啊^_^

    首先先简单提一下,C语言中多维数组的存储方式。其存储的规则是,列优先于行,也就是:若有a[3][3],则顺序是a[0][0], a[0][1], a[0][2], a[1][0], a[1][1], a[1][2],  a[2][0], a[2][1], a[2][2]。其实所谓的多维数组这样高级语言才有的数据结构,在底层实现中,无非就是一片连续分配的一维内存空间而已,大家千万别看得太过神秘了。

    接着要提一下C语言中指针在汇编实现时的情况。汇编可以说把高级语言中几乎所有现象的本质都暴露了出来。现观察一下以下情况:对于int a[3][3]定义的多维数组变量aa本身表示的是该矩阵首行的地址,而a[0](或者*a)是首行首列元素的地址,两者是不同的指针类型,然而很容易知道在地址数值上两者是相等的,我们引申开来,比如我们要访问a[m][n],我们还可以写作*(*(a + m) + n),但由上段所述,实际上就是把a所含的地址再往后移m * 3 * 4(int4个字节) + n * 4个字节(此注:a + m不是a所在处后移m个字节!可以把a理解为int[3]这种类型的指针,所以加了m,其实是加了12* mbytes)就是所要访问的内存,换句话说任何多维数组总可以用一个一维普通指针(比如使用指针类型强制转换)完全访问到,尽管在高级语言中这样的做法是非常不明智且危险的。但汇编却正是这么做的!比如我们有C语句:a[i][j] = 2;,用C还可以写作*(*(a + i) + j) = 2,而用汇编则可能是这样,

mov eax, dword ptr [i] ; i的值读入eax

imul eax, eax, 0Ch ; eax乘以12,因为一行有三个int3 * 4 = 12

lea ecx, a[eax] ; 相当于将a所含的地址与eax相加后存入ecx

mov edx, dword ptr [j] ; j的值读入edx

mov dword ptr [ecx + edx * 4], 2 ; ecx + edx就是a[i][j]的地址

    我们尝试省略寄存器的中间步骤、并逐式代入的话,就直接可以推出这个式子:

mov  dword ptr [a + i * 12 + j * 4], 2 ;这个式子和上述的完全一致。

    最后提到的就是Cfor语句的汇编实现。

for (init; condition; expr)

    loop-body

大家都应当很清楚,执行的顺序是:先init初始化循环变量,接着判断condition,若满足则执行循环体(loop-body),再执行expr,判断条件是否满足,满足则执行loop-body……汇编代码是非常机械地对应着上述过程,例:

for (i = 0; i < 5; ++i)

loop-body

则相应汇编代码:

; 初始化代码

mov dword ptr [i], 0

jmp (*) ; 无条件跳转到(*)所在代码,这里不列出具体的代码地址了

; 这段就是expr,类似用一个中间变量(只不过这里是寄存器)的办法来给i加一

(#)mov eax, dword ptr [i]

add eax, 1

mov dword ptr [i], eax

; 这段则是判断条件,condition

(*) cmp dword ptr [i], 5 ; 比较i5

jge (**) ; jge表示jump to (**) when i is greater than or equal to 5,大于等于5

; 跳转,(**)指向的是for循环后面接着的代码,也就是跳出循环

; 以下段省略,是循环体loop-body

jmp (#) ;无条件跳转到expr,并进而判断条件

; 以下是for循环以外的代码

(**)