-
-
[转帖]Ghidra 脚本
-
2020-9-29 16:20 5079
-
Ghidra是由NSA开发的免费和开源的逆向工程工具,工具的文档和实例比较丰富学习起来阻力小。本着尝鲜的目的来学习工具脚本才有这了这篇记录性的文章,希望对你有所帮助。
转载来自安全客
一、hello world 脚本
1 2 3 4 5 6 7 | public class HelloWorld extends GhidraScript { public void run() throws Exception { printf( "Hello World\n" ); / / 格式化输出 println( "Hello World" ); / / 打印字符串并换行 printerr( "Hello World" ); / / 错误消息打印控制台显示红色 } } |
运行控制台打印出Hello World。
二、获取所有的符号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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对象
1 2 3 4 5 6 7 8 9 10 | 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对象可以操作已经识别的函数,比如引用、函数内逻辑的分析、以及函数内的修改。
四、函数的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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中间码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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字符串并不方便,所以需要另外更适合编程的方法,这个留在下一部分。
------ 分割线 ----------
在有了基础知识后就可以根据需求来编写脚本,比如在挖Iot注入漏洞的时候利用脚本去过滤常量字串不可控字串就会非常节省时间。
选择跟踪 system 函数
找到疑似函数还可以增加调用栈跟踪:
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界