CALL指令有多少种写法

上传人:ji****72 文档编号:37522288 上传时间:2018-04-17 格式:DOC 页数:8 大小:280.50KB
返回 下载 相关 举报
CALL指令有多少种写法_第1页
第1页 / 共8页
CALL指令有多少种写法_第2页
第2页 / 共8页
CALL指令有多少种写法_第3页
第3页 / 共8页
CALL指令有多少种写法_第4页
第4页 / 共8页
CALL指令有多少种写法_第5页
第5页 / 共8页
点击查看更多>>
资源描述

《CALL指令有多少种写法》由会员分享,可在线阅读,更多相关《CALL指令有多少种写法(8页珍藏版)》请在金锄头文库上搜索。

1、CALLCALL 指令有多少种写法指令有多少种写法最近有一个需求,给你个地址,看看这个地址前面是不是一个 CALL 指令(请同 学们自行联想该需求的来源)。作为团队的救火队员+炮灰,这个简单的事情自 然落在了我的头上。这个事情很简单,作为一个善于站在别人肩膀上的程序员我们可以考虑使用 libdisasm;如果要考虑 x64,就试试 udis86;如果需要用 Python,就有 Python 包装好的 pydasm。不过这两个 400KB+的库,显然不值得为了一个 CALL 指令导入到编译出来大小仅仅 100K 不到的项目代码里面。那么就自己抽一个 CALL 指令解码逻辑出来好了。这个逻辑的复杂

2、性在于,你无 法知道前面一个 CALL 指令有多长。因此,首先需要枚举出所有的 CALL 指令格 式。Intel 有公开的指令集格式文档,你需要的是第二卷的上半部分,指令集从 A 到 M。这篇文档的难度超出一般人想象,里面有众多晦涩的标识、与硬件紧密 相关的介绍,拿到这后,即使直接翻到目录的 CALL 指令一节,也不见得能够 弄清楚。不相信?我们就翻到那里看看:CALL 指令格式一览表虽然很明确的列出,第一列是指令的二进制形式,第二列是指令的汇编形式, 但是面对着 E8 cw, FF/2 这样的标识,一样不知道究竟对应的二进制格式是什 么样的。那好,我们就从理解这些标识开始。文档向前翻,有一个

3、专门的节(3.1.1 Instruction Format)讲述这些标识的含义。这里抽出其中两个用得着的翻译 一下:表格中的“Opcode”列列出了所有的所有可能的指令对应的二进制格式。有可 能的话,指令代码使用十六进制显示它们在内存当中的字节。除了这些 16 进制 代码之外的部分使用下面的标记:cb,cb, cw,cw, cd,cd, cp,cp, co,co, ctct opcode 后面跟着的一个 1 字节(cb),2 字节(cw), 4 字节(cd),6 字节 (cp),8 字节(co) 或者 10 字节(ct) 的值。这个值用来表 示代码偏移地址,有可能的话还包括代码段寄存器的值。/

4、digit/digit digit 为 0 到 7 之间的数字,表示指令的 ModR/MModR/M bytebyte 只使用 r/mr/m 字段字段作为操作数,而其 regreg 字段字段作为 opcode 的一部分,使用 digit 指定的数字。红字部分不知道什么含义?没关系,我们先不看它。对于 cb/cw 之类的,基本 上能够简单看明白其中的一些指令含义了:E8 cw 的含义是:字节 0xE8 后面跟着一个 2 字节操作数表示要跳转到的地址 与当前地址的偏移量。 E8 cd 的含义是:字节 0xE8 后面跟着一个 4 字节的操作数表示要跳转的地址 与当前地址的偏移量。 9A cd 的含义

5、是:字节 0x9A 后面跟着一个 6 字节的操作数表示要跳转的地址 和代码段寄存器的值。那么,同样的 0xE8 开头的指令,CPU 如何区分后面的操作数是 2 字节还是 4 字 节?答案是和 CPU 的模式有关,在实模式下,0xE8 接受 2 字节操作数,而 32 位保护模式下接受 4 个字节,64 位保护模式下同样接受 4 字节,同时需要对该 操作数进行带符号扩展。因此,CALL 指令的前两种格式是:E8 xx xx xx xx,和 9A xx xx xx xx xx xx。一个是 5 字节长,一个是 7 字节长。其实 E8 那种,就是我们在汇编指令 里面写 CALL lable 之后产生的

