您的位置:首页 > 编程语言

c代码反汇编研究初探

2011-08-31 15:19 274 查看
c代码反汇编研究初探(1),DEBUG篇。

Author:xue23

email:xue23@163.com
下面是原代码。我在vc6.0中对这段代码进行完全的反汇编,以研究c语言在机器码级

的运行状态。这一部分研究DEBUG下的情况。

int __stdcall stdcalltest(int i, int j)

{

int ret = i + j;

return ret;

}

int __cdecl cdecltest(int i, int j)

{

int ret = i + j;

return ret;

}

int __cdecl cdecltest2(int i, int j)

{

int ret = i + j;

return ret;

}

int __stdcall stdcalltest2(int i, int j)

{

int ret = i + j;

return ret;

}

int main() {

int p1 = 1;

int p2 = 2;

int p3 = 3;

int p4 = 4;

int ret;

ret = stdcalltest(p1, p2);

ret = cdecltest(p3, p4);

ret = stdcalltest2(p1, p2);

ret = cdecltest2(p3, p4);

return 0;

}

下面是反汇编后的情况。

--- F:\Project\consoletest\consoletest.cpp

--------------------------------------------------------------------------

------

45:

46:

47:

48: int main() {

//首先保护现场,以便本程序执行完后,恢复现场。

00401150 push ebp ;保存上一层函数的栈,函数就是利用它来实现

一层层传递与回归(回溯).

00401151 mov ebp,esp ;用本函数的栈顶更新ebp.

00401153 sub esp,54h ;预留足够大的空间存储局部变量

00401156 push ebx ;

00401157 push esi ;

00401158 push edi ;保存通用寄存器啦。

00401159 lea edi,[ebp-54h] ;指向栈底

0040115C mov ecx,15h ;

00401161 mov eax,0CCCCCCCCh ;

00401166 rep stos dword ptr [edi] ;初始化这段栈区,用cc初始化每个字

节,大小为15h * 4 = 54h. 这就是为什么要循环因子设为15h的原因。

//从桡顶向下对局部变量赋值.ebp-4指向栈顶处的第一个存储区域,以此类推!

52:

53: int p1 = 1;

00401168 mov dword ptr [ebp-4],1

54: int p2 = 2;

0040116F mov dword ptr [ebp-8],2

55: int p3 = 3;

00401176 mov dword ptr [ebp-0Ch],3

56: int p4 = 4;

0040117D mov dword ptr [ebp-10h],4

57:

58: int ret;

59: ret = stdcalltest(p1, p2);

//这里要注意的是,压栈顺序从右向右,看一下__stdcall与__cdecl之间的调用区别



@ILT的含义见下面的函数表.

00401184 mov eax,dword ptr [ebp-8]

00401187 push eax

00401188 mov ecx,dword ptr [ebp-4]

0040118B push ecx

0040118C call @ILT+0(stdcalltest) (00401005);stdcalltest参数使用

的栈区在stdcalltest内清除。

00401191 mov dword ptr [ebp-14h],eax

60: ret = cdecltest(p3, p4);

00401194 mov eax,dword ptr [ebp-10h]

00401197 push eax

00401198 mov ecx,dword ptr [ebp-0Ch]

0040119B push ecx

0040119C call @ILT+30(cdecltest) (00401023);cdecltest参数使用的栈

区在cdecltest外部清除,方法就是下面这一行。

004011A1 add esp,8

004011A4 mov dword ptr [ebp-14h],eax

//下面两个函数与上面是两个函数是一样的处理方法

61: ret = stdcalltest2(p1, p2);

004011A7 mov eax,dword ptr [ebp-8]

004011AA push eax

004011AB mov ecx,dword ptr [ebp-4]

004011AE push ecx

004011AF call @ILT+10(stdcalltest2) (0040100f)

004011B4 mov dword ptr [ebp-14h],eax

62: ret = cdecltest2(p3, p4);

004011B7 mov eax,dword ptr [ebp-10h]

004011BA push eax

004011BB mov ecx,dword ptr [ebp-0Ch]

004011BE push ecx

004011BF call @ILT+5(cdecltest2) (0040100a)

004011C4 add esp,8

004011C7 mov dword ptr [ebp-14h],eax

63:

64: return 0;

004011CA xor eax,eax; main返回值是0,因为返回值是放在eax里的,故

将eax清0就OK了。

65: }

;下面按函数开始的时候的反顺序来恢复cpu现场。

004011CC pop edi

004011CD pop esi

004011CE pop ebx

004011CF add esp,54h

004011D2 cmp ebp,esp

004011D4 call __chkesp (004082d0)

004011D9 mov esp,ebp

004011DB pop ebp

004011DC ret

--- No source file

--------------------------------------------------------------------------

------------------------------

编译器建立一张函数表,每个函数在里面都有一个索引。如下所示:

函数表:

...

00401004 int 3

@ILT+0(?stdcalltest@@YGHHH@Z):

00401005 jmp stdcalltest (00401050)

@ILT+5(?cdecltest2@@YAHHH@Z):

0040100A jmp cdecltest2 (004010d0)

@ILT+10(?stdcalltest2@@YGHHH@Z):

0040100F jmp stdcalltest2 (00401110)

@ILT+15(?id@?$ctype@G@std@@$D):

00401014 jmp std::ctype::id (00401240)

@ILT+20(?id@?$ctype@G@std@@$E):

00401019 jmp std::ctype::id (004012e0)

@ILT+25(_main):

0040101E jmp main (00401150)

@ILT+30(?cdecltest@@YAHHH@Z):

