Windows逆向安全(一)之基础知识(十三)

这篇具有很好参考价值的文章主要介绍了Windows逆向安全(一)之基础知识(十三)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Switch语句

先前讲了分支结构的if else形式,除此之外还有一种分支结构:switch

此次就来以反汇编的角度研究switch语句,并与if else进行比较

Switch语句的使用

有关Switch语句在vc++6.0中生成的反汇编可分为4种情况,这4种情况的区分在于case的不同

Windows逆向安全(一)之基础知识(十三)

case数量<=3

代码

#include "stdafx.h"

void MySwitch(int x){        
        switch(x) {
        case 1:
                printf("num is 1\n");
                break;
        case 2:
                printf("num is 2\n");
                break;
        case 3:
                printf("num is 3\n");
                break;
        default:
                printf("no cases match\n");
                break;
        }
}

int main(int argc, char* argv[])
{
        MySwitch(2);
        return 0;
}

switch(表达式)中,表达式应该为整数类型:char short int long,其它类型诸如:float、double等类型均不可以

switch搭配case使用,case里如果没有添加break语句则会继续向下执行下面的case

default语句可以没有,如果所有case都不匹配会默认执行default语句

上面的代码为,判断参数是否为1或2或3,如果是则输出对应语句

运行结果

Windows逆向安全(一)之基础知识(十三)

能够正确判断出所给参数为2

反汇编代码

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   cmp         dword ptr [ebp-4],1
0040D7B2   je          MySwitch+32h (0040d7c2)
0040D7B4   cmp         dword ptr [ebp-4],2
0040D7B8   je          MySwitch+41h (0040d7d1)
0040D7BA   cmp         dword ptr [ebp-4],3
0040D7BE   je          MySwitch+50h (0040d7e0)
0040D7C0   jmp         MySwitch+5Fh (0040d7ef)
11:       case 1:
12:           printf("num is 1\n");
0040D7C2   push        offset string "num is 1\n" (00422fc4)
0040D7C7   call        printf (00401060)
0040D7CC   add         esp,4
13:           break;
0040D7CF   jmp         MySwitch+6Ch (0040d7fc)
14:       case 2:
15:           printf("num is 2\n");
0040D7D1   push        offset string "num is 2\n" (00422fb8)
0040D7D6   call        printf (00401060)
0040D7DB   add         esp,4
16:           break;
0040D7DE   jmp         MySwitch+6Ch (0040d7fc)
17:       case 3:
18:           printf("num is 3\n");
0040D7E0   push        offset string "num is 3\n" (00422fac)
0040D7E5   call        printf (00401060)
0040D7EA   add         esp,4
19:           break;
0040D7ED   jmp         MySwitch+6Ch (0040d7fc)
20:       default:
21:           printf("no cases match\n");
0040D7EF   push        offset string "Hello World!\n" (0042201c)
0040D7F4   call        printf (00401060)
0040D7F9   add         esp,4
22:           break;
23:       }
24:   }

反汇编分析

1.反汇编代码为将参数x的值赋给eax

0040D7A8   mov         eax,dword ptr [ebp+8]

2.将eax的值放入堆栈中

0040D7AB   mov         dword ptr [ebp-4],eax

3.将前面放入堆栈中的eax拿出来和第1个case中的条件进行比较(也就是比较参数x和case)

0040D7AE   cmp         dword ptr [ebp-4],1

4.判断是否要跳转,je:jump equal,前面比较的两个数相同则跳转,跳转的地址为case 1对应的地址

0040D7B2   je          MySwitch+32h (0040d7c2)

5.如果没有跳转则继续将参数和第2个case中的条件进行比较

0040D7B4   cmp         dword ptr [ebp-4],2

6.依旧是根据比较的结果判断是否要跳转,跳转的地址为case 2对应的地址

0040D7B8   je          MySwitch+41h (0040d7d1)

7.如果没有跳转则继续将参数和第3个case中的条件进行比较

0040D7BA   cmp         dword ptr [ebp-4],3

8.依旧是根据比较的结果判断是否要跳转,跳转的地址为case 3对应的地址

0040D7BE   je          MySwitch+50h (0040d7e0)

9.如果没有跳转则绝对跳转到default:

0040D7C0   jmp         MySwitch+5Fh (0040d7ef)

default:

20:       default:
21:           printf("no cases match\n");
0040D7EF   push        offset string "Hello World!\n" (0042201c)
0040D7F4   call        printf (00401060)
0040D7F9   add         esp,4
22:           break;

