在我们关于 macOS 恶意软件逆向工程技能教程的第一部分中,我们unpack.txt
在 Resources 文件夹中找到了包含加密代码的文件。在第二部分中,我们继续使用静态分析技术检查主要可执行文件以了解更多信息。结果,我们在二进制文件中找到了一个名为“enncryptDecryptString:”的方法。这看起来很可能是文本文件中的代码被读入内存的位置。
是时候在我们隔离的 VM 中以受控的方式运行我们的示例,以便我们可以对其进行检查。特别是,我们希望以明文形式读取unpack.txt
文件中的加密字符串,帮助我们理解该恶意软件的行为。
如何运行被 Apple 阻止的恶意软件
为了运行我们的恶意软件,首先我们必须确保它没有被Apple 的 Gatekeeper 或 XProtect 功能阻止。您可以通过在命令行上列出扩展属性来检查 Gatekeeper 是否已标记文件。我们通过将-l
标志和文件路径传递给xattr
实用程序来做到这一点。
$ xattr -l UnPackW
如果返回包含 的结果com.apple.quarantine
,则该文件将受到本地 Gatekeeper 策略施加的任何限制(如在 System Preferences > Security 选项卡中设置或通过spctl
并存储在 /var/db/SystemPolicy 中设置)。
com.apple.quarantine: 0083;5caf3e68;Safari;5FFF1FBA-3A55-4647-8280-DBB57E3FC8A1
Gatekeeper 还会将该文件传递给 XProtect 以检查它是否为 Apple 的恶意软件规则所知。这些检查是为了帮助保护用户的安全,但在我们的例子中,我们不希望操作系统阻止我们的样本。由于我们的可执行文件可能会调用捆绑包中的其他文件,比如unpack.txt
文件,因此我们最好从整个捆绑包中删除隔离位,而不仅仅是可执行文件。要删除扩展属性并绕过 Gatekeeper 和 XProtect,只需将-rc
标志和文件路径传递给xattr
.
$ xattr -rc ~/Malware/UnPackNw.app
如何使用 LLDB 检查恶意软件
最后,我们准备好有趣的部分了。让我们进行一些动态分析!为此,我们使用lldb
低级调试器,您在本教程开始时在第 1 部分中设置命令行工具时安装了该调试器。
打开终端会话并切换到 UnPackNw.app 包的“MacOS”目录。
$ cd ~/Malware/UnPackNw.app/Contents/MacOS
我们将lldb
在交互模式下使用,因此首先调用不带任何参数:
$ lldb
您将看到通常的命令行提示符,以$
替换为符号的符号结尾(lldb)
,表示我们已进入交互模式。下一步是告诉调试器我们要使用它的file
命令附加到哪个文件。请注意,这本身就是一个命令,与我们在本教程前面使用lldb
的实用程序无关。file
(lldb) file UnPackNw
将file
实用程序的输出与下图中的命令的输出进行比较lldb
。
现在我们已经告诉调试器我们要附加到哪个文件,因此我们不必在交互式会话中发出任何进一步的命令来传递文件名。
下一步是启动恶意软件,但我们不想完全启动。因此我们需要控制执行,我们通过使用process
命令来做到这一点。
(lldb) help process
您将看到process
命令及其各种子命令的帮助输出。让我们深入挖掘。我们将使用launch
带有-s
选项的子命令。类型:
(lldb) help process launch
您将看到每个选项的作用的说明。当我们使用subcommand 选项传递launch
子命令时,它会启动可执行文件并在遇到程序的第一个函数入口点时尝试暂停执行。process``-s
第一个入口点应该是dyld_start
,即动态链接器开始加载恶意软件所依赖的任何库,然后再获取二进制文件自己的代码(回忆第 2 部分,我们可以使用otool工具列出依赖库otool -L
)。
但是,一些恶意软件试图伪装其真正的入口点,而其他恶意软件试图阻止您使用各种技巧附加调试器,您可能需要解决这些技巧。
在 LLDB 中启动进程
让我们尝试一下,看看会发生什么(提醒:当然,您是在我们在第 1 部分中设置的隔离 VM 中执行此操作的!)。
(lldb) process launch -s
伟大的!dyld_start
正如预期的那样,我们已经在代码执行开始时停止了。现在,让我们在我们感兴趣的方法上设置一个断点。请注意,该方法可能拼写错误,因此请务必按照代码中显示的方式输入(不要自动更正,谢谢!)。
(lldb) breakpoint set -n "+[EncodeDecodeOps enncryptDecryptString:]"
检查您是否收到断点已在给定地址正确设置的确认。如果您看到 “没有位置(待定)”之类的消息或任何其他警告,请检查您的输入并重试。有很多方法可以在 中设置断点lldb
,包括使用regex,但现在你需要走很长的路,直到你对你正在做的事情更有信心。如果您不小心设置了不想要的断点,您可以使用breakpoint delete
或缩写版本br del
来删除所有断点并重新开始(您也可以单独删除断点,但我将把它留给读者作为练习) .
成功设置断点后,我们需要输入一个continue
字母或只输入一个字母c
来告诉调试器继续执行,直到它到达我们的断点。
我们已经停在函数的入口处。让我们看更多的反汇编,以便我们确定自己的方向。
(lldb) disassemble
向上滚动到输出的开头(键盘上的命令+箭头)。您将在左边距中看到指向我们当前停车地址的向右箭头。
您应该从静态分析中识别出这段代码。让我们向下滚动到我们看到“initWithString:”的地方。
这看起来像是代码将从unpack.txt
. 我们可以说这是因为它发生在函数返回的最终调用之前,并且我们假设这个函数的目的正是返回解密后的字符串。
让我们看看我们是否正确。我们将直接在“initWithString:”移动到rdi
寄存器0x100003d10的地址上设置另一个断点,然后恢复。这次我将使用缩写语法来为您节省一些输入:
(lldb) br s -a 0x100003d10
(lldb) c
如何在 LLDB 中读取寄存器
再一次,调试器在断点处停止执行,就在我们指定的地址上。我们快到了,但是要查看我们解密的字符串,我们需要学习如何读取寄存器以及如何将它们打印出来。
第一步很简单。让我们一口气转储所有寄存器。
(lldb) register read
当我们处理 64 位架构时,我们所有的通用寄存器都以“r”开头:rax
、、、rbx
等等rcx
。
当您尝试读取方法名称和参数时,最重要的两个寄存器通常是rdi
和rsi
。第一个应该保存被调用的类的名称,而第二个实际上应该给我们第一个参数。请注意前面的屏幕截图rsi
是如何在反汇编之前加载rdi
的。由于我们已经知道我们正在处理 NSString 的创建rdi
,让我们直接看一下通过什么参数传递给“initWithstring:” rsi
。
当我们想要打印或引用 中的寄存器lldb
时,我们必须在它们前面加上一个$
符号。我们使用命令的快捷方式“po”expression -O
将寄存器的内容打印为对象。
(lldb) po $rsi
答对了!现在我们看到unpack.txt
文件中的加密字符串终于显露出来了。它原来是一个将 zip 文件下载到临时目录的 shell 脚本。man
页面告诉我们,“X”字符的mktemp
字符串会产生一个相同长度的随机目录名。然后脚本解压缩并启动下载的应用程序,并s
在启动时将参数传递给它。
此时,如果您想继续执行而不跳转到另一个断点,您可以使用命令告诉lldb
前进到下一条指令next
,并以相同的方式继续检查反汇编和寄存器以完全显示其余部分恶意软件的行为。
如何退出 LLDB 调试器
如果您想让恶意软件发挥其其余行为,continue
请在调试器中再次使用。由于我们没有设置更多的断点,它要么完成它的执行,要么在进一步调用解密方法时停止。
如果您不希望恶意软件继续运行并且觉得您已经看够了,您可以使用process kill
. quit
您可以使用该命令退出低级调试器。
macOS 逆向工程的后续步骤
如果您让恶意软件运行(并假设它尝试联系的服务器仍然处于活动状态),您可以使用这个进入兔子洞并开始对下载的 porcupine.zip 进行逆向工程。练习得越多,就越容易!
注意:事实证明,porcupine.zip 包含我们之前提到的 Apple 的 MRT 工具识别的恶意软件。
随着您继续练习这些技能,您可能还需要一些额外的资源。除了本系列中的许多链接之外,还可以考虑查看本书以获取有关lldb
. 我最喜欢的用于消除二进制分析痛苦的工具之一是radare2和它附带的工具套件,rabin2
如. 奖励:& 朋友都是免费的,甚至还有一个免费的 GUI 前端Cutter,供那些不喜欢命令行的人使用!在商业产品中,Hopper是专业 macOS 逆向工程师的热门选择。rax2``radiff2``radare2
结论
在本系列文章中,我们学习了如何设置安全环境来测试macOS 恶意软件,以及如何使用静态分析和动态分析对 Mach-O 二进制文件进行逆向工程。在最后一部分中,我们学习了如何以受控方式执行代码、设置断点和读取 CPU 寄存器。我们在这三篇简短的文章中包含了很多内容,但我们几乎没有触及这个深刻而引人入胜的话题的表面。
\