吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2424|回复: 1
收起左侧

[会员申请] 申请会员ID:zhangcod

[复制链接]
吾爱游客  发表于 2025-2-15 21:27
1.申请ID:zhancod
2.个人邮箱: 3438588663@qq.com
3.原创技术文章:个人博客:https://blog.csdn.net/2401_85828611?spm=1011.2435.3001.5343 原创技术文章:https://blog.csdn.net/2401_85828611/article/details/143340354?spm=1001.2014.3001.5502
本人登录的界面:https://qr61.cn/ouVXU0/qQeCtOY
图片粘贴过去报错,有非法字符,这里提供外链跳转

目录

1.前置知识

2.C语言代码

运行环境:VS2010+Win32+Debug

sourcecode1

sourcecode2

3.反汇编分析

sourcecode1的反汇编代码

不带机器码

带机器码

逐条分析

1.向提前开辟好的栈帧空间压入几个寄存器的值(push保存关键的寄存器的值,pop在main函数的最后恢复关键寄存器的值)

2.变量的定义,赋初值

注意

3.将参数num的值写入内存

4.为等值比较指令做准备

5.★★★重点指令分析(本文的核心)★★★

cmp指令(全称compare)(用于等值比较)

ja指令(全称jump if above)

下面将会讲解本文的核心部分:跳转表

提问



IDA反汇编分析

6.程序main函数的返回,空间被销毁,交还给操作系统(非本文重点)

7.题外话:其他细节说明

附:完整流程的栈区图

思考题

sourcecode2的反汇编代码

和sourcecode1的不同点

4.总结switch&case的执行原理

5.题外话

6.if&else的处理方式

C语言代码

反汇编代码

带机器码

不带机器码

分析

7.switch的分支较少的情况分析

C语言代码

反汇编代码

分析
1.前置知识

switch的用法参见22.【C语言】选择结构之switch

附:《C语言程序与设计 第四版》 对switch的解释

8995991a9e1443859c92ed5b60f2e0e7.jpeg

注意画红线的等值比较,下面会重点讲
2.C语言代码
运行环境:VS2010+Win32+Debug

修改项目属性的两个地方,减少反汇编代码

68806fb6a2f3478a90e1f28b3b18c4d8.png
sourcecode1

    int main()
    {
      int num = 2;
      int tmp=0;
      switch (num)
      {
        case 1:tmp=1;
        case 2:tmp=2;
        case 3:tmp=3;
        case 4:tmp=4;
        default:tmp=5;
      }
      return tmp;
    }

按switch的语法分析, 依次执行int num = 2;,int tmp = 0;,tmp=2;tmp=3;tmp=4;tmp=5;最后返回tmp的值
sourcecode2

    int main()
    {
      int num = 2;
      int tmp=0;
      switch (num)
      {
        case 1:tmp=1;break;
        case 2:tmp=2;break;
        case 3:tmp=3;break;
        case 4:tmp=4;break;
        default:tmp=5;
      }
      return tmp;
    }

按switch的语法分析, 依次执行int num = 2;,int tmp = 0;,tmp=2;,break;最后返回tmp的值
3.反汇编分析

注:视频调试演示

C语言的switch结构的反汇编分析文章的配套视频

点我跳转去观看

按F11进入调试模式,右击转到反汇编

