switch语句的逆向

在vc6.0编译器中,会自动为switch转换成4种情况的汇编语句。

case1:

当c语言源代码为

 1 #include<stdio.h>
 2 void myFunction(int x)
 3 {
 4     switch(x)
 5     {
 6     case 1:
 7         printf("1");
 8         break;
 9     case 2:
10         printf("2");
11         break;
12     case 3:
13         printf("3");
14         break;
15     default:
16         printf("error");
17     }
18 }
19 int main()
20 {
21     myFunction(2);
22     return 0;
23 }

运行的结果可以看出,和if&else一样。

都是cmp&jcc的。

 1 4:        switch(x)
 2 5:        {
 3 0040D7B8   mov         eax,dword ptr [ebp+8]
 4 0040D7BB   mov         dword ptr [ebp-4],eax
 5 0040D7BE   cmp         dword ptr [ebp-4],1
 6 0040D7C2   je          myFunction+32h (0040d7d2)
 7 0040D7C4   cmp         dword ptr [ebp-4],2
 8 0040D7C8   je          myFunction+41h (0040d7e1)
 9 0040D7CA   cmp         dword ptr [ebp-4],3
10 0040D7CE   je          myFunction+50h (0040d7f0)
11 0040D7D0   jmp         myFunction+5Fh (0040d7ff)
12 6:        case 1:
13 7:            printf("1");
14 0040D7D2   push        offset string "1" (0042202c)
15 0040D7D7   call        printf (00401110)
16 0040D7DC   add         esp,4
17 8:            break;
18 0040D7DF   jmp         myFunction+6Ch (0040d80c)
19 9:        case 2:
20 10:           printf("2");
21 0040D7E1   push        offset string "2" (00422028)
22 0040D7E6   call        printf (00401110)
23 0040D7EB   add         esp,4
24 11:           break;
25 0040D7EE   jmp         myFunction+6Ch (0040d80c)
26 12:       case 3:
27 13:           printf("3");
28 0040D7F0   push        offset string "4" (00422024)
29 0040D7F5   call        printf (00401110)
30 0040D7FA   add         esp,4
31 14:           break;
32 0040D7FD   jmp         myFunction+6Ch (0040d80c)
33 15:
34 16:       default:
35 17:           printf("error");
36 0040D7FF   push        offset string "error" (0042201c)
37 0040D804   call        printf (00401110)
38 0040D809   add         esp,4

当case情况小于等于3时,ifelse和switch并没有什么区别。

case2:

当case情况大于等于4种时,且4种case是连续的。

 1 #include<stdio.h>
 2 void myFunction(int x)
 3 {
 4     switch(x)
 5     {
 6     case 1:
 7         printf("1");
 8         break;
 9     case 2:
10         printf("2");
11         break;
12     case 3:
13         printf("3");
14         break;
15     case 4:
16         printf("4");
17         break;
18     default:
19         printf("error");
20     }
21 }
22 int main()
23 {
24     myFunction(2);
25     return 0;
26 }

明显的区别是

 1 4:        switch(x)
 2 5:        {
 3 0040D7B8   mov         eax,dword ptr [ebp+8]
 4 0040D7BB   mov         dword ptr [ebp-4],eax
 5 0040D7BE   mov         ecx,dword ptr [ebp-4]
 6 0040D7C1   sub         ecx,1
 7 0040D7C4   mov         dword ptr [ebp-4],ecx
 8 0040D7C7   cmp         dword ptr [ebp-4],3
 9 0040D7CB   ja          $L538+0Fh (0040d813)
10 0040D7CD   mov         edx,dword ptr [ebp-4]
11 0040D7D0   jmp         dword ptr [edx*4+40D831h]
12 6:        case 1:
13 7:            printf("1");
14 0040D7D7   push        offset string "1" (0042213c)
15 0040D7DC   call        printf (00401110)
16 0040D7E1   add         esp,4
17 8:            break;
18 0040D7E4   jmp         $L538+1Ch (0040d820)
19 9:        case 2:
20 10:           printf("2");
21 0040D7E6   push        offset string "2" (0042202c)
22 0040D7EB   call        printf (00401110)
23 0040D7F0   add         esp,4
24 11:           break;
25 0040D7F3   jmp         $L538+1Ch (0040d820)
26 12:       case 3:
27 13:           printf("3");
28 0040D7F5   push        offset string "3" (00422028)
29 0040D7FA   call        printf (00401110)
30 0040D7FF   add         esp,4
31 14:           break;
32 0040D802   jmp         $L538+1Ch (0040d820)
33 15:       case 4:
34 16:           printf("4");
35 0040D804   push        offset string "4" (00422024)
36 0040D809   call        printf (00401110)
37 0040D80E   add         esp,4
38 17:           break;
39 0040D811   jmp         $L538+1Ch (0040d820)
40 18:       default:
41 19:           printf("error");
42 0040D813   push        offset string "error" (0042201c)
43 0040D818   call        printf (00401110)
44 0040D81D   add         esp,4
45 20:       }
46 21:   }

