2009年12月1日 星期二

[轉貼].NET Reactor 4.0 - Metadata 手工修复记

原文網址:http://www.rainsts.net/article.asp?id=831

最近剛好接到一個案子需要用到 弄老半天弄不出來  在網上找了很久看到這篇

實在是寫的太好了 詳細又清楚  大陸神人果然就是多

完整的從頭到尾做一次還附圖  少了我不少摸索的時間!!

 

.NET Reactor 4.0 (Beta) 相对于以前的版本有很大的提升,就连最常用的 "Suppress ILDASM" 都升级成 "Suppress Decompilation / Anti ILDASM"。本文就是用这个功能做案例,来锻炼自己对元数据表结构的认知。
1. 加密
找个目标程序集,用 4.0 加密,注意不要选择其他选项,仅选中 "Quick Settings" 中的 "Anti ILDASM" 即可。因为本文的目的是手工修复元数据表,而不是为了完成一个破解过程。
当我们用 .NET Reflector 或者 Mono Cecil 打开这个加密的程序都会发现出错,可见还是具有一定的防护能力的。接下来,我们就依照 .NET Reflector 给出的错误信息开始手工修复过程。
2. 修复
先准备好相应的工具软件,包括 CFF Explorer VII、ILDASM (修改版)、计算器 (16进制计算),以及一个随手记录信息的记事本。
(1) NumberOfRvaAndSizes

13_144752_1

这个错误通常是 Optional Header 中的 NumberOfRvaAndSizes 被修改造成的,其实际值应该是 0x00000010 (16)。修改保存,刷新 Reflector,继续下一个错误信息。
(2) NumberOfStreams

13_144758_2

这也是一个比较常见的手段,通过造成错误的 MetaData Streams 来达到干扰目的。

13_144804_3

多了一个冒牌的 #GUlD 和 #Blop。没关系,将后面正确的 Offset 和 Size 值拷贝到山寨货的位置,同时在 Hex Editor 中跳转到到相应的 Offset 位置,修正这两个冒牌货的名字。

13_144810_4

当然,别忘了在 MetaData Header 中将 NumberOfStreams 从 0007 改成 0005。保存后在 CFF Explorer VII 中重新打开目标文件。再次浏览 MetaData Streams 时,#Strings、#GUID、#US 和 #Blob 数据都恢复正常。
(3) Multiple Assembly Definitions
刷新 Reflector 继续下一个错误信息。

13_144825_5

晕~~~~ 这个太变态了,居然包含多个程序集。这显然是动了 #~ Stream 了。

13_144831_6

在 Tables 中我们果然看到了多出的这个破坏分子。怎么办呢?先复习一下 Metadata 结构知识。#~ 中前 24 bytes 存储了 Metadata Header 信息。从 0x00000018 (Offset 24) 开始存储的是 Rows 信息,也就是你在 Tables 中看到的各种类型统计数字。Rows 的长度是 4 * n。

13_155101_11

我们数数看,一共 15 种类型,也就是说长度是 60 (0x3c),偏移位置应该是 0x00000018 ~ 0x00000053。Assembly 的索引序号是 11 (从0开始),偏移位置就是 0x00000018 + 11 * 4 = 0x00000044。在 CFF Explorer VII 中的 #~ 右侧 16 进制编辑器中将 Row 值 从 02 改成 01。

13_144837_7

注意,事情并没有结束。在 Rows 之后,也就是 0x00000054 开始存储的是各种具体类型细节信息的 Tables,我们修改了 Row 值,也就意味着 Tables 连续读取的 Index 和 Offset 也会发生变化,因此我们需要将后续数据前移 4 byte,覆盖被剔除的那个伪造 Assembly 数据。
首先得获取伪造 Assembly 详细信息的偏移量。由于这次的偏移量计算涉及到不同具体类型信息的长度,因此我们借助 ILDASM 来获得相应数据。用 ILDASM 打开目标文件,在 View -> MetaInfo 菜单中选中 Raw:Header,Schema,Rows 和 Raw:Heaps 两项。然后 Ctrl + M 查看元数据信息。