6、,最常见的 CALL 指令。然后是下面的 FF /2。这个是 0xFF 字节后面跟上一个 blablabla 的东西。这个 blablabla 的东西是什么呢?要解释这个,首先需要知道红字标出来的部分, 即 ModR/M 是什么东西。这个要先回到最基本的一个问题:IA32 的指令格式。IA-32,Intel 64 指令格式其中每个部分是什么含义呢?首先是指令前缀。有印象的应该记得当年学习微机原理的时候提到过得循环前 缀 repnz/repne,这个前缀就是被编码在指令的前面部分的。每个前缀最多一 个字节,一条指令最多 4 个前缀。然后是指令代码(opcode),这部分标识了指令是什么。这个是指

7、令当中唯一 必需的部分。前面例子当中的 0xE8,0xFF 都是 opcode。再后面就是我们要重点关心的 ModR/M 字段了,还有和它密切相关的 SIB 字节。 手册 2.1.3 当中有对于它们的详细描述。许多指令需要引用到一个在内存当中的值作为操作数,这种指令需要一个称为 寻址模式标识字节寻址模式标识字节(addressing-form specifier byte),或者叫做 ModR/M 字 节紧跟在主 opcode 后面。ModR/M 字节包含下面三个部分的信息:mod(模式)域,连同 r/m(寄存器/内存)域共同构成了 32 个可能 的值:8 个寄存器和 24 个寻址模式。 re

8、g/opcode(寄存器/操作数)域指定了 8 个寄存器或者额外的 3 个字节的 opcode。究竟这三个字节用来做什么由主 opcode 指定。r/m(寄存器/内存)域可以指定一个寄存器作为操作数,或者可以 和 mod 域联合用来指定寻址模式。有时候,它和 mod 域一起用来 为某些指令指定额外的信息。 这一段有些晦涩。其意思解释一下是这样的:一个指令往往需要引用一个在内 存当中的值,典型的就是如 mov:MOV eax, dword ptr 123456123456 MOV eax, dword ptr esiesi这其中的 123456 或者 esi 就是 MOV 指令引用的内存地址,而

9、 MOV 关心的是 这个地址当中的内容。这个时候,需要某种方式来为指令指定这个操作数的类 型:是一个立即数表示的地址,还是一个存放在寄存器当中的地址,或者,就 是寄存器本身。这个用来区分操作数类型的指令字节就是 ModR/M,确切的说是其中的 5 个位, 即 mod 和 r/m 域。剩下的三个位,可能用来做额外的指令字节。因为,IA32 的 指令个数已经远超过一个字节所能表示的 256 个了。因此,有的指令就要复用 第一个字节,然后依据 ModR/M 当中的 reg/opcode 域进行区分。现在回头看前面的红字标识的部分,能不能理解 /digit 这种表示法了?对于 SIB 的介绍,我们先忽

10、略,看看对于 CALL 指令的枚举我们已经能做什么了。CALL 指令的表示法:FF /2,是 0xFF 后面跟着一个 /digit 表示的东西。就 是说,0xFF 后面需要跟一个 ModR/M 字节,ModR/M 字节使用 reg/opcode 域 = 2 。那么,reg/opcode = 2 的字节有 32 个,正如 ModR/M 的解释,这 32 个值 代表了 32 种不同的寻址方式。是哪 32 种呢?手册上面有张表:32 字节寻址模式下的 ModR/M 字节非常复杂的一张表。现在就看看这张表怎么读。首先是列的定义。由于 reg/opcode 域可以用来表示 opcode,也可以用来表示

