翻译:shan66
稿费:160RMB(不服你也来投稿啊!)
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
在本文中,我们将隆重向大家介绍一款脱壳神器:“Selfie”。有了它,大家就不用为了在基于自修改代码技术加壳的恶意软件中寻找OEP发愁了,因为在大部分情况下,Selfie都能自动完成这项任务。该工具现在已经开源了,大家可以从下列地址下载:https://github.com/BreakingMalware。
引言
对于恶意软件研究人员来说,我们经常会遇到经过加壳、加密或混淆处理的代码。恶意软件通常都会试图以这种方式来逃避安全检测。然而,为了研究这些恶意软件,我们需要摆脱加壳软件的干扰,找出恶意软件运行的起始点,即所谓的原始入口点(OEP)。不幸的是,脱壳工作通常是我们研究人员最耗时、最复杂的一项工作。
当我们遇到自修改代码的恶意软件时,脱壳工作就会格外的冗长乏味。自修改代码是加壳软件常用的技术,它通过增加额外的安全层来提高软件的复杂性。对于使用了自我修改代码来说,加壳后的恶意软件会对自身进行覆盖。在脱壳的时候,需要利用动态分配的内存脱壳,恢复其原始代码和映像,然后将脱壳后的代码拷回来。最令安全研究人员沮丧的事情,莫过于以手动方式来检索采用了自修改代码的恶意软件的OEP了。
由于这个缘故,我们开发了“Selfie”。对于Selfie来说,即使这些恶意软件利用自修改代码技术进行了加壳保护,它照样能够自动找出恶意软件的OEP。Selfie工具本身是基于DynamoRIO的,后者是一个动态代码插桩(Dynamic Binary Instrumentation,DBI)框架。
要注意的是,对于那些使用不太常见的方法实现的自修改代码,Selfie目前尚无法处理,例如,使用返回指令到达OEP或不改变IAT大小的自修改代码等。
手动方式为自修改代码脱壳
下面,我们详细介绍如何通过手工方式为自修改代码脱壳的具体步骤,为此,我们以Trojan Shylock为例进行演示。
第一步:观察PE文件
下面是我们得到的原始信息,我们将从这里开始我们的脱壳之旅。
图1:shylock的特征数据
图2:注意高亮显示的部分
第2步:分析第一次动态内存分配
我们从第1步所示(即地址0x00404920)的入口点开始分析。
这将我们带到了地址0x4040F8处。在那里,我们可以看到一个对VirtualAlloc函数的调用,该调用动态分配了5E200字节的内存,其权限为PAGE_EXECUTE_READWRITE。
图3:分配虚拟内存
图4:virtualalloc的参数
一旦成功地分配了内存,代码就会把经过加壳的进程映像复制到动态分配的内存中。
图5:EAX指向分配的内存的基址,ECX是计数器。
第3步:分析第二次动态内存分配
一旦完成复制,加载器就会把执行权限转移给前面分配的内存中的代码。
我们可以看到,这里进行了另外一次内存分配 ,它只有PAGE_READWRITE权限。注意,这里的VirtualAlloc是从第2步中分配的动态内存中调用的。
图6:第二个VirtualAlloc
我们又看到一个复制循环(0x00574210),在这里,原始的那个加壳后的恶意软件(别忘了,现在它被映射到动态存储器中)含有一个嵌入式加密PE。下面的代码会把这个加密的嵌入式PE复制到第二次动态分配的内存中:
图7:拷贝嵌入的加密PE的复制循环代码
图8:原始映像基地址(左)含有加密的嵌入PE(右)。
图9:被复制到动态内存中的加密PE
图10:解密后从上面复制的PE
第4步:分析第三次动态内存分配
现在分析第三次内存分配。这次,加载程序将解密的PE复制到分配好的内存中:
图11:第三个VirtualAlloc
图12:第三个拷贝循环用于复制解密的PE
当复制例程成功结束后,将进入下面的代码。简而言之,它会重置原始映像的基地址。唉,我们知道这就是自修改代码。
图13:原始的进程映像空间(0x00400000)被0填充
图14:最后,得到一个充满0的进程映象空间
第5步:分析倒数第二个感染步骤
图15:加载器将解密的EXE复制到已归零的进程映像空间。
脱壳后的恶意软件(Shylock):
图16:0x0040000就是该恶意软件元数据(图1)中的映像基地址
接下来,加载器将使用LoadLibrary/GetProcAddress重建导入地址表(IAT)。
图17:LoadLibrary
图18:GetProcAddress
第6步:终极目的:调用OEP
图19:间接调用OEP(0x00403780)
Selfie:实现自修改代码的自动脱壳
Selfie使用的是动态插桩技术,允许我们在应用程序运行时为其添加或修改代码。这真是研究人员的福音,因为它允许我们在恶意软件运行过程中对其进行监视和深入分析。我们可以认为,对恶意软件运用插桩技术后,我们的代码就能够完全控制恶意软件。
在进一步探讨之前,先让我们来了解一些插桩技术的背景知识。
DBI和DynamoRIO的简要介绍
Selfie使用DynamoRIO(VMWare的框架)来提供动态代码插桩(DBI)功能。
有关DBI和DynamoRIO的简要背景知识,我们不妨参考http://uninformed.org/index.cgi?v=7&a=1&p=3页面的介绍:
“动态代码插桩(DBI)是一种通过注入插桩代码在二进制应用程序运行时分析其行为的方法。插桩代码被注入之后,会作为正常指令流的一部分执行。在大多数情况下,测试代码对于被注入的应用程序来说是完全透明的。在运行时分析应用程序能够深入了解该应用程序在各个执行点的行为和状态。这是静态二进制分析和动态二进制分析之间的主要区别之一。与考察可能发生什么事情不同,动态二进制分析有利于对实际发生的事情直接进行干预。虽然在应用程序所有代码路径上无法面面俱到,但它能够帮助我们详细了解应用程序的具体执行状态,从而弥补了这一点。
…
DynamoRIO是DBI框架的一个实例,允许自定义的插桩代码以动态库的形式进行集成。这个工具本身是由HP研究人员开发的动态优化引擎Dynamo和MIT开发的RIO(Runtime Introspection and Optimization)结合而成的。我们不打算介绍DynamoRIO在实现方面的具体细节,对于本文来说,重点在于了解其基本概念。
…
具体来说,Dynamo是在指令流执行的时候对其进行处理。为了实现这一点,Dynamo需要承担起执行指令流的责任。它使用反汇编器来识别将要执行的代码中的下一个分支指令的目标地址。反汇编的指令集被称为片段(更常见的叫法是基本块)。如果分支指令的目标地址在Dynamo的片段缓存中,则它会在片段缓存中执行该(可能优化过的)代码。当这段代码执行完以后,它会把控制权返回Dynamo,以便反汇编下一个片段。当Dynamo遇到目的地址不在其片段缓存中的分支的时候,它会将其添加到片段缓存中并对其进行优化。这是将插桩代码注入到针对分支目的地址而生成的优化片段的完美机会。在此级别注入插桩代码对于应用程序是完全透明的。虽然应用DynamoRIO后事情会变得异常简单,但至少应该对Dynamo的功能有所了解。
从分析师的角度来看,DynamoRIO的最佳特性之一是它提供了一个框架,用于在片段被插入到片段缓存期间注入插桩代码。这对于拦截应用程序内的内存访问来说格外有用。创建片段时,DynamoRIO为注入到所生成的片段中的指令提供相应的分析库。为了优化性能,DynamoRIO提供了多级反汇编信息。在最高优化级别中,仅提供指令的最基本信息。在最低优化级别,可以获得关于指令及其操作数的非常详细的信息。分析库可以自由地控制它们检索的信息的级别。
深入了解Selfie
通过DynamoRIO的API,我们能够编写自己客户端——实际上,客户端就是DLL。DynamoRIO为每次放入代码缓存的代码都提供了hook。通过这些hook,客户端能够检查和转换将放入代码缓存中的任何代码片段。这样,我们实现了完全控制,只要我们喜欢,就可以执行任何动作。
Selfie使用指南
我们的Selfie的算法机制如下:
第1步:我们在Selfie的入口点即dr_init()处,检索用于主可执行文件(比如malware.exe)的模块数据
第2步:保存主模块的起始地址、结束地址和IAT大小。
图20:Selfie的入口点
第3步:使用dr_register_bb_event()为基本块事件的注册回调函数。
图21:注册回调函数
第4步:回调函数On_event_basic_block
通过dr_register_bb_event()注册的基本块创建事件,客户端能够在执行代码之前检查和转换任何代码段。对于每个新块,我们使用instrlist_first()/ instr_get_next()例程来遍历块指令。
图22:基本块创建事件
第5步:遍历块指令。
当instr的操作码是OP_call或OP_call_far(instr_is_call_direct())或instr的操作码是OP_call_ind或OP_call_far_ind(instr_is_call_indirect())的时候,我们就调用基本块回调函数(on_call_event):
图23:调用插桩代码
第6步:确定OEP
1. 检查该调用(被调用方)的目标地址是否在主可执行代码的起始地址和结束地址之间。
2. 如果答案是肯定的,我们就调用GetImportAddressTableSize()来获取当前的IAT大小。
3. 如果当前IAT大小不同于原始IAT大小(就像我们在主函数中得到的),被调用方地址就是可疑的OEP。
注入Selfie
为了将Selfie注入到malware.exe的进程中,我们使用drrun.exe,具体参数如下所示:
Selfie在行动
Selfie做得怎么样呢?
首先,让我们对上面手动脱壳的恶意软件(Shylock)运行Selfie:
图24:针对Shylock运行Selfie
现在让我们来考察一个Win32 / Xswkit(别名Gootkit)恶意软件。
对于那些不熟悉Xswkit恶意软件的人来说,它就是Win32 / Poweliks的一个克隆,只不过提供了一些额外的功能(它是通过使用微软的shim引擎以及不同的启动方法(rundll + mshta.exe)实现UAC绕过的)。
有关该样本的工作机制的详细信息,请参阅kernel_mode.info论坛中EP_X0FF提供的完整分析。
对于该样本加壳和脱壳后的代码,也可以从kernelmode.info论坛下载。感谢Tigzy和R136a1为我们提供了这些样本代码,以及EP_X0FF提供的未加壳的样本代码。
加壳之后的:
在运行Selfie之前,我们看到的是这样的:
图25:加壳后的恶意软件Xswkit的样本。
请注意映像基址和AddressOfEntryPoint(EP)字段。
脱壳之后的:
图26:未加壳的Xswkit恶意软件样本。注意映像基址和AddressOfEntryPoint(EP)字段。脱壳后的样本中的EP是实际的OEP
现在让我们看看,利用Selfie工具是否能够得到类似的结果:
图27:事实上,这里的OEP与未加壳的样本中的OEP的值是一致的
结束语
我们已经在GNU / PL许可下公开发布了Selfie工具及其相应的代码。如果你是研究员,请随意使用该工具。
欢迎大家积极提供反馈、评论和改进建议。
可执行文件和代码下载地址:https://github.com/BreakingMalware。
发表评论
您还未登录,请先登录。
登录