===========================================================
Metadata section: 0x424a5342, version: 1.1, extra: 0, version len: 12, version: v2.0.50727
flags: 0x00, streams: 5
Stream 0: name: #~, size 1040
Stream 1: name: #Strings, size 1448
Stream 2: name: #GUID, size 32
Stream 3: name: #US, size 216
Stream 4: name: #Blob, size 444
Metadata header: 2.0, heaps: 0x00, rid: 0x01, valid: 0x000003092000d557, sorted: 0x0000000000000000
Strings: 1448(0x5a8), Blobs: 444(0x1bc), Guids: 32(0x20), User strings: 216(0xd8)
=================================================
...
32(0x20): Assembly cRecs: 2(0x2), cbRec: 22(0x16), cbTable: 44(0x2c)
col 0: HashAlgId oCol: 0, cbCol:4, ULONG
col 1: MajorVersion oCol: 4, cbCol:2, USHORT
col 2: MinorVersion oCol: 6, cbCol:2, USHORT
col 3: BuildNumber oCol: 8, cbCol:2, USHORT
col 4: RevisionNumber oCol: a, cbCol:2, USHORT
col 5: Flags oCol: c, cbCol:4, ULONG
col 6: PublicKey oCol:10, cbCol:2, blob
col 7: Name oCol:12, cbCol:2, string
col 8: Locale oCol:14, cbCol:2, string
-------------------------------------------------
1 == 0:00008004, 1:0001, 2:0000, 3:0000, 4:0000, 5:00000000, 6:blob#0, 7:string#1, 8:string#0
2 == 0:00008004, 1:0001, 2:0000, 3:0000, 4:0000, 5:00000000, 6:blob#0, 7:string#6, 8:string#0
...

嗯,很好,有个 04 80 00 00 的标记,我们找到这个位置就行了。继续在编辑器中工作,找到第二个标记位置(0x000003AE),往后数出 22(0x16) 个字节(0x000003AE ~ 0x000003C3),这就是我们要覆盖的数据。选择 0x000003C4 开始的数据,以 Unicode 方式拷贝,并 Write 到 0x000003AE 处。
拷贝

13_144842_8

写入

13_144848_9

保存修改 (注意关掉 ILDASM,否则无法保存)。刷新 Reflector,可以看到该错误被修复。
(4) Multiple Module Definitions
这个错和上面的如出一辙,修复手段也一样。

13_144854_10

先修改 Rows 值,Module 是第一项,偏移位置就是 0x00000018,将其修改为 01 即可 (先别保存,否则 ILDASM 无法打开)。

...
0(0): Module cRecs: 2(0x2), cbRec: 10(0xa), cbTable: 20(0x14)
col 0: Generation oCol: 0, cbCol:2, USHORT
col 1: Name oCol: 2, cbCol:2, string
col 2: Mvid oCol: 4, cbCol:2, GUID
col 3: EncId oCol: 6, cbCol:2, GUID
col 4: EncBaseId oCol: 8, cbCol:2, GUID
-------------------------------------------------
1 == 0:0000, 1:string#2b, 2:guid#1, 3:guid#0, 4:guid#0
2 == 0:0000, 1:string#0, 2:guid#2, 3:guid#0, 4:guid#0
...

记录长度为 10(0xa),那么第二条记录的偏移位置就是 0x00000054 + 0xa = 0x0000005E。数出 10 bytes (0x0000005E ~ 0x00000067),将其后的数据按照前面的手法前移覆盖(顺便说一下 CFF Exploer VII 的编辑器不太好用,可能需要多次拷贝覆盖,千万记住上次覆盖的结束位置,避免出错)。
保存修改。再次用 Reflector 刷新程序集时,我们已经可以看到其原始面目了,表明我们的修复工作顺利完成。
------- 分割线 --------------
最简单的修复手法是 ILDasm + ILAsm,不过本文的目的是为了复习元数据的相关知识,因此手工修复可以加强对相关知识的了解。同时该文章也表明很多看上去貌似高深的技术,其根本还是对基础知识的掌握,当然少不了耐心和细心。
附件是修复前的原始加密文件,大家可以据此练习本文操作过程。而有关 Metadata 结构定义可以到 ECMA C# and Common Language Infrastructure Standards 下载 MS Partition II。

 

 

 

=====

心得:

後面的部份看起來有些複雜  其實基本上就是算重覆的table在哪  然後把後面的資料移上來蓋過去就好

ultraedit有個好用的功能可以幫我們完成這件事  不用用到CFF內建爛爛的編輯器

直接填入要移除BLOCK大的小就可以整個往前平移了  超方便

 

ULTRAEDITUNSERT

 

相關其他文章可以參考以下這幾篇

http://hi.baidu.com/dreamzgj/blog/item/3c0bc18e243592f0513d928a.html

http://hi.baidu.com/dreamzgj/blog/item/5cd5f1456fedc388b2b7dc8a.html