下面的内容就是 case 1,case 2,case 3了

可以注意到,case里面的break都对应为跳出switch,而default里的break因为下面就已经是退出switch所以没有生成对应的汇编代码

case1里的break

13:           break;
0040D7CF   jmp         MySwitch+6Ch (0040d7fc)

case2里的break

16:           break;
0040D7DE   jmp         MySwitch+6Ch (0040d7fc)

case3里的break

19:           break;
0040D7ED   jmp         MySwitch+6Ch (0040d7fc)

小总结

通过上面的分析,发现此时(switch 中的case数量≤3时)的反汇编代码和if else并无本质上的区别,都是要依次比较判断条件

此时的流程图为:

Windows逆向安全(一)之基础知识(十三)
前面的switch case中 case的数量只有3个,看起来和if else并无太大区别,接下来看看当case数量大于3时的情况

case数量>3且有序连续

代码

void MySwitch(int x){
        switch(x) {
        case 1:
                printf("num is 1\n");
                break;
        case 2:
                printf("num is 2\n");
                break;
        case 3:
                printf("num is 3\n");
                break;
        case 4:
                printf("num is 4\n");
                break;
        default:
                printf("no cases match\n");
                break;
        }
}

代码并没有太大的改动,只是简单得为上面的代码再添加一个case 4的情形即可,运行结果自然没有变化,也就不再贴出

反汇编代码

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   mov         ecx,dword ptr [ebp-4]
0040D7B1   sub         ecx,1
0040D7B4   mov         dword ptr [ebp-4],ecx
0040D7B7   cmp         dword ptr [ebp-4],3
0040D7BB   ja          $L539+0Fh (0040d803)
0040D7BD   mov         edx,dword ptr [ebp-4]
0040D7C0   jmp         dword ptr [edx*4+40D821h]
11:       case 1:
12:           printf("num is 1\n");
0040D7C7   push        offset string "num is 1\n" (00422fd0)
0040D7CC   call        printf (00401060)
0040D7D1   add         esp,4
13:           break;
0040D7D4   jmp         $L539+1Ch (0040d810)
14:       case 2:
15:           printf("num is 2\n");
0040D7D6   push        offset string "num is 2\n" (00422fc4)
0040D7DB   call        printf (00401060)
0040D7E0   add         esp,4
16:           break;
0040D7E3   jmp         $L539+1Ch (0040d810)
17:       case 3:
18:           printf("num is 3\n");
0040D7E5   push        offset string "num is 3\n" (00422fb8)
0040D7EA   call        printf (00401060)
0040D7EF   add         esp,4
19:           break;
0040D7F2   jmp         $L539+1Ch (0040d810)
20:       case 4:
21:           printf("num is 4\n");
0040D7F4   push        offset string "num is 4\n" (00422fac)
0040D7F9   call        printf (00401060)
0040D7FE   add         esp,4
22:           break;
0040D801   jmp         $L539+1Ch (0040d810)
23:       default:
24:           printf("no cases match\n");
0040D803   push        offset string "Hello World!\n" (0042201c)
0040D808   call        printf (00401060)
0040D80D   add         esp,4
25:           break;
26:       }
27:   }

很明显地观察到先前开头的一串比较语句不见了,接下来开始分析

反汇编分析

1.头两条语句和先前没有什么不同,都是将参数x赋值给eax,然后将eax保存到堆栈中;总得来看就是把参数先保存到堆栈里

0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax

2.第三条语句就开始和之前不同了,这里是将前面保存到堆栈里的参数x再赋值给ecx

0040D7AE   mov         ecx,dword ptr [ebp-4]

3.这里将ecx减少1

为什么要减1?是为了后面的比较,后面会说明

为什么减的是1?要注意到这里减的1实则是case中的最小值,在此次案例中就是min{1,2,3,4}=1

0040D7B1   sub         ecx,1

4.将前面的ecx,也就是参数x-1的值覆盖前面保存的参数

0040D7B4   mov         dword ptr [ebp-4],ecx

5.将参数x-1的值和3进行比较

0040D7B7   cmp         dword ptr [ebp-4],3

6.ja指令:jump above,大于时跳转(无符号),也就是比较参数x-1和3(case中的最大差值),最大差值就是最大值减最小值,此案例中就是4-1=3

