Ghidra源码分析(二)

接上篇

这里以对 Dex 文件的分析为例,来看看 Ghidra 是如何进行文件分析的。

Instruction 和对应 Pcode 的反编译。

以及 References 的建立!!!

 

DexHeaderFormatAnalyzer

DexHeaderFormatAnalyzer.analyze 函数开始。该类在 Features FileFormats ghidra.file.formats.android.dex.analyzer 包下面。

主要完成了以下几步操作:

  1. 首先获取当前工程 AnalysisState(这里是 DexAnalysisState)。这个类和 DexHeaderFormatAnalyzer 在同一个包下面。
  2. 然后通过 DexAnalysisState.getHeader 获取工程的 DexHeader。并使用 DexHeaderFormatAnalyzer.processHeader 方法来进行 Header 相关部分的处理。
  3. 再通过 DexHeaderFormatAnalyzer.createInitialFragments 来初始化 Fragments
  4. 接下来就是进一步处理 StringTypePrototypeFieldMethod 和 Class 等等数据。

上面三段主要主要牵扯到文件内容读取和相关存储创建之类的,不做考虑。

而第四部分的 processStrings、processTypes、processMethods 之类的这些实际就是对 DexFile 的深入解析。这里就不讲了。我们更加关注 creatMethods 函数,这里是真正处理 CodeItem 的地方。

DexHeaderFormatAnalyzer.createMethods

进入 DexHeaderFormatAnalyzer.createMethods 函数,有好几个 createMethods 的重载函数,这些主要就是来处理 method 内部指令内容用的。处理有 CodeItem 函数的部分就在这里。

这里关注 DexHeaderFormatAnalyzer.disassembleMethod 部分。

DisassembleCommand.applyTo 调用 DisassembleCommand.doDisassembly 函数负责完成对方法的反编译操作。后面的对局部变量、返回值、参数、调试信息等的处理不进行分析。该类位于 Features Base ghidra.app.cmd.disassemble 包下面。

函数中会选择合适的 Disassembler。对需要反编译的地址进行些操作,比如查重(比如 disassembledAddrs 成员变量就保存了已经反编译过的指令的地址)、判断是否是 Data 段、对齐等

然后生成一个 AddressSet 也就是 seedSet 里面包含要反编译的地址。

最终都是调用 DisassembleCommand.doDisassemblySeeds 函数,实际上走到其中一个 Disassembler.disassemble 函数。该类位于 Framework SoftwareModeling ghidra.program.disassemble 包下面。

这里是专门对指令进行反编译的地方。会循环从地址上读取内容到 InstructionBlock 到并进行反编译,同时还会对反编译结果 InstructionSet 进行 Pcode 的翻译

 

(成员变量 listing ListingDB 类型。在 Framework SoftwareModelingghidra.program.database 包下面。

工程在处理的时候会生成各种各样的数据结构,比如 CodeUnitInstructionFunctionDataFragment 之类的,这些数据结构中包含了具体的内容。

而且同时这些数据结构有对应的 XXXManager 来处理它们的集合 XXXDB

* Database implementation of Listing. class ListingD8 implements Listing { private ProgramDB program; private CodeManager codeMgr; private TreeManager treeMgr; private Functionmanager functionMgr; * Set the program. public void setProgram(ProgramDB program) { this . program = program; codeMgr = program. getCodeManager(); treeMgr = program. get Treemanager(); functionMgr = program.getFunctionManager();

这里 ListingDB 提供函数来操作这些 XXXManager,获取相应的信息。)

 

接着调用 Disassembler.disassembleNextInstructionSet 函数。

(这里的 InstructionBlock 是指一个控制流上的 Block

指令会一直被反编译到 invoke-directreturn 之类代表 Block 结束的指令上。

而对于 if_ez if_nez 类似的跳转指令,它们会产生不同的控制流。一个跳转到另外一个地址成为新的 Block,但是另外一个控制流的地址就在当前地址的下方,被算作进了同一个 Block。当然还有 goto 指令,这个处理方法类似。)

本文原创,作者:Galaxy,其版权均为Galaxy Lab所有。如需转载,请注明出处:http://galaxylab.pingan.com.cn/ghidra%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90%e4%ba%8c/

发表评论

*