微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

.NET Reactor 4.0 - Metadata 手工修复记

.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



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

(2) NumberOfStreams



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



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



当 然,别忘了在 MetaData Header 中将 NumberOfStreams 从 0007 改成 0005。保存后在 CFF Explorer VII 中重新打开目标文件。再次浏览 MetaData Streams 时,#Strings、#GUID、#US 和 #Blob 数据都恢复正常。

(3) Multiple Assembly DeFinitions

刷新 Reflector 继续下一个错误信息。



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



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



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



注 意,事情并没有结束。在 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,USHORT 
  col  3:  BuildNumber  oCol: 8,USHORT 
  col  4:  RevisionNumber oCol: a,USHORT 
  col  5:  Flags        oCol: c,ULONG  
  col  6:  PublicKey    oCol:10,blob   
  col  7:  Name         oCol:12,string 
  col  8:  Locale       oCol:14,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,7:string#6,8:string#0

...


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

拷贝



写入



保存修改 (注意关掉 ILDASM ,否则无法保存)。刷新 Reflector,可以看到该错误被修复。

(4) Multiple Module DeFinitions

这个错和上面的如出一辙,修复手段也一样。



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

...

 0(0): Module               cRecs:    2(0x2),cbRec: 10(0xa),cbTable:    20(0x14)
  col  0:  Generation   oCol: 0,USHORT 
  col  1:  Name         oCol: 2,string 
  col  2:  Mvid         oCol: 4,GUID   
  col  3:  EncId        oCol: 6,GUID   
  col  4:  EncBaseId    oCol: 8,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,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。

点击下载原始加密文件 (重新加密的,和上面演示的数据有出入)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...