如果x-1>3则跳转,如果前面参数没有减1的话,就变成了直接判断x>3,如果此时x=4也会产生跳转,不符合程序的逻辑(原本x=4应该对应跳转到case 4)

注意到这里采用的是无符号比较,而不采用有符号比较指令jg:jump greater,大于时跳转(有符号),为什么?

这里的比较代码其实就是判断参数是否在(case中的最小值,case中的最大值)这个区间内

当参数小于case中的最小值时,前面的sub ecx,case中的最小值就后就会产生下溢,此时将其看作无符号数就会相当大,一定会大于case中的最大差值,举个简单的例子,假如此时的参数为0,0-1 = -1对应的是十六进制为FFFF FFFF,将其看作无符号数就是4294967295

0040D7BB   ja          $L539+0Fh (0040d803)

跳转的地址为:0040d803,对应为default的地址

23:       default:
24:           printf("no cases match\n");
0040D803   push        offset string "Hello World!\n" (0042201c)
0040D808   call        printf (00401060)
0040D80D   add         esp,4
25:           break;

7.如果前面没有跳转,这里则又将前面保存的参数-1的值取了出来,并赋值给edx

0040D7BD   mov         edx,dword ptr [ebp-4]

8.这条语句就是拉开与if else性能的关键,绝对跳转语句

0040D7C0   jmp         dword ptr [edx*4+40D821h]

先不看语句中的edx*4,先看看40D821h里存储的内容是什么

Windows逆向安全(一)之基础知识(十三)
可以观察到从这个地址开始,依次存储了四个地址:

Windows逆向安全(一)之基础知识(十三)
可以将这里的40D821h看作一个表的首地址,这个表中存储了各个case对应的地址,并且每个地址之前的间距为4

现在结合前面的edx*4就不难判断出这里是通过jmp [存储case地址表的首地址+偏移×4]来跳转到对应的case,因此也减少了cmp的比较次数,提高了效率;这里就要说明前面将参数减1的真正原因了,当x为1时对应的case1首地址的偏移为0,所以需要让edx=x-1=0才能准确跳转到对应的位置,所以将参数减1是为了配合偏移寻址

剩下对应case的代码和上面并没有什么区别,就不再赘述

小总结

通过上面的分析,发现此时(switch 中的case数量>3时)的反汇编代码和if else的差别就体现出来了

有一点要重点强调的是这里关于switch中case数量>3中的这个3只针对当前使用的vc++6.0编译器,不同的编译器对于switch产生的汇编指令可能不大相同,但到达一定条件后一般都会采用到case地址表首地址+偏移的方法

此时是将参数的值减case中的最小值,然后判断这个减完的数值是否大于case中的最大差值

如果大于则直接跳转到default

如果小于或等于则通过jmp [存储case地址表的首地址+偏移×4]的方式直接跳转到对应case的地址,而不再像if else中那样依次比较来判断是否要跳转

此时的流程图为:
Windows逆向安全(一)之基础知识(十三)

case数量>3且无序连续

前面的代码中,case是按照1、2、3、4有序下来的,如果将1、2、3、4改为2、3、4、1或其他连续但顺序不同的情况时会如何?

下面以2、3、4、1为例进行分析

代码

switch(x) {
case 2:
        printf("num is 2\n");
        break;
case 3:
        printf("num is 3\n");
        break;
case 4:
        printf("num is 4\n");
        break;
case 1:
        printf("num is 1\n");
        break;
default:
        printf("no cases match\n");
        break;
}

简单地调换了一下case语句的顺序,观察其反汇编

反汇编代码

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   mov         ecx,dword ptr [ebp-4]
0040D7B1   sub         ecx,1
0040D7B4   mov         dword ptr [ebp-4],ecx
0040D7B7   cmp         dword ptr [ebp-4],3
0040D7BB   ja          $L539+0Fh (0040d803)
0040D7BD   mov         edx,dword ptr [ebp-4]
0040D7C0   jmp         dword ptr [edx*4+40D821h]
11:       case 2:
12:           printf("num is 2\n");
0040D7C7   push        offset string "num is 1\n" (00422fd0)
0040D7CC   call        printf (00401060)
0040D7D1   add         esp,4
13:           break;
0040D7D4   jmp         $L539+1Ch (0040d810)
14:       case 3:
15:           printf("num is 3\n");
0040D7D6   push        offset string "num is 2\n" (00422fc4)
0040D7DB   call        printf (00401060)
0040D7E0   add         esp,4
16:           break;
0040D7E3   jmp         $L539+1Ch (0040d810)
17:       case 4:
18:           printf("num is 4\n");
0040D7E5   push        offset string "num is 3\n" (00422fb8)
0040D7EA   call        printf (00401060)
0040D7EF   add         esp,4
19:           break;
0040D7F2   jmp         $L539+1Ch (0040d810)
20:       case 1:
21:           printf("num is 1\n");
0040D7F4   push        offset string "num is 271\n" (00422fac)
0040D7F9   call        printf (00401060)
0040D7FE   add         esp,4
22:           break;
0040D801   jmp         $L539+1Ch (0040d810)
23:       default:
24:           printf("no cases match\n");
0040D803   push        offset string "no cases match\n" (0042201c)
0040D808   call        printf (00401060)
0040D80D   add         esp,4
25:           break;
26:       }
27:   }

