Dotnet 结构分析学习笔记

二进制安全 2019-11-10

本文作者:x-encounter(信安之路作者团队成员 & 信安之路病毒分析小组组长) 成员招募:信安之路病毒分析小组寻找志同道合的朋友

这两天一直在看 dotnet,重点是对 Dotnet 的结构进行学习,分析。之前有人问我要过博客地址,我的确搭过一个博客平台,将近有半年没有跟新了,就放在 VPS 上吃灰,上面总结的都是学 Web 安全的一些知识,没有什麽技术含量。现在我喜欢把一些心得写到 Onenote 上,即便突然死机,文章也能保存下来。基本上每隔一段时间就会选择性的把一些学习笔记或者总结的东西发出来,几乎把公众号当作博客了……

由于是学习笔记可能在文章结构上比较突兀,请各位见谅

Dotnet 结构解析

.net 的文件结构是基于PE结构的,可以说是对 PE 结构的一个扩展,主要是对 Text 节进行了魔改,使用 VS 编写一个 .net 程序使用 010 和 PEView 打开

读取 PE 文件 IMAGE_NT_HEADERS 中的 IMAGE_OPTIONAL_HEADER 的数据目录最后一项

img

该 RVA 指向 IMAGE_COR20_HEADER 结构

typedef structIMAGE_COR20_HEADER
{
ULONG cb;
USHORT MajorRuntimeVersion;
USHORT MinorRuntimeVersion;
// Symbol table and startup information
IMAGE_DATA_DIRECTORY MetaData;
ULONG Flags;
union{
DWORD EntryPointToken;
DWORD EntryPointRVA;
};
// Binding information
IMAGE_DATA_DIRECTORY Resources;
IMAGE_DATA_DIRECTORY StrongNameSignature;
// Regular fixup and binding information
IMAGE_DATA_DIRECTORY CodeManagerTable;
IMAGE_DATA_DIRECTORY VTableFixups;
IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
IMAGE_DATA_DIRECTORY ManagedNativeHeader;
} IMAGE_COR20_HEADER;

其中 MetaData 表示了元数据的 RVA 和 SIZE

IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress;
DOWRD SIZE;
}

元数据的起始位置是一个 MetaData Root 结构

img

lSignature 是一个魔术字符串“BSJB”,iVersionString 代表后面版本号字符串有多长

img

接着是一个 STORAGEHEADER 结构

img

iStreams 表示有几个流

往后是 STORAGESTREAM 结构用来描述这些流

img

iOffset 表示从元数据的起始也就是 BSJB 开始的偏移

img

30C+6C=378,#~流的起始地址为 378

378 处是一个 Metadata Table Stream 结构

img

该结构需要解析 Heaps 字段和 MaskValid 字段,Heaps 字段指定了后面索引的长度,MaskValid 字段表示一共有几个表,8 个字节可以表示 64 个表,但是 Dotnet 只定义了 45 个表

img

MaskValid 等于 0x0900021547,转换为二进制 100100000000000000100001010101000111

对照表

img

可知一共有十个表,打开 dnspy 将程序进行反编译

img

接着是一个数组,数组的长度等于表的数量,数组的内容是表示表中有多少项记录

img

数值是与 dnspy 反编译后小括号中的值对应的,第一个 DWORD 表示 Module 表中有一项,第二个 DWORD 表示 TypeRef 表中有 0x12 个项,也就是 18 个项,以此类推……

接下来就是比较重点的内容了,之前说过一共有 45 个表,而表的声明很难在网站查到,这里推荐一本书叫

《Expert .NET 2.0 IL Assembler》,没有中文翻译,反正我是没找到,直接硬啃的

接着就是 Model 表的结构了

书中的说明和定义如下

img

img

转成数据结构

Typedef struct Module
{
WORD Generation;
WORD Name;
WORD Mvid;
WORD EncId;
WORD EncBaseId;
}

Generation 没什么用,主要介紹 Name 字段,Name 是一个偏移值,相对于 #String 流的偏移

img

30C+268+1B0 = 724

img

Name 为 HelloWord.dll