00401023 jmp cdecltest (00401090)

00401028 int 3

...

Release篇

release模式

release模式下,大家会看到很多栈的操作已经被优化掉了,大大加快了程序的运行效率,函体内在很多简单的情况下不会预留空间给临时变量,因为根本就没有临时变量存在了,而直接用register和memory来操作.在release下,cdcel和stdcall的区别就更明显了。

stdstall会在在退出的时候,ret 8, 也就把esp + 8, 把参数空间从栈上释放

而cdcel则由上级函数在调用它之间通过 add esp, 8来释放参数空间。因为这段代码比较简单,所以对应的asm也很简单,但这段简单的代码却提示了c++的优化机制是多少的智能和高效。

下面是release下的汇编代码

PUBLIC ?stdcalltest@@YGHHH@Z ;
stdcalltest

; COMDAT ?stdcalltest@@YGHHH@Z

_TEXT SEGMENT

_i$ = 8

_j$ = 12

?stdcalltest@@YGHHH@Z PROC NEAR ; stdcalltest, COMDAT

; 24 : int ret = i + j; 实际ret已经被优化掉了,所以在编写代码的时候,

;有的变量可能在运行的时候并没有存在,如果确保没有被优化的话,在这个变量前面加上voliate

00000 8b 44 24 08 mov eax, DWORD PTR _j$[esp-4]

00004 8b 4c 24 04 mov ecx, DWORD PTR _i$[esp-4]

00008 03 c1 add eax, ecx

; 25 : return ret;

; 26 : }

0000a c2 08 00 ret 8

?stdcalltest@@YGHHH@Z ENDP ; stdcalltest

_TEXT ENDS

PUBLIC ?cdecltest@@YAHHH@Z ; cdecltest

; COMDAT ?cdecltest@@YAHHH@Z

_TEXT SEGMENT

_i$ = 8

_j$ = 12

?cdecltest@@YAHHH@Z PROC NEAR ; cdecltest, COMDAT

; 29 : int ret = i + j;

00000 8b 44 24 08 mov eax, DWORD PTR _j$[esp-4] ;_i$和_j在编译时已经确定下来了,增加了运行速度。

;相当于 move eax, DWORD PTR [esp - 4 + _j$],至于为什么要用[esp-4]这种形式,我想应该保护上级堆,毕竟esp-4是指向函数体以下的代码区。

00004 8b 4c 24 04 mov ecx, DWORD PTR _i$[esp-4]

00008 03 c1 add eax, ecx

; 30 : return ret;

; 31 : }

0000a c3 ret 0

?cdecltest@@YAHHH@Z ENDP ; cdecltest

_TEXT ENDS

PUBLIC ?cdecltest2@@YAHHH@Z ; cdecltest2

; COMDAT ?cdecltest2@@YAHHH@Z

_TEXT SEGMENT

_i$ = 8

_j$ = 12

?cdecltest2@@YAHHH@Z PROC NEAR ; cdecltest2, COMDAT

; 34 : int ret = i + j;

00000 8b 44 24 08 mov eax, DWORD PTR _j$[esp-4]

00004 8b 4c 24 04 mov ecx, DWORD PTR _i$[esp-4]

00008 03 c1 add eax, ecx

; 35 : return ret;

; 36 : }

0000a c3 ret 0

?cdecltest2@@YAHHH@Z ENDP ; cdecltest2

_TEXT ENDS

PUBLIC ?stdcalltest2@@YGHHH@Z ; stdcalltest2

; COMDAT ?stdcalltest2@@YGHHH@Z

_TEXT SEGMENT

_i$ = 8

_j$ = 12

?stdcalltest2@@YGHHH@Z PROC NEAR ; stdcalltest2, COMDAT

; 39 : int ret = i + j;

00000 8b 44 24 08 mov eax, DWORD PTR _j$[esp-4]

00004 8b 4c 24 04 mov ecx, DWORD PTR _i$[esp-4]

00008 03 c1 add eax, ecx

; 40 : return ret;

; 41 : }

0000a c2 08 00 ret 8

?stdcalltest2@@YGHHH@Z ENDP ; stdcalltest2

_TEXT ENDS

PUBLIC _main

; COMDAT _main

_TEXT SEGMENT

_main PROC NEAR ; COMDAT

; 46 :

; 47 : int p1 = 1;

; 48 : int p2 = 2;

; 49 : int p3 = 3;

; 50 : int p4 = 4;

; 51 :

; 52 : int ret;

; 53 : ret = stdcalltest(p1, p2);

00000 6a 02 push 2

00002 6a 01 push 1

00004 e8 00 00 00 00 call ?stdcalltest@@YGHHH@Z ; stdcalltest

; 54 : ret = cdecltest(p3, p4);

00009 6a 04 push 4

0000b 6a 03 push 3

0000d e8 00 00 00 00 call ?cdecltest@@YAHHH@Z ; cdecltest

00012 83 c4 08 add esp, 8

; 55 : ret = stdcalltest2(p1, p2);

00015 6a 02 push 2

00017 6a 01 push 1

00019 e8 00 00 00 00 call ?stdcalltest2@@YGHHH@Z ; stdcalltest2

; 56 : ret = cdecltest2(p3, p4);

0001e 6a 04 push 4

00020 6a 03 push 3

00022 e8 00 00 00 00 call ?cdecltest2@@YAHHH@Z ; cdecltest2

00027 83 c4 08 add esp, 8

; 57 :

; 58 : return 0;

0002a 33 c0 xor eax, eax

; 59 : }

0002c c3 ret 0

_main ENDP

_TEXT ENDS
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: