利用Ghidra分析恶意软件Emotet

2019-04-26 约 4695 字 预计阅读 10 分钟

声明:本文 【利用Ghidra分析恶意软件Emotet】 由作者 mss**** 于 2019-04-27 06:00:00 首发 先知社区 曾经 浏览数 0 次

感谢 mss**** 的辛苦付出!

原文地址:https://medium.com/@0xd0cf11e/analyzing-emotet-with-ghidra-part-1-4da71a5c8d69
https://medium.com/@0xd0cf11e/analyzing-emotet-with-ghidra-part-2-9efbea374b14

在本文中,我们将为读者演示如何使用Ghidra来分析恶意软件Emotet的最新样本。

需要说明的是,我们的分析工作是在脱壳后的二进制文件的基础上完成的。由于这里主要是为读者展示如何使用Ghidra的python脚本管理器来对字符串和API调用进行解密,所以,具体的脱壳过程,就不再这里介绍了。

概述

什么是Ghidra?

为何选择Emotet?

  • Emotet是一种流行的银行木马恶意软件。该木马的生命力非常强,其感染机制还在不断进化。当前,已经有一些关于该恶意软件的分析文章,感兴趣的读者请访问https://www.google.com/search?q=emotet。

为什么使用Ghidra分析Emotet?

  • 为啥?还不是因为IDA Pro许可证太贵,哥又不想放弃自己的恶意软件分析师职业生涯。
  • 当然,使用免费版IDA v7.0也是一个不错的选择,但是这样的话,就无法使用IDA Python了。在试用一段时间IDA自家脚本语言IDC之后,我发现Python才是我的最爱。而Emotet不仅支持Python,还提供了许多现成的分析脚本

使用Ghidra分析Emotet

在使用Ghidra时,首先要创建相应的项目。按照屏幕上的提示,我创建了一个名为“Emotet”的项目。要想将待分析的文件添加到项目中,只需键入I或选择File -> Import File菜单项。

导入Emotet的二进制文件

导入Emotet的二进制文件后,Ghidra将显示该文件的各种属性。之后,双击文件名,就会在CodeBrowser中打开该文件。在这里,CodeBrowser是一个反汇编工具。

CodeBrowser中的Emotet视图

在符号树下(通常显示在左侧;如果没有打开的话,可以通过window->symbol tree来打开它),通过过滤“entry”,就可以找到该二进制文件的入口点。

Emotet的入口点

在Listing窗口下面,我们可以看到编译后的代码,而右边显示的是反编译后的代码。由于之前已经对这些二进制文件进行了分析,因此,图中某些子例程调用和偏移量已经被我重命名了。当我们想要对偏移量进行重命名的时候,请右键单击相应的偏移量,然后,选择“Edit Label”(或键入L)即可。

Emotet的函数调用

Emotet不仅对自身的字符串进行了加密,同时,还将自身的API调用名称存储为哈希值形式。因此,静态查看该文件的内容的话,理解起来非常困难。

为了深入了解Emotet的payload,通常需要借助Olldbg、Wingdb或其他调试器来分析所有的函数,以弄清楚其内部运作机制。而本文的目标,就是如何利用Ghidra让这个过程更轻松一些。

下面,我们将展示如何利用Emotet弄清楚两个函数的运行机制。其中,第一个函数是一个简单的xor例程,用于解密字符串。表面上看,这个函数非常复杂(因为在函数中使用了移位运算符),直到在Ollydbg中运行一遍后,我才明白它到底是干什么的……第二个函数用于查找API名称与哈希值之间的对应关系(具体在后文中介绍)。当然,如果您觉得我说的还不够清楚的很,只要在Ollydbg中运行一下相应的代码,就能很好的理解了。

然后,我们将介绍Ghidra的脚本管理器,并展示如何通过python脚本来解密字符串并解析二进制文件中用到的API调用。

字符串是如何加密的?

在这个二进制文件中,我们发现许多地方都引用了0x00401b70处的一个函数调用。实际上,这个调用是用来解密字符串的,因此,不妨将其重命名为decode_strings。要查找对该函数的引用,请右键单击该函数,然后选择References -> Show References to即可。

对decode_strings函数的引用

对decode_strings函数的调用

函数decode_strings有两个参数,分别位于ECX和EDX中。其中,ECX中存放的是加密字符串的偏移量,而EDX存放的则是xor密钥。另外,解密后的字符串将存放到在堆中分配的内存中,其地址将存放到EAX中。

(注意:我已将字符串“ecx = offset \n edx = key”添加为该函数的可重复注释(repeatable comment)。具体操作方法为,右键单击地址,然后选择Comments -> Set Repeatable Comment,或键入;即可)

位于该偏移量处的第一个dword与进行异或操作后,就会得到字符串的长度。之后,根据前面得到的字符串长度,对后面相应数量的dword进行异或处理。

现在,我们将为读者介绍最为激动人心的部分——使用Ghidra通过python脚本自动完成上述操作。

使用Python完成自动解密

脚本管理器图标

在Ghidra的顶部的工具栏中,我们可以看到如图所示的图标。点击该图标,就会打开脚本管理器。此外,我们也可以通过Window -> Script Manager来打开它。

脚本管理器

