目录
前言
1、文件类型分析
2、反汇编引擎
2.1、OllyDbg的ODDisasm
2.2、BeaEngine
2.3、Udis86
2.5、AsmJit
2.6、Keystone
2.7、小结
前言
用高级语言编写的程序有两种形式。一种程序是被编译成机器语言在CPU上执行的,例如Visual C++。机器语言与汇编语言几乎是对应的,因此,可以将机器语言转化成汇编语言,这个过程称为反汇编(Disassembler)。例如在x86系统中机器码“EB”对应的汇编语是“jmp short xx”。另一种程序是一边解释一边执行的,编写这种程序的语言称为解释性语言,例如 Visual Basic 5.0/6.0、Java。这类语言的编译后程序可以被还原成高级语言的原始结构,这个过程称为反编译(Decompiler)。
所谓静态分析,是指通过反汇编、反编译手段获得程序汇编代码或源代码,然后根据程序清单分析程序的流程,了解模块所完成的功能。
1、文件类型分析
逆向分析程序的第一步就是分析程序的类型,了解程序是用什么语言编写的或用什么编译器编译的,以及程序是否被某种加密程序处理过,然后才能有的放矢,进行下一步工作。这个分析过程需要文件分析工具的辅助。常见的文件分析工具有 PEiDExeinfoPE等。此类具可以检测大多数编译语言、病毒和加密软件,本节以PEiD为例简单讲解它们的用法。
PEiD是一款常用的文件检测分析工具,具有 GUI界面。它能检测大多数编译语言、病毒和加密的壳。如下图所示被分析的文件是用Microsoft VisualC++5.0/6.0编译的对无法分析出类型的文件可能报告“PEWin GUT”(“Win CUI”是Widows图形用户界面程序的统称。在使用时通过Options”菜单勾选“RegisterShellExtensions”选项即可在右键快捷菜单中添加相应的选项。
提示:PEiD这里推荐使用吾爱版本,放心安全的使用。下载请自行无吾爱官网下载。
PEiD这类文件分析工具是利用特征申搜索来完成识别工作的。各种开发语言都有固定的启动代码,利用这一点就可以识别程序是由何种语言编译的。被加密程序处理过的程序中会留下加密软件的相关信息,利用这一点就可以识别程序是被何种软件加密的。
如下图的这个就是未加密的。
PEiD提供了一个扩展接口文件userdb.txt用户可以自定义一些特征码,这样就可以识别新的文件类型了。签名的制作可以用Add Sigmature插件完成必要时还要用0llyDbg等调试器配合进行修正。
有些外壳程序为了欺骗 PEiD 等文件识别软件,会将一些加壳信息去除,并伪造启动代码。例如将入口代码改成与用VisuaC++6.0所编程序口处类似的代码,即可达到欺骗的目的。所以文件识别工具给出的结果只能作为参考,至于文件是否被加壳处理过,要跟踪分析程序代码才能知道。
2、反汇编引擎
在安全软件和保护软件的开发过程中经常会用到汇编引擎和反汇编引擎,例如0llyDhg、IDAVMProtect、加壳软件和反编译器等。反汇编引整的作用是把机器码解析成汇编指令。开发反汇编引擎需要对Intel的386机器指令编码有深人的了解。不过,一般不需要自己开发反汇编引擎网上有很多开源的或收费的反汇编引擎可以使用。目前主流的开源x86-64汇编引擎和反汇编引擎,在不同的使用场景中各有优势。下面对常用的汇编引擎和反汇编引擎进行比较,反汇编引擎有ODDisasm、BeaEngine、Udis86、Capstone,汇编引警有ODAssemhler、Keystone、AsmJit。
2.1、OllyDbg的ODDisasm
OllyDbg自带的反汇编引擎ODDisasm,优点是具有汇编接口(即文本解将文本字符串解析并编码成二进制值)这个特性曾经独树一帜。近些年出现的调试器x64_dbg功能与0llyDbg的文本解析功能相似,支持的指令集更加完整,Bug更少,同时支持x64平台。
ODDisasm的缺点很多,举例如下。
- 支持的指令集不全。0llyDbg不再更新对MMX指集支持不全而InteAMD的扩展指令集标准更新了多个版本,因此它无法解析SSE5、AVX、AESXOP指令集。
- 解码得到的结构不够详细。例如对指令前缀的支持不够友好,这一点可以从ollyDbg的反汇编窗口中看出来(除了moscmps等指令repcc与其他指令组合时都是单独显示的)。
- 作者一次性开源后便不再维护开源版本,很难及时修复反汇编中的Bug。
- 不支持64位指令的汇编和反汇编。
不过,存在这些缺点也是可以理解的,因为作者开发DDisasm的目的是进行文本汇编与反汇编,所以没有为解码的信息建立结构体及接口。总的来说,ODDisasm反汇编引擎已经落后于时代了。
2.2、BeaEngine
BeaEngine没有明显的缺点,能解析的扩展指令集有FPU、MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2、VMX、CLMUL、AES、MPX。BeaEngine对指令进行了分类,以便判断不同的指令。BeaEngine 还有一个特点是可以解码每一条指令所使用和影响的寄存器,包括标志位寄存器,甚至能精确解码标志位寄存器的所有位置,这个功能用来做优化器和混淆器是很有优势的。
BeaEngine除了支持对x86指令进行反汇编,还支持对x64指令进行反汇编。BeaEngine 的编码风格有些杂乱,例如对各种变量进行强制转换及使用多种命名风格。如果不在意这些,BeaEngine的性能还是不错的。
2.3、Udis86
Udis86是一款广受欢迎的反汇编引擎支持的x86扩展指今集包括MMX、FPU(x87)、AMD3DNowl、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2、AES、AMD-V、INTEL-VMX、SMXUdis86除了支持对x86指令进行反汇编,还支持对x64指令进行反汇编。Udis86的代码风格精简,功能丽数短小,变量命名和接口干净、简单操作灵活。如果需要自行维护一个分支,使用Udis86几十分钟就能熟悉整个代码架构。
Udis86的优点是接口灵活可以使用ud_decode函数对一条指令只进行解码操作,再对解码后的结构使用ud_translate_intel函数转换成文本格式也可以直接使用ud_disassemble函数一次性完成所有操作,这些接口都只需要一行代码就能实现。
Udis86的这种组合模式设计理念使它可以适应各种场景。例如开发一个像IDA那样的反汇编器,以及开发指令模拟器、分析器、优化器混器等。这种理念使 Udis86在拥有强大适应能力的同时兼顾了性能。在解码细节和能力相近的情况下,Udis86是解码速度最快的反汇编引擎。
2.4、Capstone
Capstone可以说是所有反汇编引中的集大成者。因为Capstone移植自LLVM框架的MC组件的一部分所以LLVM支持的CPU架构它也都支持Capstone 支持的CPU架构有ARM、ARM64(ARMv8)、M68K、MIPS、PowerPC、SPARC Systemz、TMS320C64X、XCORE、x86(包括x86-64)而且,Capstone对x86架构指令集的支持是最全的,这一点是其他引擎比不上的它支持的x86展指令集有3DNow、3DNowa、x86-64ADX、AES、AomAVX、AVX2、AVX512CD、AVX512ERAVX512F、AVX512PF、BMI、BMI2、FMA、FMA4、FSCSBASE、LZCNT、MMX、SCX、SHA、SLM
SSE、SSE2、SSE3、SSE4.1、SSE4.2、SSE4A、SSSE3、TBM、XOP在目前移动端开发火热的背景下,支持ARM的反汇编库却很少。如果要同时进行x86与ARM下编译器方面的开发能使用统一的接口自然更好。仅从x86-64平台上的情况来看无论是解码能力还是指令集支持,Capstone完全超越了BeaEngine。
2.5、AsmJit
AsmJit是一个以C++封装的完整的JIT汇编器和编译器它可以生成容x86和x64架构的原生汇编指令,支持x86和x64指令集包括MMX、SSExBMIxADXTBMXOPAVXxFMAxAVX512等。
AsmJit与前面介绍的开源库都不一样它不像BeaEngine、Udis86、Capstone那样能对二进制指令进行反汇编解析它只是一个汇编器。与OllyDbg 的汇编器XEDParse相比(这些都是基于文本的汇编)AsmJit的汇编方式也完全不同。一个简单的例子如下。
#include <asmjit/asmjit.h>
using namespace asmjit;
int main(int argc, char* argv[]) {
// Create JitRuntime and X86 Compiler.
JitRuntime runtime;
X86Compiler c(&runtime);
// Build function having two arguments and a return value of type 'int'.
// First type in function builder describes the return value. kFuncConvHost
// tells compiler to use a host calling convention.
c.addFunc(kFuncConvHost, FuncBuilder2<int, int, int>());
// Create 32-bit variables (virtual registers) and assign some names to
// them. Using names is purely optional and only greatly helps while
// debugging.
X86GpVar a(c, kVarTypeInt32, "a");
X86GpVar b(c, kVarTypeInt32, "b");
// Tell asmjit to use these variables as function arguments.
c.setArg(0, a);
c.setArg(1, b);
// a = a + b;
c.add(a, b);
// Tell asmjit to return 'a'.
c.ret(a);
// Finalize the current function.
c.endFunc();
// Now the Compiler contains the whole function, but the code is not yet
// generated. To tell compiler to generate the function make() has to be
// called.
// Make uses the JitRuntime passed to Compiler constructor to allocate a
// buffer for the function and make it executable.
void* funcPtr = c.make();
// In order to run 'funcPtr' it has to be casted to the desired type.
// Typedef is a recommended and safe way to create a function-type.
typedef int (*FuncType)(int, int);
// Using asmjit_cast is purely optional, it's basically a C-style cast
// that tries to make it visible that a function-type is returned.
FuncType func = asmjit_cast<FuncType>(funcPtr);
// Finally, run it and do something with the result...
int x = func(1, 2);
printf("x=%d\n", x); // Outputs "x=3".
// The function will remain in memory after Compiler is destroyed, but
// will be destroyed together with Runtime. This is just simple example
// where we can just destroy both at the end of the scope and that's it.
// However, it's a good practice to clean-up resources after they are
// not needed and using runtime.release() is the preferred way to free
// a function added to JitRuntime.
runtime.release((void*)func);
// Runtime and Compiler will be destroyed at the end of the scope.
return 0;
}
2.6、Keystone
Keystone和Capstone是同一系列的引擎,由同一维护者主导开发。Capstone主要负责跨平台多指令集的反汇编工作而Keystone主要负责跨平台多指令集的汇编工作。与0llyDbg的汇编器一样Keystone也只支持文本汇编,不支持像AsmJit那样的函数式汇编。
Keystone也移植自LLVM框架中MC组件的一部分所以LLVM支持的CPU架构Keystone也都支持。Keystone支持的CPU架构有ARM、ARM64(AArch64/ARMv8)、HexagonMIPS、PowerPC、SPARC、Systemzx86(包括16位、32位、64位)。
2.7、小结
还有一些小众的反汇编引擎,例如XDELDsm。XDE的代码小巧灵活很多小型软件都喜欢使用它。Blackbone中的一个长度反汇编引擎也值得一提名字叫“Ldasm”其实它算不上一个引擎因为它只有一个函数,作用只是计算一条指令的长度,但它在 Hook 的重定位跳转指令中很有用。
下面对Udis86BeaEngine和Capstone这3款常用的反汇编引擎进行比较分析。
- 性能:Udis86>BeaEngine>Capstone。
- 解码能力:Capstone>BeaEngine>Udis86(Udis86不支持寄存器分析其他解码能力相近)。
- 平台支持:Capstone>Udis86;Udis86=BeaEngine。
- x86扩展指令集:Capstone>Udis86;Udis86=BeaEnginea