一、Hello World 脚本
public class HelloWorld extends GhidraScript {
public void run() throws Exception {
printf("Hello World\n"); //格式化输出
println("Hello World"); //打印字符串并换行
printerr("Hello World"); //错误消息打印控制台显示红色
}
}
成功运行控制台打印出Hello World。
二、获取所有的符号
public class HelloWorld extends GhidraScript {
@Override
public void run() throws Exception {
//TODO Add User Code Here
SymbolTable st = currentProgram.getSymbolTable();
SymbolIterator iter = st.getSymbolIterator(true);
int count = 0;
while (iter.hasNext() && !monitor.isCancelled()) {
Symbol sym = iter.next();
if (sym != null) {
printf("\t%s\n",sym.getName());
count++;
}
}
println(count+" symbols");
}
}
控制台输出如下:
脚本中使用currentProgram来获取符号表,这里currentProgram为抽象类GhidraScript提供的变量。 软件中的参数和信息都是通过这些变量来获取的,常用的变量有如下几个:
- currentProgram 当前项目的程序
- currentAddress Ghidra工具中闪烁光标位置的地址对象
- currentLocation 工具中当前光标位置的程序位置;如果不存在程序位置,则为null
- currentSelection 当前的选中对象;如果不存在选择,则为null
- currentHighlight 工具中的当前突出显示;如果不存在突出显示,则为null
脚本开发中使用最多的是currentProgram了。
三、获取所有的Function对象
public class HelloWorld extends GhidraScript {
@Override
public void run() throws Exception {
FunctionIterator iterator2 =currentProgram.getListing().getFunctions(true);
for (Function function : iterator2) {
printf("\t%s\n",function.getName());
}
}
}
Function对象可以操作已经识别的函数,比如引用、函数内逻辑的分析、以及函数内的修改。
四、函数交叉引用
public class HelloWorld extends GhidraScript {
@Override
public void run() throws Exception {
FunctionIterator iterator2 =currentProgram.getListing().getFunctions(true);
for (Function function : iterator2) {
if (function.getName().equals("write")){
printf("\t%s @ %s\n",function.getName(),function.getEntryPoint());
Reference[] references = getReferencesTo(function.getEntryPoint());
for (Reference reference : references) {
Function referencefunction=getFunctionContaining(reference.getFromAddress());
if (referencefunction!=null&& !referencefunction.isThunk())
printf("\t\t%s @ %s\n",referencefunction.getName(),referencefunction.getEntryPoint().toString());
}
}
}
}
}
控制台输出信息如下:
讲解下使用的api说明:
- function.getEntryPoint() 是返回函数地址Address对象,二进制里指的是函数所在起始地址用,但在Java中是Address对象对它进行描述。
- function.getName() 是函数名,如果是有符号描述则为符号描述中但字符串,反之就是反汇编引擎随机分配的FUN_xxxx 类似名称。
- getReferencesTo() 是返回Address参数的所有引用,并返回Reference对象的数组。
- getFunctionContaining() 是根据传入的地址返回Function对象,如果是函数起始地址则返回Function对象,如果不是则返回null,所以使用这个API需要加上判断,以免脚本运行错误抛出异常。
五、函数转为P-CODE中间码
public class HelloWorld extends GhidraScript {
private DecompInterface decompInterface=null;
@Override
public void run() throws Exception {
decompInterface = getDecompInterface();
FunctionIterator iterator2 =currentProgram.getListing().getFunctions(true);
for (Function function : iterator2) {
if (function.getName().equals("FUN_000aeabc")){
printf("\t%s @ %s\n",function.getName(),function.getEntryPoint());
DecompileResults results = decompInterface.decompileFunction(function,0,monitor);
Iterator<PcodeOpAST> iterator = results.getHighFunction().getPcodeOps();
while (iterator.hasNext()){
PcodeOpAST op = iterator.next();
printf("%s\n",op.toString());
}
}
}
}
private DecompInterface getDecompInterface() throws DecompileException {
DecompileOptions options = new DecompileOptions();
DecompInterface ifc = new DecompInterface();
ifc.setOptions(options);
ifc.setSimplificationStyle("decompile");
if (!ifc.openProgram(this.getCurrentProgram())) {
throw new DecompileException("Decompiler", "Unable to initialize: "+ifc.getLastMessage());
}
return ifc;
}
}
控制台信息如下:
我们拿到Function对象需要借助DecompInterface对象来做更多事情,上面getDecompInterface()可以定式写成这样,因我们只需要拿到DecompInterface对象其他的不用考虑。
- decompInterface.decompileFunction(function,0,monitor); 参数解析:反编译function,设置等待时间,monitor为常量。通过这个api可以拿到DecompileResults对象,这个对象中存在HighFunction对象变量,HighFunction对象中getPcodeOps()函数可以返回值pcode迭代器,循环迭代就能获取函数里对应的pcode了。
六、函数转伪C代码
public class HelloWorld extends GhidraScript {
private DecompInterface decompInterface=null;
@Override
public void run() throws Exception {
decompInterface = getDecompInterface();
FunctionIterator iterator2 =currentProgram.getListing().getFunctions(true);
for (Function function : iterator2) {
if (function.getName().equals("FUN_000aeabc")){
printf("\t%s @ %s\n",function.getName(),function.getEntryPoint());
DecompileResults results = decompInterface.decompileFunction(function,0,monitor);
printf("%s\n",results.getDecompiledFunction().getC());
}
}
}
private DecompInterface getDecompInterface() throws DecompileException {
DecompileOptions options = new DecompileOptions();
DecompInterface ifc = new DecompInterface();
ifc.setOptions(options);
ifc.setSimplificationStyle("decompile");
if (!ifc.openProgram(this.getCurrentProgram())) {
throw new DecompileException("Decompiler", "Unable to initialize: "+ifc.getLastMessage());
}
return ifc;
}
}
打印输出了伪C字符
DecompileResults 调用getDecompiledFunction()函数返回DecompiledFunction对象,这个对象中的getC()方法可以返回编译好伪C。当然这里输出伪C并不是唯一方法,另外还有两种方法也可以输出伪C。因为有些需求场景使用伪C字符串并不方便,所以需要另外更适合编程的方法,这个留在下一部分。