PE文件操作码

10
我正在编写一个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寄存器,我假设这些内容在接下来的字节中。但是我还不知道如何读取这些信息 :)

2
这篇相关的帖子https://dev59.com/p3I95IYBdhLWcg3wvwsQ包含了很多有用的信息,可以帮助你。 - fvu
“.text” 部分可以包含代码和只读数据(但主要是代码)。您可以使用反汇编器来确定哪些对应于哪些。 - Sedat Kapanoglu
感谢ssg,我在顶部提到的代码位于.text部分,因此它可能是代码或数据,但我不确定。是否可以反汇编Windows DLL?如果可以,您能推荐一个Windows反汇编器吗?我知道IDA,但我也知道它不是免费的。感谢您的评论! - Tony
3
请使用/dismas选项在您的可执行文件上运行dumpbin.exe,以查看它是如何实现的。请注意,您正在重复发明轮子。始终比较dumpbin.exe告诉您的内容和您显示的内容,以确保它不是一个死循环。 - Hans Passant
2
顺便说一下,我刚刚发现了这个很棒的网站:http://www.onlinedisassembler.com/odaweb/run_hex <-- 对于少量代码来说比运行IDA快得多。 - szx
显示剩余4条评论
2个回答

7
x86编码是复杂的多字节编码,你不能像在RISC(MIPS/SPARC/DLX)中那样在指令表中找到单行来解码它。一个指令甚至可以有16个字节的编码:1-3字节的操作码+几个前缀(包括多字节VEX)+几个字段来编码立即数或内存地址、偏移量、比例因子(imm、ModR/M和SIB;moffs)。对于单个助记符,有时甚至会有几十个操作码。此外,对于几种情况,同一条汇编指令可能有两种不同的编码方式("inc eax" = 0x40 和 = 0xff 0xc0)。

一个字节的操作码,意思是mov eax。我从这里得到了这个信息:http://ref.x86asm.net/geek32.html#xA1,目前我认为这是正确的。

让我们看一下表格:

po ; flds ; mnemonic ; op1 ; op2 ; grp1 ; grp2 ; Description

A1 ; W ; MOV ; eAX ; Ov ; gen ; datamov ; 移动 ;

(提示:不要使用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 机器码。


非常感谢你,osgx,你回答了我的问题。我会浏览这些页面,看看我能理解多少。再次感谢! - Tony

5

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

  1. 指令长度可变,并且可以从任何偏移开始。某些架构需要指令对齐。但不适用于x86。如果您从地址0开始读取,则与从偏移1开始读取时结果不同。您必须知道有效的“起始位置”(函数入口点)。

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

  3. 您无法拥有完美的反汇编,适用于所有可能的程序。程序可以修改自身。在这种情况下,您必须运行程序以了解其功能,这最终会导致“停机问题”。您所能希望的最好结果是适用于“大多数”程序的反汇编。

用于解决这些问题的算法通常称为“递归下降”反汇编。它的工作方式类似于递归下降解析器,从已知的“入口点”(exe的“main”方法或dll的所有导出)开始反汇编。在反汇编过程中发现其他入口点。例如,给定一个“call”指令,目标将被认为是一个入口点。反汇编器将迭代地反汇编已发现的入口点,直到找不到更多为止。
然而,这种技术存在一些问题。它无法找到仅通过间接执行的代码。在Windows上,一个很好的例子是SEH异常的处理程序。调度到它们的代码实际上在操作系统内部,因此递归下降反汇编将无法找到它们并且无法对它们进行反汇编。但是,通过模式识别(启发式匹配)来增强递归下降可以经常检测到它们。
机器学习可用于自动识别模式,但许多反汇编器(如IDA Pro)使用手写模式并取得了很大的成功。
无论如何,如果您想要反汇编x86代码,您需要阅读Intel手册。有许多需要支持的情况。指令中相同的位模式可以根据修饰符、前缀、处理器的隐含状态等以各种不同的方式进行解释。所有这些都在手册中涵盖。首先阅读第一卷的前几个章节。这将介绍基本的执行环境。您需要的大部分其他内容都在第二卷中。

谢谢Scott。我想我主要是想大致了解PE文件中的代码结构。我试图完成的主要任务是解析PE文件,所以我想我会把反汇编工作留给现有库之一。不过还是感谢您的帖子,非常有启发性! - Tony
Scott,"recursive descent"是IDA中的首选方法吗? - osgx
是的。Ida使用递归下降。 - Scott Wisniewski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接