代码分析:

将传进来的x放在ecx中

在这一段中可以明显看到的是多出了一个sub,ecx减去一个1。ecx移动回ebp-4,ebp-4和3比较。

后面有一个ja,40d813,这里是default去的地方。即x-1和3比较,大于则跳转至default

ebp-4放入edx中。

后面还有一句dword ptr [edx*4+40D831h]

实际上,这里的sub,减去的是这4个case中的最小值。

dword ptr [edx*4+40D831h]这个就是编译器自动为我们生成的一张表。按下alt+6,将40D831拖到memery窗口中。

可以看到

可以看到40d831。

总的来说edx=x-1。即(x-1)*4+40d831h这个内存单元的内容,就是大表跳转的地方。

图中我们可以看到4个地址

40d7d7,是case1的地方。

40d7e6,是case2的地方。

40d7f5,是case3的地方。

40d804,是case4的地方。

如果中间随便调整顺序呢,结果发现调整顺序对最终结果无影响。(数据就不贴了)

那假如不是连续的呢。

原本的代码

 1 #include<stdio.h>
 2 void Function(int x)
 3 {
 4     switch(x)
 5     {
 6     case 1:
 7         printf("1");break;
 8     case 2:
 9         printf("2");break;
10     case 3:
11         printf("3");break;
12     case 4:
13         printf("4");break;
14     case 5:
15         printf("5");break;
16     case 6:
17         printf("6");break;
18     case 7:
19         printf("7");break;
20     case 8:
21         printf("8");break;
22     case 9:
23         printf("9");break;
24     default:
25         printf("error");break;
26     }
27 }
28 
29 void main()
30 {
31     Function(3);
32 }

删除后的代码

 1 #include<stdio.h>
 2 void Function(int x)
 3 {
 4     switch(x)
 5     {
 6     case 1:
 7         printf("1");break;
 8     case 2:
 9         printf("2");break;
10 
11     case 7:
12         printf("7");break;
13     case 8:
14         printf("8");break;
15     case 9:
16         printf("9");break;
17     default:
18         printf("error");break;
19     }
20 }
21 
22 void main()
23 {
24     Function(3);
25 }

反编译得到

 1 4:        switch(x)
 2 5:        {
 3 00401038   mov         eax,dword ptr [ebp+8]
 4 0040103B   mov         dword ptr [ebp-4],eax
 5 0040103E   mov         ecx,dword ptr [ebp-4]
 6 00401041   sub         ecx,1
 7 00401044   mov         dword ptr [ebp-4],ecx
 8 00401047   cmp         dword ptr [ebp-4],8
 9 0040104B   ja          $L540+0Fh (004010a2)
10 0040104D   mov         edx,dword ptr [ebp-4]
11 00401050   jmp         dword ptr [edx*4+4010C0h]
12 6:        case 1:
13 7:            printf("1");break;
14 00401057   push        offset string "1" (00422030)
15 0040105C   call        printf (004011f0)
16 00401061   add         esp,4
17 00401064   jmp         $L540+1Ch (004010af)
18 8:        case 2:
19 9:            printf("2");break;
20 00401066   push        offset string "2" (0042202c)
21 0040106B   call        printf (004011f0)
22 00401070   add         esp,4
23 00401073   jmp         $L540+1Ch (004010af)
24 10:
25 11:       case 7:
26 12:           printf("7");break;
27 00401075   push        offset string "7" (00422028)
28 0040107A   call        printf (004011f0)
29 0040107F   add         esp,4
30 00401082   jmp         $L540+1Ch (004010af)
31 13:       case 8:
32 14:           printf("8");break;
33 00401084   push        offset string "8" (00422024)
34 00401089   call        printf (004011f0)
35 0040108E   add         esp,4
36 00401091   jmp         $L540+1Ch (004010af)
37 15:       case 9:
38 16:           printf("9");break;
39 00401093   push        offset string "9" (00422020)
40 00401098   call        printf (004011f0)
41 0040109D   add         esp,4
42 004010A0   jmp         $L540+1Ch (004010af)
43 17:       default:
44 18:           printf("error");break;
45 004010A2   push        offset string "error" (00422fcc)
46 004010A7   call        printf (004011f0)
47 004010AC   add         esp,4
48 19:       }
49 20:   }