11、reg,因此同一个值在不同的指令当中可能代表不同的含义。在表当中,就表现 为每一列的表头都有很多个不同的表示。我们需要关心的就是 opcode 这一个。 注意看我用红圈圈出来的部分,这一列就是 opcode=2 的一列。而我们需要的 CALL 指令,也就是在这一列当中,0xFF 后面需要跟着的内容。行的定义就是不同的寻址模式。正如手册所说,mod + R/M 域,共 5 个字节, 定义了 32 种寻址模式。010 017 对应于寄存器寻址。例如指令 CALL dword ptr eax :eax寻址对应的是 010,因此,该指令对应的二进制就是 FF 10。同理, CALL dword ptr

12、 ebx 是 FF 13,CALL dword ptr esi 是 FF 16,这些指令都是 2 个字节。有人也许问 CALL word ptr eax 是什么? 抱歉,这不是一个合法的 32 位指令。050-057 部分需要带一个 disp8,即 8bit 立即数,也就是一个字节。这 个是基地址+8 位偏移量的寻址模式。例如 CALL dword ptr eax+10 就是 FF 50 10 。注意虽然表当中写的是 eax + disp8 这种形式,但是并不表示是取 得 eax 指向的地址当中的值再加上 disp8,而是在 eax 上加上 disp8 再进行寻 址。因此写成 eax+disp

13、8 更不容易引起误解。后面的 disp32 也是一样的。 这个类型指令是 3 个字节。090 097 部分需要带 disp32,即 4 字节立即数。这个是基地址+32 位偏 移量。例如 CALL dword ptr eax+12345 就是 FF 90 00 01 23 45。有趣的 是, CALL dword ptr eax+10 也可以写成 FF 90 00 00 00 10。至于汇编成 哪个二进制形式,这是汇编器的选择。这个类型的指令是 6 个字节。0xD0 0xD7 部分则直接是寄存器。这边引用的寄存器的类型有很多,但是在 CALL 指令当中只能引用通用寄存器,因此 CALL eax

14、就是 FF D0,臭名昭著的 CALL esp 就是 FF D4。注意 CALL eax 和 CALL eax 是不一样的。这些指令 也是 2 个字节。仔细的人也许主要到了,在表当中,014, 015, 054 和 094 是不一样 的。015 比较简单,这个要求 ModR/M 后面跟上一个 32 位立即数作为地址。 即常见的 CALL dword ptr 004F778e 这种格式的,直接跳转到一个固定内存 地址处存放的值,常见于调用 Windows 的导出表。对应的二进制是 FF 15 00 4F 77 8E ,有 6 个字节。014,054,094 部分是最复杂的,因为这个时候,ModR

15、/M 不足以指定寻 址方式,而是需要一个额外的字节,这个字节就是指令当中的第 4 个字节, SIB。同样在手册的 2.1.3,紧跟着 ModR/M 的定义:某些特定的 ModR/M 字节需要一个后续字节,称为 SIB 字节。32 位指令的基地 址+偏移量,以及 比例*偏移量 的形式的寻址方式需要 SIB 字节。 SIB 字节包 括下列信息:scale(比例)域指定了放大的比例。 index(偏移)域指定了用来存放偏移量 的寄存器。 base (基地址)域用来标识存放基地址的寄存器。 014, 054, 094 就是这里所说的“特定的 ModR/M 字节。这个字节后面跟 着的 SIB 表示了一个

16、复杂的寻址方式,典型的见于虚函数调用:CALL dword ptr ecx+4*eax就是调用 ecx 指向的虚表当中的第 eax 个虚函数。这个指令当中,因为没有立 即数,因此 FF 后面的字节就是 014,而 ecx+4*eax 就需要用 SIB 字节来 表示。在这个指令当中,ecx 就是 Base,4 是 Scale,eax 是 Index。那么,Base, Scale 和 Index 是如何确定的呢?手册上同样有一张表(又是巨 大的表):32 位寻址模式当中的 SIB 字节列是 Base,行是 Index*Scale,例如ecx+4*eax 就是 081。根据这张表,CALL dword ptr ecx+4*eax 就是 FF 14 81 。由此可见,对于 014 系列的来说,CALL 指令就是 3 个字节。 而 054 带 8bit 立即数,就是对应于 CALL 指令:CALL dwor

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号