Switch语句逆向
break加与不加有什么特点?default语句可以省略吗?
答: 不加,每个都会执行,可以省略,不过怕会出错
添加case后面的值,一个一个增加,观察反汇编代码的变化(何时生成大表).
将3中的常量值的顺序打乱,观察反汇编代码(观察顺序是否会影响生成大表).
答: 不会
将case后面的值改成从100开始到109,观察汇编变化(观察值较大时是否生成大表).
答: 会,减的常数不同
将连续的10项中抹去1项或者2项,观察反汇编有无变化(观察大表空缺位置的处理).
答: 无,空缺位置填充了default分支
在10项中连续抹去,不要抹去最大值和最小值(观察何时生成小表).
答: 大表+小表,达到一定数量会生成小表,小表用来查偏移,直接跳,节省内存
将case后面常量表达式改成毫不连续的值,观察反汇编变化.
答:搜索二叉树,骚啊
- switch分支少于4个没意思,因为会生成类似if_else的结构
- sub ecx,常数,是为了跳转表转向,将其转换成index
- 4个分支上会生成大表,直接计算地址,而大表需要连续的数值判断,如果相隔太大,会生成if_else结构
练习
写一个switch语句,不生产大表也不生产小表,贴出对应的反汇编
这个分支少于4个就会生成if_else结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 8: switch(num) 9: { 0040D708 mov eax,dword ptr [ebp+8] 0040D70B mov dword ptr [ebp-4],eax 0040D70E cmp dword ptr [ebp-4],1 0040D712 je func+32h (0040d722) 0040D714 cmp dword ptr [ebp-4],2 0040D718 je func+41h (0040d731) 0040D71A cmp dword ptr [ebp-4],3 0040D71E je func+50h (0040d740) 0040D720 jmp func+5Dh (0040d74d) 10: case 1: 11: printf("1\n"); 0040D722 push offset string "1\n" (00422024) 0040D727 call printf (00401060) 0040D72C add esp,4 12: break; 0040D72F jmp func+5Dh (0040d74d) 13: case 2: 14: printf("2\n"); 0040D731 push offset string "2\n" (00422020) 0040D736 call printf (00401060) 0040D73B add esp,4 15: break; 0040D73E jmp func+5Dh (0040d74d) 16: case 3: 17: printf("3\n"); 0040D740 push offset string "Hello World!\n" (0042201c) 0040D745 call printf (00401060) 0040D74A add esp,4 18: break; 19: } 20: }
|
写一个switch语句,只生成大表,贴出对应的反汇编.
这个分支多于4个就可以生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 8: switch(num) 9: { 0040D7B8 mov eax,dword ptr [ebp+8] 0040D7BB mov dword ptr [ebp-4],eax 0040D7BE mov ecx,dword ptr [ebp-4] 0040D7C1 sub ecx,1 0040D7C4 mov dword ptr [ebp-4],ecx 0040D7C7 cmp dword ptr [ebp-4],3 0040D7CB ja $L590+0Dh (0040d811) 0040D7CD mov edx,dword ptr [ebp-4] 0040D7D0 jmp dword ptr [edx*4+40D822h] 10: case 1: 11: printf("1\n"); 0040D7D7 push offset string "1\n" (00422028) 0040D7DC call printf (00401060) 0040D7E1 add esp,4 12: break; 0040D7E4 jmp $L590+0Dh (0040d811) 13: case 2: 14: printf("2\n"); 0040D7E6 push offset string "2\n" (00422024) 0040D7EB call printf (00401060) 0040D7F0 add esp,4 15: break; 0040D7F3 jmp $L590+0Dh (0040d811) 16: case 3: 17: printf("3\n"); 0040D7F5 push offset string "3\n" (00422020) 0040D7FA call printf (00401060) 0040D7FF add esp,4 18: break; 0040D802 jmp $L590+0Dh (0040d811) 19: case 4: 20: printf("4\n"); 0040D804 push offset string "4\n" (0042201c) 0040D809 call printf (00401060) 0040D80E add esp,4 21: break; 22: } 23: }
|
还有一种大表,带default的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| 8: switch(num) 9: { 00401038 mov eax,dword ptr [ebp+8] 0040103B mov dword ptr [ebp-4],eax 0040103E mov ecx,dword ptr [ebp-4] 00401041 sub ecx,1 00401044 mov dword ptr [ebp-4],ecx 00401047 cmp dword ptr [ebp-4],9 0040104B ja $L598+0Fh (004010d3) 00401051 mov edx,dword ptr [ebp-4] 00401054 jmp dword ptr [edx*4+4010F1h] 10: case 1: 11: printf("1\n"); 0040105B push offset string "1\n" (00422040) 00401060 call printf (004011b0) 00401065 add esp,4 12: break; 00401068 jmp $L598+1Ch (004010e0) 13: /* 14: case 2: 15: printf("2\n"); 16: break; 17: 18: case 3: 19: printf("3\n"); 20: break; 21: */ 22: case 4: 23: printf("4\n"); 0040106A push offset string "4\n" (0042203c) 0040106F call printf (004011b0) 00401074 add esp,4 24: break; 00401077 jmp $L598+1Ch (004010e0) 25: case 5: 26: printf("5\n"); 00401079 push offset string "5\n" (00422038) 0040107E call printf (004011b0) 00401083 add esp,4 27: break; 00401086 jmp $L598+1Ch (004010e0) 28: case 6: 29: printf("6\n"); 00401088 push offset string "6\n" (00422034) 0040108D call printf (004011b0) 00401092 add esp,4 30: break; 00401095 jmp $L598+1Ch (004010e0) 31: case 7: 32: printf("7\n"); 00401097 push offset string "7\n" (00422030) 0040109C call printf (004011b0) 004010A1 add esp,4 33: break; 004010A4 jmp $L598+1Ch (004010e0) 34: case 8: 35: printf("8\n"); 004010A6 push offset string "8\n" (0042202c) 004010AB call printf (004011b0) 004010B0 add esp,4 36: break; 004010B3 jmp $L598+1Ch (004010e0) 37: case 9: 38: printf("9\n"); 004010B5 push offset string "9\n" (00422028) 004010BA call printf (004011b0) 004010BF add esp,4 39: break; 004010C2 jmp $L598+1Ch (004010e0) 40: case 10: 41: printf("10\n"); 004010C4 push offset string "10\n" (00422024) 004010C9 call printf (004011b0) 004010CE add esp,4 42: break; 004010D1 jmp $L598+1Ch (004010e0) 43: default: 44: printf("error\n"); 004010D3 push offset string "error\n" (0042201c) 004010D8 call printf (004011b0) 004010DD add esp,4 45: break; 46: } 47: }
|
004010F1 5B 10 40 00 [.@.
004010F5 D3 10 40 00 ..@.
004010F9 D3 10 40 00 ..@.
004010FD 6A 10 40 00 j.@.
00401101 79 10 40 00 y.@.
00401105 88 10 40 00 ..@.
00401109 97 10 40 00 ..@.
0040110D A6 10 40 00 ..@.
00401111 B5 10 40 00 ..@.
这是生成的大表,中间被我注释掉的全部生成了default语句
写一个switch语句,生成大表和小表,贴出对应的反汇编.
11个注释掉6个后终于生成了小表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| 8: switch(num) 9: { 00401038 mov eax,dword ptr [ebp+8] 0040103B mov dword ptr [ebp-4],eax 0040103E mov ecx,dword ptr [ebp-4] 00401041 sub ecx,1 00401044 mov dword ptr [ebp-4],ecx 00401047 cmp dword ptr [ebp-4],9 0040104B ja $L590+0Fh (0040109b) 0040104D mov eax,dword ptr [ebp-4] 00401050 xor edx,edx 00401052 mov dl,byte ptr (004010cd)[eax] 00401058 jmp dword ptr [edx*4+4010B9h] 10: case 1: 11: printf("1\n"); 0040105F push offset string "7\n" (00422030) 00401064 call printf (004011b0) 00401069 add esp,4 12: break; 0040106C jmp $L590+1Ch (004010a8) 13: /* 14: case 2: 15: printf("2\n"); 16: break; 17: 18: case 3: 19: printf("3\n"); 20: break; 21: 22: case 4: 23: printf("4\n"); 24: break; 25: 26: case 5: 27: printf("5\n"); 28: break; 29: 30: case 6: 31: printf("6\n"); 32: break; 33: 34: case 7: 35: printf("7\n"); 36: break; 37: */ 38: case 8: 39: printf("8\n"); 0040106E push offset string "8\n" (0042202c) 00401073 call printf (004011b0) 00401078 add esp,4 40: break; 0040107B jmp $L590+1Ch (004010a8) 41: case 9: 42: printf("9\n"); 0040107D push offset string "9\n" (00422028) 00401082 call printf (004011b0) 00401087 add esp,4 43: break; 0040108A jmp $L590+1Ch (004010a8) 44: case 10: 45: printf("10\n"); 0040108C push offset string "10\n" (00422024) 00401091 call printf (004011b0) 00401096 add esp,4 46: break; 00401099 jmp $L590+1Ch (004010a8) 47: default: 48: printf("error\n"); 0040109B push offset string "error\n" (0042201c) 004010A0 call printf (004011b0) 004010A5 add esp,4 49: break; 50: } 51: }
|
这就是小表
004010CD 00 04 04 04 ….
004010D1 04 04 04 01 ….
小表查找的语句
1 2 3 4
| 040104D mov eax,dword ptr [ebp-4] 00401050 xor edx,edx 00401052 mov dl,byte ptr (004010cd)[eax] 00401058 jmp dword ptr [edx*4+4010B9h]
|
小表就是个存了偏移的表,通过比较,取出相对应的偏移位置,然后在jmp
为do..while语句生成的反汇编填写注释.
1 2 3 4 5 6 7 8 9 10 11 12
| 9: do{ 10: printf("A"); 0040103F push offset string "error\n" (0042201c) 00401044 call printf (004011b0) 00401049 add esp,4 11: }while(i++ < 10); 0040104C mov eax,dword ptr [ebp-4] 0040104F mov ecx,dword ptr [ebp-4] 00401052 add ecx,1 00401055 mov dword ptr [ebp-4],ecx 00401058 cmp eax,0Ah 0040105B jl func+1Fh (0040103f)
|
do while先执行,后面cmp跳转回去,明显特征,只有往上跳,没有往下跳的
总结:
- 根据条件跳转指令所跳转到的地址,可以得到循环语句块的起始地址。
- 根据条件跳转指令所在的地址,可以得到循环语句块的结束地址。
- 条件跳转的逻辑与源码相同。
为while语句生成的反汇编填写注释.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 9: while(i++ < 10) 0040103F mov eax,dword ptr [ebp-4] 00401042 mov ecx,dword ptr [ebp-4] 00401045 add ecx,1 00401048 mov dword ptr [ebp-4],ecx 0040104B cmp eax,0Ah 0040104E jge func+3Fh (0040105f) 10: { 11: printf("A"); 00401050 push offset string "error\n" (0042201c) 00401055 call printf (004011b0) 0040105A add esp,4 12: }; 0040105D jmp func+1Fh (0040103f)
|
while语句特征,先cmp比较,然后在执行,最后往上跳,特征,cmp后才会执行
总结:
- 根据条件跳转指令所跳转到的地址,可以得到循环语句块的结束地址;
- 根据jmp 指令所跳转到的地址,可以得到循环语句块的起始地址;
- 在还原while 比较时,条件跳转的逻辑与源码相反。
为for语句生成的反汇编填写注释.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 9: for(i=0; i<10; i++) 0040103F mov dword ptr [ebp-4],0 00401046 jmp func+31h (00401051) 00401048 mov eax,dword ptr [ebp-4] 0040104B add eax,1 0040104E mov dword ptr [ebp-4],eax 00401051 cmp dword ptr [ebp-4],0Ah 00401055 jge func+4Ah (0040106a) 10: { 11: printf("%d" , i); 00401057 mov ecx,dword ptr [ebp-4] 0040105A push ecx 0040105B push offset string "1" (0042201c) 00401060 call printf (004011b0) 00401065 add esp,8 12: } 00401068 jmp func+28h (00401048)
|
for循环的话,开头跳去比较,比较过后执行,最后自增
总结:
- 第一个jmp 指令之前为赋初值部分.
- 第一个jmp 指令所跳转的地址为循环条件判定部分起始.
- 判断条件后面的跳转指令条件成立时跳转的循环体外面
- 条件判断跳转指令所指向的地址上面有一个jmp jmp地址为表达式3的起始位置
本文作者:NoOne
本文地址: https://noonegroup.xyz/posts/e4f694a1/
版权声明:转载请注明出处!