删除后我们发现原本的那张表删除的位置上添加了一个地址,而这个地址我们过去查看发现正是default部分的地址。

这么弄不就浪费了么,于是我们继续删,看看编译器会如何处理。

当我们删的只有3个的时候,代码为

 1 #include<stdio.h>
 2 void Function(int x)
 3 {
 4     switch(x)
 5     {
 6     case 1:
 7         printf("1");break;
 8     case 2:
 9         printf("2");break;
10 
11 
12     case 9:
13         printf("9");break;
14     default:
15         printf("error");break;
16     }
17 }
18 
19 void main()
20 {
21     Function(3);
22 }

反编译得到的代码已经变成这样了

 1 4:        switch(x)
 2 5:        {
 3 00401038   mov         eax,dword ptr [ebp+8]
 4 0040103B   mov         dword ptr [ebp-4],eax
 5 0040103E   cmp         dword ptr [ebp-4],1
 6 00401042   je          Function+32h (00401052)
 7 00401044   cmp         dword ptr [ebp-4],2
 8 00401048   je          Function+41h (00401061)
 9 0040104A   cmp         dword ptr [ebp-4],9
10 0040104E   je          Function+50h (00401070)
11 00401050   jmp         Function+5Fh (0040107f)
12 6:        case 1:
13 7:            printf("1");break;
14 00401052   push        offset string "7" (00422028)
15 00401057   call        printf (004011f0)
16 0040105C   add         esp,4
17 0040105F   jmp         Function+6Ch (0040108c)
18 8:        case 2:
19 9:            printf("2");break;
20 00401061   push        offset string "2" (00422024)
21 00401066   call        printf (004011f0)
22 0040106B   add         esp,4
23 0040106E   jmp         Function+6Ch (0040108c)
24 10:
25 11:
26 12:       case 9:
27 13:           printf("9");break;
28 00401070   push        offset string "9" (00422020)
29 00401075   call        printf (004011f0)
30 0040107A   add         esp,4
31 0040107D   jmp         Function+6Ch (0040108c)
32 14:       default:
33 15:           printf("error");break;
34 0040107F   push        offset string "error" (00422fcc)
35 00401084   call        printf (004011f0)
36 00401089   add         esp,4
37 16:       }
38 17:   }

编译器会自动为我们做出最优化的汇编代码。当浪费超过一定的时候,还是回归到cmp&jcc上了。

那假如我的case都是连续的,只有一个很突兀,结果会如何呢。

Case3

此时代码为

 1 #include<stdio.h>
 2 void Function(int x)
 3 {
 4     switch(x)
 5     {
 6     case 1:
 7         printf("1");break;
 8     case 2:
 9         printf("2");break;
10     case 3:
11         printf("3");break;
12     case 4:
13         printf("4");break;
14     case 5:
15         printf("5");break;
16 
17     case 233:
18         printf("233");break;
19 
20     case 7:
21         printf("7");break;
22     case 8:
23         printf("8");break;
24     case 9:
25         printf("9");break;
26     default:
27         printf("error");break;
28 
29     }
30 }
31 
32 void main()
33 {
34     Function(3);
35 }

