Santé

为明天干杯!
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Reflector保护方法初探

Posted on 2006-07-17 11:08  smalldust  阅读(8653)  评论(8编辑  收藏  举报
Reflector作为一个如此强大的反编译工具,其本身的保护也一定很完善。曾经有高手的文章[1][2]介绍Reflector的保护方法,但是时过境迁,新版本的Reflector又采用了更为隐晦的保护措施,给想了解Reflector内部细节的人提高了门槛。

下面,就本着学习的态度,把我在分析Reflector时所看到的保护方法简单介绍一下,以当前的最新版本4.2.45.0为例。

1,混淆
混淆应该是Assembly保护的最基本措施了,混淆工具也是比比皆是。用Reflector打开Reflector自己,便可以看到除了若干接口、枚举的定义,就是8,9个名字都为“□”的类;类里面的所有成员以及Nested Class的名字也都叫“□”。这里和以前的版本是相同的,只不过是把各种标识符的名字用系统无法显示的unicode字符代替而已吧。
名字混淆时,很重要的一点就是把实现接口的方法名、Main方法名等等也要加以混淆——不要以为这些名字是不能混淆的。

此外,Reflector还对IL代码进行了简单的混淆,使得很多重要的方法用Reflector反编译成其他语言时,会显示“This item appears to be obfuscated and can not be translated.”。这种情况下为了分析代码,只能读IL;或者是用其他反编译工具分析。

2,防篡改·反Debug
为了防止篡改Assembly,Reflector中为Assembly增添StrongName,以及在程序中检测StrongName是否正常。
为了防止被动态跟踪,在Reflector当中很多地方都会用System.Diagnostics.Debugger.IsAttached检测Debugger是否存在。

3,字符串加密
通常的混淆当中,虽然可以将类和成员的名字变成“乱码”,但是源代码中的字符串(例如要显示一个消息)往往会轻易暴露一段代码的意图。假如某段代码中含有“Error: Can not open file”字样的字符串,我们很容易猜测到这里是不是在打开一个文件。Reflector当中为了防止别人轻易地看到字符串,使用了一个简单的加密函数。这样,别人在阅读源代码时,所有字符串的部分都是毫无意义的一些乱码。Reflector在使用这些字符串时,首先将他们解密,然后再使用。

4,动态加载Assembly
当我们打开Reflector.exe查看Reflector的源代码时,发现代码量很少;也就是说,真正的应用部分被隐藏起来了——隐藏到哪里了呢?前版本的Reflector据说是隐藏在Menifest Resource里面了;新版本的Reflector为了增强隐蔽性,直接把它隐藏在PE文件的.rsrc段的末尾了。为了读取这段“真身”,Reflector.exe当中专门有一个读取PE文件的类,基本逻辑就是找到.rsrc Section,加上.rsrc Section的长度(也即定位到其末尾),再读取一个Int32(“真身”的长度),然后将这段内容读取到内存当中。

这段内容是经过3DES加密的,很有趣的是,密钥是一段劝说你不要破解Reflector的文字的MD5 Hash值。

就算解密之后,这段内容仍然不是一个有效的Assembly——它到底是什么呢?仔细看看Reflector之后的举动,我们就发现,这段内容其实是一个Zip文件。Reflector把这段内容在内存中解压缩,再使用Assembly.Load(byte[])将其载入(并且载入过程全部是用反射方法,程序中根本不会直接出现Assembly.Load字样)。

因此整个过程就是,Reflector是将其主要逻辑所在的Assembly(Reflector.Application.dll)先压缩成一个Zip文件,然后用3DES算法加密,塞到.rsrc段的末尾;在执行时,进行反向操作,动态加载Assembly。

以上简单介绍了Reflector的保护方法。和传统的EXE文件的加密和保护相比,.Net Assembly的保护还显得很稚嫩,Reflector的IL虽然也经过了简单混淆,但是也没有汇编代码那样的错综复杂的、大段无用的代码,看起来仍然是比较易懂的。