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

  • A+
所属分类:逆向工程

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
《加密与解密 第四版》

相关推荐: 韭讯K2openwrt 抓包解密

前言:前文已经写了把韭讯K2刷成openwrt,并通过tcpdump进行抓包。这篇文章介绍如何对https进行解密。1.https加解密原理具体就不说了,自己去看https://zh.wikipedia.org/zh-cn/超文本传输安全协议https://z…