汇编代码为

 1 4:        switch(x)
 2 5:        {
 3 0040D898   mov         eax,dword ptr [ebp+8]
 4 0040D89B   mov         dword ptr [ebp-4],eax
 5 0040D89E   mov         ecx,dword ptr [ebp-4]
 6 0040D8A1   sub         ecx,1
 7 0040D8A4   mov         dword ptr [ebp-4],ecx
 8 0040D8A7   cmp         dword ptr [ebp-4],0E8h
 9 0040D8AE   ja          $L548+0Fh (0040d950)
10 0040D8B4   mov         eax,dword ptr [ebp-4]
11 0040D8B7   xor         edx,edx
12 0040D8B9   mov         dl,byte ptr  (0040d996)[eax]
13 0040D8BF   jmp         dword ptr [edx*4+40D96Eh]
14 6:        case 1:
15 7:            printf("1");break;
16 0040D8C6   push        offset string "1" (0042203c)
17 0040D8CB   call        printf (004011f0)
18 0040D8D0   add         esp,4
19 0040D8D3   jmp         $L548+1Ch (0040d95d)
20 8:        case 2:
21 9:            printf("2");break;
22 0040D8D8   push        offset string "2" (00422038)
23 0040D8DD   call        printf (004011f0)
24 0040D8E2   add         esp,4
25 0040D8E5   jmp         $L548+1Ch (0040d95d)
26 10:       case 3:
27 11:           printf("3");break;
28 0040D8E7   push        offset string "3" (00422034)
29 0040D8EC   call        printf (004011f0)
30 0040D8F1   add         esp,4
31 0040D8F4   jmp         $L548+1Ch (0040d95d)
32 12:       case 4:
33 13:           printf("4");break;
34 0040D8F6   push        offset string "4" (00422030)
35 0040D8FB   call        printf (004011f0)
36 0040D900   add         esp,4
37 0040D903   jmp         $L548+1Ch (0040d95d)
38 14:       case 5:
39 15:           printf("5");break;
40 0040D905   push        offset string "5" (0042202c)
41 0040D90A   call        printf (004011f0)
42 0040D90F   add         esp,4
43 0040D912   jmp         $L548+1Ch (0040d95d)
44 16:
45 17:       case 233:
46 18:           printf("233");break;
47 0040D914   push        offset string "12" (0042201c)
48 0040D919   call        printf (004011f0)
49 0040D91E   add         esp,4
50 0040D921   jmp         $L548+1Ch (0040d95d)
51 19:
52 20:       case 7:
53 21:           printf("7");break;
54 0040D923   push        offset string "7" (00422028)
55 0040D928   call        printf (004011f0)
56 0040D92D   add         esp,4
57 0040D930   jmp         $L548+1Ch (0040d95d)
58 22:       case 8:
59 23:           printf("8");break;
60 0040D932   push        offset string "10" (00422024)
61 0040D937   call        printf (004011f0)
62 0040D93C   add         esp,4
63 0040D93F   jmp         $L548+1Ch (0040d95d)
64 24:       case 9:
65 25:           printf("9");break;
66 0040D941   push        offset string "11" (00422020)
67 0040D946   call        printf (004011f0)
68 0040D94B   add         esp,4
69 0040D94E   jmp         $L548+1Ch (0040d95d)
70 26:       default:
71 27:           printf("error");break;
72 0040D950   push        offset string "error" (00422fcc)
73 0040D955   call        printf (004011f0)
74 0040D95A   add         esp,4
75 28:
76 29:       }
77 30:   }

可以很明显看到不一样的地方

有一个ja,跳转至default。然后多了个edx,且将edx归零。而重点在这0040d996mov dl,byte ptr (0040d996)[eax],这句话的意思是将内存单元[eax+40d996]的内容放到dl中。

我们打开这个地址,发现里面是一个个数字

这张表就是小表。这里的09似乎是最多的。当[40d996+eax]为09时,edx*4+40D96Eh就是24+40D96eh。即为40d992。

然后40d992中存放的是40d950。这个地址是default的地址。

