反汇编基本原理与x86指令构造

反汇编基本原理与x86指令构造

概要:旨在讲述程序的二进制代码转换到汇编,即反汇编的基本原理。以及 x86 架构的 CPU 的指令构造,有这个基础后就可以自己编写汇编程序了,也可以将二进制代码数据转换成汇编助记指令。当然,把本文当作手册的阅读指导也是可以的。本文还讲述了 DEBUG 工具的部分功能,32位平台下有一个 DEBUG32 版本可以配合 DOSBOX 工具运行在 Windos 7 这些 NT 系统上,DEBUG 要使用 MSDOS 5.0 版本中的。这是一个十分有用的工具,它同时是 x86 的汇编程序,又是反汇编程序,同时又可以作为交互命令使用,如读写磁盘扇区等系统功能。本文需要汇编与计算机原理等基础知识,如果阅读时发现无法理解的内容就表明需要补充基础知识,此时请停下来,或者跳过部分内容也是不错的阅读方法。再次,祝贺你,当你看到这篇文章时,你已经开始学会如何把握计算机软件领域的核心所在了!

“不管现在流行什么语言,你都可以肯定十年二十年之后它不再风光。我总是在自己的书中写些不时髦的东西,但这些东西却值得后代子孙记取。” -- Donald E. Knuth
问题由反汇编开始

对已经开始接触反汇编深层的读者,可以已经使用过甚至自己编写过反汇编引擎了,如 x86 Disassembler Librarys 。所谓反汇编即通过 CPU 的指令构造原理将指令的二进制代码转换成助记符 Mnemonic 的过程,而二进制表达的指令就称为操作码 OpCode, 这是 CPU 可以理解的指令形式。那么先来看看一条简单的代码片断,操作码为数据:eb 00 eb fe 90。使用 DEBUG 工具,通过 e cs:ip 命令在当前代码段的入口来输入以下指令代码,输入完一个字节按空格,完成时按回车结束,然后通过 u cs:ip 得到类似以下反汇编代码:

1C8D:0100 eb00    jmp short 0102
1C8D:0102 ebfe    jmp short 0102
1C8D:0103 90      NOP
通过 DEBUG 的汇编命令也可以直接输入以上的汇编程序,现在通过  t 命令来执行单步调试,看看程序如何运行。在 DEBUG 会高亮显示补指令改动过的内容,通过 r 命令来显示当前寄存器的值。其中就有 IP 寄存器,调试时第一条指令执行后,显示 IP 改变为 0102,这是因为 jmp 跳转指令起作用了。第二条指令也是一条 jmp 指令,可留意到其操作码极为相似。可以联想,操作码的第二个字节其实就是跳转的地址,即指令的操作数 Operand。因为第一条指令操作码为 2-Byte,跳转的偏移量为 0x00+2 即跳转到 0x0100+2=0x0102;因此第二条指令跳转的偏移地址应为0xFE+2=0x100,但是由于此指令的操作数为一个字节,因此截留结果的低 8-bit 即最终偏移量为 0,正因此,程序在继续执行时 IP 始终停留在 0x0102。

这里就出现了一个疑问,eb00 这样的操作码是如何得到的呢?这个问题关系到,DEBUG 如何反汇编 eb00 得到 jmp 这样的汇编指令,也关系到 DEBUG 如何将汇编指令转译为 CPU 认识的操作码。

指令操作码构造

先来看一段代码:

1C8D:0100 B80000    mov ax, 0
1C8D:0103 B80100    mov ax, 1
1C8D:0106 BB0000    mov bx, 0
1C8D:0109 BB0100    mov bx, 1
以上程序可以通过推断了解到,操作码 B8 可能代表了 mov ax,BB 代表了 mov bx,寄存器的信息已经包含在操作码,CPU 能够将其与特定的寄存器关联,0、1 这些操作数也包含在指令操作码内。有一个重要的环节,x86 CPU 最初是从 16 位机发展起来的,这种机器的运行时内存通过地址总线直接存取,这种模式就称作实地址模式 Real Mode,与之后来研发的 32 位机 80386 的引入内存分页机制及保护机制,内存可以通过分页的形式来管理并在保护机制下运行,这种模式称为保护模式 Protect Mode,同时为运行 16 位的程序提供了一个虚拟 x86 模式 Virtual 8086 Mode 简称 V86,这种模式通过虚拟机技术实现,能有效地利用 80386 的多任务特性来运行多个实模式程序。在这之前是没有 eax 这样的 32 位的寄存器,但是由向下兼容的要求,以上指令的操作码在 16 位机上具有相同的作用。尽管如此,因为后来的 CPU 指令集在增长,在不同的模式下,相同的操作码会出现的不同的功能,例如:

1C8D:0100 66B800000000    mov eax, 00
在 16 位机上,将会解释为:

1C8D:0100 66        DB 66
1C8D:0101 B80000    mov ax, 0
1C8D:0104 0000      mov [bx+si], al
可见,CPU 的运行模式对操作的解码很重要。为了深入操作码的内部,那么只有找 CPU 厂商了,x86 当然就是找 Intel 了,它共享发布有开发者手册共为3卷7个PDF。到 Intel® 64 架构发布后,其对应的手册已经整理成了全集组成一个 PDF 文档。这套手册可以说是低层学习的必需文档,它详尽地记录了开发人员需要了解的 CPU 信息,第二卷全三册指令系统作为开发人员最基本的要求,自然也就是重点。

前面说了这些就是为了热身,在 x86 架构上,一个指令即操作码由6个部分构造而成:

Prefixes 指令代码的前缀,每指令最多可以有4个/种前缀修饰。有操作数大小前缀,如前面提到的 66,它指定 32-bit 操作数大小,FE 前缀则表示 8-bit 操作数,按 16 位机上的传统,默认的操作为 16 位,不使用前缀。有重复类型前缀,如最常见的 F3 表示 REP、REPE、REPZ 重复前缀,还有 F2 表示 REPNE、REPNZ 前缀。有段超越前缀,2E 对应 CS、36 对应 SS、3E 对应 DS、26 对应 ES,64、65 则对应 FS、GS,段超越是可内存寻址有关的内容。还有寻址地址大小前缀,67 表示 32-bit 内存寻址。以及官方手册中提及的一些特别功能的前缀,这些前缀可以按任意的顺序与指令码组合;
Code 指令本体,且称指令码,是唯一必需的部分,如前面 B80000 中的 B8 就是一个 Code。然而,指令码还可能有一个额外的 3-bit 存放在 ModR/M 处;
ModR/M,尽管这部分可能包含指令码的一额外字节,但它主要功能是用来标识操作数的内存寻址信息,就此称作寻址模式,显得恰当。它被细分为三个区域,高 2-bit 为 mod 区、低 3-bit 为 R/M 区。
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值