接下来是 TypeRef 表结构

img

Typedef struct TypeRef
{
WORD ResolutionScope;
WORD Name;
WORD Namespace;
}

typeRef 這個表描述了从另一个模块导入的类

ResolutionScope 字段表示在哪个模块中,可以查看 dnspy

img

AssemblyRef 也是一個表,描述从外部导入的程序集的信息

Typedef struct AssemblyRef
{
USHORT MajorVersion;
USHORT MinorVersion;
USHORT BuildNumber;
USHORT RevisionNumber;
ULONG Flags;
WORD PublicKeyOrToken;#Blob
WORD Name;#STRING
WORD Locale;#STRING
WORD HashValue;#Blob
}

关注的重点在如何通过 ResolutionScope 的值找到 AssemblyRef 表,实际上是进行了一步抽象化,书中也讲到了相应的方法,我想从 dnspy 源码的角度进行说明

从 git 上下载 dnspy 源码,我非常佩服这个人,大名鼎鼎的 de4dot 脱壳程序和 dnlib 庫都是这位大佬写的

https://github.com/0xd4d/dnSpy

源碼中的算法

img

Name 和 Namespace 字段都是相對於 #String 流的偏移

接下來是 TypeDef 表

img

img

之前说过 typeRef 是从其他模块导入的类,而 typeDef 就是自身模块所用的类

Typedef struct typeDef
{
Unsigned integer Flag;
WORD Name;
WORD Namespace;
WORD Extends;
WORD FieldList;
WORD MethodList;
}

FLAG 字段表示一些属性,描述 Class 的類型,public,private 等等

img

其余字段所代表的意义可以通过 dnspy 查看

img

FieldList 指的是类的成员变量表的索引,MethodList 指的是成员方法表的索引,都是通过 List 进行寻找的,在后面再讲

Filed 表的定义:

img

Typedef struct Filed
{
WORD Flags;
WORD Name;
WORD Signature;
}

Filed 表指的是类中的成员变量,Flags 字段表示该变量的一些属性,Name 代表相对于 #String 流的偏移,Signature 字段代表相对于 #Blob 的偏移,这里 Signature 表示的是加密后的类型,书中专门有一节讲 Dotnet 的一些表中 Signature 字段的含义:

img

之後看 Method 表,這個表在 Dotnet 结构中有着举足轻重的作用,在运行过程中所调用的函数大部分都来自 Method 表中:

img

Typedef Struct Method

{

DWORD RVA;

WORD ImplFlags;

WORD FlagS;

WORD Name;

WORD Signature;

WORD ParamList;

}

Method 表中主要是自身模块的类的成员函数

RVA 表示該函數IL指令的地址

img

RVA=0x2050,轉換爲 Offset=0x250。在文件中查看

img

dnspy 中的 IL 指令相対應

img

Flag 字段和 typeDef 中的 Flag 相似,表示方法是 Public,private 的……

这里有趣的一点是 Dotnet 程序是如何通过类来找到类中所有的方法的,举一个简单的例子

查看 TypeDef 中的类,以 MyWebServices 为例

查看这个类的结构

MethodList 为 8,在以同样的方法查看 ThreadSafeObjectProvider'1 类的 MethodList

img

在 Method 表中寻找第 8 个函数一直到第十五个函数

img

都为 MyWebServices 类中的成员方法

以此类推 FiledList 和 ParamList 都是上述的思路

限於篇幅只能說到這了,剩下的表研究方法大概都是這個樣子,最好把這 45 個表都過一遍,加深一下対 .net 的理解

后记

实际上所谓的混淆器都是混淆这些成员方法,常量名字,成员变量的名字,再加上一些控制流混淆和压缩加密技术就组合成了混淆器,貌似 dotnet 平台上有带虚拟机的壳……不过不是很常见,有兴趣的可以拜读一下 de4dot 程序看是如何脱壳的。壳这个东西啊,还是 win32 上面的叼啊……

参考文献

《加密於解密第三版》

《Expert .NET 2.0 IL Assembler》


本文由 信安之路 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

楼主残忍的关闭了评论