0%

C++反汇编与逆向分析 - SWITCH

switch是比较常用的分支结构,并且效率上优于IF…ELSE IF等多分支结构。

运行环境:

  • 操作系统: Windows 7家庭版
  • 编译器:VC6 VS2013

switch

switch分为有三个case以下的和三个cese以上的,从代码来揭秘他神奇的面纱:

短分支

debug版本

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
// Test.cpp : Defines the entry point for the console application.
//
//debug版本
#include "stdafx.h"

int main(int argc, char* argv[])
{
switch(argc)
{
case 0:
printf("case 0\n");
break;
case 1:
printf("case 1\n");
break;
case 2:
printf("case 1\n");
break;
default:
break;
}
printf("HelloWorld\n");
return 0;
}

8: switch(argc)
9: {
00401028 mov eax,dword ptr [ebp+8]
0040102B mov dword ptr [ebp-4],eax
0040102E cmp dword ptr [ebp-4],0
00401032 je main+32h (00401042) //case0
00401034 cmp dword ptr [ebp-4],1
00401038 je main+41h (00401051) //case1
0040103A cmp dword ptr [ebp-4],2
0040103E je main+50h (00401060) //case2
00401040 jmp main+5Dh (0040106d) //default
10: case 0:
11: printf("case 0\n");
00401042 push offset string "case 0\n" (00422038)
00401047 call printf (004010b0)
0040104C add esp,4
12: break;
0040104F jmp main+5Dh (0040106d)
13: case 1:
14: printf("case 1\n");
00401051 push offset string "case 1\n" (0042202c)
00401056 call printf (004010b0)
0040105B add esp,4
15: break;
0040105E jmp main+5Dh (0040106d)
16: case 2:
17: printf("case 1\n");
00401060 push offset string "case 1\n" (0042202c)
00401065 call printf (004010b0)
0040106A add esp,4
18: break;
19: default:
20: break;
21: }
22: printf("HelloWorld\n");
0040106D push offset string "HelloWorld\n" (0042201c)
00401072 call printf (004010b0)
00401077 add esp,4
23: return 0;
0040107A xor eax,eax
24: }

Release版本:

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
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main proc near ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 mov eax, [esp+argc]
.text:00401004 sub eax, 0
.text:00401007 jz short loc_40102F
.text:00401009 dec eax
.text:0040100A jz short loc_40101F
.text:0040100C dec eax
.text:0040100D jnz short loc_40103C
.text:0040100F push offset Format ; "argc = 2\n"
.text:00401014 call _printf
.text:00401019 add esp, 4
.text:0040101C xor eax, eax
.text:0040101E retn
.text:0040101F ; ---------------------------------------------------------------------------
.text:0040101F
.text:0040101F loc_40101F: ; CODE XREF: _main+A↑j
.text:0040101F push offset aArgc1 ; "argc = 1\n"
.text:00401024 call _printf
.text:00401029 add esp, 4
.text:0040102C xor eax, eax
.text:0040102E retn
.text:0040102F ; ---------------------------------------------------------------------------
.text:0040102F
.text:0040102F loc_40102F: ; CODE XREF: _main+7↑j
.text:0040102F push offset aArgc0 ; "argc = 0\n"
.text:00401034 call _printf
.text:00401039 add esp, 4
.text:0040103C
.text:0040103C loc_40103C: ; CODE XREF: _main+D↑j
.text:0040103C xor eax, eax
.text:0040103E retn
.text:0040103E _main endp
.text:0040103E

比较一下短分支和if else的区别:

引导表方案之一:

  • 只有跳转没有实际代码
  • 可从引导表中看出最小case值(Min + x = 0)、最大case值、switch_end的位置
  • 所有条件不满足且当有条件满足且不去的代码段,则为default代码段

case表

如果switch为0的时候;理想状态下会按照表跳转;