d08ed8d49cc24d2484e45728111d37eb.png
sourcecode1的反汇编代码
不带机器码

    int main()
    {
     push        ebp  
     mov         ebp,esp  
     sub         esp,4Ch  
     push        ebx  
     push        esi  
     push        edi  
      int num = 2;
     mov         dword ptr [num],2  
      int tmp=0;
     mov         dword ptr [tmp],0  
      switch (num)
     mov         eax,dword ptr [num]  
     mov         dword ptr [ebp-4Ch],eax  
     mov         ecx,dword ptr [ebp-4Ch]  
     sub         ecx,1  
     mov         dword ptr [ebp-4Ch],ecx  
     cmp         dword ptr [ebp-4Ch],3  
     ja          $LN2+7 (0B712A2h)  
     mov         edx,dword ptr [ebp-4Ch]  
     jmp         dword ptr  (0B712B4h)[edx*4]  
      {
        case 1:tmp=1;
     mov         dword ptr [tmp],1  
        case 2:tmp=2;
     mov         dword ptr [tmp],2  
        case 3:tmp=3;
     mov         dword ptr [tmp],3  
        case 4:tmp=4;
     mov         dword ptr [tmp],4  
        default:tmp=5;
     mov         dword ptr [tmp],5  
      }
      return tmp;
     mov         eax,dword ptr [tmp]  
    }
     pop         edi  
     pop         esi  
     pop         ebx  
     mov         esp,ebp  
     pop         ebp  
     ret  
     nop  

带机器码

    int main()
    {
    55                   push        ebp  
    8B EC                mov         ebp,esp  
    83 EC 4C             sub         esp,4Ch  
    53                   push        ebx  
    56                   push        esi  
    57                   push        edi  
      int num = 2;
    C7 45 FC 02 00 00 00 mov         dword ptr [num],2  
      int tmp=0;
    C7 45 F8 00 00 00 00 mov         dword ptr [tmp],0  
      switch (num)
    8B 45 FC             mov         eax,dword ptr [num]  
    89 45 B4             mov         dword ptr [ebp-4Ch],eax  
    8B 4D B4             mov         ecx,dword ptr [ebp-4Ch]  
    83 E9 01             sub         ecx,1  
    89 4D B4             mov         dword ptr [ebp-4Ch],ecx  
    83 7D B4 03          cmp         dword ptr [ebp-4Ch],3  
    77 26                ja          $LN2+7 (0B712A2h)  
    8B 55 B4             mov         edx,dword ptr [ebp-4Ch]  
    FF 24 95 B4 12 B7 00 jmp         dword ptr  (0B712B4h)[edx*4]  
      {
        case 1:tmp=1;
    C7 45 F8 01 00 00 00 mov         dword ptr [tmp],1  
        case 2:tmp=2;
    C7 45 F8 02 00 00 00 mov         dword ptr [tmp],2  
        case 3:tmp=3;
    C7 45 F8 03 00 00 00 mov         dword ptr [tmp],3  
        case 4:tmp=4;
    C7 45 F8 04 00 00 00 mov         dword ptr [tmp],4  
        default:tmp=5;
    C7 45 F8 05 00 00 00 mov         dword ptr [tmp],5  
      }
      return tmp;
    8B 45 F8             mov         eax,dword ptr [tmp]  
    }
    5F                   pop         edi  
    5E                   pop         esi  
    5B                   pop         ebx  
    8B E5                mov         esp,ebp  
    5D                   pop         ebp  
    C3                   ret  
    90                   nop  

逐条分析

画栈区图会对反汇编分析有帮助
1.向提前开辟好的栈帧空间压入几个寄存器的值(push保存关键的寄存器的值,pop在main函数的最后恢复关键寄存器的值)

     push        ebp  
     mov         ebp,esp  
     sub         esp,4Ch  
     push        ebx  
     push        esi  
     push        edi  

push ebp指令未执行时,打开寄存器窗口

dd37b6699a3c4046b37c373c83e69d57.png

发现ESP=0x0012FFC18,为画栈区图做准备

EBP的值入栈后,再次查看寄存器窗口

e566b6acedf9452b895aafd049a2f3ae.png
发现有两个寄存器的值发生变动:EIP和ESP

EIP寄存器(Extend Instruction Pointer 扩展指令指针寄存器,其存储指令的地址)的值+1,表明push ebp机器码(0x55)占1个字节,此时EIP指向下一条指令mov ebp,esp,eax的机器码的第一个字节(0x8B),不过EIP的值不是我们所关心的