小表是两位数字。那假如突然出来一个超过255的case呢。

整个全变了。

 1 4:        switch(x)
 2 5:        {
 3 0040D898   mov         eax,dword ptr [ebp+8]
 4 0040D89B   mov         dword ptr [ebp-4],eax
 5 0040D89E   cmp         dword ptr [ebp-4],104h
 6 0040D8A5   jg          Function+4Dh (0040d8cd)
 7 0040D8A7   cmp         dword ptr [ebp-4],104h
 8 0040D8AE   je          $L540+0Fh (0040d920)
 9 0040D8B0   mov         ecx,dword ptr [ebp-4]
10 0040D8B3   sub         ecx,1
11 0040D8B6   mov         dword ptr [ebp-4],ecx
12 0040D8B9   cmp         dword ptr [ebp-4],8
13 0040D8BD   ja          $L548+0Fh (0040d95c)
14 0040D8C3   mov         edx,dword ptr [ebp-4]
15 0040D8C6   jmp         dword ptr [edx*4+40D97Ah]
16 0040D8CD   jmp         $L548+0Fh (0040d95c)
17 6:        case 1:
18 7:            printf("1");break;
19 0040D8D2   push        offset string "1" (0042203c)
20 0040D8D7   call        printf (004011f0)
21 0040D8DC   add         esp,4
22 0040D8DF   jmp         $L548+1Ch (0040d969)
23 8:        case 2:
24 9:            printf("2");break;
25 0040D8E4   push        offset string "2" (00422038)
26 0040D8E9   call        printf (004011f0)
27 0040D8EE   add         esp,4
28 0040D8F1   jmp         $L548+1Ch (0040d969)
29 10:       case 3:
30 11:           printf("3");break;
31 0040D8F3   push        offset string "3" (00422034)
32 0040D8F8   call        printf (004011f0)
33 0040D8FD   add         esp,4
34 0040D900   jmp         $L548+1Ch (0040d969)
35 12:       case 4:
36 13:           printf("4");break;
37 0040D902   push        offset string "4" (00422030)
38 0040D907   call        printf (004011f0)
39 0040D90C   add         esp,4
40 0040D90F   jmp         $L548+1Ch (0040d969)
41 14:       case 5:
42 15:           printf("5");break;
43 0040D911   push        offset string "5" (0042202c)
44 0040D916   call        printf (004011f0)
45 0040D91B   add         esp,4
46 0040D91E   jmp         $L548+1Ch (0040d969)
47 16:
48 17:       case 260:
49 18:           printf("260");break;
50 0040D920   push        offset string "260" (0042201c)
51 0040D925   call        printf (004011f0)
52 0040D92A   add         esp,4
53 0040D92D   jmp         $L548+1Ch (0040d969)
54 19:
55 20:       case 7:
56 21:           printf("7");break;
57 0040D92F   push        offset string "7" (00422028)
58 0040D934   call        printf (004011f0)
59 0040D939   add         esp,4
60 0040D93C   jmp         $L548+1Ch (0040d969)
61 22:       case 8:
62 23:           printf("8");break;
63 0040D93E   push        offset string "10" (00422024)
64 0040D943   call        printf (004011f0)
65 0040D948   add         esp,4
66 0040D94B   jmp         $L548+1Ch (0040d969)
67 24:       case 9:
68 25:           printf("9");break;
69 0040D94D   push        offset string "11" (00422020)
70 0040D952   call        printf (004011f0)
71 0040D957   add         esp,4
72 0040D95A   jmp         $L548+1Ch (0040d969)
73 26:       default:
74 27:           printf("error");break;
75 0040D95C   push        offset string "error" (00422fcc)
76 0040D961   call        printf (004011f0)
77 0040D966   add         esp,4
78 28:
79 29:       }
80 30:   }

首先把参数放到eax,eax和104h对比(104h==260)。大于等于则跳走。小于则继续走大表。

最后一种

Case4:

整个case完全没有规律。那么将按照二叉树来进行查找每个case的地址。然本人目前还没学到。暂时不多叙述了,学到二叉树时再将其补上。

原文地址:https://www.cnblogs.com/zimudao/p/8618150.html