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

news2025/7/18 22:02:38

Switch语句

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

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

Switch语句的使用

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

在这里插入图片描述

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,如果是则输出对应语句

运行结果

在这里插入图片描述

能够正确判断出所给参数为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并无本质上的区别,都是要依次比较判断条件

此时的流程图为:

在这里插入图片描述
前面的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里存储的内容是什么

在这里插入图片描述
可以观察到从这个地址开始,依次存储了四个地址:

在这里插入图片描述
可以将这里的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中那样依次比较来判断是否要跳转

此时的流程图为:
在这里插入图片描述

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中存储的内容:

在这里插入图片描述

可以看到从地址表开始连续存储了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所对应的地址的,可以将其称之为大表

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

在这里插入图片描述
因为前面对应的代码为mov dl,byte ptr … 所以这里的每项的长度为byte:8字节对应2个十六进制数,在图中就是对应每一小格

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

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

在这里插入图片描述看到这里想必就明白了这个表的作用:存储每个case对应的偏移,每个偏移的宽度为byte,也就是最大为FF

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

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

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

在这里插入图片描述

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

在这里插入图片描述
并且会发现,大表(case地址表)之后紧跟着的就是小表(偏移表)

小总结二

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

此时的流程图为:

在这里插入图片描述

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没有实质性区别

总结

在这里插入图片描述
可以看到,虽然switch分的情况不少,但其反汇编的结果只有3种,无非是:

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

汇总反汇编流程图

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

在这里插入图片描述采用case地址表

在这里插入图片描述
采用case地址表和偏移表

在这里插入图片描述

比较if else和switch case

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

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

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

当switch语句有序且连续且case数量>3时,其运行的效率最高,也解释了开发过程中为什么要使用连续的case

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/444085.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不用科学上网,免费的GPT-4 IDE工具Cursor保姆级使用教程

1、Cursor 编辑器 可以直接官方网站下载&#xff1a;https://www.cursor.so/ &#xff08;这里以Mac为例&#xff09; 这是一款与OpenAI合作并且基于GPT4的新一代辅助编程神器&#xff0c;它支持多种文件类型&#xff0c;支持格式化文本、多种主题、多语言语法高亮、快捷键设…

react-7 组件库 Ant Design Mobile(移动端)

1.安装组件库 npm install --save antd-mobile 常用组件 tabbar 底部导航 Swiper 轮播图&#xff08;走马灯&#xff09; NavBar&#xff08;顶部返回累&#xff09; 配合 Dialog&#xff0c;Toast InfiniteScroll 无限滚动&#xff08;实现下拉刷新&#xff09; Skeleto…

ROS学习第九节——服务通信

1.基本介绍 服务通信较之于话题通信更简单些&#xff0c;理论模型如下图所示&#xff0c;该模型中涉及到三个角色: ROS master(管理者)Server(服务端)Client(客户端) ROS Master 负责保管 Server 和 Client 注册的信息&#xff0c;并匹配话题相同的 Server 与 Client &#…

远程控制电脑的软件哪个比较好用

有多种软件选项可用于远程控制计算机&#xff0c;最适合您的软件选项取决于您的具体需要和要求。 以下是一些最流行的远程控制软件选项及其功能和优势&#xff1a; TeamViewer TeamViewer 是使用最广泛的远程控制软件选项之一。 它具有用户友好的界面&#xff0c;并提供文件传…

Vue核心 Vue简介 初识

Vue核心 Vue简介 初识 1.1.Vue核心 Vue简介 初识 1.1.1.Vue核心 Vue简介 初识 英文官网 中文官网 1.1.2.介绍与描述 Vue 是一套用来动态构建用户界面的渐进式JavaScript框架 构建用户界面&#xff1a;把数据通过某种办法变成用户界面渐进式&#xff1a;Vue可以自底向上逐层…

YOLO V8实战入门篇 | Anaconda3 | ultralytics

目录 一、虚拟环境1.1 Anaconda3 安装1.2 创建适用YOLO V8的虚拟环境 二、YOLO v82.1 YOLO v8简介2.2 下载YOLO v8源码2.3 安装依赖 三、运行目标检测模型 一、虚拟环境 1.1 Anaconda3 安装 https://blog.csdn.net/weixin_42855758/article/details/122795125 参考这个链接&a…

NDK OpenCV 身份证信息离线识别

NDK系列之OpenCV 身份证信息离线识别技术实战&#xff0c;本节主要是通过OpenCV C库&#xff0c;实现身份证信息识别&#xff0c;如身份证号码识别&#xff0c;本节使用到的技术点同样适用于车牌号识别、银行卡号码识别等。 实现效果&#xff1a; 本节主要内容&#xff1a; 1…

数据库底层运行原理之——事务管理器

