原文链接: https://www.shogunlab.com/blog/2019/12/22/here-be-dragons-ghidra-1.html
翻译: 看雪翻译小组 - Nxe
校对: 看雪翻译小组 - 玉林小学生
欢迎来到使用Ghidra逆向Windows二进制文件系列教程的第二部分! 在这篇文章中, 我们将在Part 0学习的基础上介绍一些新的概念, 其中包括转换/应用数据类型, 函数调用树/图, 脚本管理器和内存映射. 与此同时, 我们还会应用我们所学的来逆向一些CrackMe二进制文件和19年Flare-On 6CTF题的二进制文件. 我会把所有需要逆向的二进制文件都放在这个Github仓库中, 这样你就能在Ghidra中直接导入, 你应该准备好了!
当你查看二进制中的数据时, 有时它不是你想要的格式. 比如你想让数据以十进制显示, 而它却以十六进制显示. 让我们看看如何在Ghidra中进行这个转换. 首先打开这个Github仓库中名为crackme0x01
的IOLI CrackMe二进制文件. 在导入该文件后, 选择“Symbol Tree”窗口然后展开“Functions”文件夹来找到_main
函数, 或者使用“Filter”输入框来查找_main
. 当你点击它时, 你应该已经在主函数里了, 你可以在右侧看到反编译的结果.
高亮“if”语句后, 你会看到相关的反汇编代码也同样被高亮了. 这里它将用户输入local_8
与0x149a
数值进行比较. 但是, 如果我们试着在运行该文件时以这个值作为密码输入却会失败. 这是因为要输入的是十进制, 而不是十六进制.
我们可以在Ghidra的反汇编列表中, 右键点击数值, 然后选择“Convert”来将它转换成十进制格式. 现在, 我们看到反汇编列表和反编译窗口中该数值都变成十进制了. 输入对应的十进制数5274
之后就可以了!
我们可以使用Ghidra来应用数据类型, 比如把字节数组转换为字符串. 为了实验这个特性, 我们要使用到名为Overlong.exe
的二进制文件, 这是Flare-On 6 CTF中的一道题, 由Eamon Walsh编写. 你可以在这个Github仓库找到该文件.
为了查看如何应用数据类型, 我们首先要将Overlong.exe
导入进Ghidra. 然后展开Symbol Tree中的函数文件夹并点击“entry”来定位到主函数. 我们可以在反编译窗口中看到有一个名为FUN_00401160
的函数被调用, 而且在第二个实参的地方引用了一些数据. 让我们双击反编译窗口中的&DAT_00402008
, 之后Listing窗口就来到了该地址. 我们能看到大量的问好标记和一长串数值.
让我们看看在这串数据上应用“string”数据类型会发生什么. 右键点击DAT_00402008
名字下面的第一个值(E0h
), 然后选择“Data”, 你应该会看到你可以应用的一系列数据类型. 这里我们选择“string”数据类型, 你会看到在entry
函数的反编译窗口中直接显示出了flag值.
当你想要尝试找出一个二进制文件中的某个函数做了什么, 查看这个函数中调用了哪些函数可能会有所帮助. 比如, 如果你看到调用了与网络和加密相关的几个函数, 那么你可能在分析一些与互联网通信相关并尝试隐藏发送的数据的功能. 为了在Ghidra中获取此类信息, 你可以生成函数调用树. 让我们通过在Ghidra中导入这个Github仓库中名为crackme0x04
的CrackMe二进制文件来查看如何生成.
首先, 展开Symbol Tree窗口中的函数文件夹然后过滤_main
函数. 在此之后点击函数名, 反汇编列表和反编译窗口中会进入主函数. 不像之前的IOLI CrackMe二进制文件, 我们在主函数中看不到任何将用户输入值与正确密码相比较的语句. 然而, 我们可以看到一个名为_check
的函数被调用, 且该函数带有名为local_7c
的参数, 这个变量是我们命令行中的用户输入.
在反编译窗口中双击_check
, 让我们仔细分析一下这个函数. 我们可以通过生成函数调用树的方式来获取这个函数可能表现的行为. 在Ghidra工具栏中选择“Window”, 然后点击“Function Call Trees: _check”. 调用树表明该函数被_main
函数调用, 然后它调用了_strlen
, _sscanf
, _exit
和_printf
.
这告诉我们, 该函数可能进行了与用户输入长度(strlen)有关的操作, 从我们输入的字符串读取了格式化数据(sscanf), 以及当函数结束时可能退出程序(exit). 我们可以生成类似功能的函数调用图, 通过点击工具栏的“Window”, 然后点击“Function Call Graph”. 之后会显示一个可视化的图表, 由调用_check
的函数和_check
调用的函数组成.
有了这些信息我们可以做什么呢? 我们知道了该函数中可能会有利用我们的字符串长度继续某些计算. 让我们来发掘更多信息, 在函数调用树的向外调用(Outgoing Calls)窗口点击_strlen
, 我们会来到_strlen
被使用的位置. 用户数值的长度会与一个计数值相比较, 该计数值在每次while循环中逐一递增. 这是用来检查密码的长度是否小于或者等于计数值. 所以, 如果我们输入了4个字符长的密码, while循环的计数值为0, 直到计数值与密码的长度相同这个循环会运行4次. 此时如果没有满足密码正确的条件, 它就会输出“Password Incorrect!”(密码不正确!).
我们还可以发现, 每次循环会读取密码中的单个字符, 使用_sscanf
将它格式化为整数, 然后将这个值加到一个变量上. 如果这个变量的值等于15, 循环会停止, 我们就会得到“Password OK!”(密码正确!)的信息.
汇总这些信息, 如果我们提供了一个密码, 该密码每位数字加起来的和等于15, 这样我们就能通过检查. 让我们拿555作为密码试一下. 密码正确! 我们还可以使用5541, 771, 111111111111111, 12345等等.
我把这个变量重新命名了一个有意义的名字, 并在完成这个CrackMe的过程中添加了注释以提高可读性, 这是一个不错的习惯! 这样能节省你大量时间, 比如你想暂停几天或者完全忘记某一块汇编代码是做什么的时候. 如下图所示, 你可以在反汇编窗口中看到我最终的Ghidra项目是什么样子.
Ghidra带有不少Java和Python编写的脚本来帮助你完成逆向工程的任务. 你也可以编写自己的脚本或者是加入社区中其他人编写的脚本. 你可以点击工具栏中的“Window”, 然后点击“Script
Manager”来访问这些脚本. 你会看到Script Manager中有各种种类的文件夹, 其中包括Search, ARM, Analysis和Binary.
让我们用“Examples”文件夹里的脚本来体验一下, 点击文件夹然后双击HelloWorldScript.java
. 如果我们返回到Ghidra的主窗口, 我们会看到Console(控制台)窗口正在显示“Hello World”. 你也许会注意到, 在脚本管理器下方的窗口会显示该脚本的文档. 输出不仅限于控制台, 你也可以利用脚本创建一个GUI窗口. 双击HelloWorldPopupScript.java
脚本, 我们可以看到弹出了一个写着“Hello World”的小窗口.
让我们来看一下这些脚本在编辑器中是什么样子, 点击python_basics.py
脚本然后右键选择“Edit with basic editor”(你也可以选择在Eclipse中编辑该脚本). 我们可以在右侧的窗口中看到该脚本的Python源代码. 需要注意的是, 如果你想运行Python脚本, 你必须安装Jython, 然后重新启动Ghidra才能运行.
如果你想要添加自己的脚本, 我们可以在右上角点击“New Script”按钮, 选择是Java还是Python. 一个基本文本编辑器窗口会弹出, 你可以选择在这里面编写脚本, 或者访问默认的文件夹目录$USER_HOME/ghidra_scripts
来使用你喜欢的文本编辑器来打开脚本文件. 如果是要添加一个新文件夹, 点击右上角的“Script Directories”.
最后要讲的Ghidra中的东西是内存映射. 许多时候你想设置反汇编的二进制文件的装载基址(image base). 为了做到这一点, 选择“Window”并点击“Memory Map”. 你会看到二进制文件的内存区块(memory block)以及它们相应的权限. 为了设置装载基址, 你可以点击右上角的房子图标来修改装载基址.
恭喜完成了另一篇教程, 学习到了Ghidra的更多特性! 总结一下, 我们讲了以下东西:
你已经完成了另一篇Ghidra教程, 我希望你能学到点新东西, 并且在逆向二进制文件中获得些许乐趣. 感谢你花费时间来阅读这篇文章, 请期待下一篇!
如果你发现哪里没有讲清楚或者有什么建议/意见, 请在推特上私信(@shogun_lab)或者发送电子邮件至steven@shogunlab.com.
辛苦了.
Ghidra 资源
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-1-20 00:28
被Nxe编辑
,原因: 更换图床