我正处于编写PE文件解析器的过程中,我已经达到了解析和解释PE文件中实际代码的程度,我假设它存储为x86操作码。
作为示例,DLL中的每个导出都指向函数将存储在内存中的RVAs(相对虚拟偏移),并且我编写了一个函数来将这些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寄存器中,并且我假设某些内容来自以下字节。但是我不知道如何阅读这些信息:)
答案 0 :(得分:6)
x86编码是复杂的多字节编码,您不能简单地在指令表中找到单行来解码它,就像在RISC(MIPS / SPARC / DLX)中一样。一条指令甚至可以有16字节编码:1-3字节操作码+几个前缀(包括multibyte VEX)+几个字段用于编码立即或存储器地址,偏移,缩放(imm,ModR / M和SIB;总督们)。单个助记符有时会有几十个操作码。而且,对于几种情况,有两种编码可能是相同的asm行(“inc eax”= 0x40和= 0xff 0xc0)。
一个字节的操作码,意思是mov eax。我从这里得到了这个:http://ref.x86asm.net/geek32.html#xA1而且我认为它暂时是正确的。
让我们看看桌子:
po; flds;助记符op1; op2; grp1; grp2;描述
A1; W; MOV; eAX; Ov; gen;数据;移动;
(提示:不要使用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机器代码。
答案 1 :(得分:4)
反汇编很困难,特别是对于Visual Studio编译器生成的代码,尤其是x86程序。有几个问题:
指令是可变长度的,可以从任何偏移量开始。一些架构需要指令对齐。不是x86。如果你开始在地址0读书,那么你会得到不同的结果然后如果你开始读取偏移1.你必须知道有效的“起始位置”(功能入口点)是什么。
并非可执行文件部分的所有地址都是代码。有些是数据。 Visual Studio将在读取它们的过程下的文本部分中放置“跳转表”(用于实现switch语句的数组)。将数据误解为代码会导致产生错误的拆卸。
你不可能拥有适用于所有可能程序的完美dis-assemby。程序可以自行修改。在这些情况下,你必须运行程序才能知道它的作用,最终导致“暂停问题”。您可以期待的最好的解决方案是适用于“大多数”程序。
通常用于尝试解决这些问题的算法称为“递归下降”拆卸。它类似于递归下降解析器,因为它以已知的“入口点”(exe的“main”方法或dll的所有导出)开始,然后开始反汇编。在拆卸过程中发现了其他入口点。例如,给定“调用”指令,目标将被假定为入口点。反汇编程序将迭代地反汇编已发现的入口点,直到找不到更多入口点。
然而,这种技术存在一些问题。它不会找到仅通过间接执行的代码。在Windows上,一个很好的例子是SEH异常的处理程序。分派给它们的代码实际上是在操作系统内部,因此递归下降分解将无法找到它们,也不会对它们进行反汇编。然而,它们通常可以通过增加模式识别(启发式匹配)的递归下降来检测。机器学习可用于自动识别模式,但许多反汇编程序(如IDA pro)使用手写模式取得了很大成功。
在任何情况下,如果要反汇编x86代码,则需要阅读Intel Manual。有很多场景需要支持。根据修饰符,前缀,处理器的隐式状态等,可以以各种不同的方式解释指令中的相同位模式。这些都在手册中有所涉及。首先阅读第一卷的前几节。这将介绍基本的执行环境。你需要的大部分其他东西都在第二卷。