前面我的几篇文章提到Reflector在反混淆技术中的应用,都是拿Reflector对其他软件开刀。Reflector的作者,Lutz Roeder,深知他写出来的Reflector的强大威力,那么Lutz又是怎样保护自己的软件作品不被Reflector分析个透彻哪?
带着这个疑问,我用Reflector打开了Reflector自身,里面除了一堆Interface定义以外(估计是方便其他coder做add-ins),其他功能在哪里实现,根本没有发现蛛丝马迹。疑点还是有的,就是那五个被简单混淆过的类,不过里面也没有主要功能的代码,只是一些转换,估计是用来做加密的。
最后找到这
<<Reflector.Application>>
public Application(IWindowManager windowManager)
{
this._1 = base.GetType().Assembly;
Type type1 = this._1.GetType("Reflector.ApplicationManager");
if (type1 == null)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this._1);
using (Stream stream1 = this._1.GetManifestResourceStream("Reflector.resources"))//秘密就是在这里
{
int num1 = (int) stream1.Length;
byte[] buffer1 = new byte[num1];
stream1.Read(buffer1, 0, num1);//读入字节流
byte num2 = 0x89;
byte num3 = 0x16;
for (int num4 = 0; num4 < num1; num4++)
{
buffer1[num4] = (byte) (buffer1[num4] ^ num2);//解密变换
num2 = (byte) (num2 + num3);
}
_3 _1 = new _3(new MemoryStream(buffer1));//下面几句是核心部分
_1.MoveNext();
_4 _2 = (_4) _1.Current;
this._1 = Assembly.Load(_2._1());//得到解密结果,加载Assembly到this._1
type1 = this._1.GetType("Reflector.ApplicationManager", true);
}
}
object[] objArray1 = new object[1] { windowManager } ;
this._1 = (IServiceProvider) Activator.CreateInstance(type1, objArray1);
}
public void Run()
{
if (this._1 != null)
{
this._1.GetType().GetMethod("Run").Invoke(this._1, null);//直接调用加载的Assembly的Run启动核心部分程序
}
}
[STAThread]
private static void _1()//嘿嘿,这个就是Main了,居然被改成这个名字
{
if (Application._1())
{
using (Application application1 = new Application(null))
{
application1.Run();
return;
}
}
MessageBox.Show("You cannot launch this application from a network share. Please try running it from a local directory.", typeof(Application).Assembly.Location, MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
秘密就是在这里,原来把一个assembly藏在Reflector.resources文件中了。这里的工作就类似Win32 PE的加密壳技术,把代码段载入内存并解密,然后转移控制权。而_3, _4就是解密的部分,包含了一大堆眼花缭乱的变换,类当中还有子类,看来作者是以此阻止别人对解密原理进行静态分析。
和Win32PE不同的是,似乎动态调试技术在.NET中并没有发展到Win32 PE那么成熟,用过vs.net自带的调试器,如果加载的程序没有调试信息,就只能显示出机器码,甚至连按照机器码单步执行都不行。这层障碍使得解密者无法采用类似Win32 PE的停在OEP然后dump进程的办法破解。
解密的思路,可以从Reflector中把解密类导出,然后手动编写相关的输入输出接口。这种方法依赖于reflector代码的正确性(reflector输出的c#代码并不能100%编译成功)。更通用的办法类似Win32PE打补丁的办法,插入一个method到代码中,在解密完成时把Assembly的内容保存在硬盘上。有空我会尝试这两种方法的可行性。