注意看ESP寄存器的值减了4(0x0012FFC18-4=0x0012FFC14)ESP指针从高地址向低地址移动

下面依次将EBX,ESI,EDI寄存器的值入栈,执行完后ESP=0x0012FFBBC

(ESP值的变动过程0x0012FFC18-->0x0012FFC14-(-4Ch)->0x0012FFBC8-->0x0012FFCBC4-->0x0012FFBC0-->0x0012FFBBC)

可以画栈区图:

82f5dc8d94b04fc0a786238c7f29374f.png

备注:为什么有sub esp,4Ch这一指令见第2点的分析
2.变量的定义,赋初值

      int num = 2;
     mov         dword ptr [num],2  
      int tmp=0;
     mov         dword ptr [tmp],0  

备注:有关为什么用mov指令为变量赋初值参见动态内存管理练习题的反汇编代码分析(底层),非本文的重点

视频中打开了内存窗口

f675c9f52d794a4cbddab149d4a627aa.png

输入了&num,发现num存储在0x0012FFC10~0x0012FFC13处,分析0x0012FFC13的相邻地址就是0x0012FFC14( sub esp,4Ch指令执行前ESP寄存器的值)

看栈区图更直观:

59380a5418ef48c088c4ce3a78488f8e.png
注意

1.push指令为压栈从高地址向低地址存储数据

2.mov指令从低地址向高地址存储数据

可知sub esp,4Ch的作用:为num和tmp变量留出空间
3.将参数num的值写入内存

     mov         eax,dword ptr [num]  
     mov         dword ptr [ebp-4Ch],eax  

先把num的值暂存到eax中转寄存器中,再将eax寄存器的值复制到dword ptr [ebp-4Ch]指向的地址空间中

(注:x86汇编不支持从[地址]到[地址]的格式,即像mov dword ptr [ebp-4Ch],dword ptr [num]这样的指令是非法的)

在mov dword ptr [ebp-4Ch],eax指令执行前看一下EBP寄存器的值

e406e1796da64ca5a3df66d9305623da.png

计算EBP-4Ch=0x012FFC60

则mov dword ptr [ebp-4Ch],eax是将eax的值复制到0x012FFC60~0x012FFC63处
4.为等值比较指令做准备

    sub         ecx,1  
    mov         dword ptr [ebp-4Ch],ecx  

更新ecx的值,重新写入dword ptr [ebp-4Ch]指向的地址空间中,为cmp指令的执行做准备,ja和jmp会依据cmp指令的比较结果做跳转(跳转到case1?case2?case3?case4?default?)
5.★★★重点指令分析(本文的核心)★★★

     cmp         dword ptr [ebp-4Ch],3  
     ja          $LN2+7 (0B712A2h)  
     mov         edx,dword ptr [ebp-4Ch]  
     jmp         dword ptr  (0B712B4h)[edx*4]  

先讲几个汇编指令
cmp指令(全称compare)(用于等值比较)

格式:cmp dest,src

作用:顾名思义,compare是比较,因此比较dest和src的值,将比较后的结果写入标志寄存器(x86下的EFL:Extend FLags),但不改变dest和src原来的值
ja指令(全称jump if above)

格式:ja 地址

作用:顾名思义,jump if above指的是:如果上面比较的结果大于(即不小于)时跳转.ja指令会影响程序的控制流,但它不会直接影响如果高于(dest>src)则跳转,否则(dest=src或dest<src)按顺序执行ja的下一条指令(这里的反汇编指令mov edx,dword ptr [ebp-4Ch])

底层原理:ja指令执行时,CPU会先读取标志寄存器的CF位(进位标志位Carry Flag)和ZF(零标志位Zero Flag)位,只有当CF=0(未发生进位)且ZF=0(cmp dest,src中dest不等于src)时,ja指令才会触发跳转

     cmp         dword ptr [ebp-4Ch],3  
     ja          $LN2+7 (0B712A2h)  

