9

我只是在编写 PE 文件解析器的过程中,我已经到了想要解析和解释 PE 文件中的实际代码的地步,我假设这些代码存储为 x86 操作码。

例如,DLL 中的每个导出都指向函数将存储在内存中的 RVA(相对虚拟偏移),我编写了一个函数来将这些 RVA 转换为物理文件偏移。

问题是,这些真的是操作码,还是别的什么?

函数在文件中的存储方式是否取决于编译器/链接器,或者它们是一字节还是两字节 X86 操作码。

例如,Windows 7 DLL 'BWContextHandler.dll' 包含四个加载到内存中的函数,使它们在系统中可用。第一个导出的函数是“DllCanUnloadNow”,它位于文件内的偏移量 0x245D。该数据的前四个字节为:0xA1 0x5C 0xF1 0xF2

那么这些是一字节还是两字节的操作码,还是完全不同?

如果有人可以提供有关如何检查这些的任何信息,将不胜感激。

谢谢!

经过进一步阅读,并通过 IDA 的演示版运行文件,我认为我说第一个字节 0xA1 是一个字节操作码是正确的,意思是 mov eax。我是从这里得到的:http ://ref.x86asm.net/geek32.html#xA1我假设它暂时是正确的。

但是,对于后面的字节如何构成指令的其余部分,我有点困惑。从我所知道的 x86 汇编器中,移动指令需要两个参数,即目标和源,因此该指令是将(某物)移动到 eax 寄存器中,并且我假设某物出现在以下字节中。但是我还不知道如何阅读该信息:)

4

2 回答 2

6

x86 编码是复杂的多字节编码,您不能像在 RISC (MIPS/SPARC/DLX) 中那样简单地在指令表中找到一行来对其进行解码。一条指令甚至可以有 16 字节编码:1-3 字节操作码 + 几个前缀(包括多字节 VEX) + 几个字段来编码立即或内存地址、偏移量、缩放(imm、ModR/M 和 SIB;moffs)。有时单个助记符有几十个操作码。而且,在某些情况下,同一 asm 行有两种可能的编码(“inc eax”= 0x40 和 = 0xff 0xc0)。

一字节操作码,意思是 mov eax。我是从这里得到的:http ://ref.x86asm.net/geek32.html#xA1我假设它暂时是正确的。

让我们看一下桌子:

宝; 佛罗里达州; 助记符;操作1;操作2; grp1 ; grp2 ; 描述

A1 ; W; 移动;eAX ; OV; 创; 数据移动;移动 ;

(提示:不要使用 geek32 表,切换到http://ref.x86asm.net/coder32.html#xA1 - 具有更多解码的更少字段,例如“A1 MOV eAX moffs16/32 Move”)

有用于操作数的列 op1 和 op2,http ://ref.x86asm.net/#column_op。A1 操作码的第一个总是eAX,第二个 (op2) 是 Ov。根据表http://ref.x86asm.net/#Instruction-Operand-Codes

O / moffs 原始指令没有ModR/M字节;操作数的偏移量在指令中编码为字、双字或四字(取决于地址大小属性)。不能应用基址寄存器、变址寄存器或比例因子(仅限 MOV (A0, A1, A2, A3))。

因此,在 A1 操作码之后,内存偏移量被编码。我认为,x86(32 位模式)有 32 位偏移。

PS:如果您的任务是解析 PE 而不是发明反汇编程序,请使用一些 x86 反汇编库,例如 libdisasm 或 libudis86 或其他任何东西。

PPS:对于原始问题:

问题是,这些真的是操作码,还是别的什么?

是的,“A1 5C F1 F2 05 B9 5C F1 F2 05 FF 50 0C F7 D8 1B C0 F7 D8 C3 CC CC CC CC CC”是x86机器码。

于 2012-12-07T16:47:10.253 回答
5

反汇编很困难,特别是对于由 Visual Studio 编译器生成的代码,特别是对于 x86 程序。有几个问题:

  1. 指令是可变长度的,并且可以从任何偏移量开始。一些架构需要指令对齐。不是 x86。如果您从地址 0 开始读取,那么如果您从偏移量 1 开始读取,您将得到不同的结果。您必须知道有效的“起始位置”(函数入口点)是什么。

  2. 并非可执行文件的文本部分中的所有地址都是代码。有些是数据。Visual Studio 会将“跳转表”(用于实现 switch 语句的数组)放在读取它们的过程下方的文本部分中。将数据误解为代码会导致您产生不正确的反汇编。

  3. 您不可能拥有适用于所有可能程序的完美反汇编。程序可以自行修改。在这些情况下,您必须运行程序才能知道它的作用,而这最终会导致“停止问题”。您可以期望的最好的结果是反汇编适用于“大多数”程序。

通常用于尝试解决这些问题的算法称为“递归下降”反汇编。它的工作方式类似于递归下降解析器,因为它从已知的“入口点”(exe 的“main”方法或 dll 的所有导出)开始,然后开始反汇编。在拆卸过程中会发现其他入口点。例如,给定“调用”指令,目标将被假定为入口点。反汇编器将迭代地反汇编发现的入口点,直到找不到更多的入口点。

然而,这种技术存在一些问题。它不会找到仅通过间接执行的代码。在 Windows 上,一个很好的例子是 SEH 异常的处理程序。分派给它们的代码实际上在操作系统内部,因此递归下降反汇编不会找到它们,也不会反汇编它们。但是,它们通常可以通过使用模式识别(启发式匹配)增强递归下降来检测。

机器学习可用于自动识别模式,但许多反汇编程序(如 IDA pro)使用手写模式并取得了很大成功。

无论如何,如果你想反汇编 x86 代码,你需要阅读英特尔手册。有很多场景需要支持。指令中的相同位模式可以根据修饰符、前缀、处理器的隐式状态等以各种不同的方式进行解释。手册中涵盖了所有这些内容。首先阅读第一卷的前几节。这将介绍基本的执行环境。您需要的其余大部分内容都在第二卷中。

于 2012-12-08T20:04:34.637 回答