要自行开发Ghidra脚本,需要单击脚本管理器菜单上的创建新脚本选项功能。 随后将决定使用哪种编程语言来开发对应的脚本:
如果决定使用Java语言来编写Ghidra脚本,Ghidra脚本的大致框架将由三部分组成。 第一部分是注释信息:
//TODO write a description for this script
//@author
//@category _NEW_
//@keybinding
//@menupath
//@toolbar
有些注释是很容易理解的,但其中还是有一些值得一提。 例如,@menupath指定允许在启用脚本时将其放在菜单中的具体位置:
图2 – 启用脚本管理器与查看Ghidra中的具体代码
请注意,路径必须由.字符来进行拆分:
//@menupath Tools.Packt.Learn Ghidra script
之前的源代码注释生成了以下内容 并与Ghidra菜单的脚本进行集成:
接下来Ghidra脚本最重要的功能则是导入。 所有脚本必须从该类继承并实现run()方法(这是脚本的主要方法):
import ghidra.app.script.GhidraScript;
import ghidra.program.model.mem.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.util.*;
import ghidra.program.model.reloc.*;
import ghidra.program.model.data.*;
import ghidra.program.model.block.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.scalar.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.address.*;
所有导入的函数都记录在Ghidra的Javadoc文档中;在开发Ghidra脚本时可以进行参考。
Javadoc Ghidra API文档 可以通过单击帮助,然后单击Ghidra API帮助,如果Ghidra的JavaDoc文档不存在,将自动生成。 能够通过以下路径访问上述导入包的文档:
/api/ghidra/app/script/package-summary.html/api/ ghidra/program/model/.
脚本的主体继承自GhidraScript,其中run()方法必须使用自己的代码进行实现。 可以在具体实现中设置以下几种的Ghidra脚本状态:currentProgram、currentAddress、currentLocation、currentSelection和currentHighlight:
public class NewScript extends GhidraScript {
public void run() throws Exception {
//TODO Add User Code Here
}
}
如果想使用Python来编写Ghidra脚本,其使用的API与Java相同,脚本的主要框架包含一个标头(脚本的其余部分必须用自己的代码填充),它与Java非常相似:
#TODO write a description for this script
#@author
#@category Strings
#@keybinding
#@menupath
#@toolbar
#TODO Add User Code Here
事实上,Java API可以通过使用Jython来包装 Python,Jython是设计在Java平台上运行的Python编程语言的实现。(Jython项目提供了Python在Java中的实现,为Python提供了在JVM上运行和访问用Java编写的类。)
如果转到Window,然后转到Python,将出现一个Python解释器,允许在Tab键点击时自动完成:
它还允许使用help()函数查看内置的文档。 在开发Ghidra脚本时可以打开Ghidra Python解释器,以快速访问文档、测试代码片段等。 对开发来说非常有用:
在本节中,我们介绍了脚本类及其结构,如何查询使用Ghidra API文档并实现它,以及Python解释器在开发过程中如何帮助我们。 在下一节中,我们将通过编写Ghidra脚本来将其付诸实践。
脚本开发
现在你知道了实现自己的Ghidra脚本所需的所有东西。 让我们从最基础的开始。 此脚本将允许在没有操作指令(NOP程序集操作码)的情况下修补字节。
现在开始编写脚本。@keybinding允许我们用Ctrl + Alt + Shift + N组合键执行脚本:
//This simple script allows you to patch bytes with NOP opcode
//@author Packt
//@category Memory
//@keybinding ctrl alt shift n
//@menupath Tools.Packt.nop
//@toolbar
import ghidra.app.script.GhidraScript;
import ghidra.program.model.util.*;
import ghidra.program.model.reloc.*;
import ghidra.program.model.data.*;
import ghidra.program.model.block.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.scalar.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.address.*;
然后,我们的脚本需要做的就是在Ghidra(当前位置变量)中获取当前光标位置,然后获取它的地址(第03行),该地址的指令未定义(第06-08行),用NOP指令操作码修补字节,即0x90(第09-11行),然后再次拆解字节(第12行)。 这里的开发参考主要是在提到的Javadoc文档中搜索适当的API函数:
以下是nop功能的Java代码:
// nop_script_Java.java
public class NopScript extends GhidraScript {
public void run() throws Exception {
Address startAddr = currentLocation.getByteAddress();
byte nop = (byte)0x90;
try {
int istructionSize = getInstructionAt(startAddr).getDefaultFallThroughOffset();
removeInstructionAt(startAddr);
for(int i=0; i<istructionSize; i++){
setByte(startAddr.addWrap(i), nop);
}
disassemble(startAddr);
}
catch (MemoryAccessException e) {
popup("Unable to nop this byte");
return;
}
}
}
将这段代码使用Python会更简洁。如以上代码,API的使用对两种语言都是一样的:
以下是Python代码 nop脚本的具体实现:
# nop_script_python.py
#This simple script allows you to patch bytes with NOP opcode
#@author Packt
#@category Memory
#@keybinding ctrl alt shift n
#@menupath Tools.Packt.Nop
#@toolbar
currentAddr = currentLocation.getByteAddress()
nop = 0x90
instructionSize = getInstructionAt(currentAddr).getDefaultFallThroughOffset()
removeInstructionAt(currentAddr)
for i in range(instructionSize):
setByte(currentAddr.addWrap(i), nop)
disassemble(currentAddr)
在本节中,我们介绍了如何用两种语言Java和Python编写简单的Ghidra脚本。
总结
在本章中,我们学习了如何使用现有的Ghidra脚本,通过修改相关脚本使其适应我们的需求。如何使用Java或Python语言开发一个非常简单的脚本来协助我们进行逆向分析。
原文始发于微信公众号(山石网科安全技术研究院):逆向工程系列 | Ghidra for Beginner IV