Anonymous 发表于 2016-12-12 19:39:28

反汇编switch优化扫盲

本帖最后由 匿名 于 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)
00A813DEmov         eax,dword ptr
00A813E1mov         dword ptr ,eax                                //switch语句很明显的特征,判断语句全部放在前面                               
00A813E7cmp         dword ptr ,1
00A813EEje          wmain+44h (0A81404h)
00A813F0cmp         dword ptr ,2
00A813F7je          wmain+5Fh (0A8141Fh)
00A813F9cmp         dword ptr ,3
00A81400je          wmain+7Ch (0A8143Ch)
00A81402jmp         wmain+97h (0A81457h)
        {
        case 1:        printf("argc=%d", argc);
00A81404mov         esi,esp
00A81406mov         eax,dword ptr
00A81409push      eax
00A8140Apush      0A85858h
00A8140Fcall      dword ptr ds:
00A81415add         esp,8
00A81418cmp         esi,esp
00A8141Acall      __RTC_CheckEsp (0A81140h)                                //假设你不加break就继续执行下面的。
        case 2:        printf("argc=%d", argc); break;
00A8141Fmov         esi,esp
00A81421mov         eax,dword ptr
00A81424push      eax
00A81425push      0A85858h
00A8142Acall      dword ptr ds:
00A81430add         esp,8
00A81433cmp         esi,esp
00A81435call      __RTC_CheckEsp (0A81140h)
00A8143Ajmp         wmain+97h (0A81457h)                                //加了break就等于多了个jmp 执行跳出switch-case外面

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


那么问题来了,如果有一百个case程序是否就有一百个判读语句在前面?
当switch大于3,他的优化就体现出来了
switch把case做成地址数组,这个数组保存每个case的首地址,因为地址都是0开始算,所以ecx-1
提示下:case做地址数组上限至255(8位)
        switch (argc)
008E13DEmov         eax,dword ptr
008E13E1mov         dword ptr ,eax
008E13E7mov         ecx,dword ptr
008E13EDsub         ecx,1                                                               //下标是从0开始的哦                               
008E13F0mov         dword ptr ,ecx                             
008E13F6cmp         dword ptr ,3                           //当argc-1还大于switch里面最大值那就跳出
008E13FDja          $LN4+72h (08E147Eh)                                  //跳出去
008E13FFmov         edx,dword ptr                          //edx得到下标
008E1405jmp         dword ptr                           //地址是从0开始,8E1494h存放的是每个case的地址

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


然而事情并没有那么简单,如果这个数大于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)
01063C7Emov         eax,dword ptr
01063C81mov         dword ptr ,eax
01063C87cmp         dword ptr ,1F4h                //很明显取个中间值做比较走哪边                       
01063C91jg          wmain+6Ah (01063CCAh)                     //大于跳到另一条路
01063C93cmp         dword ptr ,1F4h                //判断==500               
01063C9Dje          wmain+0FCh (01063D5Ch)
01063CA3cmp         dword ptr ,19h                   //判断==25
01063CAAje          wmain+9Fh (01063CFFh)
01063CACcmp         dword ptr ,62h                  //判断==98
01063CB3je          wmain+0DFh (01063D3Fh)
01063CB9cmp         dword ptr ,0FFh               //判断==255
01063CC3je          wmain+0BFh (01063D1Fh)
01063CC5jmp         wmain+16Eh (01063DCEh)                  //都不是跳出switch外

01063CCAcmp         dword ptr ,320h               //判断==800
01063CD4je          wmain+119h (01063D79h)
01063CDAcmp         dword ptr ,457h               //判断==1111
01063CE4je          wmain+136h (01063D96h)
01063CEAcmp         dword ptr ,5FBh               //判断==1531
01063CF4je          wmain+153h (01063DB3h)
01063CFAjmp         wmain+16Eh (01063DCEh)               //都不是跳出switch外

        return 0;
01063DCExor         eax,eax

F3IYcZ7 发表于 2016-12-12 21:32:57

少年真是天赋异禀。
页: [1]
查看完整版本: 反汇编switch优化扫盲