逆向工程中涉及到多种多样的工具(例如IDA Pro,Angr等),熟练使用这些工具可以化繁为简,使得程序分析工作得以顺利开展。本文介绍众多逆向工具中的一种——Ghidra,它是由美国国家安全局(NSA,National Security Agency)的研究理事会为 NSA 的网络安全任务开发的软件逆向工程(SRE)框架,它有助于分析恶意代码和病毒等恶意软件,并可以让网络安全专业人员更好地了解其网络和系统中的潜在漏洞。Ghidra跟IDA Pro用起来很像,但Ghidra是免费的,而且是开源的!
Ghidra支持多种操作系统,例如Windows, Linux,或者MacOS。本文中的示例主要在Windows下完成(其他平台上也是类似的,因为本文不是一蹴而就的,后面某些截图是在Ubuntu上做的,但这没有多大影响)。你可以从【1】中下载Ghidra,它是一个压缩包,解压到你指定的目录后,即可开始使用了。注意:Ghidra的运行需要有Java环境作为支持,所以请确保电脑上已经正确安装并配置好了JRE和JDK。
现在打开Windows的命令行程序,切换到Ghidra的安装目录,并输入ghidraRun,程序就被启动了,如下图所示:
开始使用Ghidra
在File菜单中选择新建一个项目(如下图所示),然后在弹出的对话框中选择“Non-Shared Project”,并按【Next>>】按钮。再选择项目的保存路径,并给新项目命名(我这里使用的名称是demo_proj,你会在接下来的示例截图中看到这个名字),然后点击【Finish】按钮,就成功创建了我们的第一个Ghidra项目了。
如果要对一个二进制文件进行分析,你可以在File菜单中选择【Import File】,或者你也可以直接将文件“拖拽”到Ghidra的项目管理界面上。下面将以Crackmes上的一道题目为例来演示Ghidra的使用。对于逆向工程爱好者来说,Crackmes应该是比较熟悉的一个网站,其中有很多一些公开给别人尝试破解的小程序。读者可以从【2】中下载到本文例子中要使用的文件。文件是一个受密码保护的压缩包,密码是crackmes.one。解密后将得到一个名为rev50_linux64-bit的二进制文件,将其导入到Ghidra项目中。
在新弹出的对话框中,为了让Ghidra更好地解析你的二进制文件,这里你可以选择项目的格式(例如ELF)以及使用的编译器等等。就目前的这个项目来说,Ghidra自动检测到的结果是比较合适的,所以此处可以使用默认选项,然后单击【OK】按钮。
文件解析完成后,弹出的对话框里(如下图所示)给出了一些总结信息,你可以看到例如程序中包含了23个函数这样的内容。在附加信息栏中给出了例如libc.so.6这样的shared library没有找到,以及有些内容无法解析等提示。就目前这个项目而已,这些内容不会有什么影响,所以直接点击【OK】按钮。
接下来,在项目管理界面上,双击载入的项目来打开Code Browser。新弹出的提示框会询问你"现在是否要对新载入的项目进行分析",单击【Yes】按钮。然后你就会看到Analysis Options对话框,如下所示:
Analysis Options对话框给出的是需要分析的条目,例如在ASCII Strings中,你可以设置最小的字符串长度等选项。大部分选项,只要保持默认设置即可。为了便于后续分析,此处我们将Decompiler Parameter ID勾选。最后,单击【Analyze】按钮,Ghidra主界面右下角给出了分析进度,快慢视程序大小、是否加壳等而定。当前程序较小,所以很快便得到了结果。
Code Browser里面展示了该程序的解析结果,如果你选择其中的某个函数,右侧则会给出对应的反汇编得到的源代码(更准确的说应该是伪代码),如下图所示。
此外,选择Show Overview,你还会看到下图中的彩条,它给出了程序的Overview。单击彩条上不同的位置,汇编代码窗口会直接跳转到相应的汇编代码处。注意彩条的不同颜色表示了程序中内容的不同类型,例如绿色表示Data,红色表示Undefined等等(具体颜色的含义在帮助文档里都有说明,这里不再赘述)。
对于某条跳转指令,如果你把鼠标“悬浮”在上面,程序会直接给出跳转目的地处的代码,如下图所示:
你也可以禁用悬浮功能,如下图所示,Mouse Hover Popups就被禁用了。但是,你双击JMP后面的地址,程序还是会直接跳到目标位置处,所以Ghidra的用户交互设计还是非常不错的。
此外,像Program Trees和Symbol Tree都是常常会用到的模块,以及Window菜单下的Bytes选项等等,限于篇幅这里不再逐个解释。安装好的Ghidra中有一份很好的帮助文档,图文并茂地介绍了Ghidra的各个模块,各种功能,各种操作,当你遇到困难或者怀有疑问的时候,请参阅它!
Crackmes破解应用实例
下面正式开始对rev50程序进行逆向。首先,如下图所示,为了找到程序的入口,在Symbol Tree窗口上的Filter栏目里搜索main,然后在得到的结果中点击main,这样Code Browser中的代码就跳转到了主函数处。
此时,在反编译窗口中我们将得到main函数的伪代码,如下图所示。Ghidra程序试图推断main函数的function signature,例如参数param_1的类型是int。但函数的返回值类型,以及第二参数的类型仍然是未定义类型。
但C语言main函数的定义方式是有一定标准的,而且这个标准我们是已知的,即int main(int argc, char * argv[])。于是,想到把这个信息导入Ghidra,具体方法是在反编译窗口中右键单击main函数,然后从右键菜单中选择Edit Function Signature,程序会弹出如下所示的新对话框:
将标准的C语言main函数声明方式替换进去:
如果点击【OK】按钮的话,Ghidra弹出了如下所示的错误提示。注意Ghidra给出的反汇编结果仅仅是一种类似C的伪代码,这与纯粹意义上的C语言代码是不同的,所以同理Ghidra也不能完全理解此处的函数声明中用到的C语法。它不会把 [ ] 看成是一种数组标记,而是把 argv[ ] 整体看成是一个变量名,这也就是下面这个错误提示的由来。
其实解决这个问题并不复杂,Ghidra可以理解 * 作为指针语法的含义,那么可以把数组再次转换成指针的形式,也就是下面这样的写法
点击【OK】按钮后,不难发现main函数的变量名已经被自动替换了。现在这个函数的意思其实已经比较好理解了。根据C语言main函数的声明的意义,易知argc表示命令行接收参数的个数。下面程序要首先验证argc是否等于2,注意,这里2的意思表示程序的参数只有1个,因为程序名本身也算一个参数。也就是说后面的参数列表argv中,argv[1]就表示传进来的那个参数(argv[0]就是程序名称本身)。接下来,程序取得字符串argv[1]的长度,判断其是否等于10,并且判断其中索引为4的字符是否等于@。至此,大概已经可以猜到,这其实就是程序密钥的形式。
如果想要再确定一下的话,可以点击查看usage函数(也就是条件不满足时的处理函数)的内容,如下所示。可见,这是密钥不正确时程序的处理函数,因为它会输出“Try again!”字样。这也再次令我们确认,可以通过测试的密钥形式了。
最后来验证一下我们的猜测,注意rev50是一个ELF文件,也就是Linux下的可执行文件,所以下面的测试是在Ubuntu上执行的。可见此前的推断是完全正确的,我们已经成功破解了crackmes上的easy_reverse问题。
参考文献与推荐阅读材料
来源:oschina
链接:https://my.oschina.net/u/4364002/blog/4616590