使用Ghidra对恶意软件进行逆向分析
使用Ghidra对恶意软件进行逆向分析
本文是对国外逆向工程师David Álvarez Pérez所著《Ghidra Software Reverse Engineering for Beginners》第五章《Reversing Malware Using Ghidra》主要内容的翻译,版权归原作者所有。文章详细介绍了使用Ghidra工具对恶意软件进行逆向分析的具体步骤和方法,适合有一定技术基础的读者学习。
正如你可能在前几章中记得的那样,Ghidra 使用包含零个或多个文件的项目进行工作。Alina 恶意软件由两个组件组成:一个 Windows 驱动程序(rt.sys)和一个可移植可执行文件(Spark.exe)。因此,你可以在为本书创建的 GitHub 项目中找到一个包含这两个组件的压缩 Ghidra 项目(alina_ghidra_project.zip)
如果你想直接获取 Alina 恶意软件样本,而不是 Ghidra 项目,你也可以在 GitHub 项目(alina_malware_sample.zip)中找到它,该文件经过压缩并需要输入密码 infected 进行解压。这种分享恶意软件的方式是很常见的,以防止其意外感染。
接下来,我们将尝试快速判断我们正在分析的恶意软件大致属于哪种类型。为此,我们将查找字符串,这些字符串在许多情况下可能会揭示一些信息。我们还会依靠外部资源检索,如果该恶意软件已经被分析或归类,这些资源可能会很有用。最后,我们将通过查找动态链接库(DLL)函数来分析其功能。
让我们从打开 Ghidra 项目开始,然后双击 Ghidra 项目中的 Spark.exe 文件,以便使用 CodeBrowser 对其进行分析。显然,不要在 Ghidra 项目之外点击 Spark.exe,因为它是恶意软件,你的系统可能会被感染。展示样本中的字符串会是一个好的开始。我们可以点击
Search | For Strings…
并开始分析:
如前面的截图所示,似乎是用户 Benson 编译了这款恶意软件。
这一信息可能对调查该恶意软件的来源很有帮助。这里有许多可疑的字符串。例如,很难想象一个合法程序会引用 windefender.exe。此外,SHELLCODE_MUTEX 和系统服务描述符表(SSDT)挂钩的使用都明确表明这是恶意行为。
System Service Dispatch Table(SSDT)
SSDT 是一个数组,其中包含 32 位 Windows 操作系统中内核调用例程的地址,或者 64 位 Windows 操作系统中相同例程的相对偏移量。
有时,在开展进一步分析之前,快速浏览程序的字符串可以揭示它是否是恶意软件。 这种方法简单而有效。
利用外部资源(如情报工具)检索已发现的信息也很有用。例如,如下图所示,我们在查找字符串时发现了两个域名,可以使用 VirusTotal 对这些域名进行调查:
要在 VirusTotal 中分析一个 URL,请访问以下链接,输入域名,然后点击搜索图标以继续:
27dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2$3K9i4u0#2M7%4c8G2N6r3q4D9i4K6u0W2j5$3!0E0i4K6u0r3k6%4g2A6i4K6u0r3K9r3!0E0k6g2)9J5c8Y4g2J5L8l9.
.
搜索结果会动态变化,可能会不时更新。 在本例中,两个域名在 VirusTotal 中都显示了 Positive 结果(译者注,翻译这篇文章时第一个链接在 VirusTotal 检测已经不是 Positive 了)。可以通过以下链接查看结果:8f2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2$3K9i4u0#2M7%4c8G2N6r3q4D9i4K6u0W2j5$3!0E0i4K6u0r3k6%4g2A6i4K6u0r3N6i4u0D9i4K6u0r3y4o6t1J5k6U0p5@1x3U0f1I4x3o6S2S2k6e0x3#2y4U0j5$3k6o6u0X3z5o6k6X3y4o6k6X3z5h3y4X3y4e0j5#2x3e0b7I4j5$3j5$3y4U0l9I4j5K6j5&6x3U0b7#2x3K6c8U0j5U0N6V1z5h3p5#2x3K6j5$3y4o6g2T1j5#2)9J5c8X3c8W2N6r3g2U0N6r3W2G2L8R3.
.
除此之外,VirusTotal 还可以提供更多有用的信息,你可以通过浏览页面找到这些信息。 例如,它检测到 javaoracle2.ru 域名也被其他可疑文件引用过:
在分析恶意软件时,建议在开始分析之前先查阅公开资源, 因为这些资源可以为你提供许多有用的信息。
如何检测恶意软件特征
在检测恶意软件特征时,不要仅仅尝试查找用于恶意目的的字符串,还要寻找异常情况。恶意软件通常很容易被识别,原因有很多:某些字符串永远不会在正常的软件文件中出现,而且代码可能会被人为地复杂化。
检查文件的导入项也很有趣, 这有助于分析其功能。
由于该二进制文件链接了一些恶意服务器,它必须实现某种网络通信。在本例中,这种通信是通过 HTTP 协议进行的,如下所示,这些导入函数位于 Ghidra 的 CodeBrowser 符号树窗口中:
查看 ADVAPI32.DLL, 我们可以识别出一些以
Reg*
命名的函数,这些函数允许我们更改 Windows 注册表;而其他提到 Service 或 SCManager 的函数则允许我们与 Windows 服务控制管理器(Service Control Manager)交互,这使我们能够加载驱动程序:
从 KERNEL32.DLL 中导入的函数非常多, 因此,除了许多其他功能外,它还允许我们与命名管道、文件和进程进行交互并执行相关操作:
Runtime imports
请记住,运行时导入的库和/或运行时解析的函数不会列在符号树(Symbol Tree)中,因此请注意,程序的功能可能尚未完全识别。
我们通过快速分析已经识别出了许多信息。如果你有经验,你会熟悉恶意代码的模式,从而在脑海中将 API 函数与字符串匹配,并根据之前显示的信息轻松推断出恶意软件会尝试做什么。
如前所述,该恶意软件由两个组件组成:一个可移植可执行文件(park.exe)和一个 Windows 驱动文件(rk.sys)。
当在计算机上发现多个恶意文件时,通常是因为其中一个文件会生成其他文件。由于 Spark.exe 可以通过双击执行,而 rk.sys 必须由其他组件(如 Windows 服务控制管理器或另一个驱动程序)加载,我们可以初步假设 Spark.exe 被执行后,将 rk.sys 释放到磁盘上。事实上,在我们对导入函数的静态分析中,我们注意到 Spark.exe 具有与 Windows 服务控制管理器交互的 API。
如下截图所示,该文件以以下模式开头:4d 5a 90 00。这些起始字节也被用作文件的标志;这些标志通常被称为魔数(magic numbers)或魔字节(magic bytes)。在本例中,该标志表明该文件是一个 PE 文件(Portable Executable,PE 文件格式,用于 32 位和 64 位 Windows 操作系统中的可执行文件、object code、DLL 等)。
通过计算起始地址和结束地址之间的差值,我们得到了文件的大小,即 0x51ff,这将用于稍后提取嵌入在 Spark.exe 中的 rk.sys 文件。(译者注:不知道为什么我看的 End 是
ffdfffff
,不是书上的
000151ff
对于这种简单的计算,使用 Python Interpreter(译者注: Ghidra 自带有Python Interpreter,可在工具栏中的 Windows 选中使用)是一个很好的选择:
接下来,我们打开 Spark.exe 并通过点击
Search | Memory…
来查找文件,搜索
4D 5A 90 00
。点击
Search All
以查看所有匹配项:
你会看到该 PE 头标志出现了两次。第一次出现对应的是我们正在分析的文件 Spark.exe 的头部,而第二次出现则对应的是嵌入的 rk.sys 文件的头部。
既然我们现在知道它从地址 0x004f6850 开始,并且之前使用 Python 解释器计算出其大小为 0x51FF 字节,我们可以通过点击
Select | Bytes...
来选择这些字节,输入从当前地址开始要选择的字节长度,最后点击 Select Bytes:
通过右键点击选中的字节并选择
Extract and Import…
(也可以使用快捷键
Ctrl + Alt + I
),我们会看到以下界面,其中会向项目中添加一个包含所选字节的数据文件:
、
我们已经识别了所有恶意软件组件。现在,让我们从程序的入口点开始分析恶意软件。
让我们分析 park.exe。我们首先用 CodeBrowser 打开它并跳转到入口点。你可以在 Symbol Tree 中查找入口函数(entry) 来完成这一操作:
该函数的反编译结果看起来是可读的。
__security_init_cookie
是编译器引入的一种用于防止破坏内存的函数,因此我们可以继续双击
__tmainCRTStartup
进行分析。Ghidra 在这里识别了许多函数,因此我们重点关注唯一一个尚未被识别的函数——
thunk_FUN_00455f60
:
这是程序的主函数。如果你有一些 C++ 背景,你还会注意到
__wincmdln
会初始化一些全局变量、环境以及进程的堆,然后调用
WinMain
函数。因此,
thunk_FUN_00455f60
函数在
__wincmdln
之后,就是
WinMain
函数。让我们将
thunk_FUN_00455f60
重命名为
WinMain
,方法是选中
thunk_FUN_00455f60
并按
L
键:
Ghidra 允许你重命名变量和函数、添加注释,并在许多方面修改反汇编和反编译代码。这在对恶意软件进行逆向分析时非常重要:
我们采取了这些步骤来识别恶意软件从何处开始,以便从头分析其运行流程,但在反编译代码列表中,我们对其中一些函数的功能一无所知。因此,我们的任务是揭示它们的功能,以便理解恶意软件的行为。
请记住,恶意软件分析是一项耗时的任务,因此不要在细节上浪费时间,但也不要忽略任何重要的内容。接下来,我们将分析
WinMain
反编译代码中列出的每个函数。我们将从第一个函数开始分析,它位于第 20 行,名为
thunk_FUN_00453340
。
我们将从第一个函数
thunk_FUN_00453340
开始分析:
它正在使用
operator_new
创建一个类,然后调用其构造函数:
thunk_FUN_0044d440
。
在这个函数中,你会看到一些 Windows API 调用。然后,你可以通过按
L
键重命名局部变量,使代码更具可读性
你可以根据 Microsoft 文档(528K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8$3y4K6i4K6u0W2L8h3W2U0M7X3!0K6L8$3k6@1i4K6u0W2j5$3!0E0i4K6u0r3k6h3&6Q4x3X3c8#2M7#2)9J5c8Y4N6A6L8X3c8G2N6%4y4Q4x3V1k6%4K9h3^5K6x3W2)9J5c8X3q4H3K9g2)9J5c8Y4N6A6L8X3u0S2M7$3g2Q4x3V1k6F1k6W2)9J5k6s2N6A6L8X3u0S2M7$3g2Q4x3X3c8Y4k6i4c8U0L8$3#2H3N6i4c8W2M7X3&6S2L8h3g2S2i4@1g2r3i4@1u0o6i4K6R3&6i4@1f1$3i4K6W2p5i4@1p5#2i4@1f1#2i4@1q4q4i4K6S2o6i4@1f1$3i4K6R3^5i4K6V1H3i4@1f1^5i4@1u0r3i4K6V1&6i4@1f1@1i4@1t1^5i4K6R3H3i4@1f1$3i4K6V1K6i4K6S2p5i4@1f1@1i4@1u0p5i4K6W2o6i4@1g2r3i4@1u0o6i4K6W2m8
事实上,还可以通过点击
Edit Function Signature
来完全修改一个函数:
在这种情况下,这个函数是 strcpy,它将 errorretriving 字符串复制到 computerName 字符串的末尾(当执行到这一行时,computerName 的值为 NULL)。然后,我们可以根据其名称和参数修改函数 Signature。
我们还可以修改函数的调用约定。这一点很重要,因为一些关键细节取决于调用约定:
参考以下截图,了解如何将
thunk_FUN_004721f0
重命名为 strcpy:
我们还可以在第 105 行写前置注释——
0x1a = CSIDL_APPDATA
,
这表明 SHGetFolderPathA 的第二个参数表示 %APPDATA% 目录:
经过一些分析,你会注意到这个函数将恶意软件的一个 RC4 加密副本创建为 windefender.exe,并保存到 %APPDATA%\ntkrnl\ 目录中。