x86 反汇编/汇编器和编译器
汇编器 比编译器简单得多,通常实现为通过一对一的对应关系将汇编代码转换为二进制机器码。汇编器很少进行优化,除了选择指令的最短形式或填充延迟槽。
由于汇编是一个如此简单的过程,反汇编通常也同样简单。汇编指令和机器码字之间存在一对一的对应关系,因此每个机器码字都将精确地映射到一个汇编指令。然而,反汇编有一些其他困难,无法使用简单的代码字查找来解决。我们将在这里介绍汇编器,并在后面讨论反汇编。
汇编器在最基本的层面上将汇编指令转换为机器码,并且一一对应。它们还可以将命名的变量转换为硬编码的内存地址,并将标签转换为它们在代码中的相对地址。
通常,汇编器不执行代码优化。从汇编器中输出的机器码等效于输入汇编器的汇编指令。一些汇编器在宏的形式下具有高级功能。
在汇编过程中,程序的一些信息会丢失。首先也是最重要的是,程序数据存储在与机器码指令相同的原始二进制格式中。这意味着很难确定程序的哪些部分实际上是指令。请注意,您可以反汇编原始数据,但结果的汇编代码将毫无意义。其次,来自汇编源代码文件的文本信息,例如变量名、标签名和代码注释,在汇编过程中都会被破坏。当您反汇编代码时,指令将相同,但所有其他有用的信息都会丢失。代码将是准确的,但更难阅读。
正如我们将在后面看到的,编译器会导致更多信息丢失,反编译通常非常困难和复杂,以至于几乎不可能准确地完成。
由于基于 Intel 的 IA-32 微处理器在家庭 PC 市场中无处不在,因此完成的大多数汇编工作(以及此维基教科书中考虑的大多数汇编工作)都是基于 x86 的。许多这些汇编器(或它们的更新版本)也可以处理 amd64/x86_64/EMT64 代码,尽管此维基教科书将主要关注 32 位(x86/IA-32)代码示例。
MASM 是微软的汇编器,是“宏汇编器”的缩写。然而,许多人将其作为“微软汇编器”的首字母缩写,而这种差异根本不是问题。MASM 具有强大的宏功能,能够编写非常低级的语法和使用其宏功能的伪高级代码。MASM 6.15 目前可以从微软免费下载,而 MASM 7.xx 目前作为微软平台 DDK 的一部分提供。
- MASM 使用 Intel 语法。
- MASM 被微软用来实现其 Windows 操作系统的一些低级部分。
- 与普遍的看法相反,MASM 自 1980 年以来一直在不断开发,并且根据需要进行升级。
- MASM 始终由微软与当前平台和可执行文件类型保持兼容。
- MASM 目前支持所有 Intel 指令集,包括 SSE2。
许多用户喜欢 MASM,但更多用户仍然不喜欢它无法移植到其他系统的事实。
TASM 是 Borland 的“Turbo Assembler”,是 Borland 的一个功能性汇编器,可以与 Borland 的其他软件开发工具无缝集成。当前发布版本是版本 5.0。TASM 语法与 MASM 非常相似,尽管它有一个“IDEAL”模式,许多用户更喜欢它。TASM 不是免费的。
NASM,“Netwide Assembler”,是一个免费、可移植且可重新定位的汇编器,可在 Windows 和 Linux 上运行。它支持各种 Windows 和 Linux 可执行文件格式,甚至输出纯二进制文件。NASM 不像 MASM 或 TASM 那样“成熟”,但它
- 比 MASM 更可移植
- 比 TASM 更便宜
- 努力做到非常用户友好
NASM 自带反汇编器 ndisasm
,并支持 64 位(x86-64/x64/AMD64/Intel 64)CPU。
NASM 在 LGPL 下发布。
FASM,“Flat Assembler”,是一个开源汇编器,支持 x86 和 IA-64 Intel 架构。
x86 微处理器汇编代码的 AT&T 语法不像 Intel 语法那样常见,但 GNU 汇编器 (GAS) 使用它,并且它是 Unix 和类 Unix 操作系统上的事实上的汇编标准。
GNU 汇编器 (GAS) 是 GNU 编译器集合 (GCC) 套件的默认后端。因此,GAS 与 GCC 一样可移植且可重新定位。但是,GAS 默认使用 AT&T 语法来表示其指令,一些用户认为它不如 Intel 语法易读。较新的 gas 版本可以使用指令“.intel_syntax noprefix”切换到 Intel 语法。
GAS 的开发专门用于用作 GCC 后端。由于 GCC 始终向它提供语法正确的代码,因此 GAS 通常只有最少的错误检查。
GAS 作为 GCC 包或 GNU binutils 包的一部分提供。 [1]
HLA,“High Level Assembler”的缩写,是 Randall Hyde 主导的一个项目,旨在创建一个具有高级语法的汇编器。HLA 充当其他汇编器的前端,例如 FASM(默认)、MASM、NASM 和 GAS。HLA 支持“常见”汇编语言指令,但也实现了一系列更高层次的结构,例如循环、if-then-else 分支和函数。HLA 附带一个全面的标准库。
由于 HLA 充当了另一个汇编器的前端,因此程序员必须安装另一个汇编器才能使用 HLA 汇编程序。因此,HLA 代码输出与底层汇编器一样好,但对于开发人员来说,代码更容易编写。HLA 的高级组件可能会使程序效率降低,但这种代价通常远远低于编写代码的便利性。在许多方面,HLA 的高级语法与 Pascal 非常相似,而 Pascal 本身在许多方面又与 C 非常相似,因此许多高级程序员会立即掌握 HLA 的许多方面。
以下是一些 HLA 代码的示例
mov(src, dest); // C++ style comments
pop(eax);
push(ebp);
for(mov(0, ecx); ecx < 10; inc(ecx)) do
mul(ecx);
endfor;
一些反汇编器和调试器可以将二进制代码反汇编为 HLA 格式,但没有一个可以忠实地重现 HLA 宏。
一个 编译器 是一个将一种语言的指令转换为另一种语言的等效指令的程序。有一种普遍的误解认为,编译器总是直接将高级语言转换为机器语言,但这并非总是如此。许多编译器将代码转换为汇编语言,而有些编译器甚至将代码从一种高级语言转换为另一种语言。常见的编译语言有:C/C++、Fortran、Ada 和 Visual Basic。下图显示了使用 C 编程语言构建程序的常见编译时步骤。编译器生成目标文件,这些文件被链接起来形成最终的可执行文件
就本书而言,我们将只考虑将 C 或 C++ 转换为汇编代码或机器语言的编译器的案例。一些编译器,例如 Microsoft C 编译器,将 C 和 C++ 源代码直接编译为机器代码。另一方面,GCC 将 C 和 C++ 编译为汇编语言,然后使用汇编器将其转换为相应的机器代码。从反汇编器的角度来看,原始程序的创建方式无关紧要。还要注意,无法完全再现用于创建可执行文件的原始 C 或 C++ 代码。但是,可以创建编译相同的代码,或者执行相同任务的代码。
C 语言语句与汇编语言之间没有一对一的关系。请考虑以下 C 语句通常都会编译成相同的汇编语言代码
*arrayA = arrayB[x++];
*arrayA = arrayB[x]; x++;
arrayA[0] = arrayB[x++];
arrayA[0] = arrayB[x]; x++;
另外,请考虑以下循环结构如何执行相同的任务,以及它们很可能生成相似甚至相同的汇编语言代码
for(;;) { ... }
while(1) { ... }
do { ... } while(1)
本节的目的是列出一些用于开发生产级软件的最常见的 C 和 C++ 编译器。世界上有许多 C 编译器,但反向工程师不需要考虑所有情况,尤其是在查看专业软件时。本页将讨论每个编译器的优缺点、可用性(下载网站或成本信息),以及如何从每个编译器生成汇编列表文件。
Microsoft C 编译器可从 Microsoft 免费获得,作为 Windows Server 2003 SDK 的一部分。它与 MS Visual Studio 中使用的编译器和库相同,但没有附带花哨的 IDE。MS C 编译器具有非常好的优化引擎。它编译 C 和 C++,并可以选择将 C++ 代码编译为 MSIL(.NET 字节码)。
Microsoft 的编译器只支持 Windows 系统和 Intel 兼容的 16/32/64 位架构。
Microsoft C 编译器是cl.exe,链接器是link.exe
在本维基教科书中,cl.exe 经常被用来生成 C 源代码的汇编列表文件。要自己生成汇编列表文件,请使用以下语法
cl.exe /Fa<assembly file name> <C source file>
"/Fa" 开关是告诉编译器生成汇编列表文件的命令行选项。
例如,以下命令行
cl.exe /FaTest.asm Test.c
将从 C 源文件 "Test.c" 生成一个名为 "Test.asm" 的汇编列表文件。请注意,"/Fa" 开关和输出文件名称之间没有空格。
GNU C 编译器是 GNU 编译器集合 (GCC) 套件的一部分。该编译器适用于大多数系统,并且是免费软件。许多人专门使用它,以便他们只需处理一个编译器就可以支持多个平台。GNU GCC 编译器是 Linux 和 Unix 系统的事实上的标准编译器。它是可移植的,允许使用多种输入语言(C、C++、Obj-C、Ada、Fortran 等),并支持多种目标操作系统和架构。它优化得很好,但有一个非激进的 IA-32 代码生成引擎。
GCC 前端程序是“gcc”(在 Windows 上为“gcc.exe”),关联的链接器是“ld”(在 Windows 上为“ld.exe”)。Windows cmd 会自动搜索带“.exe”扩展名的程序,因此您不需要键入文件名扩展名。
要在 GCC 中生成汇编列表文件,请使用以下命令行语法
gcc -S /path/to/sourcefile.c
例如,以下命令行
gcc -S test.c
将生成一个名为“test.s”的汇编列表文件。GCC 生成的汇编列表文件将采用 GAS 格式。在 x86 上,您可以使用-masm=intel
或-masm=att
选择语法。GCC 列表文件通常没有 cl.exe 的列表文件注释和布局那么好。
您可以添加 `-g3` 标志来启用源代码级别的调试符号,以便您可以在列表中看到行号。-fno-asynchronous-unwind-tables
标志可以帮助消除列表中的某些宏。
该编译器仅用于 x86、x86-64 和 IA-64 代码。它适用于 Windows 和 Linux。Intel C 编译器是由发明原始 x86 架构的人员编写的:英特尔。英特尔的开发工具生成针对在英特尔微处理器上运行而优化的代码,旨在从应用程序中挤出每一丝速度。AMD IA-32 兼容处理器不保证获得相同的速度提升,因为它们具有不同的内部架构。
该编译器通常用于经典 MacOS 和嵌入式系统。如果您尝试反向工程消费电子产品,您可能会遇到 Metrowerks CodeWarrior 生成的代码。
该编译器通常用于嵌入式系统。如果您尝试反向工程消费电子产品,您可能会遇到 Green Hills C/C++ 生成的代码。