一般所有关系型数据库内部都有自己的事务机制&#xff0c;进程是如何保证每个查询在自己的事务内执行的&#xff0c;通过这篇文章来简单介绍一下。 我们可以理解为数据库是由多种相互交互的组件构成的&#xff0c;数据库一般可以用如下图形来理解&#xff1a; 事务管理器就是今…

两种方法,计算带地形起伏的地表面积

很多同学会经常计算占地面积, 就会用到投影面积计算和椭球体面积计算; 还有一些,需要去计算表面积, 也就是带地形起伏的地表面积, 这......咋办呢? 我们来介绍两种方法, 计算下面这个区域的地表面积—— 两种方法各有优势, 大家各取所需 方法一:表面体积工具 这…

【hello Linux】进程优先级

目录 1. 基本概念 2. 查看系统进程&#xff1a;&#xff08;包括优先级&#xff09; 2.1 使用命令查看系统进程 2.2 各字段分析 2.3 优先级的修正解释 2.4 使用 top 命令修改优先级 其他概念 Linux&#x1f337; 1. 基本概念 进程的优先权&#xff08; priority&#xff09;&…

Java版企业电子招投标系统源代码之电子招投标系统建设的重点和未来趋势

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及…

网工神器:PNETLab模拟器踩坑过程(一)

0、前言 由于工作需要&#xff0c;想测试一下SD-WAN&#xff0c;手边既没有测试环境又没有测试设备。突然想到为什么不用模拟器测试。经过我一番操作好像发现了新大陆&#xff0c;没想到模拟器的世界发生了翻天覆地的变化。真是“一日学习一日功&#xff0c;一日不学十日空”。…

【IoT】以一款实际产品为例,来谈谈如何做商业计划分析

本篇内容以笔者早期刚转型做产品时,实际负责的一款产品为例,来谈谈如何做产品的商业计划分析。 首先简单介绍一下这款产品: 这是一款电子便签产品,目的是为了替换纸质便签,增加一些智能化的提醒控制。 该产品通过蓝牙与手机端连接,应用端配置好提醒信息后一键同步至产…

鼠标悬停发光按钮,流转边框

提示&#xff1a;css 动画实现&#xff0c;鼠标悬停发光按钮&#xff0c;流转边框。鼠标border可以旋转 前言 提示&#xff1a;以下是本篇文章的代码内容,供大家参考,相互学习 一、html代码 <!DOCTYPE html> <html><head><meta http-equiv"content…

企业信息化建设该怎么做?方向和手段都在这了

企业信息化建设该怎么做&#xff1f; 如果现在是十年前&#xff0c;我一定会说&#xff0c;做信息化需要寻找熟悉不同编程语言、有经验的程序员。 但是现在&#xff0c;如果不是特别复杂的信息化系统&#xff0c;其实公司完全可以使用零代码平台自主开发&#xff0c;不需要再…

[计算机图形学]光线追踪:加速结构(前瞻预习/复习回顾)

一、前言 上篇我们提到了&#xff0c;如果在光线追踪中&#xff0c;我们真的用每个像素发出的光线&#xff0c;以及在场景中弹射之后的光线与场景中的许多模型的上千万个三角形求交那将是一个非常慢的计算过程&#xff0c;所以&#xff0c;本篇我们将介绍一些加速结构来加速这个…

【FTP服务】

目录 一、FTP服务二、FTP服务器安装配置FTP服务的安装匿名访问开启防火墙设置本地用户修改配置文件 以图形化的格式来写入文件 三、设置白名单&#xff0c;黑名单用户 一、FTP服务 作用: 是用来传输文件的协议 端口: FTP服务器默认使用TCP协议的20、21端口与客户端进行通信 2…

OpenHarmony生态贡献获肯定,华秋践行加速硬件创业初心

4月19日,以“开源正当时,共赢新未来”为主题的开放原子开源基金会OpenHarmony开发者大会2023(以下简称“大会”)成功举办。大会现场,来自开放原子开源基金会和OpenHarmony项目的领导与专家、以及共建单位、行业伙伴和社区开发者们共聚一堂。值得信赖的电子产业一站式服务平台华…

【软件测试】四面成功上岸美团

最后&#xff0c;总结一下个人认为比较重要的知识点&#xff1a;接口自动化测试 &#xff1a;测试框架&#xff0c;多个有关联的接口的用例编写&#xff0c;用例的组织及存储&#xff0c;接口测试的覆盖率&#xff0c;RESTAssured 的封装等。UI 自动化测试 &#xff1a;iOS 和 …

二维码+互联网云技术在中建二局施工项目管理中的应用实践

中建二局&#xff08;全称&#xff1a;中国建筑第二工程局有限公司&#xff09;是世界500强企业—中国建筑股份有限公司的全资子公司&#xff0c;是集房建、基建、核电、火电、风电等多种建设和投资相融合的、国内最具综合实力的大型国有企业集团公司。中建二局具有土木建筑、设…