Ghidra脚本学习(Java版本)

 

一、Hello World 脚本

public class HelloWorld extends GhidraScript {
                public void run() throws Exception {
                            printf("Hello World\n");            //格式化输出
                            println("Hello World");             //打印字符串并换行
                            printerr("Hello World");            //错误消息打印控制台显示红色
                }
  }

成功运行控制台打印出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对象可以操作已经识别的函数,比如引用、函数内逻辑的分析、以及函数内的修改。

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;
    }
}

控制台信息如下:

pcode中间码

我们拿到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字符

伪C字符串

DecompileResults 调用getDecompiledFunction()函数返回DecompiledFunction对象,这个对象中的getC()方法可以返回编译好伪C。当然这里输出伪C并不是唯一方法,另外还有两种方法也可以输出伪C。因为有些需求场景使用伪C字符串并不方便,所以需要另外更适合编程的方法,这个留在下一部分。

(完)