switch 的简单情景(case 不超过 3 项)

首先,我们分析一下 switch 语句的一种简单情景,我们可以用 C 写出如下如下代码。

编译后用 OllyDBG 载入,它将显示出如下的反汇编代码。

首先,我们可以看到 ESP 减少了 8,除了定义变量 a 外,编译器还分配了一个临时变量(这里暂且叫它 t)用于比较。t 被赋值成 a 的值,然后与立即数 0x10,0x20,0x30 依次比较。如果有一项相等,那么就跳转到 case 里面,如果都不相等,就会无条件跳转到 default 里面。执行完 case 或 default 里面的代码之后,就会无条件跳转到 end 的位置。

switch 跳转表情景(case 超过 3 项)

上面是 switch 中比较简单的情景,但是当 case 项超过 3 项时,情景将发生很大的变化。我们可以先编写如下 C 语言代码,这里 switch 的 case 项已经超过 3 项。

编译后,用 OllyDBG 载入,可以得到如下的反汇编代码。与之前的简单情景类似,除了定义变量 a 外,编译器还分配了一个临时变量(这里暂且叫它 t)用于比较,t 被赋值成,接着我们可以观察到 t 减了 0x10,然后与 0x8A 做比较。实际上,我们不难看出 t 先减去了 case 项中的最小值,然后与 case 项中的最大值和最小值的差进行比较。这样做的目的是首先排除极端情况(如果 t 比最大值要大,或者比最小值小,那么肯定是 default),接下来的 JA 指令就能排除这类极端情况,提高程序执行效率。

说到这里,有些人可能不能理解为啥要先减去最小值,然后再与最大值和最小值的差进行比较。其实道理很简单,假设变量的值是 t,最小值是 min,最大值是 max,我们很容易建立一个比较关系,t – min 与 max – min 之间的比较。这里很容易化简成 t 与 max 的比较,如果 t 比 max 大,说明这是极端情况应该去 default。还有一种可能就是,t – min 的时候,t 比 min 小,这样寄存器就会溢出,t 会变得很大很大且大过 max,这也会变成极端情况。换言之,这样的一套操作下来,程序能保证 t 在 min 和 max 之间是能被 case 接受的,否则就是极端情况而进入 default。

我们往后看,假设变量的值不是极端情况,那么程序就会把 t 的值赋值给 EDX 寄存器,注意,这里的 t 已经减去了 case 项的最小值,换言之,如果把 case 项中的最小值看做起始点 0,最大值看做最大值与最小值的差,那么 t 就是介于最小值与最大值之间的某个点。

接下来,eax 被赋值成某个基址 + EDX 的值。我们跟进这个基址可以看到以下的内容:

我们仔细分析一下 EDX 的值(也是 t 的值)与基址起始的字节数据的内在联系。

  • 当 EDX == 0x0 时,取出数据 0x0 给 EAX。
  • 当 EDX == 0x22 时,取出数据 0x1 给 EAX。
  • 当 EDX == 0x34 时,取出数据 0x2 给 EAX。
  • 当 EDX == 0x8A 时,取出数据 0x3 给 EAX。
  • 当 EDX >= 0 且 EDX <= 0x8A 时,除去以上情况,将取出数据 0x4 给 EAX。

仔细观察可以发现,这里的内存数据从基址偏移 0x0 ~ 0x8A 的每个字节都是一个指示值,在此例中 EAX 可以被赋值成五个值 0,1,2,3,4,5。EDX 实际上也就是 a 减去 case 项最小值的结果,所以,最终我们能建立一个映射关系:

  • case 0x10 –> EAX = 0x0
  • case 0x32 –> EAX = 0x1
  • case 0x44 –> EAX = 0x2
  • case 0x9A –> EAX = 0x3
  • default –> EAX = 0x4

而 EAX 的值最终会被用于跳转表,跳转表里保存了每一个指示值所对应的内存地址,这些内存地址就是每个分支的入口。回到反汇编,接着程序将执行一个跳转,目的地址是某个基址 + EAX * 4,让我们在数据窗口跟随到这个基址:

我们很容易发现,这个基址就在之前指示器基址的上方。本例中它存储了 0x14 字节(20 字节)的数据,每个地址是 4 字节,那么就是五个地址,十六进制分别是 004095E6,004095E5,00409604,00409613,00409622。这刚好是程序中各个分支的入口地址。EAX(本例中 0x0 ~ 0x4)的值恰好是指示取这五个值中的哪一个值。