反汇编分析

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   mov         ecx,dword ptr [ebp-4]
0040D7B1   sub         ecx,1
0040D7B4   mov         dword ptr [ebp-4],ecx
0040D7B7   cmp         dword ptr [ebp-4],3
0040D7BB   ja          $L539+0Fh (0040d803)
0040D7BD   mov         edx,dword ptr [ebp-4]
0040D7C0   jmp         dword ptr [edx*4+40D821h]

将这里的反汇编代码与先前的反汇编代码进行对比,发现并没有差别,也就是说当case连续时顺序并不影响其反汇编代码的生成结果

case数量>3但有序不连续

因为前面的案例中case的特点为都为连续(都是从1到4)中间没有间隔其它数字,现在来个讲讲不连续的例子

这里将原本的1~4改为1~10,并且在中间跳过一个case 5,使其不连续

注意此时只跳过了一个case,属于跳过数较少的情况

代码

switch(x) {
        case 1:
                printf("num is 1\n");
                break;
        case 2:
                printf("num is 2\n");
                break;
        case 3:
                printf("num is 3\n");
                break;
        case 4:
                printf("num is 4\n");
                break;
    //这里少了case 5    
        case 6:
                printf("num is 6\n");
                break;
        case 7:
                printf("num is 7\n");
                break;
        case 8:
                printf("num is 8\n");
                break;
        case 9:
                printf("num is 9\n");
                break;
        case 10:
                printf("num is 10\n");
                break;
        default:
                printf("no cases match\n");
                break;
        }

反汇编代码

因为后面case的代码和之前并无不同,所以这里只给出了前面的关键语句

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   mov         ecx,dword ptr [ebp-4]
0040D7B1   sub         ecx,1
0040D7B4   mov         dword ptr [ebp-4],ecx
0040D7B7   cmp         dword ptr [ebp-4],9
0040D7BB   ja          $L549+0Fh (0040d855)
0040D7C1   mov         edx,dword ptr [ebp-4]
0040D7C4   jmp         dword ptr [edx*4+40D873h]

省略了中间的case的代码

0040D853   jmp         $L549+1Ch (0040d862)
38:       default:
39:           printf("no cases match\n");
0040D855   push        offset string "Hello World!\n" (0042201c)
0040D85A   call        printf (00401060)
0040D85F   add         esp,4
40:           break;
41:       }
42:   }

反汇编分析

可以看到此时的代码依旧符合先前的小总结,但是这里就有了个问题,在case地址表中对应的空缺的case 5里存储的是什么?

这里的case地址表首地址为40D873h

0040D7C4   jmp         dword ptr [edx*4+40D873h]

查看40D873h中存储的内容:

Windows逆向安全(一)之基础知识(十三)

可以看到从地址表开始连续存储了10个case对应的地址,和代码中的case数相匹配

按顺序找下来,查看对应case 5的地址存储的是什么:

对应的地址为40D873+4(5-1)=40D883,这里的计算是根据上面的edx\4+40D873h所得

0040D883这个地址里存储的数据是0040D855

而0040D855这个地址对应的则是default的地址

38:       default:
39:           printf("no cases match\n");
0040D855   push        offset string "Hello World!\n" (0042201c)

小总结

当case有序,但中间跳过了较少的case情况时,依旧会按照前面有序连续的模式进行处理,只不过期间会浪费缺少的case的空间,这些被浪费的空间会被default的地址填充,即被跳过的case在case地址表中的地址会被设置为default的地址

代码二