一看到ja就可以知道当dword ptr [ebp-4Ch]指向的地址空间大于3时,会跳到0x0B712A2h处执行

对应的default:tmp=5;的部分,可以验证下

内存窗口输入0x00B712A2,查看对应的机器码,内存窗口中的数据确实对应default:tmp=5;的机器码

cab2682f2df443d69562f8e366aa820d.png

dword ptr [ebp-4Ch]的值为1,因此不会跳转到地址0x0B712A2h处执行,而是按顺序执行下一条指令mov edx,dword ptr [ebp-4Ch],执行完后edx的值为0x00000001
下面将会讲解本文的核心部分:跳转表

switch&case语句的核心汇编指令就是这个jmp dword ptr  (0B712B4h)[edx*4]

(0B712B4h)[edx*4]意思是[0x0B&12B4+edx*4]寻址方式为基址(0x005412B4)+变址(edx*4)

因此会取出0x0B712B4+edx*4地址处的值,跳转到这个地方执行

内存窗口中输入0x0B712B4+1*4(即0x0B712B8)
提问

从下面图片的数据中你发现数据有什么规律?



注意到画框的地方,从高地址向地址方向读:0054129b 00541294 0054128d 00541286

这些数据是连续的!!!

右击选择显示代码字节

470886989ee24318aa017e198d9afaaf.png

如果在内存窗口中把他们视为地址依次输入就会发现

2866658a59d94371864e14bba8bfbcc0.png

ecb075b61c0e4af6a149fd4303580b25.png

回过头来看,实际上下方这张图片的内存区域属于跳转表,拥有连续的地址数据,而jmp dword ptr  (0B712B4h)[edx*4]履行查表的功能,跳转到对应的地方执行(case1?case2?case3?case4?)
IDA反汇编分析

利用IDA软件会看的更清楚

将VS2010生成的exe文件用IDA打开

注意看:off_411449C处

    dd offset $LN4
    dd offset $LN5_0
    dd offset $LN6_0
    dd offset $LN7

这个就是跳转表

取出ds*16+edx*4地址处的值,跳转到这个地方执行

6.程序main函数的返回,空间被销毁,交还给操作系统(非本文重点)

     pop         edi  
     pop         esi  
     pop         ebx  
     mov         esp,ebp  
     pop         ebp  
     ret  

7.题外话:其他细节说明

9d8dd1362ed141f8947b3e1c48ddab33.png

9f1d50a133074d45b29b6d1834d5bcbf.png
附:完整流程的栈区图

da17994c130a4ec89c4aad4af5789d9f.png
思考题

如果为sourcecode1的case后都添加break;反汇编指令是什么样的?
sourcecode2的反汇编代码

    int main()
    {
     push        ebp  
     mov         ebp,esp  
     sub         esp,4Ch  
     push        ebx  
     push        esi  
     push        edi  
      int num = 2;
     mov         dword ptr [num],2  
      int tmp=0;
     mov         dword ptr [tmp],0  
      switch (num)
     mov         eax,dword ptr [num]  
     mov         dword ptr [ebp-4Ch],eax  
     mov         ecx,dword ptr [ebp-4Ch]  
     sub         ecx,1  
     mov         dword ptr [ebp-4Ch],ecx  
     cmp         dword ptr [ebp-4Ch],3  
     ja          $LN2+9 (6112AAh)  
     mov         edx,dword ptr [ebp-4Ch]  
     jmp         dword ptr  (6112BCh)[edx*4]  
      {
        case 1:tmp=1;break;
     mov         dword ptr [tmp],1  
     jmp         $LN2+10h (6112B1h)  
        case 2:tmp=2;break;
     mov         dword ptr [tmp],2  
     jmp         $LN2+10h (6112B1h)  
        case 3:tmp=3;break;
     mov         dword ptr [tmp],3  
     jmp         $LN2+10h (6112B1h)  
        case 4:tmp=4;break;
     mov         dword ptr [tmp],4  
     jmp         $LN2+10h (6112B1h)  
        default:tmp=5;
     mov         dword ptr [tmp],5  
      }
      return tmp;
     mov         eax,dword ptr [tmp]  
    }
     pop         edi  
     pop         esi  
     pop         ebx  
     mov         esp,ebp  
     pop         ebp  
     ret  

