原创干货 | 【恶意代码分析技巧】06-exe_.net

1.NET的介绍

.NET并不是一种编程语言,而是一个平台,一种运行环境。无论是什么操作系统,只要安装了.NET框架,便可以运行.NET可执行程序。目前,.NET平台支持20多种编程语言,包括Visual Basic.NET(VB7.0之后的版本)、C++、C#等。 为了支持多种操作系统,.NET程序不能直接存储x86、arm等汇编指令,而是存储“.NET汇编指令”,在.NET程序运行的时候,.NET环境将“.NET汇编指令”即时编译(JIT)成x86、arm等CPU能直接识别的汇编指令(而不是翻译),然后再执行。这里的“.NET汇编指令”被称为中间语言(IL,Intermediate Language),又是也叫MSIL,Microsoft Intermediate Language。这些IL代码,被直接保存在.NET程序中。而编译的过程就是CLR,Common Language Runtime,通用语言运行时,CLR是.NET的核心,是IL语言的运行环境。 图片 103.png

在Windows下,.NET程序仍然是以PE文件的形式存在,但是Windows不再直接负责程序的运行。一个普通的PE程序,从启动到终止,完全受到Windows控制,Windows的Loader加载器会负责该程序的内存分配,线程管理等工作。而在.NET程序中,Windows只是作为一个入口,作为进入CLR的跳板,windows只负责跳转到CLR的执行引擎(EE)中,将控制权交由CLR,由CLR进行分配内存,线程管理,异常处理等。可以看到,一个经典的.NET程序中,只有一条x86指令:jmp _CorExeMain: 图片 102.png 进入_CorExeMain,就是进入.NET环境了,接下来.NET环境执行IL代码,直接由.NET环境负责程序的运行。 除了通过_CorExeMain进入.NET环境,还可以通过_CorDllMain进入。_CorExeMain和_CorDllMain都是MSCOREE.DLL中的导出函数,MSCOREE.DLL就是.NET环境的载体。这种方式类似VB 使用MSVBVM60.DLL的方式。

2.文件解析

在正式分析前,我们首先要弄明白几个名词:中间语言、元数据、Token、流数据、表和堆。 中间语言,IL,在前文中以及提到过。IL是.NET唯一能读懂的语言,也是唯一可执行的语言,运行完全受.NET监控。 元数据,Metadata,描述.NET程序运行时必需的一切信息的数据,包括版本、类型的各个成员(方法、字段、属性、事件)等,元数据中每个项的数据被称为一个流。流按存储结构的不同分为堆(Heap)和表(Table), Token,是为了区分各项元数据,而设置的单独的标识。 .NET文件结构的解析工具是CFF Explore,.NET PE文件结构整体如下,接下来我们进行详细介绍。 图片 138.png

2.1. .NET文件格式分析

.NET环境下的PE文件,结构上和传统的PE文件相同,但是使用了数据目录表中的IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR条目保存.NET的信息结构,该条目指向IMAGE_COR20_HEADER结构。 图片 106.png IMAGE_COR20_HEADER的RVA是0x2008,计算得到offset是0x208,在.text节中。 IMAGE_COR20_HEADER一共0x48字节,记录了元数据(Metadata)的RVA(offset是0x26C)和大小,也记录入口IL代码的Token: 图片 105.png IMAGE_COR20_HEADER结构大小是0x48,所以结束位置是0x250,Metadata开始位置是0x26C,两者中间的0x1C字节的数据就是IL代码,但我们还不清楚这段IL代码是从那里开始执行的: 图片 107.png Metadata结构开始的地方是元数据头,记录了流的数量: 图片 110.png 紧跟着元数据头的就是,流数据头,流数据头的结构: 图片 111.png 本例中一共5个流数据: 图片 113.png 图片 112.png 第一个指向的流是#~流,也就是元数据表流,其结构如下: 图片 114.png 图片 121.png 通过计算得到元数据表流的偏移是0x2D8: 图片 115.png 其中最重要的Mask Valid值是0x0000000900001447,表示该程序使用了哪些表,本例中使用的表有Module、Method等8个表: 图片 119.png 紧跟元数据表流头的是一串4字节数组,每个元素代表该表中有多少项记录,8个表共32字节: 图片 120.png 接下来就是每个表的内容了,每个表又都有自己结构: 图片 117.png 其中最重要的是Method表结构,它指明了IL代码的位置,RVA是0x2050,计算offset是0x250,和前面判断的内容相符。 至此我们完全掌握了IL代码的位置,关于其他表和流数据的结构就不详细介绍了。

2.2.解析IL代码

通过前面的分析,我们发现Main的IL代码内容如下: 图片 122.png 使用ILDASM分析,可以将其反汇编为IL代码: 图片 124.png 可以看到IL的“机器码”中,只有操作码和操作数,而操作数是以Token的形式存在的。 每个Token的值是AABBBBBB的形式存在的,AA表示对应的表(其中0x70对应的是用户字符串流(#US)),BBBBBB表示偏移: 图片 125.png

3.分析技巧

3.1.dnSpy

.NET程序反编译的工具很多,但功能上都大同小异。笔者推荐使用的是dnSpy,因为他不仅能将.NET程序反编译为C#代码,还支持动态调试。 该软件的使用很简单,直接将.NET程序拖进工具中,你就能看到程序的入口点: 图片 126.png 点击进入,分析Main函数: 图片 127.png 你还可以进行动态调试: 图片 128.png

3.2.de4dot

在某些情况下,你可能看到入口点名称是不可读的,那说明该程序可能被加壳了: 图片 129.png 这种时候你就可以祭出de4dot,它支持十几种.NET壳的识别与处理(Agile.NET (aka CliSecure)、Babel.NET、CodeFort、CodeVeil、CodeWall、CryptoObfuscator、DeepSea Obfuscator、Dotfuscator、.NET Reactor、Eazfuscator.NET、Goliath.NET、ILProtector、MaxtoCode、MPRESS、Rummage、Skater.NET、SmartAssembly、Spices.Net、Xenocode )。 使用-d参数 识别壳: 图片 130.png 直接脱壳: 图片 131.png 脱壳后效果: 图片 132.png

3.3.恶意代码隐藏

.NET支持内存中的Assembly载入,然后由Invoke调用该Assembly的方法(在某种程度上,类似于java反射执行): 图片 137.png 所以有一些程序会将恶意代码存放在资源中,通过ResourceManager、ResourceReader等方式将资源加载进内存,并经过一系列的处理释放恶意代码: 图片 133.png 图片 135.png 最后执行恶意代码中的关键函数: 图片 136.png 在分析使用此类技术的样本时,使用dnSpy动态调试分析非常方便。

参考资料:

https://www.cnblogs.com/dwlsxj/p/MSIL.html https://www.cnblogs.com/Mikhail/p/6134647.html https://www.codeguru.com/csharp/.net/net_general/il/article.php/c4635/MSIL-Tutorial.htm 《加密与解密 第四版》

免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。查看原文

为您推荐