前面的例子是只跳过了一个case的情况,于是也只浪费了一个case的空间,那么当跳过了很多case的情况下,也就意味着会浪费很多的空间,但是实际上,编译器并没有那么愚蠢,当跳过的case超过了一定限度,或者说是浪费的空间到达一定的限度后,就会采取另一种方式生成对应的反汇编代码

修改前面的代码,跳过3、4、5、6、7、8,共6个case

switch(x) {
case 1:
        printf("num is 1\n");
        break;
case 2:
        printf("num is 2\n");
        break;

//这里跳过了3、4、5、6、7、8,共6个case

case 9:
        printf("num is 9\n");
        break;
case 10:
        printf("num is 10\n");
        break;
default:
        printf("no cases match\n");
        break;
}

反汇编代码二

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   mov         ecx,dword ptr [ebp-4]
0040D7B1   sub         ecx,1
0040D7B4   mov         dword ptr [ebp-4],ecx
0040D7B7   cmp         dword ptr [ebp-4],9
0040D7BB   ja          $L539+0Fh (0040d80b)
0040D7BD   mov         eax,dword ptr [ebp-4]
0040D7C0   xor         edx,edx
0040D7C2   mov         dl,byte ptr  (0040d83d)[eax]
0040D7C8   jmp         dword ptr [edx*4+40D829h]

省略了中间case的代码

27:           printf("no cases match\n");
0040D80B   push        offset string "Hello World!\n" (0042201c)
0040D810   call        printf (00401060)
0040D815   add         esp,4
28:           break;
29:       }
30:   }

反汇编分析二

前面的反汇编代码与先前一致,不同之处在于判断完参数x是否在(case中的最小值,case中的最大值)这个区间内后

0040D7BD   mov         eax,dword ptr [ebp-4]
0040D7C0   xor         edx,edx
0040D7C2   mov         dl,byte ptr  (0040d83d)[eax]
0040D7C8   jmp         dword ptr [edx*4+40D829h]

1.首先是将先前参数x-1的值赋值给eax

0040D7BD   mov         eax,dword ptr [ebp-4]

2.然后是将edx清零

0040D7C0   xor         edx,edx

3.接下来这句就比较关键了,是将0040d83d+eax对应地址里的内容取出byte赋值给dl

0040D7C2   mov         dl,byte ptr  (0040d83d)[eax]

这里的语句貌似不符合汇编代码的规范,实际上是VC6的编译器为了方便我们查看所生成的

实际的代码对应为:

 mov         dl,byte ptr  ds:[eax+0040d83d]

这个形式是不是似曾相识,都是一个地址+偏移来取得数据

这里就要引入第二个表的概念了,先前的那个表是用来存储所有case所对应的地址的,可以将其称之为大表

这里的第二个表可以将其称为小表,来查看小表中存储的数据:

Windows逆向安全(一)之基础知识(十三)
因为前面对应的代码为mov dl,byte ptr … 所以这里的每项的长度为byte:8字节对应2个十六进制数,在图中就是对应每一小格

可以观察到图中共有10格是有数值的(不为CC),正好对应case的1~10

并且可以观察到从第三格开始一直到第八格存储的数字都是04(对应了跳过的case)

Windows逆向安全(一)之基础知识(十三)看到这里想必就明白了这个表的作用:存储每个case对应的偏移,每个偏移的宽度为byte,也就是最大为FF

4.这里和先前一样,通过存储case地址表的首地址+偏移×4(查询大表)来跳转到对应的case地址,不同之处在于偏移是从小表中取出的

0040D7C8   jmp         dword ptr [edx*4+40D829h]

此时再观察大表中的内容:

Windows逆向安全(一)之基础知识(十三)

可以发现大表中只存储了5个地址:

Windows逆向安全(一)之基础知识(十三)
并且会发现,大表(case地址表)之后紧跟着的就是小表(偏移表)

小总结二

当case有序,但中间跳过了较多的case时,就会再使用一个小表(偏移表)来存储偏移以达到节省空间的目的,(每个偏移只占1个字节,但如果直接存储地址的话则要占用4个字节)

此时的流程图为:

Windows逆向安全(一)之基础知识(十三)

case数量>3但无序不连续

前面的代码中是按照1、2、9、10有序地下来,将顺序打乱后再观察:

代码

switch(x) {

case 9:
        printf("num is 9\n");
        break;
case 1:
        printf("num is 1\n");
        break;
case 10:
        printf("num is 10\n");
        break;
case 2:
        printf("num is 2\n");
        break;
default:
        printf("no cases match\n");
        break;
}