但是会遇到幺蛾子情况一之越界情况:

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
8:        argc = 8;
00401028 mov dword ptr [ebp+8],8
9: switch(argc)
10: {
0040102F mov eax,dword ptr [ebp+8]
00401032 mov dword ptr [ebp-4],eax ;表达式求值
00401035 cmp dword ptr [ebp-4],4 ;和4做比较
00401039 ja $L542+0Dh (0040108e) ;;范围检查
0040103B mov ecx,dword ptr [ebp-4] ;查表跳转
0040103E jmp dword ptr [ecx*4+4010A1h]
11: case 0:
12: printf("argc = 0\n");
00401045 push offset string "argc = 0\n" (0042204c)
0040104A call printf (004010e0)
0040104F add esp,4
13: break;
00401052 jmp $L542+0Dh (0040108e)
14: case 1:
15: printf("argc = 1\n");
00401054 push offset string "argc = 1\n" (00422040)
00401059 call printf (004010e0)
0040105E add esp,4
16: break;
00401061 jmp $L542+0Dh (0040108e)
17: case 2:
18: printf("argc = 2\n");
00401063 push offset string "argc = 2\n" (00422034)
00401068 call printf (004010e0)
0040106D add esp,4
19: break;
00401070 jmp $L542+0Dh (0040108e)
20: case 3:
21: printf("argc = 3\n");
00401072 push offset string "argc = 3\n" (00422028)
00401077 call printf (004010e0)
0040107C add esp,4
22: break;
0040107F jmp $L542+0Dh (0040108e)
23: case 4:
24: printf("argc = 4\n");
00401081 push offset string "argc = 4\n" (0042201c)
00401086 call printf (004010e0)
0040108B add esp,4
25: break;
26: }
27: return 0;

幺蛾子情况二之没有命中:

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
31:       argc = 8;
0040D788 mov dword ptr [ebp+8],8
32: switch(argc)
33: {
0040D78F mov eax,dword ptr [ebp+8]
0040D792 mov dword ptr [ebp-4],eax
0040D795 mov ecx,dword ptr [ebp-4] ;坐标平移
0040D798 sub ecx,0Ah ;表达式求值部分
0040D79B mov dword ptr [ebp-4],ecx
0040D79E cmp dword ptr [ebp-4],4
0040D7A2 ja $L556+0Dh (0040d7f7) ;范围检测
0040D7A4 mov edx,dword ptr [ebp-4]
0040D7A7 jmp dword ptr [edx*4+40D80Ah] ;查表跳转
34: case 10:
35: printf("argc = 0\n");
0040D7AE push offset string "argc = 0\n" (0042204c)
0040D7B3 call printf (004010e0)
0040D7B8 add esp,4
36: break;
0040D7BB jmp $L556+0Dh (0040d7f7)
37: case 11:
38: printf("argc = 1\n");
0040D7BD push offset string "argc = 1\n" (00422040)
0040D7C2 call printf (004010e0)
0040D7C7 add esp,4
39: break;
0040D7CA jmp $L556+0Dh (0040d7f7)
40: case 12:
41: printf("argc = 2\n");
0040D7CC push offset string "argc = 2\n" (00422034)
0040D7D1 call printf (004010e0)
0040D7D6 add esp,4
42: break;
0040D7D9 jmp $L556+0Dh (0040d7f7)
43: case 13:
44: printf("argc = 3\n");
0040D7DB push offset string "argc = 3\n" (00422028)
0040D7E0 call printf (004010e0)
0040D7E5 add esp,4
45: break;
0040D7E8 jmp $L556+0Dh (0040d7f7)
46: case 14:
47: printf("argc = 4\n");
0040D7EA push offset string "argc = 4\n" (0042201c)
0040D7EF call printf (004010e0)
0040D7F4 add esp,4
48: break;
49: }

  • 当case数量大于3个时且差值不大时,即会生成case表
  • case值之间的差值用defult的地址填充

