从python到ghidra jython的rpc桥
ghidra-bridge的Python项目详细描述
Ghidra桥
Ghidra很棒,我喜欢尽可能多地编写脚本。但是ghidra的python脚本是基于jython的,现在jython的状态不太好(并不是说ida的python环境更好……)。如果新软件包甚至可以在Jython环境中运行,那么安装新软件包就很麻烦,而且随着Python2慢慢关闭,情况只会变得更糟。
因此,ghidra bridge是一种回避这个问题的努力,它不是被困在jython中,而是为python对象设置一个rpc代理,这样我们就可以调用ghidra/jython land来获取我们需要的数据,然后将它带回到一个更为新的python中,其中包含了完成工作所需的所有包。
目标是尽可能透明,因此一旦设置好,就不需要知道对象是本地的还是远程的ghidra-网桥应该无缝地处理对它的获取/设置/调用。
如何用于ghidra
- 将ghidra_bridge目录的路径添加为ghidra脚本管理器中的脚本目录(脚本管理器顶部红色加号左侧的"3行"按钮)
- 从ghidra脚本管理器运行ghidra_bridge_server.py
- 在客户端python环境中安装ghidra_bridge(打包在https://pypi.org/project/ghidra-bridge/" rel="nofollow">https://pypi.org/project/ghidra bridge/):
pip install ghidra_bridge
- 从客户端python:
import ghidra_bridge
with ghidra_bridge.GhidraBridge(namespace=globals()):
print(getState().getCurrentAddress().getOffset())
ghidra.program.model.data.DataUtilities.isUndefinedData(currentProgram, currentAddress)
或
import ghidra_bridge
b = ghidra_bridge.GhidraBridge(namespace=globals()) # creates the bridge and loads the flat API into the global namespace
print(getState().getCurrentAddress().getOffset())
# ghidra module implicitly loaded at the same time as the flat API
ghidra.program.model.data.DataUtilities.isUndefinedData(currentProgram, currentAddress)
安全警告
请注意,在运行时,ghidra网桥服务器有效地将代码执行作为服务提供。如果攻击者能够与运行ghidra网桥的端口进行对话,则他们可以使用运行ghidra的权限轻松获得执行。
还要注意,用于发送和接收Ghidra网桥消息的协议是未加密和未经验证的-中间人攻击将允许完全控制命令和响应,再次在服务器上提供琐碎的代码执行(在e客户端)。
默认情况下,ghidra网桥服务器只监听本地主机,以稍微减少攻击面。只有当你确信自己在一个安全的网络上时,才能监听外部网络地址。此外,攻击者仍有可能向本地主机发送消息(例如,通过浏览器中的恶意javascript,或通过利用不同的进程并攻击ghidra网桥来提升权限)。您可以通过从具有较低权限的ghidra服务器(非管理员用户或容器内)运行ghidra网桥、仅在需要时运行它或在非网络连接的系统上运行来降低此风险。
远程评估
ghidra桥被设计为透明的,允许在不做太多更改的情况下轻松移植非桥接脚本。但是,如果您乐于进行更改,并且由于运行大量远程查询而遇到了速度减慢的问题(例如,currentprogram.getfunctionmanager().getfunctions():dosomething()中的函数的速度会很慢,因为每个函数都会有大量的函数结果生成一条跨网桥的消息),您可以使用bridge.remote_eval()函数一次请求在网桥服务器上评估结果,这只需要一次消息往返。
下面的示例演示如何获取二进制文件中所有函数的名称列表:
import ghidra_bridge
b = ghidra_bridge.GhidraBridge(namespace=globals())
name_list = b.bridge.remote_eval("[ f.getName() for f in currentProgram.getFunctionManager().getFunctions(True)]")
如果您的评估需要一段时间,您可能需要使用timeout_override参数来增加桥在判断出问题之前等待的时间。
如果需要为远程求值提供参数,可以为远程求值函数提供任意关键字参数,该函数将作为本地变量传递到求值上下文中。以下参数传入函数:
import ghidra_bridge
b = ghidra_bridge.GhidraBridge(namespace=globals())
func = currentProgram.getFunctionManager().getFunctions(True).next()
mnemonics = b.bridge.remote_eval("[ i.getMnemonicString() for i in currentProgram.getListing().getInstructions(f.getBody(), True)]", f=func)
作为简化,还需注意评估上下文具有加载到启动服务器的脚本的主脚本中的相同全局变量-对于ghidra网桥服务器,这些全局变量包括平面api和currentprogram等值。
交互模式
通常,ghidra脚本在首次启动时获取ghidra状态和current*变量(currentprogram、currentdaddress等)的实例,并且在脚本运行时不会更新。但是,如果运行ghidra python解释器,它会用每个命令更新其状态,以便currentaddress始终与gui匹配。
为了反映这一点,ghidrabridge将自动尝试确定您是在交互式环境(例如python解释器,ipython)中运行客户机,还是仅仅从脚本中运行客户机。如果是一个交互式环境,它将向ghidra注册一个事件侦听器,并执行一些可疑的幕后诡计,以确保使用gui更改更新状态,使其行为类似于ghidra python解释器。如果给它一个桥接的对象,它也会用伸出手来使用ghidra的帮助来代替help()
。
您不必关心这个问题,但是如果由于某种原因自动检测没有提供所需的结果,则可以在创建客户端ghidrabridge时指定boolean interactive_mode参数,以便根据需要强制打开或关闭它。
工作原理
py包含一个py2/3兼容的python对象rpc代理。一个python环境在一个端口上设置一个服务器,客户端连接到该端口。网桥提供了一些命令,用于在其他环境中对python对象执行远程操作。
典型的第一步是使用要在目标环境中加载的模块远程导入()。这将对远程网桥进行rpc调用,远程网桥将加载模块,然后创建一个bridgehandle来保持其活动状态并在网桥上引用它。然后它将返回到本地网桥,以及模块的可调用和不可调用属性列表。
在本地网桥上,这将被反序列化为一个bridgedobject,它将重写get attribute和setattr以捕获任何get/set到属性字段,并使用网桥句柄引用将它们代理回远程网桥,以便它知道我们是哪个模块(或其他对象)正在谈论。
getattribute覆盖也会影响可调用性,因此执行bridged obj.func()实际上会返回一个bridgedCallable对象,然后调用该对象(以及使用中的任何args/kwargs)。这会将调用参数打包到远程网桥,远程网桥识别适当的对象并针对该对象调用调用,然后返回结果。
网桥是对称的,因此本地网桥可以将对本地python对象的引用发送到远程网桥,并在那里使用它们,同时将交互发送回本地网桥(例如,在参数工作时提供回调函数)。
最后,还有一些其他的特性可以让生活变得更简单-桥接对象(python iterators/iterables)在远程环境中将表现为iterators/iterables,表示类型的桥接对象可以继承来创建它们自己的子类(注意这实际上会在远程环境中创建子类——这是设计的,因此可以创建类型来实现Ghidra的Java回调/侦听器的接口,因此如果在Jython环境中创建它们,则更容易确保它们的行为。
设计原则
- 需要在ghidra/jython 2.7和python 3中运行
- 需要易于在ghidra中安装-没有pip安装,只需添加一个目录 (这两个需求排除了我研究过的一些更成熟的python rpc项目)
测试
- 在Windows上测试并使用Ghidra 9.0.4(Jython 2.7.1)<;->;Python 3.7.3 < Automati >在Ghidra 9.0(Jython 2.7.1)<;->;Python 3.5.3 Linux(bskaggs/Ghidra docker image)上进行了Caly测试
待办事项
- 用于服务器控制的ghidra插件(更干净的启动/停止、端口选择、易于打包/安装)
- 干净地处理服务器/客户端的拆卸
- 异常-在我们处理的异常中提取回溯信息以进行回推
- 更好的传输/序列化(json/tcp感觉不对)
- 保留远程查询的统计信息,以便用户可以识别脚本中导致最远程流量的部分以进行优化
- 实例
- Jupyter笔记本
- 更好的线程池控制(不要永远保留所有线程,让一些线程消亡)
贡献者
- thx@fmagin提供更好的ipython支持和更有用的代表!
- 还感谢@fmagin for remote_eval,允许更快的批查询远程处理!