title: 'C++反汇编: 基础知识(7)'
date: 2019-09-07 12:18:41
tags:
- 汇编与反汇编
categories: 汇编与反汇编
copyright: true
---
反汇编(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, 13 0040D711 |. B8 CCCCCCCC mov eax, CCCCCCCC 0040D716 |. F3:AB rep stos dword ptr es:[edi] 0040D718 |. C745 FC 0A000>mov dword ptr [ebp-4], 0A 0040D71F |. C745 F8 14000>mov dword ptr [ebp-8], 14 0040D726 |. C745 F4 00000>mov dword ptr [ebp-C], 0 0040D72D |. 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, 13 00410661 |. B8 CCCCCCCC mov eax, CCCCCCCC 00410666 |. F3:AB rep stos dword ptr es:[edi] 00410668 |. C645 FC 61 mov byte ptr [ebp-4], 61 0041066C |. C645 F8 62 mov byte ptr [ebp-8], 62 00410670 |. C645 F4 63 mov byte ptr [ebp-C], 63 00410674 |. 0FBE45 FC movsx eax, byte ptr [ebp-4] 00410678 |. 0FBE4D F8 movsx ecx, byte ptr [ebp-8] 0041067C |. 03C1 add eax, ecx 0041067E |. 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, 14 00401021 |. B8 CCCCCCCC mov eax, CCCCCCCC 00401026 |. F3:AB rep stos dword ptr es:[edi] 00401028 |. A1 1C204200 mov eax, dword ptr [42201C] 0040102D |. 8945 F0 mov dword ptr [ebp-10], eax 00401030 |. 8B0D 20204200 mov ecx, dword ptr [422020] 00401036 |. 894D F4 mov dword ptr [ebp-C], ecx 00401039 |. 8B15 24204200 mov edx, dword ptr [422024] 0040103F |. 8955 F8 mov dword ptr [ebp-8], edx 00401042 |. 66:A1 28204200 mov ax, word ptr [422028] 00401048 |. 66:8945 FC mov word ptr [ebp-4], ax 0040104C |. 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, 16 00401021 |. B8 CCCCCCCC mov eax, CCCCCCCC 00401026 |. F3:AB rep stos dword ptr es:[edi] 00401028 |. B9 06000000 mov ecx, 6 0040102D |. 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, 11 00401021 |. B8 CCCCCCCC mov eax, CCCCCCCC 00401026 |. F3:AB rep stos dword ptr es:[edi] 00401028 |. C745 FC 1C204200 mov dword ptr [ebp-4], 0042201C 0040102F |. 8B45 FC mov eax, dword ptr [ebp-4] ; aaa.0042201C 00401032 |. 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], 1 0040D71F |. C745 F0 02000000 mov dword ptr [ebp-10], 2 0040D726 |. C745 F4 03000000 mov dword ptr [ebp-C], 3 0040D72D |. C745 F8 04000000 mov dword ptr [ebp-8], 4 0040D734 |. C745 FC 05000000 mov dword ptr [ebp-4], 5 0040D73B |. 8B45 EC mov eax, dword ptr [ebp-14] 0040D73E |. 50 push eax ; /<%d> = 1 0040D73F |. 68 1C204200 push 0042201C ; |format = "%d " 0040D744 |. E8 37FFFFFF call printf ; \printf 0040D749 |. 83C4 08 add esp, 8 0040D74C |. 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], 1 0040D43F |. C745 F8 01000000 mov dword ptr [ebp-8], 2 0040D446 |. 6A 03 push 3 ; 1+2直接Push3 0040D448 |. 68 1C204200 push 0042201C ; |format = "%d" 0040D44D |. E8 9E020000 call printf ; \printf 0040D452 |. 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], 1 0040D43F |. C745 F8 03000>mov dword ptr [ebp-8], 3 0040D44C |. 50 push 4 ; /<%d> 0040D44D |. 68 1C204200 push 0042201C ; |format = "%d" 0040D452 |. E8 99020000 call printf ; \printf 0040D457 |. 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], 0A 0040D43F |. C745 F8 2C010>mov dword ptr [ebp-8], 12C 0040D446 |. C745 F4 00000>mov dword ptr [ebp-C], 0 0040D44D |. 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], eax 0040D457 |. 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], 3E8 0040D43F |. C745 F8 14000>mov dword ptr [ebp-8], 14 0040D446 |. C745 F4 00000>mov dword ptr [ebp-C], 0 0040D44D |. 8B45 FC mov eax, dword ptr [ebp-4] 0040D450 |. 99 cdq 0040D451 |. F77D F8 idiv dword ptr [ebp-8] 0040D454 |. 8945 F4 mov dword ptr [ebp-C], eax 0040D457 |. 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], 0A 0040102F |. C745 F8 14000>mov dword ptr [ebp-8], 14 00401036 |. 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], eax 00401043 |. 8B4D F4 mov ecx, dword ptr [ebp-C] 00401046 |. 6BC9 05 imul ecx, ecx, 5 00401049 |. 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 ecx 0040F97E |. 1BC9 sbb ecx, ecx 0040F980 |. 51 push ecx ; /<%d> 0040F981 |. 68 802E4200 push 00422E80 ; |format = "%d" 0040F986 |. E8 45FFFFFF call printf ; \printf 0040F98B |. 83C4 08 add esp, 8
针对无符号数
0040F990 |. 837D FC 00 cmp dword ptr [ebp-4], 0 0040F994 |. 0F94C2 sete dl 0040F997 |. 52 push edx ; /<%d> 0040F998 |. 68 802E4200 push 00422E80 ; |format = "%d" 0040F99D |. E8 2EFFFFFF call printf ; \printf 0040F9A2 |. 83C4 08 add esp, 8
大于等于符号
0040F9A5 |. 837D FC 01 cmp dword ptr [ebp-4], 1 0040F9A9 |. 1BC0 sbb eax, eax 0040F9AB |. 83E0 3F and eax, 3F 0040F9AE |. 83C0 23 add eax, 23 0040F9B1 |. 50 push eax ; /<%d> 0040F9B2 |. 68 802E4200 push 00422E80 ; |format = "%d" 0040F9B7 |. E8 14FFFFFF call printf ; \printf 0040F9BC |. 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], 0A 0040102F C745 F8 1400000>mov dword ptr [ebp-8], 14 00401036 C745 F4 1E00000>mov dword ptr [ebp-C], 1E 0040103D 8B45 FC mov eax, dword ptr [ebp-4] 00401040 3B45 F8 cmp eax, dword ptr [ebp-8] 00401043 7C 0F jl short 00401054 00401045 68 24204200 push 00422024 ; ASCII "x >= y" 0040104A E8 51000000 call printf 0040104F 83C4 04 add esp, 4 00401052 EB 15 jmp short 00401069 00401054 8B4D F4 mov ecx, dword ptr [ebp-C] 00401057 3B4D FC cmp ecx, dword ptr [ebp-4] 0040105A 7C 0D jl short 00401069 0040105C 68 1C204200 push 0042201C ; ASCII "z >= x" 00401061 E8 3A000000 call printf 00401066 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"); }else{ printf(" z >=y and x >y"); } } return 0; }
00401028 |. C745 FC 0A000000 mov dword ptr [ebp-4], 0A 0040102F |. C745 F8 14000000 mov dword ptr [ebp-8], 14 00401036 |. C745 F4 1E000000 mov dword ptr [ebp-C], 1E 0040103D |. 8B45 F4 mov eax, dword ptr [ebp-C] 00401040 |. 3B45 F8 cmp eax, dword ptr [ebp-8] 00401043 |. 7C 24 jl short 00401069 00401045 |. 8B4D FC mov ecx, dword ptr [ebp-4] 00401048 |. 3B4D F8 cmp ecx, dword ptr [ebp-8] 0040104B |. 7D 0F jge short 0040105C 0040104D |. 68 1C204200 push 0042201C ; /format = " z>=y and x<y" 00401052 |. E8 49000000 call printf ; \printf 00401057 |. 83C4 04 add esp, 4 0040105A |. EB 0D jmp short 00401069 0040105C |> 68 AC2F4200 push 00422FAC ; /format = " z >=y and x >y" 00401061 |. E8 3A000000 call printf ; \printf 00401066 |. 83C4 04 add esp, 4
and
int main(int argc, char* argv[]) { int x = 10; int y = 20; int z = 30; if(x<y && z>x) { printf("x<y && z>x"); } return 0; }
00401028 |. C745 FC 0A000000 mov dword ptr [ebp-4], 0A 0040102F |. C745 F8 14000000 mov dword ptr [ebp-8], 14 00401036 |. C745 F4 1E000000 mov dword ptr [ebp-C], 1E 0040103D |. 8B45 FC mov eax, dword ptr [ebp-4] 00401040 |. 3B45 F8 cmp eax, dword ptr [ebp-8] 00401043 |. 7D 15 jge short 0040105A 00401045 |. 8B4D F4 mov ecx, dword ptr [ebp-C] 00401048 |. 3B4D FC cmp ecx, dword ptr [ebp-4] 0040104B |. 7E 0D jle short 0040105A 0040104D |. 68 AC2F4200 push 00422FAC ; /format = "x<y && z>x" 00401052 |. E8 49000000 call printf ; \printf 00401057 |. 83C4 04 add esp, 4
OR:
int main(int argc, char* argv[]) { int x = 10; int y = 20; int z = 30; if(x<y || z>x) { printf("x<y && z>x"); } return 0; }
00401028 |. C745 FC 0A000000 mov dword ptr [ebp-4], 0A 0040102F |. C745 F8 14000000 mov dword ptr [ebp-8], 14 00401036 |. C745 F4 1E000000 mov dword ptr [ebp-C], 1E 0040103D |. 8B45 FC mov eax, dword ptr [ebp-4] 00401040 |. 3B45 F8 cmp eax, dword ptr [ebp-8] 00401043 |. 7C 08 jl short 0040104D 00401045 |. 8B4D F4 mov ecx, dword ptr [ebp-C] 00401048 |. 3B4D FC cmp ecx, dword ptr [ebp-4] 0040104B |. 7E 0D jle short 0040105A 0040104D |. 68 AC2F4200 push 00422FAC ; /format = "x<y && z>x" 00401052 |. E8 49000000 call printf ; \printf 00401057 |. 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], 0 0040102F |> 837D FC 0A |cmp dword ptr [ebp-4], 0A 00401033 |. 7D 1C |jge short 00401051 00401035 |. 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 ; \printf 00401043 |. 83C4 08 |add esp, 8 00401046 |. 8B4D FC |mov ecx, dword ptr [ebp-4] 00401049 |. 83C1 01 |add ecx, 1 0040104C |. 894D FC |mov dword ptr [ebp-4], ecx 0040104F |.^ 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], 0 0040102F |. C745 F8 00000000 |mov dword ptr [ebp-8], 0 00401036 |> 837D FC 0A |cmp dword ptr [ebp-4], 0A 0040103A |. 7F 38 |jg short 00401074 0040103C |> 837D F8 05 |cmp dword ptr [ebp-8], 5 00401040 |. 7F 20 |jg short 00401062 00401042 |. 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 ; \printf 00401054 |. 83C4 0C |add esp, 0C 00401057 |. 8B55 F8 |mov edx, dword ptr [ebp-8] 0040105A |. 83C2 01 |add edx, 1 0040105D |. 8955 F8 |mov dword ptr [ebp-8], edx 00401060 |.^ EB DA |jmp short 0040103C 00401062 |> 8B45 FC |mov eax, dword ptr [ebp-4] 00401065 |. 83C0 01 |add eax, 1 00401068 |. 8945 FC |mov dword ptr [ebp-4], eax 0040106B |. C745 F8 00000000 |mov dword ptr [ebp-8], 0 00401072 |.^ 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], 0 0040102F |> 8B45 FC |mov eax, dword ptr [ebp-4] 00401032 |. 99 |cdq 00401033 |. 2BC2 |sub eax, edx 00401035 |. D1F8 |sar eax, 1 00401037 |. 85C0 |test eax, eax 00401039 |. 75 11 |jnz short 0040104C 0040103B |. 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 ; \printf 00401049 |. 83C4 08 |add esp, 8 0040104C |> 8B4D FC |mov ecx, dword ptr [ebp-4] 0040104F |. 83C1 01 |add ecx, 1 00401052 |. 894D FC |mov dword ptr [ebp-4], ecx 00401055 |. 837D FC 0A |cmp dword ptr [ebp-4], 0A 00401059 |.^ 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], 0 0040102F |. EB 09 |jmp short 0040103A 00401031 |> 8B45 FC |mov eax, dword ptr [ebp-4] 00401034 |. 83C0 01 |add eax, 1 00401037 |. 8945 FC |mov dword ptr [ebp-4], eax 0040103A |> 837D FC 0A |cmp dword ptr [ebp-4], 0A 0040103E |. 7F 13 |jg short 00401053 00401040 |. 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 ; \printf 0040104E |. 83C4 08 |add esp, 8 00401051 |.^ 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 0040D784 0040D77B |> |8B45 E4 |mov eax, dword ptr [ebp-1C] 0040D77E |. |83C0 01 |add eax, 1 0040D781 |. |8945 E4 |mov dword ptr [ebp-1C], eax 0040D784 |> \837D E4 02 |cmp dword ptr [ebp-1C], 2 0040D788 |. 7D 3A |jge short 0040D7C4 0040D78A |. C745 E0 00000000 |mov dword ptr [ebp-20], 0 0040D791 |. EB 09 |jmp short 0040D79C 0040D793 |> 8B4D E0 |mov ecx, dword ptr [ebp-20] 0040D796 |. 83C1 01 |add ecx, 1 0040D799 |. 894D E0 |mov dword ptr [ebp-20], ecx 0040D79C |> 837D E0 03 |cmp dword ptr [ebp-20], 3 0040D7A0 |. 7D 20 |jge short 0040D7C2 0040D7A2 |. 8B55 E4 |mov edx, dword ptr [ebp-1C] 0040D7A5 |. 6BD2 0C |imul edx, edx, 0C 0040D7A8 |. 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 ; \printf 0040D7BD |. 83C4 08 |add esp, 8 0040D7C0 |.^ EB D1 |jmp short 0040D793 0040D7C2 |>^ 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], ecx 0040F9FF |. 837D F8 03 |cmp dword ptr [ebp-8], 3 0040FA03 |. 77 46 |ja short 0040FA4B 0040FA05 |. 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 ; \printf 0040FA19 |. 83C4 04 |add esp, 4 0040FA1C |. EB 3A |jmp short 0040FA58 0040FA1E |> 68 10304200 |push 00423010 ; /format = "case 1" 0040FA23 |. E8 7816FFFF |call printf ; \printf 0040FA28 |. 83C4 04 |add esp, 4 0040FA2B |. EB 2B |jmp short 0040FA58 0040FA2D |> 68 08304200 |push 00423008 ; /format = "case 2" 0040FA32 |. E8 6916FFFF |call printf ; \printf 0040FA37 |. 83C4 04 |add esp, 4 0040FA3A |. EB 1C |jmp short 0040FA58 0040FA3C |> 68 00304200 |push 00423000 ; /format = "case 3" 0040FA41 |. E8 5A16FFFF |call printf ; \printf 0040FA46 |. 83C4 04 |add esp, 4 0040FA49 |. EB 0D |jmp short 0040FA58 0040FA4B |> 68 F82F4200 |push 00422FF8 ; /format = "none" 0040FA50 |. E8 4B16FFFF |call printf ; \printf 0040FA55 |. 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 num1 0040F9ED |. 68 F82F4200 push 00422FF8 ; /format = "%d" 0040F9F2 |. E8 79FFFFFF call scanf ; \scanf 0040F9F7 |. 83C4 08 add esp, 8 0040F9FA |. A1 68514200 mov eax, dword ptr [num1] 0040F9FF |. 50 push eax ; /<%d> => 0 0040FA00 |. 68 F82F4200 push 00422FF8 ; |format = "%d" 0040FA05 |. E8 9616FFFF call printf ; \printf 0040FA0A |. 83C4 08 add esp, 8 0040FA0D |. 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], 0 0040F9EF |. C745 F8 01000>|mov dword ptr [ebp-8], 1 0040F9F6 |. 8D45 FC |lea eax, dword ptr [ebp-4] 0040F9F9 |. 50 |push eax 0040F9FA |. 68 F82F4200 |push 00422FF8 ; /format = "%d" 0040F9FF |. E8 6CFFFFFF |call scanf ; \scanf 0040FA04 |. 83C4 08 |add esp, 8 0040FA07 |. 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 ; \printf 0040FA15 |. 83C4 08 |add esp, 8 0040FA18 |. C745 F8 0A000 |mov dword ptr [ebp-8], 0A
堆变量:
#include <stdlib.h> int 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 ; \malloc 0040103F |. 83C4 04 add esp, 4 00401042 |. 8945 FC mov dword ptr [ebp-4], eax 00401045 |. 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 ; \printf 00401053 |. 83C4 08 add esp, 8 00401056 |. 8B4D FC mov ecx, dword ptr [ebp-4] 00401059 |. 51 push ecx ; /block 0040105A |. 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 ebp 00401021 8BEC mov ebp, esp 00401023 83EC 40 sub esp, 40 ........ ....... ................ 00401058 83C4 40 add esp, 40 00401062 8BE5 mov esp, ebp 00401064 5D pop ebp 00401065 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 20 0040106A 6A 0A push 0A push 10 0040106C E8 9EFFFFFF call 0040100F
观察 print()函数汇编代码片段,通过ebp指针在堆栈中取出参数,然后进行相加运算,最后将返回值放到eax中。
00401020 > 55 push ebp 00401021 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], eax 00401041 8B45 FC mov eax, dword ptr [ebp-4] ........ 00401047 8BE5 mov esp, ebp 00401049 5D pop ebp 0040104A C3 retn
传递数组:
void Print(int arr[], int size) { int i=0; for (i=0; i<size; ++i) { printf("%d \r",arr[i]); } } int main(int argc, char* argv[]) { int array[10]={1,2,3,4,5,6,7,8,9,0}; Print(array,10); return 0; }
观察Print的参数传递,首先传递一个0A也就是10,然后传递数组指针,最后调用Print函数.
004010FE 6A 0A push 0A 00401100 8D45 D8 lea eax, dword ptr [ebp-28] 00401103 50 push eax 00401104 E8 FCFEFFFF call 00401005
00401038 C745 FC 0000000>mov dword ptr [ebp-4], 0 0040103F C745 FC 0000000>mov dword ptr [ebp-4], 0 00401046 EB 09 jmp short 00401051 00401048 8B45 FC mov eax, dword ptr [ebp-4] 0040104B 83C0 01 add eax, 1 0040104E 8945 FC mov dword ptr [ebp-4], eax 00401051 8B4D FC mov ecx, dword ptr [ebp-4] 00401054 3B4D 0C cmp ecx, dword ptr [ebp+C] 00401057 7D 19 jge short 00401072 00401059 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 ecx 00401063 68 1C204200 push 0042201C ; ASCII "%d \r" 00401068 E8 D3000000 call printf 0040106D 83C4 08 add esp, 8 00401070 ^ 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 eax 004010C3 E8 47FFFFFF call 0040100F
函数内部调用。
00401038 8B45 08 mov eax, dword ptr [ebp+8] 0040103B 50 push eax 0040103C 68 A42F4200 push 00422FA4 ; ASCII "%s\n" 00401041 E8 FA000000 call printf 00401046 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], 0 0040107F 6A 64 push 64 00401081 E8 7FFFFFFF call 00401005 00401086 83C4 04 add esp, 4 00401089 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, 4 0040103E 8B08 mov ecx, dword ptr [eax] 00401040 894D FC mov dword ptr [ebp-4], ecx 00401043 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], 0A 0040D77F C745 F8 14000000 mov dword ptr [ebp-8], 14 0040D786 C645 FC 41 mov byte ptr [ebp-4], 41 0040D78A 8B45 08 mov eax, dword ptr [ebp+8] 0040D78D 8B4D F4 mov ecx, dword ptr [ebp-C] 0040D790 8908 mov dword ptr [eax], ecx 0040D792 8B55 F8 mov edx, dword ptr [ebp-8] 0040D795 8950 04 mov dword ptr [eax+4], edx 0040D798 8B4D FC mov ecx, dword ptr [ebp-4] 0040D79B 8948 08 mov dword ptr [eax+8], ecx 0040D79E 8B45 08 mov eax, dword ptr [ebp+8]
0040D7D4 8B08 mov ecx, dword ptr [eax] 0040D7D6 894D E8 mov dword ptr [ebp-18], ecx 0040D7D9 8B50 04 mov edx, dword ptr [eax+4] 0040D7DC 8955 EC mov dword ptr [ebp-14], edx 0040D7DF 8B40 08 mov eax, dword ptr [eax+8] 0040D7E2 8945 F0 mov dword ptr [ebp-10], eax 0040D7E5 8B4D E8 mov ecx, dword ptr [ebp-18] 0040D7E8 894D F4 mov dword ptr [ebp-C], ecx 0040D7EB 8B55 EC mov edx, dword ptr [ebp-14] 0040D7EE 8955 F8 mov dword ptr [ebp-8], edx 0040D7F1 8B45 F0 mov eax, dword ptr [ebp-10] 0040D7F4 8945 FC mov dword ptr [ebp-4], eax 0040D7F7 8B4D F4 mov ecx, dword ptr [ebp-C] 0040D7FA 51 push ecx 0040D7FB 68 A42F4200 push 00422FA4 ; ASCII "%d \n" 0040D800 E8 CB38FFFF call printf 0040D805 83C4 08 add esp, 8 0040D808 8B55 F8 mov edx, dword ptr [ebp-8] 0040D80B 52 push edx 0040D80C 68 A42F4200 push 00422FA4 ; ASCII "%d \n" 0040D811 E8 BA38FFFF call printf 0040D816 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], 0x1 0040102F |. C745 F0 02000>mov dword ptr [ebp-0x10], 0x2 00401036 |. C745 F4 03000>mov dword ptr [ebp-0xC], 0x3 0040103D |. C745 F8 04000>mov dword ptr [ebp-0x8], 0x4 00401044 |. C745 FC 05000>mov dword ptr [ebp-0x4], 0x5 0040104B |. C745 E8 00000>mov dword ptr [ebp-0x18], 0x0 00401052 |. EB 09 jmp short 0040105D 00401054 |> 8B45 E8 /mov eax, dword ptr [ebp-0x18] 00401057 |. 83C0 01 |add eax, 0x1 0040105A |. 8945 E8 |mov dword ptr [ebp-0x18], eax 0040105D |> 837D E8 05 cmp dword ptr [ebp-0x18], 0x5 00401061 |. 7D 17 |jge short 0040107A 00401063 |. 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 ; \printf 00401075 |. 83C4 08 |add esp, 0x8 00401078 |.^ 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], 1 0040102F C745 EC 0200000>mov dword ptr [ebp-14], 2 00401036 C745 F0 0300000>mov dword ptr [ebp-10], 3 0040103D C745 F4 0400000>mov dword ptr [ebp-C], 4 00401044 C745 F8 0500000>mov dword ptr [ebp-8], 5 0040104B C745 FC 0600000>mov dword ptr [ebp-4], 6 00401052 C745 E4 0000000>mov dword ptr [ebp-1C], 0 00401059 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 eax 00401077 68 1C204200 push 0042201C 0040107C E8 5F000000 call printf 00401081 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 = 1 0040107B 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=x 00401059 |. 6BC0 30 imul eax, eax, 30 ; sizeof(type[C][H])*x 0040105C |. 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, 4 00401066 |. 03CA add ecx, edx 00401068 |. 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; }
指向数组的指针变量:
函数指针:
结构与联合体
未完待续。。。。。