在本例中,EAX 会取到 0x4 这个值,最终会 JMP 到 00409622 这个地址,也就是 default 分支的入口点,这样 switch 就执行完毕了。

switch 语句的反汇编浅析的更多相关文章

  1. C++ 反汇编:关于Switch语句的优化措施

    流程控制语句是C语言中最基本的判断语句,通常我们可以使用IF来构建多分支结构,但同样可以使用Switch语句构建,Switch语句针对多分支的优化措施有4种形式,分别是,IF-ELSE优化,有序线性优 ...

  2. 逆向随笔 - switch 语句深入分析

    switch case 语句在c语言里还是比較简单的.可是被编译出来之后,优化结果往往让人非常疑惑.全然看不懂,以下我们一次次的尝试,看看编译器究竟把switch语句变成什么样了.   ① 先上个最简 ...

  3. switch语句分析

    1.关于switch语句 如果if语句中表达式是判断是否等于一个常量时,可以用switch语句来代替 if(表达式 == 常量1)                        {          ...

  4. switch语句的妙用

    switch语句的普通用法很简单,如下: var a = 3; switch (a) { case 1: console.log(a); break; case 2: case 3: console. ...

  5. 106运用SWITCH语句打印星期几的单词

    package com.chongrui.test;/*运用SWITCH语句打印星期几的单词 * */ public class TypeConvertion { public static void ...

  6. 通过goto语句学习if...else、switch语句并简单优化

    goto语句在C语言中实现的就是无条件跳转,第二章一上来就介绍goto语句就是要通过goto语句来更加清楚直观的了解控制结构. 我理解的goto语句其实跟switch语句有相似之处,都是进行跳转.不同 ...

  7. Java中简单的操作(if语句、常用操作符、switch语句、变量赋值等)

    ---------------------if语句介绍--------------------------------------------------- class IfDemo { public ...

  8. Switch语句的case穿透

    Switch语句的case穿透 一 switch语句几点说明: 1. case后面只能是常量,不能是变量,而且,多个case后面的值不能出现相同的. 2.case后面表达式可以接受: 基本数据类型,b ...

  9. ECMA中的switch语句

    switch借鉴自其他语言,但也有自己的特色. 1.可以在switch语句中使用任何数据类型(数值.字符串.对象等),很多其他语言中只能使用数值. 2.每个case的值不一定是常量,可以是变量或者表达 ...

随机推荐

  1. HDU 3666

    此题不难,不等式很空易就列出来了,只是要把它转化成减法形式..卡在这了... 其实取一个log对数就好了...要记住这个技巧.用基于dfs的spfa.. #include<iostream> ...

  2. jenkins集成多个项目

    https://www.cnblogs.com/gossip/p/5961376.html 需要jenkins版本高点 安装插件:Multijob plugin

  3. oc65--协议应用1,接口.做数据类型限定

    // WifeCondition.h #import <Foundation/Foundation.h> @protocol WifeCondition <NSObject> ...

  4. Ubuntu Linux 安装 .7z 解压和压缩文件

    安装方法: sudo apt-get install p7zip 解压文件: 7z x manager.7z -r -o /home/xx解释如下:x 代表解压缩文件,并且是按原始目录解压(还有个参数 ...

  5. 学习笔记二十三——字符函数库cctype【转】

    本文转载自: 字符函数库cctype 在头文件cctype(ctype.h)中定义了一些函数原型,可以简化输入确定字符是否为大写字母.数字.标点符号等工作. 例如: 如果ch是一个字母,则isalph ...

  6. 解决WinForm下ListBox控件“设置DataSource属性后无法修改项集合”

    解决WinForm下ListBox控件“设置DataSource属性后无法修改项集合” 最近更新: 2013-2-15    587   很少写WinForm程序第一次使用ListBox控件就遇到了比 ...

  7. hdoj--5630--Rikka with Chess(规律题)

     Rikka with Chess Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Othe ...

  8. 假脱机服务(SPOOLing service)

    1. 基本含义 SPOOLing 是 Simultaneous Peripheral(外设) Operation On-Line(联机) 的缩写,是关于慢速字符设备(慢速外设,比如打印机)如何与计算机 ...

  9. C++ 单例模式(读书笔记)

    #include <iostream> class Singleton {    static Singleton s;    int i;    Singleton(int x):i(x ...

  10. hdu5249

    这个是去年astar的题~ 标准做法主席树,然而渣渣并不会(我确实叫zhazha...), 所以,他先离线,离散化,然后树状数组+二分水过了.... 离线的目的主要是为了离散化,剩下的就和用一个树状数 ...