打开脚本管理器后,我们会看到许多使用Java或Python语言编写的脚本,这些都是软件自带的。此外,脚本管理器还提供了许多python脚本示例。所以,要想学习如何编写python脚本,我们可以通过.py过滤相应的脚本。通过Python Interpreter,我们还可以使用Jython与Ghidra的Java API进行交互。至于Java API的文档,可以在Ghidra安装目录下的docs文件夹中的压缩文件中找到。

新建脚本图标

要新建python脚本,可以点击上图所示的图标;或者,我们也可以选择Python,然后输入为脚本指定的名称即可。

示例脚本test.py

此外,建议大家阅读帮助文档(请访问Help -> Contents)中“Script Development”一节的内容,那里详细介绍了创建新脚本时生成的各种元数据标记。

我已将脚本上传到我的github repo中,访问地址为https://github.com/0xd0cf11e/ghidra/blob/master/ghidra_emotet_decode_strings.py。

解密后的字符串以注释的形式显示

该脚本的思路就是,在将偏移量保存到ECX的指令旁边,以注释的形式显示解密后的字符串。

二进制文件中修补的字节。

然后,修补二进制中的字节。

首先,找出引用decode_strings函数的所有代码。

为此,需要遍历所有引用,并找出操作码指令MOV ECX和MOV EDX。实际上,这些指令并不总是位于函数调用之前。所以,为了查找这些操作码,我最多遍历100条指令。

完成上述操作后,执行xor例程,修补相应的字节,并在相应位置写入注释。

上面,我们介绍了如何使用Ghidra来静态分析恶意软件Emotet。我们知道,Emotet通过简单的xor函数对其字符串进行了加密处理。由于在整个文件中都使用了xor例程,手动解密非常繁琐,所以,我们为大家介绍了如何使用Ghidra的脚本管理器编写python脚本,以自动解码字符串。

接下来,我们将为大家演示如何利用类似的方法,来将Emotet中编码为哈希值解析为API名称。

Emotet是如何解析API地址的?

对于这款恶意软件的二进制文件来说,它并没有对API名称字符串进行加密。相反,它将API名称存储为哈希值,以加大安全人员的分析难度。

这些哈希值数组存储在堆栈中,然后,在函数0x401230中通过指针来引用这些哈希值。所以,我们已将函数0x401230函数标记为decodeAPINames。

保存在堆栈中的哈希值

传递给函数的其他参数

首先,在调用decodeAPINames之前需要获取DLL的句柄。为此,该恶意软件通过PEB枚举来查找kernel32.dll和ntdll.dll的句柄。而其余的DLL,则通过LoadLibraryW(DLL名称由xor例程进行了编码)进行加载。

该函数使用这个句柄来读取DLL的导出地址表。同时,它会计算表中的每个API名称的哈希值,并与压入堆栈的哈希值进行比较。如果匹配,则将相应的API地址将保存为文件内部偏移量。

注意,堆栈中并非所有哈希值都是有用的。相反,许多哈希值都是用来滥竽充数的,所以,乍一看该文件的API地址列表硕大无比,实际上,这只是迷惑安全分析人员的假象。

使用Python将哈希值解析为函数名称

这里有一个通过哈希值解析函数名称的脚本,下载地址为:  https://github.com/0xd0cf11e/ghidra/blob/master/ghidra_emotet_decode_hash.py。

当然,还有许多其他方法也可以解决这个问题。这里使用的方法一方面需要用户进行参与,同时,还需要将API名称列表保存到一个文件中。正是因为这个过程有些繁琐,因此,我才编写了一个简单的脚本,让它来替我们完成这些准备工作。

来自Kernel32的部分API名称

首先,我编写了一个脚本,用来将DLL的所有导出函数都保存到一个文件中。

然后,用这个脚本来处理ntdll.dll、kernel32.dll、advapi32.dll、shell32.dll、crypt32.dll、urlmon.dll、userenv.dll、wininet.dll和wtsapi.dll。

接下来是找到所有引用decodeAPINames的偏移量。在这个过程中,我在脚本管理器中无意中发现了Ghidra提供的Java脚本ShowCCallScripts.java。根据该脚本名的提示,我将光标放在decodeAPINames函数中,然后运行了该脚本,结果如下所示:

运行ShowCCallScripts.java后,控制台的输出结果

运行该脚本时,需要注意哪个引用的偏移量处的函数解析哪个DLL的API名称的。
例如,0x4079c0处的函数解析crypt32.dll API名称。为什么这么说呢?因为我发现,当调用decode_strings时,其名称是由0x407afa处的函数进行解析的(详见前文)。然后,会在偏移量0x407b3a处调用decodeAPINames。

输入引用decodeAPINames的偏移量。

脚本运行时,会要求输入相应的偏移量。例如上图中,我们输入的是407b3a。

crypt32.list文件,其中含有crypt32.dll导出函数名称列表。

接下来,它会提示输入文件名,这里,我们输入的是用来保存crypt32.dll导出函数名称的文件。

在控制台中,我们看到解析了哪些API名称。就crypt32.dll来说,只有一个API名称解析了出来,即CryptDecodeObjectEx,尽管有39个(十六进制为0x27)哈希值被压入堆栈。

控制台输出结果

之后,该脚本会使用API名称来标记偏移量。

创建CryptDecodeObjectEx标签。

下面是处理kernel32.dll的结果:

kernel32的API名称

希望本文能够对读者有所启发,如果您已经找到了更好的方法来解析哈希值,或发现了与Ghidra有关的有趣的技术,欢迎与我们一起分享。

关键词:[‘技术文章’, ‘技术文章’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now