switch 语句的反汇编浅析
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 语句的反汇编浅析的更多相关文章
- C++ 反汇编:关于Switch语句的优化措施
流程控制语句是C语言中最基本的判断语句,通常我们可以使用IF来构建多分支结构,但同样可以使用Switch语句构建,Switch语句针对多分支的优化措施有4种形式,分别是,IF-ELSE优化,有序线性优 ...
- 逆向随笔 - switch 语句深入分析
switch case 语句在c语言里还是比較简单的.可是被编译出来之后,优化结果往往让人非常疑惑.全然看不懂,以下我们一次次的尝试,看看编译器究竟把switch语句变成什么样了. ① 先上个最简 ...
- switch语句分析
1.关于switch语句 如果if语句中表达式是判断是否等于一个常量时,可以用switch语句来代替 if(表达式 == 常量1) { ...
- switch语句的妙用
switch语句的普通用法很简单,如下: var a = 3; switch (a) { case 1: console.log(a); break; case 2: case 3: console. ...
- 106运用SWITCH语句打印星期几的单词
package com.chongrui.test;/*运用SWITCH语句打印星期几的单词 * */ public class TypeConvertion { public static void ...
- 通过goto语句学习if...else、switch语句并简单优化
goto语句在C语言中实现的就是无条件跳转,第二章一上来就介绍goto语句就是要通过goto语句来更加清楚直观的了解控制结构. 我理解的goto语句其实跟switch语句有相似之处,都是进行跳转.不同 ...
- Java中简单的操作(if语句、常用操作符、switch语句、变量赋值等)
---------------------if语句介绍--------------------------------------------------- class IfDemo { public ...
- Switch语句的case穿透
Switch语句的case穿透 一 switch语句几点说明: 1. case后面只能是常量,不能是变量,而且,多个case后面的值不能出现相同的. 2.case后面表达式可以接受: 基本数据类型,b ...
- ECMA中的switch语句
switch借鉴自其他语言,但也有自己的特色. 1.可以在switch语句中使用任何数据类型(数值.字符串.对象等),很多其他语言中只能使用数值. 2.每个case的值不一定是常量,可以是变量或者表达 ...
随机推荐
- [转]十五天精通WCF——第十三天 用WCF来玩Rest
在我们玩wcf的时候,都会潜意识的觉得wcf就是通过soap协议交换消息的,并且可以在basic,tcp,msmq等等绑定中任意切换, 牛逼的一塌糊涂,但是呢,如果说哪一天wcf不再使用soap协议, ...
- Retrofit网络框架入门使用
1.简单介绍 retrofit事实上就是对okhttp做了进一步一层封装优化. 我们仅仅须要通过简单的配置就能使用retrofit来进行网络请求了. Retrofit能够直接返回Bean对象,比如假设 ...
- 【转】】}linux awk 命令详解
http://www.cnblogs.com/ggjucheng/archive/2013/01/13/2858470.html ----------------------------------- ...
- nginx access 日志位置
nginx access 日志位置 /var/log/nginx tail -f access.log
- 切换到android studio遇到的svn问题
眼下的android studio 2.0(包括preview版)都已经把git.svn,cvs 等工具集成进来了.所以,我们仅仅须要依据代码server选择使用就可以. 这里以开源中国上的git私有 ...
- 使用汇编分析c代码的内存分布
arm平台下使用反汇编分析c内存分布: arm:使用arm-linux-objdump命令将编译完毕之后的elf文件,进行反汇编. 之后重定向到tmp.s文件里. 第一步变量例如以下c文件. vim ...
- [Android]Fragment源代码分析(三) 事务
Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的很的出彩.我们先引入一个简单经常使用的Fragment事务管理代码片段: FragmentTransaction ft = thi ...
- ExtJs grid单选,多选
一. selType : 'checkboxmodel',singleSelect : true, // 单选multiSelect : true, // 多选singleSelects:['edit ...
- 将byte[]转为各种进制的字符串
/** * 将byte[]转为各种进制的字符串 * @param bytes byte[] * @param radix 基数可以转换进制的范围(2-36),从Chara ...
- 使用poi读取word2007(.docx)中的复杂表格
使用poi读取word2007(.docx)中的复杂表格 最近工作需要做一个读取word(.docx)中的表格,并以html形式输出.经过上网查询,使用了poi. 对于2007及之后的word文档,需 ...