和sourcecode1的不同点

break;相当于反汇编代码的

jmp         $LN2+10h (6112B1h)  

显示代码字节并代打开内存窗口,输入0x006112B1

674c4de7ea274c88b20d11ddc7c242ab.png

因此 jmp $LN2+10h (6112B1h)是跳转到return tmp;处,脱离了switch&case结构
4.总结switch&case的执行原理

对于VS2022,当case分支较多时(分支较少会退化为if/else结构),某一个地址空间得到switch (num)的num-1的值,先判断是否为default情况,若不是,则通过基址+变址来查询跳转表(跳转表中存储了所有case情况的所有地址),再依据跳转表的数据跳到指定的地址处执行;如果case的某个分支含有break;则break;的反汇编指令jmp会跳离switch&case结构
5.题外话

其实在Dev C++中查询反汇编指令中会发现不同于VS2022的地方,选择TDM-GCC 4.9.2 32-bit Debug环境

下断点后,将sourcecode1在Dev C++中调试,查看CPU窗口

7aeeb8f3788844aab90285880c9d473d.png

好像退化成if&else了,也有可能Dev C++对跳转表这个部分做隐藏了
6.if&else的处理方式

将sourcecode1的switch&case结构改成if&else结构
C语言代码

    int main()
    {
      int num = 2;
      int tmp=0;
      if (num==1)
              tmp=1;//case 1
      else if (num==2)
              tmp=2;//case 2
      else if (num==3)
              tmp=3;//case 3
      else if (num==4)
              tmp=4;//case 4
      else
              tmp=5;//default
      return tmp;
    }

反汇编代码
带机器码

    int main()
    {
    55                   push        ebp  
    8B EC                mov         ebp,esp  
    83 EC 48             sub         esp,48h  
    53                   push        ebx  
    56                   push        esi  
    57                   push        edi  
      int num = 2;
    C7 45 FC 02 00 00 00 mov         dword ptr [num],2  
      int tmp=0;
    C7 45 F8 00 00 00 00 mov         dword ptr [tmp],0  
      if (num==1)
    83 7D FC 01          cmp         dword ptr [num],1  
    75 09                jne         main+26h (0B1276h)  
              tmp=1;//case 1
    C7 45 F8 01 00 00 00 mov         dword ptr [tmp],1  
    EB 34                jmp         main+5Ah (0B12AAh)  
      else if (num==2)
    83 7D FC 02          cmp         dword ptr [num],2  
    75 09                jne         main+35h (0B1285h)  
              tmp=2;//case 2
    C7 45 F8 02 00 00 00 mov         dword ptr [tmp],2  
    EB 25                jmp         main+5Ah (0B12AAh)  
      else if (num==3)
    83 7D FC 03          cmp         dword ptr [num],3  
    75 09                jne         main+44h (0B1294h)  
              tmp=3;//case 3
    C7 45 F8 03 00 00 00 mov         dword ptr [tmp],3  
    EB 16                jmp         main+5Ah (0B12AAh)  
      else if (num==4)
    83 7D FC 04          cmp         dword ptr [num],4  
    75 09                jne         main+53h (0B12A3h)  
              tmp=4;//case 4
    C7 45 F8 04 00 00 00 mov         dword ptr [tmp],4  
      else
    EB 07                jmp         main+5Ah (0B12AAh)  
              tmp=5;//default
    C7 45 F8 05 00 00 00 mov         dword ptr [tmp],5  
      return tmp;
    8B 45 F8             mov         eax,dword ptr [tmp]  
    }
    5F                   pop         edi  
    5E                   pop         esi  
    5B                   pop         ebx  
    8B E5                mov         esp,ebp  
    5D                   pop         ebp  
    C3                   ret  

