本文为看雪论坛精选文章 看雪论坛作者ID:jishuzhain
恶意代码分析是在应急响应中需要的一部分,如今,随着恶意软件(特别是其反分析技术)的复杂性,很难及时地消除威胁。在完成分析时,威胁可能已经扩散到整个环境。 该文章将重点介绍传统的手动分析(仅人为操作)步骤,以使读者了解如何分析 DotNet 文件。
在开始之前,先提及下DotNET的基础。如今,有多种编程语言允许程序员开发可执行文件。其中一些语言是 Microsoft Visual C/C++,VB6(DotNET 的前身)和 DotNET 框架,这些语言中的每一种都有其缺点和优点。
相对于C/C ++,Microsoft C#是一种用于应用程序开发的简便语言。因此,许多攻击者和合法开发人员都喜欢使用 C# 开发其 PE 文件和应用程序,但是许多编译的 DotNET 可执行文件也可能会使用反分析技术,以使它们的代码混乱并在系统上执行的期间隐藏其操作。
DotNET 可执行文件有许多静态分析技术,接下来将提出两个分析案例。第一个案例将关注混淆的键盘记录器(keylogger),而第二个案例将研究DotNET 可执行文件在内存中的动态加载。
以下有一些识别 DotNET 可执行文件特征的方法。
DotNET 可执行文件的特征
已编译的 DotNET 可执行文件通常会导入DLL 文件 mscoree.dll,也会导入API 函数 _CorExeMain 或 _CorDllMain 下图(取自VirusTotal)说明了 DotNET 可执行文件导入的 DLL 和 Windows API 函数的导入:
如果仅看到 mscoree.dll 文件的导入和对 API 函数 _CoreExeMain 的调用,则可能是遇到了 DotNET 可执行文件。
如何识别?
当遇到可执行文件,并且不确定是使用哪个编译器将文件编译为可执行文件的,则可以使用一些免费工具对文件进行静态分析。这些工具中的有 Easy-It-Easy(DIE),PEstudio 和 CFF Explorer。但在大多数情况下,这些工具都可以用来确定可执行文件链接到哪些导入 DL L和 API 。
DotNET 可执行文件还具有另一个非常独特的特性,比如使用 Visual Studio开发 DotNET 可执行文件,该可执行文件本身将获得两个不同的全局唯一标识符(GUID)。
一旦编译为可执行文件,通常会将它们作为字符串存储在DotNET 文件中。可以在 DotNET 可执行文件上搜索字符串找到这些 GUID,或者如图所示,VirusTotal 将在可执行文件的详细信息部分下列出它们:
Module Version ID(MVID)GUID 是在 DotNET 项目构建期间生成的,并且对于当前构建的项目是唯一的。TypeLib ID 是在项目创建期间由 Visual Studio 生成的。
这些GUID除了可以帮助将文件标识为 DotNET 可执行文件之外,还可以用于将文件与特定项目相关联,因为如果不断维护同一 DotNET项目,则编译后的可执行文件应该会存在类似的 GUID 。
在谈到 DotNET GUID 时,安全研究人员还将它们称为 NetGUID 。这些NetGUID 还可用于识别具有特定项目的 DotNET 文件,如果 DotNET 开发的恶意软件变体具有相同的值,则它们也可以由同一 NetGUID 关联。几年前,发表了一篇有关通过NetGUIDS搜索恶意软件的好文章(https://www.virusbulletin.com/virusbulletin/2015/06/using-net-guids-help-hunt-malware/)。
接下来开始分析两个案例。
技术细节
本节中分析的可执行文件包含以下静态特征: 文件名:Mixed Grade.exe
SHA1:
b33bb284b540a69b9c6a65912a439d00682c896a
大小:2350080 字节
编译时间:2020-03-23 07:57:14
Mixed Grade.exe 文件是与 HawkEye Keylogger 相关的恶意软件。但是,Mixed Grade.exe 本身不是 DotNET 文件,只有它产生的实际有效的恶意文件是 DotNET 文件。
当执行 Mixed Grade.exe 文件时,它将释放文件 RegAsm.exe (一个合法的 Windows 程序集注册工具)。但是在生成之后,Mixed Grade 会将恶意PE 文件注入到挂起模式的 RegAsm.exe 中(此技术也称为进程镂空)。
此时 RegAsm 不再是合法进程,因为它包含主要的 HawkEye 恶意文件。通过查看 RegAsm.exe 内存转储内容,很明显可以发现注入文件的名称是 Reborn stub.exe,而它是 DotNET 可执行文件,下图说明了该过程:
如版本信息所示,文件名取自 PE 头。有很多方法可以从内存中转储 Reborn Stub.exe文件。但是,从内存中转储此文件的最简单最直观的方法之一就是使用 pe-sieve (Hook finder)之类的工具。从内存中转储该可执行文件后,发现它确实是 DotNET 可执行文件。
如下图所示,确实是 DotNET 可执行文件:
Reborn Stub.exe静态特征: 文件名:Reborn Stub.exe
SHA1:5fb78f117d48b7f2cdb312cb553a33aa8bc17346
编译时间:2019-01-22 15:01:45
现在通过 DnSpy 分析此文件,将可疑的 DotNET 可执行文件 Reborn Stub.exe 拖放到 DnSpy 中。如下图所示, DnSpy 能够将可执行文件反编译为“源代码”:
为了查看程序的 main 函数,请在程序名称上单击鼠标右键,然后选择“转到入口点”或单击程序主单元中的“主程序”,如下所示:
通过查看代码发现不可读,因为代码被混淆了,默认情况下混淆了类,模块以及介于两者之间的所有内容。此处使用的混淆技术可能是通过免费的 .NET 混淆工具 ConfuserEx 进行的。
为了对此代码进行反混淆,可以使用免费的反混淆器和解包工具 de4dot ,该工具非常适合查找混淆的模式并对其进行反混淆。下图说明了使用 de4fot 工具对该 DotNET 可执行文件进行反混淆处理的命令:
如果去混淆处理成功完成,则应该有一个新的输出文件。在上面这种情况下,经过反混淆的输出文件是 400000.RegAsm.output ,该文件默认位于运行de4dot 工具的当前文件夹中(如果此过程使用了与上面相同的命令)。 现在,有了一个新的输出文件,通过将其拖回 DnSpy 中来查看去混淆的文件。
在新的输出文件中,分析人员可以了解到一些内容。首先,新的 DotNET 可执行文件在文件头中包含一个 NetGUID ,如下图所示:
由于现在可以阅读函数和代码,因此分析人员可以通过阅读代码来尝试学习和理解 HawkEye 恶意文件的作用。例如,在阅读代码时,它很清楚地提到此恶意文件是 HawkEye 键盘记录器:
可以查看去混淆的可执行文件中的静态变量,如下图所示:
在代码中,这些变量用于键盘记录密码,从剪贴板中获取信息等等。此DotNET 可执行文件是 Keylogger 的变体,典型的键盘记录器会将其窃取的信息存储在一个 .tmp 文件中。 但是,在我们的情况下,写入 .tmp 文件后,获取的信息立即复制到内存中并发送到远程服务器,然后从系统中删除临时文件的内容。 通过更深入的分析和调试,我们在将 tmp 文件从系统中删除之前捕获了该文件的内容。该文件包含来自 Chrome 和 Internet Explorer Web 浏览器的被盗信息,但它也设法从 Internet Explorer(IE)窃取密码,如下图所示:
MixedGrade键盘记录器通过将Reborn Stub.exe文件注入到合法的RegAsm.exe 中来执行进程镂空技术。 Reborn Stub.exe 是 DotNET 可执行文件,我们使用DnSpy将其反编译为“源代码”。该代码原来是不可读且难以理解的,因此我们运行de4dot命令并创建了一个具有相同内容的新文件,但是这次它是可读的。
可执行文件包含以下静态特征:
名称:TenClips.exe
SHA1:0acefc6bd7e1620eba0198255a220948b4723cb2
编译时间:2010-12-09 18:58:13
这种情况比前一种情况更有趣。该案例包含一个合法的应用程序。但是,应用程序会动态加载内存中的另一个可执行文件,该可执行文件似乎是 DotNET 可执行文件。
在这里,我们将介绍在调试过程中使用 DnSpy 加载原始程序后如何从内存中转储原始程序。将可执行文件拖放到 DnSpy 并查看入口点(主函数)。下图说明了逐步执行 main 函数时代码的显示方式:
乍一看,代码并不能说明太多,但是变量 rawAssembly 似乎很有趣。理解代码后发现获取 rawAssembly 的值内容唯一方法是通过 DnSpy 调试此可执行文件。让我们从两个断点开始,然后进入函数。
下图显示了 248 行(if 条件)上的一个断点, 258 行(rawAssembly 加载过程)上的另一个断点:
通过按开始(绿色播放键),调试器将在第一个断点运行并停止。如果单击“继续运行”,它将进入下一个断点。到达第二个断点后,请查看 Locals 字段RawAssembly 。
本地变量是实时加载到内存中的变量,可以在调试过程中查看。如果在两个断点处比较 Locals 下的 RawAssembly 变量,将会注意到第二个断点处的 rawAssembly 变量现在的内存大小为:
一个非常有趣的问题是,“两个断点之间的变量发生了什么,实际值是多少?” DnSpy 程序具有一项很酷的功能,该功能使分析人员可以通过右键单击值->在内存窗口中显示->选择一个内存号(与选择哪一个无关)来检查变量的内存。
通常,内存部分的显示内容不是很明显。但是在这种情况下,很明显代码段是可执行文件。存在 MZ 字符串(十六进制:4D 5A),这意味着rawAssembly 的值是已加载到内存中的可执行文件。
DnSpy 的另一个很酷的功能是它能够将内存部分导出到磁盘上的文件。要将该部分保存到文件中,只需右键单击内存->保存选择...(此处的输出保存为output_TenClips.exe):
如之前所述,有多种方法来检查文件是否是 DotNET 可执行文件。这次选择的方法是 CFF Explorer 。如下图所示,该文件导入 DLL 文件 mscoree.dll:
之前提到过,RawAssembly 在第二个断点处具有一定的大小。
在本地中查看rawAssembly 的大小(以十六进制表示)(右侧图),然后查看output_TenClips.exe 可执行文件的大小(左侧图),会注意到它们是相同的(0x00044A08 HEX等于到281096字节的文件大小):
由于TenClips通常是合法文件,因此我们将不再继续分析output_TenClips.exe 。但是,如果对原始可执行文件 TenClips.exe 中的可执行代码来自何处感到好奇,请进一步看一下调试过程。而不是从一个断点跳到另一个断点,而是在调试过程中通过使用 step into 选项或仅按 F11 键逐行进入代码内部。
246 行上的 byte [] rawAssembly 只是一个声明,意味着 246 和 258 行之间的 rawAssembly 会得到一个值。
如果仔细观察,会在第一个 if 条件(第248行)中看到变量 raw Assembly 被声明为引用,这意味着在该行之后rawAssembly可能会有一个值。
通过按 F11 键,将使调试器进入第 248 行,它将进入 lf 函数内部:
大致解释一下 lf 的功能。通过使用许多条件,它会在 TenClips.exe 程序本身中查找特定的代码部分,然后将它们组合为一个可执行代码-output_TenClips.exe 。现在可以了解主函数(入口点- 248 行)中的 if 条件会检查拼接是否成功。如果失败,将打印错误。如果成功,它将执行后续的代码。
当执行 TenClips.exe 可执行文件时,程序将从程序本身收集代码段,将它们组装成一个可执行代码(output_TenClips.exe),将其加载到内存中,然后执行它。
此技术称为“动态加载内存”。如果想知道为什么合法应用程序会执行这种技巧,答案很简单,与恶意可执行文件一样,程序员可能希望隐藏代码并使其难以静态读取。
为什么?可能会有很多答案。但是,如果恶意文件完成这个操作,则通常会隐藏恶意文件的实际代码,并仅在执行后才将其加载到内存中。但是,动态加载内存中的另一个可执行文件并不一定总是意味着它是恶意可执行文件或恶意操作,记住这一点!
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8