鱼C论坛

 找回密码
 立即注册
查看: 1648|回复: 1

[技术交流] 反汇编switch优化扫盲

[复制链接]
匿名鱼油
匿名鱼油  发表于 2016-12-12 19:39:28 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 匿名 于 2016-12-12 20:32 编辑

参考:
书籍C++反汇编与逆向技术
任老师http://bbs.pediy.com/showthread.php?t=114479

首先代码并无实际意义

先看代码VS2013写的,当switch小于3
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
        switch (argc)
        {
        case 1:        printf("argc=%d", argc);                         //故意写的别喷,天书夜谈第二章课后习题反汇编就有这个陷阱
        case 2:        printf("argc=%d", argc); break;
        case 3:        printf("argc=%d", argc); break;
        }
        return 0;
}
反汇编后
        switch (argc)
00A813DE  mov         eax,dword ptr [argc]  
00A813E1  mov         dword ptr [ebp-0C4h],eax                                  //switch语句很明显的特征,判断语句全部放在前面                               
00A813E7  cmp         dword ptr [ebp-0C4h],1  
00A813EE  je          wmain+44h (0A81404h)  
00A813F0  cmp         dword ptr [ebp-0C4h],2  
00A813F7  je          wmain+5Fh (0A8141Fh)  
00A813F9  cmp         dword ptr [ebp-0C4h],3  
00A81400  je          wmain+7Ch (0A8143Ch)  
00A81402  jmp         wmain+97h (0A81457h)  
        {
        case 1:        printf("argc=%d", argc);
00A81404  mov         esi,esp  
00A81406  mov         eax,dword ptr [argc]  
00A81409  push        eax  
00A8140A  push        0A85858h  
00A8140F  call        dword ptr ds:[0A89114h]  
00A81415  add         esp,8  
00A81418  cmp         esi,esp  
00A8141A  call        __RTC_CheckEsp (0A81140h)                                  //假设你不加break就继续执行下面的。
        case 2:        printf("argc=%d", argc); break;
00A8141F  mov         esi,esp  
00A81421  mov         eax,dword ptr [argc]  
00A81424  push        eax  
00A81425  push        0A85858h  
00A8142A  call        dword ptr ds:[0A89114h]  
00A81430  add         esp,8  
00A81433  cmp         esi,esp  
00A81435  call        __RTC_CheckEsp (0A81140h)  
00A8143A  jmp         wmain+97h (0A81457h)                                  //加了break就等于多了个jmp 执行跳出switch-case外面

题外话:
各位有没看过天书夜谈,第二章节课后习题,就故意写少了个break。不知道有没有迷惑到你们
1.png

那么问题来了,如果有一百个case程序是否就有一百个判读语句在前面?
当switch大于3,他的优化就体现出来了
switch把case做成地址数组,这个数组保存每个case的首地址,因为地址都是0开始算,所以ecx-1
提示下:case做地址数组上限至255(8位)
        switch (argc)
008E13DE  mov         eax,dword ptr [argc]  
008E13E1  mov         dword ptr [ebp-0C4h],eax  
008E13E7  mov         ecx,dword ptr [ebp-0C4h]  
008E13ED  sub         ecx,1                                                                 //下标是从0开始的哦                               
008E13F0  mov         dword ptr [ebp-0C4h],ecx                               
008E13F6  cmp         dword ptr [ebp-0C4h],3                             //当argc-1还大于switch里面最大值那就跳出
008E13FD  ja          $LN4+72h (08E147Eh)                                      //跳出去
008E13FF  mov         edx,dword ptr [ebp-0C4h]                         //edx得到下标
008E1405  jmp         dword ptr [edx*4+8E1494h]                            //地址是从0开始,8E1494h存放的是每个case的地址

//我只复制关键代码其他省略了
2.png

然而事情并没有那么简单,如果这个数大于255又该如何优化呢,那么数据结构二叉树就登场了
代码如下:
        switch (argc)
        {
        case 25:                 printf("argc=%d", argc); break;
        case 255:               printf("argc=%d", argc); break;
        case 98:                 printf("argc=%d", argc); break;
        case 500:               printf("argc=%d", argc); break;
        case 800:               printf("argc=%d", argc); break;
        case 1111:            printf("argc=%d", argc); break;
        case 1531:            printf("argc=%d", argc); break;
        }
                                      平均值
                                        500
                                        /   \
                                     25    800
                                      /        \
                                 98         1111
                                  /                  \
                               255                 1531
                               
        switch (argc)
01063C7E  mov         eax,dword ptr [argc]  
01063C81  mov         dword ptr [ebp-0C4h],eax  
01063C87  cmp         dword ptr [ebp-0C4h],1F4h                  //很明显取个中间值做比较走哪边                       
01063C91  jg          wmain+6Ah (01063CCAh)                       //大于跳到另一条路
01063C93  cmp         dword ptr [ebp-0C4h],1F4h                  //判断==500               
01063C9D  je          wmain+0FCh (01063D5Ch)  
01063CA3  cmp         dword ptr [ebp-0C4h],19h                   //判断==25
01063CAA  je          wmain+9Fh (01063CFFh)  
01063CAC  cmp         dword ptr [ebp-0C4h],62h                  //判断==98
01063CB3  je          wmain+0DFh (01063D3Fh)  
01063CB9  cmp         dword ptr [ebp-0C4h],0FFh                 //判断==255
01063CC3  je          wmain+0BFh (01063D1Fh)  
01063CC5  jmp         wmain+16Eh (01063DCEh)                  //都不是跳出switch外

01063CCA  cmp         dword ptr [ebp-0C4h],320h               //判断==800
01063CD4  je          wmain+119h (01063D79h)  
01063CDA  cmp         dword ptr [ebp-0C4h],457h               //判断==1111
01063CE4  je          wmain+136h (01063D96h)  
01063CEA  cmp         dword ptr [ebp-0C4h],5FBh               //判断==1531
01063CF4  je          wmain+153h (01063DB3h)  
01063CFA  jmp         wmain+16Eh (01063DCEh)                 //都不是跳出switch外

        return 0;
01063DCE  xor         eax,eax
回复

使用道具 举报

发表于 2016-12-12 21:32:57 | 显示全部楼层
少年真是天赋异禀。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 0 反对 1

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-19 14:24

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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