反汇编代码

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   mov         ecx,dword ptr [ebp-4]
0040D7B1   sub         ecx,1
0040D7B4   mov         dword ptr [ebp-4],ecx
0040D7B7   cmp         dword ptr [ebp-4],9
0040D7BB   ja          $L539+0Fh (0040d80b)
0040D7BD   mov         eax,dword ptr [ebp-4]
0040D7C0   xor         edx,edx
0040D7C2   mov         dl,byte ptr  (0040d83d)[eax]
0040D7C8   jmp         dword ptr [edx*4+40D829h]

省略了中间case的代码

24:       default:
25:           printf("no cases match\n");
0040D80B   push        offset string "Hello World!\n" (0042201c)
0040D810   call        printf (00401060)
0040D815   add         esp,4
26:           break;
27:       }
28:   }

反汇编分析

可以看到,和先前有序的代码并无差别,也就是说当case不连续时顺序也并不影响其反汇编代码的生成结果

代码二

前面会注意到偏移表中最大取值为FF,也就是说最大偏移至多不能超过FF,同时也表明case中的最大差值不能超过FF

那么当最大差值为FF时反汇编代码又会如何?

修改原本case中的最大值10为256,使其最大差值正好为255,对应十六进制为FF

switch(x) {

        case 9:
                printf("num is 9\n");
                break;
        case 1:
                printf("num is 1\n");
                break;
        case 256:
                printf("num is 256\n");
                break;
        case 2:
                printf("num is 2\n");
                break;
        default:
                printf("no cases match\n");
                break;
        }

反汇编代码二

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   cmp         dword ptr [ebp-4],9
0040D7B2   jg          MySwitch+38h (0040d7c8)
0040D7B4   cmp         dword ptr [ebp-4],9
0040D7B8   je          MySwitch+43h (0040d7d3)
0040D7BA   cmp         dword ptr [ebp-4],1
0040D7BE   je          MySwitch+52h (0040d7e2)
0040D7C0   cmp         dword ptr [ebp-4],2
0040D7C4   je          MySwitch+70h (0040d800)
0040D7C6   jmp         MySwitch+7Fh (0040d80f)
0040D7C8   cmp         dword ptr [ebp-4],100h
0040D7CF   je          MySwitch+61h (0040d7f1)
0040D7D1   jmp         MySwitch+7Fh (0040d80f)

反汇编分析二

这里不难看出,此时的情形和case数量<=3的情况一样,都是采用了多次判断跳转,和if else本质并无差别

反汇编对比

如果将前面的case 256改为case 255,则反汇编代码又为:

10:       switch(x) {
0040D7A8   mov         eax,dword ptr [ebp+8]
0040D7AB   mov         dword ptr [ebp-4],eax
0040D7AE   mov         ecx,dword ptr [ebp-4]
0040D7B1   sub         ecx,1
0040D7B4   mov         dword ptr [ebp-4],ecx
0040D7B7   cmp         dword ptr [ebp-4],0FEh
0040D7BE   ja          $L539+0Fh (0040d80e)
0040D7C0   mov         eax,dword ptr [ebp-4]
0040D7C3   xor         edx,edx
0040D7C5   mov         dl,byte ptr  (0040d840)[eax]
0040D7CB   jmp         dword ptr [edx*4+40D82Ch]

小总结

当case不连续时顺序也并不影响其反汇编代码的生成结果

当case中的最大差值大于等于FF时,汇编会转变为采用了多次判断跳转的方式,变得和if else没有实质性区别

总结

Windows逆向安全(一)之基础知识(十三)
可以看到,虽然switch分的情况不少,但其反汇编的结果只有3种,无非是:

  1. 采用多个比较和jcc跳转指令
  2. 采用case地址表,直接通过该表首地址+偏移跳转到对应的地址
  3. 采用case地址表的同时,也使用偏移表,两表共同作用来找到地址

汇总反汇编流程图

采用多个比较和jcc跳转指令

Windows逆向安全(一)之基础知识(十三)采用case地址表

Windows逆向安全(一)之基础知识(十三)
采用case地址表和偏移表

Windows逆向安全(一)之基础知识(十三)

比较if else和switch case

通过前面的分析可以得出结论:

当switch语句中的case数量≤3或case中的最大数值和最小数值相差≥6时,两种语句的效率几乎相同

其它情况下一般为switch语句的效率更高

当switch语句有序且连续且case数量>3时,其运行的效率最高,也解释了开发过程中为什么要使用连续的case文章来源地址https://www.toymoban.com/news/detail-430178.html

到了这里,关于Windows逆向安全(一)之基础知识(十三)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Windows逆向安全(一)之基础知识(十四)

    一般关于指针的解释都离不开地址。这里先暂且忘记这个概念 指针其实也是一种数据类型,和先前学习的int float等数据类型没有实质上的区别,只不过这个数据类型是在先前学习的所有数据类型后面加上若干个*号,如char *,int *等等,这种数据类型被称为指针 任意类型后面

    2024年02月13日
    浏览(37)
  • Windows安全基础:认证基础知识

    目录 Windows凭据 Windows访问控制模型 访问令牌: 安全标识符(SID): 安全描述符: 令牌安全防御 1、禁止域管理员异机登录 2、开启“审核进程创建”策略 SSPI(Security Support Provider Interface ,安全支持提供程序接口):是windows操作系统中用于执行各种安全相关操作的公用API,

    2024年02月02日
    浏览(58)
  • 以太网基础知识——PHY,MAC,MII,switch

    在以太网开发中,常常会听到一些专业名词,例如PHY,MAC,MII,switch,下面是解释 PHY PHY 是物理接口收发器,它实现物理层。包括 MII/GMII (介质独立接口) 子层、PCS (物理编码子层) 、PMA (物理介质附加) 子层、PMD (物理介质相关) 子层、MDI 子层。定义了数据传送与接收所需要的

    2024年02月04日
    浏览(47)
  • 高级篇十三、事务基础知识

    事务是数据库区别文件系统的重要特性之一,当我们有了事务会让数据库始终保持一致性,同时我们还能通过事务的机制恢复到某个时间点,这样可以保证已提交到数据库的修改不会因为系统的崩溃而丢失! 1.1 存储引擎支持情况 SHOW ENGINES 命令来查看当前 MySQL 支持的存储引

    2024年02月04日
    浏览(33)
  • 【STM32】基础知识 第十三课 中断

    今天小白我将带领大家详细介绍 STM32 单片机中的中断处理机制, 包括中断的基本概念, 配置和使用方法. 中断在嵌入式系统中扮演着重要角色, 使系统能够快速响应外部事件, 提高系统的实时性和效率. 中断 (Interrupt) 是单片机和其他嵌入式系统中的一种重要机制, 用于在发生特定

    2024年02月17日
    浏览(60)
  • 【前端知识】React 基础巩固(十三)——列表渲染

    在 React 中没有像 Vue 模块语法中的 v-for 指令,而是需要我们通过 JS 代码的方式组织数据,转成 JSX 在 React 中,展示列表最多的方式就是使用数组的 map 高阶函数 在展示数组前,有时会进行一些处理 过滤一些内容(filter 函数) 截取数组中的一部分内容(slice 函数) 列表中的

    2024年02月10日
    浏览(54)
  • 计算机视觉基础知识(十三)--推理和训练

    Supervisied Learning 输入的数据为训练数据; 模型在训练过程中进行预期判断; 判断错误的话进行修正; 直到模型判断预期达到要求的精确性; 关键方法为分类和回归 逻辑回归(Logistic Regression) BP神经网络(Back Propagation Neural Network) Unsupervisied Learning 没有训练数据; 模型基于无标记数据

    2024年02月22日
    浏览(141)
  • 【前端知识】React 基础巩固(四十三)——Effect Hook

    Effect Hook 用来完成一些类似class中生命周期的功能。 在使用类组件时,不管是渲染、网路请求还是操作DOM,其逻辑和代码是杂糅在一起的。例如我们希望把计数器结果显示在标签上,在类组件中,我们通过生命周期进行实现,如下所示: 在函数组件中,我们可以利用useEffec

    2024年02月14日
    浏览(71)
  • 【前端知识】React 基础巩固(三十三)——Redux的使用详解

    针对 React 基础巩固(三十二) 中的案例,我们希望抽取页面中共有的代码(例如下方的代码),使用高阶组件统一拦截。 为了让react和redux产生联系,安装一款工具 react-redux 使用 react-redux ,在index.js中统一注入store 新建about.js页面,通过 react-redux 引入store 在App.jsx中引入新的

    2024年02月15日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包