调色板存储

  • VS case 最大最小值相差12 , case值相差过大 ,只造表,空间不足,所以加个索引
  • 最大最小值插值不超过255会生成方案三
  • 一个存储下标的表(Byte)
  • 在查询索引表后得到下标,跳转至存储地址标中按下标查询(Dword)
  • 第一次取索引表相对下表至一个8位寄存器中(例:bl、dl、al、cl)然后到地址表中下标寻址得到真正跳转地址
  • 计算得到的值是最小值
  • cmp比较的值加上最小值就是最大值
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
85
86
87
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main proc near ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 mov eax, [esp+argc]
.text:00401004 add eax, 0FFFFFFECh ; switch 20 cases
.text:00401007 cmp eax, 13h
.text:0040100A ja short def_401014 ; jumptable 00401014 default case, cases 22,28-30,32-38
.text:0040100C xor ecx, ecx
.text:0040100E mov cl, ds:byte_401094[eax]
.text:00401014 jmp ds:jpt_401014[ecx*4] ; switch jump
.text:0040101B ; ---------------------------------------------------------------------------
.text:0040101B
.text:0040101B loc_40101B: ; CODE XREF: _main+14↑j
.text:0040101B ; DATA XREF: .text:jpt_401014↓o
.text:0040101B push offset Format ; jumptable 00401014 case 23
.text:00401020 call _printf
.text:00401025 add esp, 4
.text:00401028 xor eax, eax
.text:0040102A retn
.text:0040102B ; ---------------------------------------------------------------------------
.text:0040102B
.text:0040102B loc_40102B: ; CODE XREF: _main+14↑j
.text:0040102B ; DATA XREF: .text:jpt_401014↓o
.text:0040102B push offset aArgc1 ; jumptable 00401014 case 24
.text:00401030 call _printf
.text:00401035 add esp, 4
.text:00401038 xor eax, eax
.text:0040103A retn
.text:0040103B ; ---------------------------------------------------------------------------
.text:0040103B
.text:0040103B loc_40103B: ; CODE XREF: _main+14↑j
.text:0040103B ; DATA XREF: .text:jpt_401014↓o
.text:0040103B push offset aArgc2 ; jumptable 00401014 case 25
.text:00401040 call _printf
.text:00401045 add esp, 4
.text:00401048 xor eax, eax
.text:0040104A retn
.text:0040104B ; ---------------------------------------------------------------------------
.text:0040104B
.text:0040104B loc_40104B: ; CODE XREF: _main+14↑j
.text:0040104B ; DATA XREF: .text:jpt_401014↓o
.text:0040104B push offset aArgc3 ; jumptable 00401014 case 26
.text:00401050 call _printf
.text:00401055 add esp, 4
.text:00401058 xor eax, eax
.text:0040105A retn
.text:0040105B ; ---------------------------------------------------------------------------
.text:0040105B
.text:0040105B loc_40105B: ; CODE XREF: _main+14↑j
.text:0040105B ; DATA XREF: .text:jpt_401014↓o
.text:0040105B push offset aArgc4 ; jumptable 00401014 cases 20,21,27,31,39
.text:00401060 call _printf
.text:00401065 add esp, 4
.text:00401068 xor eax, eax
.text:0040106A retn
.text:0040106B ; ---------------------------------------------------------------------------
.text:0040106B
.text:0040106B def_401014: ; CODE XREF: _main+A↑j
.text:0040106B ; _main+14↑j
.text:0040106B ; DATA XREF: ...
.text:0040106B push offset aHello ; jumptable 00401014 default case, cases 22,28-30,32-38
.text:00401070 call _printf
.text:00401075 add esp, 4
.text:00401078 xor eax, eax
.text:0040107A retn
.text:0040107A _main endp
.text:0040107A
.text:0040107A ; ---------------------------------------------------------------------------
.text:0040107B align 4
.text:0040107C jpt_401014 dd offset CASE_23 ; DATA XREF: _main+14↑r
.text:0040107C dd offset loc_40101B ; jump table for switch statement
.text:0040107C dd offset loc_40102B
.text:0040107C dd offset loc_40103B
.text:0040107C dd offset loc_40104B
.text:0040107C dd offset def_401014
.text:00401094 byte_401094 db 0, 0, 5, 1
.text:00401094 ; DATA XREF: _main+E↑r
.text:00401094 db 2, 3, 4, 0 ; indirect table for switch statement
.text:00401094 db 5, 5, 5, 0
.text:00401094 db 5, 5, 5, 5
.text:00401094 db 5, 5, 5, 0
.text:004010A8 align 10h
.text:004010B0

AVL树状存储

  • 会取一个中间值作比较

  • 还原须看jz的上一句sub,遇到jnz则为defautl。

  • jg跳转的地址为树的另外一部分