不带机器码

    int main()
    {
     push        ebp  
     mov         ebp,esp  
     sub         esp,48h  
     push        ebx  
     push        esi  
     push        edi  
      int num = 2;
     mov         dword ptr [num],2  
      int tmp=0;
     mov         dword ptr [tmp],0  
      if (num==1)
     cmp         dword ptr [num],1  
     jne         main+26h (0B1276h)  
              tmp=1;//case 1
     mov         dword ptr [tmp],1  
     jmp         main+5Ah (0B12AAh)  
      else if (num==2)
     cmp         dword ptr [num],2  
     jne         main+35h (0B1285h)  
              tmp=2;//case 2
     mov         dword ptr [tmp],2  
     jmp         main+5Ah (0B12AAh)  
      else if (num==3)
     cmp         dword ptr [num],3  
     jne         main+44h (0B1294h)  
              tmp=3;//case 3
     mov         dword ptr [tmp],3  
     jmp         main+5Ah (0B12AAh)  
      else if (num==4)
     cmp         dword ptr [num],4  
     jne         main+53h (0B12A3h)  
              tmp=4;//case 4
     mov         dword ptr [tmp],4  
      else
     jmp         main+5Ah (0B12AAh)  
              tmp=5;//default
     mov         dword ptr [tmp],5  
      return tmp;
     mov         eax,dword ptr [tmp]  
    }
     pop         edi  
     pop         esi  
     pop         ebx  
     mov         esp,ebp  
     pop         ebp  
     ret  

分析

注:视频调试演示

C语言的switch&case结构文章的if&else的视频

点我去跳转观看

相对于switch&case结构,if&else的反汇编分析比较简单

很容易总结出if&else的反汇编代码的格式

7.switch的分支较少的情况分析

第4点提到了当switch的分支较少会退化为if/else结构
C语言代码

    int main()
    {
             int num = 2;
             int c1 = 0;
             int c2 = 0;
             switch (num)
             {
                  case 1:c1 = 3;
                  case 2:c2 = 1;
             }
             return 0;
    }

反汇编代码

    int main()
    {
     push        ebp  
     mov         ebp,esp  
     sub         esp,50h  
     push        ebx  
     push        esi  
     push        edi  
             int num = 2;
     mov         dword ptr [num],2  
             int c1 = 0;
     mov         dword ptr [c1],0  
             int c2 = 0;
     mov         dword ptr [c2],0  
             switch (num)
     mov         eax,dword ptr [num]  
     mov         dword ptr [ebp-50h],eax  
     cmp         dword ptr [ebp-50h],1  
     je          main+32h (0ED1282h)  
     cmp         dword ptr [ebp-50h],2  
     je          main+39h (0ED1289h)  
     jmp         main+40h (0ED1290h)  
             {
                  case 1:c1 = 3;
     mov         dword ptr [c1],3  
                  case 2:c2 = 1;
     mov         dword ptr [c2],1  
             }
             return 0;
     xor         eax,eax  
    }
     pop         edi  
     pop         esi  
     pop         ebx  
     mov         esp,ebp  
     pop         ebp  
     ret  

分析

只看switch&case部分

将num的值加载到dword ptr [ebp-50h]指向的地址空间中后,下面反复出现cmp和je,和之前的if&else的处理方式(cmp和jne)很像,但并没有出现类似jmp dword ptr  (0B712B4h)[edx*4]这样的指令,原因是当switch的分支较少,编译器认为没有必要提供跳转表

     cmp         dword ptr [ebp-50h],1  
     je          main+32h (0ED1282h)  
     cmp         dword ptr [ebp-50h],2  
     je          main+39h (0ED1289h)  
     jmp         main+40h (0ED1290h)


发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2025-2-17 14:52
抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-16 07:29

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表