反汇编(Disassembly),即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编语言对软件调试、漏洞分析、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面,该笔记整理了C++反汇编的一些常识.
变量与常量
数值类型变量: 整数,浮点数,等类型其反汇编结果基本相同.
int main(int argc, char* argv[]){ int x = 10; int y = 20; int z = 0; z = x + y; return 0;}
反汇编结果如下,首先lea取地址,然后ECX=13,这里的13是因为我们有3个整数型变量,每个变量占用4个字节,所以4x3=12次,将EAX设置为CCCCCCC,并通过rep指令填充内存地址从[edi]指向的位置开始填充,填充14次,同样的也初始化了堆栈,接着通过mov赋值语句将内存地址分别初始化为指定的数值.
0040D709 |. 8D7D B4 lea edi, dword ptr [ebp-4C]0040D70C |. B9 13000000 mov ecx, 130040D711 |. B8 CCCCCCCC mov eax, CCCCCCCC0040D716 |. F3:AB rep stos dword ptr es:[edi]0040D718 |. C745 FC 0A000>mov dword ptr [ebp-4], 0A0040D71F |. C745 F8 14000>mov dword ptr [ebp-8], 140040D726 |. C745 F4 00000>mov dword ptr [ebp-C], 00040D72D |. 8B45 FC mov eax, dword ptr [ebp-4]0040D730 |. 0345 F8 add eax, dword ptr [ebp-8]0040D733 |. 8945 F4 mov dword ptr [ebp-C], eax
字符类型:
int main(int argc, char* argv[]){ char x = 'a'; char y = 'b'; char z = 'c'; z = x+y; printf("%s",z); return 0;}
反汇编结果如下,观察发现字符型的表现形式与整数类型基本一致,只是在数据位大小方面有所区别,如上int类型使用dword
作为存储单位,而字符类型则默认使用byte
形式存储.
00410659 |. 8D7D B4 lea edi, dword ptr [ebp-4C]0041065C |. B9 13000000 mov ecx, 1300410661 |. B8 CCCCCCCC mov eax, CCCCCCCC00410666 |. F3:AB rep stos dword ptr es:[edi]00410668 |. C645 FC 61 mov byte ptr [ebp-4], 610041066C |. C645 F8 62 mov byte ptr [ebp-8], 6200410670 |. C645 F4 63 mov byte ptr [ebp-C], 6300410674 |. 0FBE45 FC movsx eax, byte ptr [ebp-4]00410678 |. 0FBE4D F8 movsx ecx, byte ptr [ebp-8]0041067C |. 03C1 add eax, ecx0041067E |. 8845 F4 mov byte ptr [ebp-C], al
字符串:
int main(int argc, char* argv[]){ char x[] = "hello lyshark"; printf("%s",x); return 0;}
00401019 |. 8D7D B0 lea edi, dword ptr [ebp-50]0040101C |. B9 14000000 mov ecx, 1400401021 |. B8 CCCCCCCC mov eax, CCCCCCCC00401026 |. F3:AB rep stos dword ptr es:[edi]00401028 |. A1 1C204200 mov eax, dword ptr [42201C]0040102D |. 8945 F0 mov dword ptr [ebp-10], eax00401030 |. 8B0D 20204200 mov ecx, dword ptr [422020]00401036 |. 894D F4 mov dword ptr [ebp-C], ecx00401039 |. 8B15 24204200 mov edx, dword ptr [422024]0040103F |. 8955 F8 mov dword ptr [ebp-8], edx00401042 |. 66:A1 28204200 mov ax, word ptr [422028]00401048 |. 66:8945 FC mov word ptr [ebp-4], ax0040104C |. 8D4D F0 lea ecx, dword ptr [ebp-10] ; 存储字符串首地址
长字符串:
int main(int argc, char* argv[]){ char temp[] = "welcome to lyshark blog"; printf("%s",temp); return 0;}
00401019 |. 8D7D A8 lea edi, dword ptr [ebp-58]0040101C |. B9 16000000 mov ecx, 1600401021 |. B8 CCCCCCCC mov eax, CCCCCCCC00401026 |. F3:AB rep stos dword ptr es:[edi]00401028 |. B9 06000000 mov ecx, 60040102D |. BE AC2F4200 mov esi, 00422FAC ; ASCII "welcome to lyshark blog"00401032 |. 8D7D E8 lea edi, dword ptr [ebp-18]00401035 |. F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]00401037 |. 8D45 E8 lea eax, dword ptr [ebp-18]0040103A |. 50 push eax ; /<%s>0040103B |. 68 04244200 push 00422404 ; |format = "%s"00401040 |. E8 3BC60000 call printf ; \printf
字符串指针:
int main(int argc, char* argv[]){ char *x = "hello lyshark"; printf("%s",x); return 0;}
00401019 |. 8D7D BC lea edi, dword ptr [ebp-44]0040101C |. B9 11000000 mov ecx, 1100401021 |. B8 CCCCCCCC mov eax, CCCCCCCC00401026 |. F3:AB rep stos dword ptr es:[edi]00401028 |. C745 FC 1C204200 mov dword ptr [ebp-4], 0042201C0040102F |. 8B45 FC mov eax, dword ptr [ebp-4] ; aaa.0042201C00401032 |. 50 push eax ; /<%s>00401033 |. 68 04244200 push 00422404 ; |format = "%s"00401038 |. E8 43C60000 call printf ; \printf
数组类型:
int main(int argc, char* argv[]){ int array[] = {1,2,3,4,5}; printf("%d\n",array[0]); printf("%d\n",array[1]); return 0;}
0040D718 |. C745 EC 01000000 mov dword ptr [ebp-14], 10040D71F |. C745 F0 02000000 mov dword ptr [ebp-10], 20040D726 |. C745 F4 03000000 mov dword ptr [ebp-C], 30040D72D |. C745 F8 04000000 mov dword ptr [ebp-8], 40040D734 |. C745 FC 05000000 mov dword ptr [ebp-4], 50040D73B |. 8B45 EC mov eax, dword ptr [ebp-14]0040D73E |. 50 push eax ; /<%d> = 10040D73F |. 68 1C204200 push 0042201C ; |format = "%d"0040D744 |. E8 37FFFFFF call printf ; \printf0040D749 |. 83C4 08 add esp, 80040D74C |. 8B4D F0 mov ecx, dword ptr [ebp-10]0040D74F |. 51 push ecx ; /<%d>0040D750 |. 68 1C204200 push 0042201C ; |format = "%d"0040D755 |. E8 26FFFFFF call printf ; \printf
二维数组是一位的高阶抽象.
常量折叠: 在编译前遇到常量,都会进行计算,得出一个新的常量值,这样则可以直接Push常量节约资源.
int main(int argc, char* argv[]){ printf("%d\n",1+2); // 常量+常量 return 0;}
0040D438 |. C745 FC 00000000 mov dword ptr [ebp-4], 10040D43F |. C745 F8 01000000 mov dword ptr [ebp-8], 20040D446 |. 6A 03 push 3 ; 1+2直接Push30040D448 |. 68 1C204200 push 0042201C ; |format = "%d"0040D44D |. E8 9E020000 call printf ; \printf0040D452 |. 83C4 08 add esp, 8
常量传播: 如果两个变量从来都没被修改过,也没有传入过参数,在VS2013编译器中会被优化成常量.
int main(int argc, char* argv[]){ int x = 1; int y = 3; printf("%d\n",x+y); // 变量+变量 return 0;}
0040D438 |. C745 FC 01000>mov dword ptr [ebp-4], 10040D43F |. C745 F8 03000>mov dword ptr [ebp-8], 30040D44C |. 50 push 4 ; /<%d>0040D44D |. 68 1C204200 push 0042201C ; |format = "%d"0040D452 |. E8 99020000 call printf ; \printf0040D457 |. 83C4 08 add esp, 8
窥孔优化:一种很局部的优化方式,编译器仅仅在一个基本块或者多个基本块中,针对已经生成的代码,结合CPU自己指令的特点,过一些认为可能带来性能提升的转换规则,或者通过整体的分析,通过指令转换,提升代码性能。这个窥孔,你可以认为是一个滑动窗口,编译器在实施窥孔优化时,就仅仅分析这个窗口内的指令。每次转换之后,可能还会暴露相邻窗口之间的某些优化机会,所以可以多次调用窥孔优化,尽可能提升性能
运算符相关
乘法:
int main(int argc, char* argv[]){ int x = 10; int y = 300; int z = 0; z = x * y; printf("%d\n",z); return 0;}
0040D438 |. C745 FC 0A000>mov dword ptr [ebp-4], 0A0040D43F |. C745 F8 2C010>mov dword ptr [ebp-8], 12C0040D446 |. C745 F4 00000>mov dword ptr [ebp-C], 00040D44D |. 8B45 FC mov eax, dword ptr [ebp-4]0040D450 |. 0FAF45 F8 imul eax, dword ptr [ebp-8]0040D454 |. 8945 F4 mov dword ptr [ebp-C], eax0040D457 |. 8B4D F4 mov ecx, dword ptr [ebp-C]0040D45A |. 51 push ecx ; /<%d>0040D45B |. 68 1C204200 push 0042201C ; |format = "%d"0040D460 |. E8 8B020000 call printf ; \printf
除法: cdq指令扩展标志位 edx 高位 :eax 低位
int main(int argc, char* argv[]){ int x = 1000; int y = 20; int z = 0; z = x/y; printf("%d\n",z); return 0;}
0040D438 |. C745 FC E8030>mov dword ptr [ebp-4], 3E80040D43F |. C745 F8 14000>mov dword ptr [ebp-8], 140040D446 |. C745 F4 00000>mov dword ptr [ebp-C], 00040D44D |. 8B45 FC mov eax, dword ptr [ebp-4]0040D450 |. 99 cdq0040D451 |. F77D F8 idiv dword ptr [ebp-8]0040D454 |. 8945 F4 mov dword ptr [ebp-C], eax0040D457 |. 8B45 F4 mov eax, dword ptr [ebp-C]0040D45A |. 50 push eax ; /<%d>0040D45B |. 68 1C204200 push 0042201C ; |format = "%d"0040D460 |. E8 8B020000 call printf ; \printf
复合运算:
int main(int argc, char* argv[]){ int a = 10; int b = 20; int x = (a*a+b); int y = x*5; return 0;}
00401028 |. C745 FC 0A000>mov dword ptr [ebp-4], 0A0040102F |. C745 F8 14000>mov dword ptr [ebp-8], 1400401036 |. 8B45 FC mov eax, dword ptr [ebp-4]00401039 |. 0FAF45 FC imul eax, dword ptr [ebp-4]0040103D |. 0345 F8 add eax, dword ptr [ebp-8]00401040 |. 8945 F4 mov dword ptr [ebp-C], eax00401043 |. 8B4D F4 mov ecx, dword ptr [ebp-C]00401046 |. 6BC9 05 imul ecx, ecx, 500401049 |. 894D F0 mov dword ptr [ebp-10], ecx
多目运算符:
int main(int argc, char* argv[]){ unsigned int temp; scanf("%d",&temp); printf("%d\r\n",temp == 0 ? 0:-1); // 针对有符号数 printf("%d\r\n",temp == 0 ? 1:0); // 针对无符号数 printf("%d\r\n",temp >= 1 ? 35:98); // 大于等于 return 0;}
针对有符号数
0040F979 |. 8B4D FC mov ecx, dword ptr [ebp-4]0040F97C |. F7D9 neg ecx0040F97E |. 1BC9 sbb ecx, ecx0040F980 |. 51 push ecx ; /<%d>0040F981 |. 68 802E4200 push 00422E80 ; |format = "%d"0040F986 |. E8 45FFFFFF call printf ; \printf0040F98B |. 83C4 08 add esp, 8
针对无符号数
0040F990 |. 837D FC 00 cmp dword ptr [ebp-4], 00040F994 |. 0F94C2 sete dl0040F997 |. 52 push edx ; /<%d>0040F998 |. 68 802E4200 push 00422E80 ; |format = "%d"0040F99D |. E8 2EFFFFFF call printf ; \printf0040F9A2 |. 83C4 08 add esp, 8
大于等于符号
0040F9A5 |. 837D FC 01 cmp dword ptr [ebp-4], 10040F9A9 |. 1BC0 sbb eax, eax0040F9AB |. 83E0 3F and eax, 3F0040F9AE |. 83C0 23 add eax, 230040F9B1 |. 50 push eax ; /<%d>0040F9B2 |. 68 802E4200 push 00422E80 ; |format = "%d"0040F9B7 |. E8 14FFFFFF call printf ; \printf0040F9BC |. 83C4 08 add esp, 8
流程控制
IF
条件分支:
int main(int argc, char* argv[]){ int x = 10; int y = 20; int z = 30; if( x >= y){ printf("x >= y"); }else if(z >= x){ printf("z >= x"); } return 0;}
00401028 C745 FC 0A00000>mov dword ptr [ebp-4], 0A0040102F C745 F8 1400000>mov dword ptr [ebp-8], 1400401036 C745 F4 1E00000>mov dword ptr [ebp-C], 1E0040103D 8B45 FC mov eax, dword ptr [ebp-4]00401040 3B45 F8 cmp eax, dword ptr [ebp-8]00401043 7C 0F jl short 0040105400401045 68 24204200 push 00422024 ; ASCII "x >= y"0040104A E8 51000000 call printf0040104F 83C4 04 add esp, 400401052 EB 15 jmp short 0040106900401054 8B4D F4 mov ecx, dword ptr [ebp-C]00401057 3B4D FC cmp ecx, dword ptr [ebp-4]0040105A 7C 0D jl short 004010690040105C 68 1C204200 push 0042201C ; ASCII "z >= x"00401061 E8 3A000000 call printf00401066 83C4 04 add esp, 4
嵌套条件分支:
int main(int argc, char* argv[]){ int x = 10; int y = 20; int z = 30; if( z >= y){ if(x < y){ printf(" z>=y and x=y and x >y"); } } return 0;}
00401028 |. C745 FC 0A000000 mov dword ptr [ebp-4], 0A0040102F |. C745 F8 14000000 mov dword ptr [ebp-8], 1400401036 |. C745 F4 1E000000 mov dword ptr [ebp-C], 1E0040103D |. 8B45 F4 mov eax, dword ptr [ebp-C]00401040 |. 3B45 F8 cmp eax, dword ptr [ebp-8]00401043 |. 7C 24 jl short 0040106900401045 |. 8B4D FC mov ecx, dword ptr [ebp-4]00401048 |. 3B4D F8 cmp ecx, dword ptr [ebp-8]0040104B |. 7D 0F jge short 0040105C0040104D |. 68 1C204200 push 0042201C ; /format = " z>=y and x68 AC2F4200 push 00422FAC ; /format = " z >=y and x >y"00401061 |. E8 3A000000 call printf ; \printf00401066 |. 83C4 04 add esp, 4
and
int main(int argc, char* argv[]){ int x = 10; int y = 20; int z = 30; if(xx) { printf("x x"); } return 0;}
00401028 |. C745 FC 0A000000 mov dword ptr [ebp-4], 0A0040102F |. C745 F8 14000000 mov dword ptr [ebp-8], 1400401036 |. C745 F4 1E000000 mov dword ptr [ebp-C], 1E0040103D |. 8B45 FC mov eax, dword ptr [ebp-4]00401040 |. 3B45 F8 cmp eax, dword ptr [ebp-8]00401043 |. 7D 15 jge short 0040105A00401045 |. 8B4D F4 mov ecx, dword ptr [ebp-C]00401048 |. 3B4D FC cmp ecx, dword ptr [ebp-4]0040104B |. 7E 0D jle short 0040105A0040104D |. 68 AC2F4200 push 00422FAC ; /format = "xx"00401052 |. E8 49000000 call printf ; \printf00401057 |. 83C4 04 add esp, 4
OR:
int main(int argc, char* argv[]){ int x = 10; int y = 20; int z = 30; if(xx) { printf("x x"); } return 0;}
00401028 |. C745 FC 0A000000 mov dword ptr [ebp-4], 0A0040102F |. C745 F8 14000000 mov dword ptr [ebp-8], 1400401036 |. C745 F4 1E000000 mov dword ptr [ebp-C], 1E0040103D |. 8B45 FC mov eax, dword ptr [ebp-4]00401040 |. 3B45 F8 cmp eax, dword ptr [ebp-8]00401043 |. 7C 08 jl short 0040104D00401045 |. 8B4D F4 mov ecx, dword ptr [ebp-C]00401048 |. 3B4D FC cmp ecx, dword ptr [ebp-4]0040104B |. 7E 0D jle short 0040105A0040104D |. 68 AC2F4200 push 00422FAC ; /format = "xx"00401052 |. E8 49000000 call printf ; \printf00401057 |. 83C4 04 add esp, 4
While
while 单循环:
int main(int argc, char* argv[]){ int x = 0; while(x <= 10){ printf("%d",x); x++; } return 0;}
x++ 是jge来操控,++x是jg
00401028 |. C745 FC 00000 |mov dword ptr [ebp-4], 00040102F |> 837D FC 0A |cmp dword ptr [ebp-4], 0A00401033 |. 7D 1C |jge short 0040105100401035 |. 8B45 FC |mov eax, dword ptr [ebp-4]00401038 |. 50 |push eax ; /<%d>00401039 |. 68 1C204200 |push 0042201C ; |format = "%d"0040103E |. E8 5D000000 |call printf ; \printf00401043 |. 83C4 08 |add esp, 800401046 |. 8B4D FC |mov ecx, dword ptr [ebp-4]00401049 |. 83C1 01 |add ecx, 10040104C |. 894D FC |mov dword ptr [ebp-4], ecx0040104F |.^ EB DE |jmp short 0040102F
while 嵌套循环:
int main(int argc, char* argv[]){ int x = 0; int y = 0; while(x <= 10){ while(y <=5){ printf("%d,%d \n",x,y); y++; } x++; y=0; } return 0;}
00401028 |. C745 FC 00000000 |mov dword ptr [ebp-4], 00040102F |. C745 F8 00000000 |mov dword ptr [ebp-8], 000401036 |> 837D FC 0A |cmp dword ptr [ebp-4], 0A0040103A |. 7F 38 |jg short 004010740040103C |> 837D F8 05 |cmp dword ptr [ebp-8], 500401040 |. 7F 20 |jg short 0040106200401042 |. 8B45 F8 |mov eax, dword ptr [ebp-8]00401045 |. 50 |push eax ; /<%d>00401046 |. 8B4D FC |mov ecx, dword ptr [ebp-4] ; |00401049 |. 51 |push ecx ; |<%d>0040104A |. 68 1C204200 |push 0042201C ; |format = "%d,%d"0040104F |. E8 4C000000 |call printf ; \printf00401054 |. 83C4 0C |add esp, 0C00401057 |. 8B55 F8 |mov edx, dword ptr [ebp-8]0040105A |. 83C2 01 |add edx, 10040105D |. 8955 F8 |mov dword ptr [ebp-8], edx00401060 |.^ EB DA |jmp short 0040103C00401062 |> 8B45 FC |mov eax, dword ptr [ebp-4]00401065 |. 83C0 01 |add eax, 100401068 |. 8945 FC |mov dword ptr [ebp-4], eax0040106B |. C745 F8 00000000 |mov dword ptr [ebp-8], 000401072 |.^ EB C2 |jmp short 00401036
do-while 循环:
int main(int argc, char* argv[]){ int x = 0; do { if(x/2 == 0){ printf("%d \n",x); } x++; }while(x<=10); return 0;}
00401028 |. C745 FC 00000000 |mov dword ptr [ebp-4], 00040102F |> 8B45 FC |mov eax, dword ptr [ebp-4]00401032 |. 99 |cdq00401033 |. 2BC2 |sub eax, edx00401035 |. D1F8 |sar eax, 100401037 |. 85C0 |test eax, eax00401039 |. 75 11 |jnz short 0040104C0040103B |. 8B45 FC |mov eax, dword ptr [ebp-4]0040103E |. 50 |push eax ; /<%d>0040103F |. 68 1C204200 |push 0042201C ; |format = "%d"00401044 |. E8 57000000 |call printf ; \printf00401049 |. 83C4 08 |add esp, 80040104C |> 8B4D FC |mov ecx, dword ptr [ebp-4]0040104F |. 83C1 01 |add ecx, 100401052 |. 894D FC |mov dword ptr [ebp-4], ecx00401055 |. 837D FC 0A |cmp dword ptr [ebp-4], 0A00401059 |.^ 7E D4 |jle short 0040102F
FOR
单分支:
int main(int argc, char* argv[]){ int x; for(x=0;x<=10;x++) { printf("%d\n",x); } return 0;}
00401028 |. C745 FC 00000000 |mov dword ptr [ebp-4], 00040102F |. EB 09 |jmp short 0040103A00401031 |> 8B45 FC |mov eax, dword ptr [ebp-4]00401034 |. 83C0 01 |add eax, 100401037 |. 8945 FC |mov dword ptr [ebp-4], eax0040103A |> 837D FC 0A |cmp dword ptr [ebp-4], 0A0040103E |. 7F 13 |jg short 0040105300401040 |. 8B4D FC |mov ecx, dword ptr [ebp-4]00401043 |. 51 |push ecx ; /<%d>00401044 |. 68 1C204200 |push 0042201C ; |format = "%d"00401049 |. E8 52000000 |call printf ; \printf0040104E |. 83C4 08 |add esp, 800401051 |.^ EB DE |jmp short 00401031
多分支:
int main(int argc, char* argv[]){ int array[2][3] = { {1,2,3},{1,2,3}}; int x,y; for(x=0;x<2;x++) { for(y=0;y<3;y++) { printf("%d \n",array[x][y]); } } return 0;}
0040D779 |. /EB 09 |jmp short 0040D7840040D77B |> |8B45 E4 |mov eax, dword ptr [ebp-1C]0040D77E |. |83C0 01 |add eax, 10040D781 |. |8945 E4 |mov dword ptr [ebp-1C], eax0040D784 |> \837D E4 02 |cmp dword ptr [ebp-1C], 20040D788 |. 7D 3A |jge short 0040D7C40040D78A |. C745 E0 00000000 |mov dword ptr [ebp-20], 00040D791 |. EB 09 |jmp short 0040D79C0040D793 |> 8B4D E0 |mov ecx, dword ptr [ebp-20]0040D796 |. 83C1 01 |add ecx, 10040D799 |. 894D E0 |mov dword ptr [ebp-20], ecx0040D79C |> 837D E0 03 |cmp dword ptr [ebp-20], 30040D7A0 |. 7D 20 |jge short 0040D7C20040D7A2 |. 8B55 E4 |mov edx, dword ptr [ebp-1C]0040D7A5 |. 6BD2 0C |imul edx, edx, 0C0040D7A8 |. 8D4415 E8 |lea eax, dword ptr [ebp+edx-18]0040D7AC |. 8B4D E0 |mov ecx, dword ptr [ebp-20]0040D7AF |. 8B1488 |mov edx, dword ptr [eax+ecx*4]0040D7B2 |. 52 |push edx ; /<%d>0040D7B3 |. 68 1C204200 |push 0042201C ; |format = "%d"0040D7B8 |. E8 E338FFFF |call printf ; \printf0040D7BD |. 83C4 08 |add esp, 80040D7C0 |.^ EB D1 |jmp short 0040D7930040D7C2 |>^ EB B7 |jmp short 0040D77B
Switch
基本循环:
int main(int argc, char* argv[]){ int temp; scanf("%d",&temp); switch(temp) { case 0: printf("case 0");break; case 1: printf("case 1");break; case 2: printf("case 2");break; case 3: printf("case 3");break; default: printf("none");break; } return 0;}
jmp dword ptr [edx*4+40FA6B]
跳转的地址.
0040F9F9 |. 8B4D FC |mov ecx, dword ptr [ebp-4]0040F9FC |. 894D F8 |mov dword ptr [ebp-8], ecx0040F9FF |. 837D F8 03 |cmp dword ptr [ebp-8], 30040FA03 |. 77 46 |ja short 0040FA4B0040FA05 |. 8B55 F8 |mov edx, dword ptr [ebp-8]0040FA08 |. FF2495 6BFA40>|jmp dword ptr [edx*4+40FA6B]0040FA0F |> 68 18304200 |push 00423018 ; /format = "case 0"0040FA14 |. E8 8716FFFF |call printf ; \printf0040FA19 |. 83C4 04 |add esp, 40040FA1C |. EB 3A |jmp short 0040FA580040FA1E |> 68 10304200 |push 00423010 ; /format = "case 1"0040FA23 |. E8 7816FFFF |call printf ; \printf0040FA28 |. 83C4 04 |add esp, 40040FA2B |. EB 2B |jmp short 0040FA580040FA2D |> 68 08304200 |push 00423008 ; /format = "case 2"0040FA32 |. E8 6916FFFF |call printf ; \printf0040FA37 |. 83C4 04 |add esp, 40040FA3A |. EB 1C |jmp short 0040FA580040FA3C |> 68 00304200 |push 00423000 ; /format = "case 3"0040FA41 |. E8 5A16FFFF |call printf ; \printf0040FA46 |. 83C4 04 |add esp, 40040FA49 |. EB 0D |jmp short 0040FA580040FA4B |> 68 F82F4200 |push 00422FF8 ; /format = "none"0040FA50 |. E8 4B16FFFF |call printf ; \printf0040FA55 |. 83C4 04 |add esp, 4
变量作用域
全局变量:
具有初始值的全局变量,其值在链接时被写入所创建的PE文件中,PE文件首先会加载这些全局变量,所以这些变量可以不受作用域的影响,在程序中的任何位置都可以被访问。
int num1 = 1;int num2 = 2;int main(int argc, char* argv[]){ scanf("%d",&num1); printf("%d",num1); num2=10; return 0;}
0040F9E8 |. 68 68514200 push offset num10040F9ED |. 68 F82F4200 push 00422FF8 ; /format = "%d"0040F9F2 |. E8 79FFFFFF call scanf ; \scanf0040F9F7 |. 83C4 08 add esp, 80040F9FA |. A1 68514200 mov eax, dword ptr [num1]0040F9FF |. 50 push eax ; /<%d> => 00040FA00 |. 68 F82F4200 push 00422FF8 ; |format = "%d"0040FA05 |. E8 9616FFFF call printf ; \printf0040FA0A |. 83C4 08 add esp, 80040FA0D |. C705 6C514200>mov dword ptr [num2], 0A
局部变量:
局部变量的访问是通过栈指针相对间接访问,也就是说局部变量是程序动态创建的,局部变量作用域也仅限于函数内部,且其地址也是一个未知数,编译器无法预先计算。int main(int argc, char* argv[]){ int num1=0; int num2=1; scanf("%d",&num1); printf("%d",num1); num2=10; return 0;}
0040F9E8 |. C745 FC 00000 |mov dword ptr [ebp-4], 00040F9EF |. C745 F8 01000>|mov dword ptr [ebp-8], 10040F9F6 |. 8D45 FC |lea eax, dword ptr [ebp-4]0040F9F9 |. 50 |push eax0040F9FA |. 68 F82F4200 |push 00422FF8 ; /format = "%d"0040F9FF |. E8 6CFFFFFF |call scanf ; \scanf0040FA04 |. 83C4 08 |add esp, 80040FA07 |. 8B4D FC |mov ecx, dword ptr [ebp-4]0040FA0A |. 51 |push ecx ; /<%d>0040FA0B |. 68 F82F4200 |push 00422FF8 ; |format = "%d"0040FA10 |. E8 8B16FFFF |call printf ; \printf0040FA15 |. 83C4 08 |add esp, 80040FA18 |. C745 F8 0A000 |mov dword ptr [ebp-8], 0A
堆变量:
#includeint main(int argc, char* argv[]){ int *pMalloc = (int*) malloc(10); printf("%x",pMalloc); free(pMalloc); char *pChar = new char[10]; printf("%x",pChar); delete [] pChar; return 0;}
00401038 |. 6A 0A push 0A ; /size = A (10.)0040103A |. E8 41350000 call malloc ; \malloc0040103F |. 83C4 04 add esp, 400401042 |. 8945 FC mov dword ptr [ebp-4], eax00401045 |. 8B45 FC mov eax, dword ptr [ebp-4]00401048 |. 50 push eax ; /<%x>00401049 |. 68 1C204200 push 0042201C ; |format = "%x"0040104E |. E8 7D000000 call printf ; \printf00401053 |. 83C4 08 add esp, 800401056 |. 8B4D FC mov ecx, dword ptr [ebp-4]00401059 |. 51 push ecx ; /block0040105A |. E8 A13F0000 call free ; \free
关于函数
无参函数:
void Print(){ ::MessageBox(NULL,TEXT("hello lyshark"),TEXT("MSG"),MB_OK);}int main(int argc, char* argv[]){ Print(); return 0;}
上方代码定义无参数传递的函数,观察反汇编代码,函数的开场白与收场白代码,VC6.0如下。
00401020 > 55 push ebp00401021 8BEC mov ebp, esp00401023 83EC 40 sub esp, 40........ ....... ................00401058 83C4 40 add esp, 4000401062 8BE5 mov esp, ebp00401064 5D pop ebp00401065 C3 retn
传递整数: 如下C代码,传递整数参数
int Print(int x,int y){ int sum; sum=x+y; return (sum);}int main(int argc, char* argv[]){ int sum; sum = Print(10,20); printf("%d \n",sum); return 0;}
如下代码片段为,参数调用片段,可以看到在32位汇编中是通过堆栈传递参数的,参数传递是从后向前
00401068 6A 14 push 14 push 200040106A 6A 0A push 0A push 100040106C E8 9EFFFFFF call 0040100F
观察 print()函数汇编代码片段,通过ebp指针在堆栈中取出参数,然后进行相加运算,最后将返回值放到eax中。
00401020 > 55 push ebp00401021 8BEC mov ebp, esp........00401038 8B45 08 mov eax, dword ptr [ebp+8]0040103B 0345 0C add eax, dword ptr [ebp+C]0040103E 8945 FC mov dword ptr [ebp-4], eax00401041 8B45 FC mov eax, dword ptr [ebp-4]........00401047 8BE5 mov esp, ebp00401049 5D pop ebp0040104A C3 retn
传递数组:
void Print(int arr[], int size){ int i=0; for (i=0; i
观察Print的参数传递,首先传递一个0A也就是10,然后传递数组指针,最后调用Print函数.
004010FE 6A 0A push 0A00401100 8D45 D8 lea eax, dword ptr [ebp-28]00401103 50 push eax00401104 E8 FCFEFFFF call 00401005
00401038 C745 FC 0000000>mov dword ptr [ebp-4], 00040103F C745 FC 0000000>mov dword ptr [ebp-4], 000401046 EB 09 jmp short 0040105100401048 8B45 FC mov eax, dword ptr [ebp-4]0040104B 83C0 01 add eax, 10040104E 8945 FC mov dword ptr [ebp-4], eax00401051 8B4D FC mov ecx, dword ptr [ebp-4]00401054 3B4D 0C cmp ecx, dword ptr [ebp+C]00401057 7D 19 jge short 0040107200401059 8B55 FC mov edx, dword ptr [ebp-4]0040105C 8B45 08 mov eax, dword ptr [ebp+8]0040105F 8B0C90 mov ecx, dword ptr [eax+edx*4]00401062 51 push ecx00401063 68 1C204200 push 0042201C ; ASCII "%d \r"00401068 E8 D3000000 call printf0040106D 83C4 08 add esp, 800401070 ^ EB D6 jmp short 00401048
传递指针:
void Print(char *str){ printf("%s\n",str); str = "hello world"; printf("%s\n",str);}int main(int argc, char* argv[]){ char *str = "hello lyshark"; Print(str); return 0;}
函数调用片段。
004010B8 C745 FC C02F4200 mov dword ptr [ebp-4], 00422FC0 ; ASCII "hello lyshark"004010BF 8B45 FC mov eax, dword ptr [ebp-4]004010C2 50 push eax004010C3 E8 47FFFFFF call 0040100F
函数内部调用。
00401038 8B45 08 mov eax, dword ptr [ebp+8]0040103B 50 push eax0040103C 68 A42F4200 push 00422FA4 ; ASCII "%s\n"00401041 E8 FA000000 call printf00401046 83C4 08 add esp, 8
返回指针:
int GetAddr(int number){ int nAddr; nAddr = *(int*)(&number-1); return nAddr;}int main(int argc, char* argv[]){ int address = 0; address = GetAddr(100); printf("%x\n",address); return 0;}
首先是函数调用代码。
00401078 C745 FC 00000000 mov dword ptr [ebp-4], 00040107F 6A 64 push 6400401081 E8 7FFFFFFF call 0040100500401086 83C4 04 add esp, 400401089 8945 FC mov dword ptr [ebp-4], eax
函数内部核心代码,ebp+8 = 64 ,区下面的地址。
00401038 8D45 08 lea eax, dword ptr [ebp+8]0040103B 83E8 04 sub eax, 40040103E 8B08 mov ecx, dword ptr [eax]00401040 894D FC mov dword ptr [ebp-4], ecx00401043 8B45 FC mov eax, dword ptr [ebp-4]
返回结构:
struct tag{ int x; int y; char z;};tag RetStruct(){ tag temp; temp.x = 10; temp.y = 20; temp.z = 'A'; return temp;}int main(int argc, char* argv[]){ tag temp; temp = RetStruct(); printf("%d \n",temp.x); printf("%d \n",temp.y); printf("%d \n",temp.z); return 0;}
观察一下结构的初始化
0040D778 C745 F4 0A000000 mov dword ptr [ebp-C], 0A0040D77F C745 F8 14000000 mov dword ptr [ebp-8], 140040D786 C645 FC 41 mov byte ptr [ebp-4], 410040D78A 8B45 08 mov eax, dword ptr [ebp+8]0040D78D 8B4D F4 mov ecx, dword ptr [ebp-C]0040D790 8908 mov dword ptr [eax], ecx0040D792 8B55 F8 mov edx, dword ptr [ebp-8]0040D795 8950 04 mov dword ptr [eax+4], edx0040D798 8B4D FC mov ecx, dword ptr [ebp-4]0040D79B 8948 08 mov dword ptr [eax+8], ecx0040D79E 8B45 08 mov eax, dword ptr [ebp+8]
0040D7D4 8B08 mov ecx, dword ptr [eax]0040D7D6 894D E8 mov dword ptr [ebp-18], ecx0040D7D9 8B50 04 mov edx, dword ptr [eax+4]0040D7DC 8955 EC mov dword ptr [ebp-14], edx0040D7DF 8B40 08 mov eax, dword ptr [eax+8]0040D7E2 8945 F0 mov dword ptr [ebp-10], eax0040D7E5 8B4D E8 mov ecx, dword ptr [ebp-18]0040D7E8 894D F4 mov dword ptr [ebp-C], ecx0040D7EB 8B55 EC mov edx, dword ptr [ebp-14]0040D7EE 8955 F8 mov dword ptr [ebp-8], edx0040D7F1 8B45 F0 mov eax, dword ptr [ebp-10]0040D7F4 8945 FC mov dword ptr [ebp-4], eax0040D7F7 8B4D F4 mov ecx, dword ptr [ebp-C]0040D7FA 51 push ecx0040D7FB 68 A42F4200 push 00422FA4 ; ASCII "%d \n"0040D800 E8 CB38FFFF call printf0040D805 83C4 08 add esp, 80040D808 8B55 F8 mov edx, dword ptr [ebp-8]0040D80B 52 push edx0040D80C 68 A42F4200 push 00422FA4 ; ASCII "%d \n"0040D811 E8 BA38FFFF call printf0040D816 83C4 08 add esp, 8
数组与指针
简单的数组: 一维数组的寻址方式。
int main(int argc ,char *argv[]){ int array[5] = { 1, 2, 3, 4, 5 }; int x; for (x = 0; x < 5; x++){ printf("%d \n", array[x]); } return 0;}
其反汇编代码如下,其中数组的寻址方式使用了 [ebp+ecx*4-0x14]
比例因子寻址。
00401028 |. C745 EC 01000>mov dword ptr [ebp-0x14], 0x10040102F |. C745 F0 02000>mov dword ptr [ebp-0x10], 0x200401036 |. C745 F4 03000>mov dword ptr [ebp-0xC], 0x30040103D |. C745 F8 04000>mov dword ptr [ebp-0x8], 0x400401044 |. C745 FC 05000>mov dword ptr [ebp-0x4], 0x50040104B |. C745 E8 00000>mov dword ptr [ebp-0x18], 0x000401052 |. EB 09 jmp short 0040105D00401054 |> 8B45 E8 /mov eax, dword ptr [ebp-0x18]00401057 |. 83C0 01 |add eax, 0x10040105A |. 8945 E8 |mov dword ptr [ebp-0x18], eax0040105D |> 837D E8 05 cmp dword ptr [ebp-0x18], 0x500401061 |. 7D 17 |jge short 0040107A00401063 |. 8B4D E8 |mov ecx, dword ptr [ebp-0x18]00401066 |. 8B548D EC |mov edx, dword ptr [ebp+ecx*4-0x14]0040106A |. 52 |push edx ; /<%d>0040106B |. 68 1C204200 |push 0042201C ; |format = "%d "00401070 |. E8 3B000000 |call printf ; \printf00401075 |. 83C4 08 |add esp, 0x800401078 |.^ EB DA \jmp short 00401054
二维数组:
int main(int argc ,char *argv[]){ int array[2][3] ={ {1,2,3},{4,5,6}}; int x =0; int *Pointer = &array[0][0]; printf("Base: %x \n",Pointer); printf("%d\n",*(Pointer+2)); printf("%d\n",*(Pointer+4)); return 0;}
如下汇编代码就是二维数组的表现形式,可以看出二维数组的存储方式与一维相同.
00401028 C745 E8 0100000>mov dword ptr [ebp-18], 10040102F C745 EC 0200000>mov dword ptr [ebp-14], 200401036 C745 F0 0300000>mov dword ptr [ebp-10], 30040103D C745 F4 0400000>mov dword ptr [ebp-C], 400401044 C745 F8 0500000>mov dword ptr [ebp-8], 50040104B C745 FC 0600000>mov dword ptr [ebp-4], 600401052 C745 E4 0000000>mov dword ptr [ebp-1C], 000401059 8D45 E8 lea eax, dword ptr [ebp-18]0040105C 8945 E0 mov dword ptr [ebp-20], eax
寻址代码如下,其通过dword ptr [ebp-20]
指向第1个元素,通过[edx+8]
遍历第3个元素
00401070 8B55 E0 mov edx, dword ptr [ebp-20] ;指向第1个元素前1个元素00401073 8B42 08 mov eax, dword ptr [edx+8] ;遍历第2个元素00401076 50 push eax00401077 68 1C204200 push 0042201C0040107C E8 5F000000 call printf00401081 83C4 08 add esp, 8
稍微修改下代码.
int main(int argc ,char *argv[]){ int array[2][3] ={ {1,2,3},{4,5,6}}; int x=0,y=1; array[x][y] = 0; return 0;}
在Debug模式下,其公式: 数组首地址 + sizeof(type[一维数组元素]) * x + sizeof(数据类型) * y
0040106E 8B45 E4 mov eax, dword ptr [ebp-1C] ; eax = x 坐标00401071 6BC0 0C imul eax, eax, 0C ; eax = x * 0c 索引数组00401074 8D4C05 E8 lea ecx, dword ptr [ebp+eax-18] ; ecx = y 坐标00401078 8B55 E0 mov edx, dword ptr [ebp-20] ; edx = 10040107B C70491 00000000 mov dword ptr [ecx+edx*4], 0 ; 1+1*4=5 4字节中的5,指向第2个元素
上方汇编代码,解释:
1.第1条代码中的EAX是获取到的x的值,此处为C语言中的x=0
. 2.第2条代码中的0C: 每个元素占4字节,而每个数组有3个元素,3x4=0C. 3.第3条代码中的ECX: 代表数组的y坐标. 4.第5条代码:ecx + edx * 4
相当于数组首地址 + sizeof(int) * y
.
三维数组:
int main(int argc, char* argv[]){ int Array[2][3][4] = {NULL}; int x = 0; int y = 1; int z = 2; Array[x][y][z] = 3; return 0;}
针对三维数组 int Array[M][C][H]
其下标操作Array[x][y][z]=3
数组寻址公式为:
Array + sizeof(type[C][H]) * x + sizeof(type[H])*y + sizeof(type)*z
00401056 |. 8B45 9C mov eax, dword ptr [ebp-64] ; eax=x00401059 |. 6BC0 30 imul eax, eax, 30 ; sizeof(type[C][H])*x0040105C |. 8D4C05 A0 lea ecx, dword ptr [ebp+eax-60] ; 00401060 |. 8B55 98 mov edx, dword ptr [ebp-68] ; Array[C]00401063 |. C1E2 04 shl edx, 400401066 |. 03CA add ecx, edx00401068 |. 8B45 94 mov eax, dword ptr [ebp-6C] ; Array[Z]0040106B |. C70481 030000>mov dword ptr [ecx+eax*4], 3
上方汇编代码,解释:
1.第1条指令中得出eax=x
的值. 2.第2条指令eax * 30
,相当于求出sizeof(type[C][H]) * x
3.第3条指令求出数组首地址+eax-60
也就求出Array[M]
位置,并取地址放入ECX 4.第4条指令:[ebp-68]
存放Y的值,此处就是求出y的值 5.第5条指令:左移4位,相当于2^4次方也就是16这一步相当于求sizeof(type[H])
的值 6.Array[M] + sizeof(type[H])
的值求出Array[M][C]
的值
存放指针类型数据的数组: 数组中各数据元素都是由相同类型的指针组成,我们就称之为指针数组.
指针数组主要用于管理同种类型的指针,一般用于处理若干个字符串的操作,使用指针数组处理多字符串更加的方便,简洁,高效,需要注意的是,虽然同属于数组但是指针数组,但与常规的数组还有所差别,指针数组中的数据为地址类型,寻址时需要再次进行间接访问才能够获取到真正的数据,这也是他们之间最大的不同.
int main(int argc, char* argv[]){ char *pBuffer[3] = { "hello ", "lyshark ", "!\r\n" }; for (int x = 0; x < 3; x++){ printf(pBuffer[x]); } return 0;}
上方的代码经过编译后,我们定位到字符串初始化的位置,发现字符串数组中只向数组的第1个成员赋值字符串的首地址.
00401028 |. C745 F4 2C204200 mov dword ptr [ebp-C], 0042202C ; ASCII "hello "0040102F |. C745 F8 20204200 mov dword ptr [ebp-8], 00422020 ; ASCII "lyshark "00401036 |. C745 FC 1C204200 mov dword ptr [ebp-4], 0042201C ; ASCII "!\r\n"
如下片段代码就是打印环节了,由于指针数组本质上也是数组,故寻址方式与我们的数组基本相同.
0040103D |. C745 F0 00000000 |mov dword ptr [ebp-10], 0 ; x=000401044 |. EB 09 |jmp short 0040104F00401046 |> 8B45 F0 |mov eax, dword ptr [ebp-10] ; 取出x00401049 |. 83C0 01 |add eax, 1 ; 递增10040104C |. 8945 F0 |mov dword ptr [ebp-10], eax0040104F |> 837D F0 03 |cmp dword ptr [ebp-10], 3 ; 与3作比较00401053 |. 7D 12 |jge short 0040106700401055 |. 8B4D F0 |mov ecx, dword ptr [ebp-10] ; 取出下标值00401058 |. 8B548D F4 |mov edx, dword ptr [ebp+ecx*4-C] ; 定位字符串首地址0040105C |. 52 |push edx ; format0040105D |. E8 3E000000 |call printf ; printf00401062 |. 83C4 04 |add esp, 400401065 |.^ EB DF |jmp short 00401046
上方代码定义了1维字符串数组,该数组由3个指针变量组成,故长度为12字节,数组所指向的字符串长度与数组本身没有关系,而2维数组则与之不同,我们接着将上方代码稍加修改:
int main(int argc, char* argv[]){ char cArray[2][10] = { {"hello"}, {"lyshark"} }; for(int x=0;x<2;x++) { printf(cArray[x]); } return 0;}
以下反汇编代码可看出,2维字符数组初始化过程中,赋值的不是字符串地址,而是其中的字符数据,据此可以明显的区分它与字符指针数组的区别.
00401057 |. C745 E8 00000000 |mov dword ptr [ebp-18], 00040105E |. EB 09 |jmp short 0040106900401060 |> 8B45 E8 |mov eax, dword ptr [ebp-18]00401063 |. 83C0 01 |add eax, 100401066 |. 8945 E8 |mov dword ptr [ebp-18], eax00401069 |> 837D E8 02 |cmp dword ptr [ebp-18], 20040106D |. 7D 15 |jge short 004010840040106F |. 8B4D E8 |mov ecx, dword ptr [ebp-18]00401072 |. 6BC9 0A |imul ecx, ecx, 0A ; 遍历数组每次递增1000401075 |. 8D540D EC |lea edx, dword ptr [ebp+ecx-14] ; 取出1维数组首地址00401079 |. 52 |push edx ; format = "hello"0040107A |. E8 21000000 |call printf ; printf0040107F |. 83C4 04 |add esp, 400401082 |.^ EB DC |jmp short 00401060
总结:字符指针数组寻址后,得到的是数组成员内容,而2维字符数组寻址后,得到的却是数组中某个1维数组的首地址.
指向数组的指针变量: 当指针变量保存的数据为数组的首地址,且将此地址解释为数组时,此指针变量被称为数组指针.
如下我们定义了指向数组的指针为char (*pArray)[10]=Array
,并通过循环遍历指针中的数据.
int main(int argc, char* argv[]){ char Array[3][10] = { "hello","lyshark","! \r\n" }; char (*pArray)[10] = Array; for(int x=0;x<3;x++) { printf(*pArray); pArray++; } return 0;}
反汇编代码如下,程序通过lea eax, dword ptr [ebp-20]
遍历数组元素,指针[ebp-24]
每次递增10,递增10是因为char类型为1个字节,而1维数组为10所以递增为10,如果为整数类型就需要递增4*10
0040D790 |. 8D45 E0 |lea eax, dword ptr [ebp-20] ; 取打印元素0040D793 |. 8945 DC |mov dword ptr [ebp-24], eax0040D796 |. C745 D8 00000000 |mov dword ptr [ebp-28], 0 ; 初始化x=00040D79D |. EB 09 |jmp short 0040D7A80040D79F |> 8B4D D8 |mov ecx, dword ptr [ebp-28]0040D7A2 |. 83C1 01 |add ecx, 1 ; x每次递增0040D7A5 |. 894D D8 |mov dword ptr [ebp-28], ecx0040D7A8 |> 837D D8 03 |cmp dword ptr [ebp-28], 30040D7AC |. 7D 17 |jge short 0040D7C50040D7AE |. 8B55 DC |mov edx, dword ptr [ebp-24]0040D7B1 |. 52 |push edx ; format0040D7B2 |. E8 E938FFFF |call printf ; printf0040D7B7 |. 83C4 04 |add esp, 40040D7BA |. 8B45 DC |mov eax, dword ptr [ebp-24]0040D7BD |. 83C0 0A |add eax, 0A ; 指针递增100040D7C0 |. 8945 DC |mov dword ptr [ebp-24], eax0040D7C3 |.^ EB DA |jmp short 0040D79F
指向函数的指针: 程序通过CALL指令跳转到函数首地址执行代码,既然是地址那就可以使用指针变量来存储函数的首地址,该指针变量被乘坐函数指针.
void __stdcall Show(){ printf("hello lyshark\n");}int main(int argc, char* argv[]){ void (__stdcall *pShow)(void) = Show; pShow(); Show(); return 0;}
函数指针的类型由返回值,参数信息,调用约定组成,它决定了函数指针在函数调用过程中参数的传递,返回,以及如何堆栈平衡等,上方的代码中__stdcall
就是VC编译器中的默认平栈方式,这里也可以省略.
0040D788 |. C745 FC 0F104000 mov dword ptr [ebp-4], 0040100F ; 入口地址0040D78F |. 8B45 FC mov eax, dword ptr [ebp-4]0040D792 |. 8945 FC mov dword ptr [ebp-4], eax0040D795 |. 8BF4 mov esi, esp0040D797 |. FF55 FC call dword ptr [ebp-4] ; 间接调用函数0040D79A |. 3BF4 cmp esi, esp ; 检查栈平衡0040D79C |. E8 7F39FFFF call _chkesp0040D7A1 |. E8 6938FFFF call 0040100F ; 直接调用函数0040D7A6 |. 33C0 xor eax, eax
如上代码清单,演示了函数指针的赋值和调用过程,与函数调用最大的区别在于函数是直接调用,而函数指针的调用需要取出指针变量中保存的地址,然后进行call dword ptr [ebp-4]
间接的调用,将C代码稍作修改.
int __stdcall Show(int nShow){ printf("hello %d\n",nShow); return nShow;}int main(int argc, char* argv[]){ int (__stdcall *pShow)(int) = Show; int Ret = pShow(10); printf("Ret = %d \n",Ret); return 0;}
如下反汇编代码,代码中的函数指针调用只是多了参数的传递,返回值的接收,其他地方没有太大变化,都是间接调用函数.
0040D788 |. C745 FC 14104000 mov dword ptr [ebp-4], 004010140040D78F |. 8B45 FC mov eax, dword ptr [ebp-4]0040D792 |. 8945 FC mov dword ptr [ebp-4], eax0040D795 |. 8BF4 mov esi, esp ; 保存进入函数前的堆栈,用于检查0040D797 |. 6A 0A push 0A ; 传递参数0040D799 |. FF55 FC call dword ptr [ebp-4] ; 获取函数指针中的地址,间接调用函数0040D79C |. 3BF4 cmp esi, esp0040D79E |. E8 7D39FFFF call _chkesp0040D7A3 |. 8945 F8 mov dword ptr [ebp-8], eax ; 接收函数返回数据0040D7A6 |. 8B4D F8 mov ecx, dword ptr [ebp-8]0040D7A9 |. 51 push ecx ; <%d>0040D7AA |. 68 1C204200 push 0042201C ; format = "Re =%d"0040D7AF |. E8 EC38FFFF call printf ; printf0040D7B4 |. 83C4 08 add esp, 8
未完待续............................