"怎样保护我程序中的 DLL 不被别人盗用……"
当我看到这个问题的第一反应不是保护,而是满脑子的 IL 反编译代码。说实话,无论你用什么方法,都不能避免被有心人 "破解"。混淆也好,加壳也罢,不过是让别人多费些功夫而已。当然,这会大大缩减 "有心人" 的范围。要保护自己的 DLL 不被别人盗用,最好的办法是把它合并到 Entry EXE 中,然后无论是 public 还是 protected 都统统混淆(再加 n 层壳),这样自然没有 DLL 可供盗用了。(很遗憾,还是可以盗用的。使用 脱壳器 + dump 还原程序集,然后将 EXE 作为一个 Assembly 反射 Load 到程序域中调用执行,只不过要花点时间对付那些怪莫怪样的类型名称而已)"你说的那些太复杂了,而且我不大熟悉混淆工具,听说容易出错…… 有没有简单点的方法…… 阻止普通人就行了…… 破解高手就算了吧……"
OK! 简单是吧?本文教的方法都简单到了极点,权作无聊时玩玩吧。
方法一. 调用验证
所谓调用验证,就是在 DLL 相关类型中验证调用者是不是某个特定的程序集,如果不是则抛出异常进行阻止。
核对 Entry Assembly Name,最好使用强签名,这样就不能简单通过相同程序集名冒充。
public class MyClass
{
static MyClass()
{
if (Assembly.GetEntryAssembly().FullName != "MyExe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
throw new Exception("报告警长!发现一只耳侵入……");
}
public void Test()
{
Console.WriteLine("MyClass.Test...");
}
}
另外一种情况是,这个 DLL 会被公司 n 个已知或未知的程序使用。我们自然不能把所有的 FullName 都写进去。解决方法是:判断 PublicKeyToken。通常一个公司的所有产品都会使用同一个签名密钥文件(snk/pfx),所生成的程序集 PublicKeyToken 都会相同。不要告诉我,你每个程序集的签名文件都是临时生成的……
public class MyClass
{
static MyClass()
{
var bytes = Assembly.GetEntryAssembly().GetName().GetPublicKeyToken();
var publicToken = new StringBuilder();
Array.ForEach(bytes, b => publicToken.Append(b.ToString("x")));
if (publicToken.ToString() != "fcc8632e2470ea51")
throw new Exception("报告警长!发现一只耳侵入……");
}
public void Test()
{
Console.WriteLine("MyClass.Test...");
}
}
这样,非法调用者无论是通过 "new MyClass()" 还是 "Activator.CreateInstance",都会抛出 TypeInitializeException。
如果你要保护的是整个 DLL,懒得为每个类型添加 .cctor 验证代码,可以将验证代码写到 <Module>.cctor 中,不过这有点麻烦。首先 ILAsm 反编译你的 DLL。
d:\temp> ildasm MyLib.dll /out:MyLib.il
然后用编辑器打开 MyLib.il,添加如下代码 (<Module>.cctor,IL 直接从 MyClass.cctor 中拷过来就行。如果 MyLib.dll 有 PublicKey,注意删除)。
.method private hidebysig specialname rtspecialname static void .cctor()
{
// Code size 102 (0x66)
.maxstack 3
.locals init ( uint8[] bytes,
class System.Text.StringBuilder publicToken,
int32 i,
bool CS$4$0000)
IL_0000: nop
IL_0001: call class System.Reflection.Assembly System.Reflection.Assembly::GetEntryAssembly()
IL_0006: callvirt instance class System.Reflection.AssemblyName System.Reflection.Assembly::GetName()
IL_000b: callvirt instance uint8[] System.Reflection.AssemblyName::GetPublicKeyToken()
IL_0010: stloc.0
IL_0011: newobj instance void System.Text.StringBuilder::.ctor()
IL_0016: stloc.1
IL_0017: ldc.i4.0
IL_0018: stloc.2
IL_0019: br.s IL_0039
IL_001b: nop
IL_001c: ldloc.1
IL_001d: ldloc.0
IL_001e: ldloc.2
IL_001f: ldelema System.Byte
IL_0024: ldstr "x"
IL_0029: call instance string System.Byte::ToString(string)
IL_002e: callvirt instance class System.Text.StringBuilder System.Text.StringBuilder::Append(string)
IL_0033: pop
IL_0034: nop
IL_0035: ldloc.2
IL_0036: ldc.i4.1
IL_0037: add
IL_0038: stloc.2
IL_0039: ldloc.2
IL_003a: ldloc.0
IL_003b: ldlen
IL_003c: conv.i4
IL_003d: clt
IL_003f: stloc.3
IL_0040: ldloc.3
IL_0041: brtrue.s IL_001b
IL_0043: ldloc.1
IL_0044: callvirt instance string System.Object::ToString()
IL_0049: ldstr "fcc8632e2470ea51"
IL_004e: call bool System.String::op_Inequality(string,
string)
IL_0053: ldc.i4.0
IL_0054: ceq
IL_0056: stloc.3
IL_0057: ldloc.3
IL_0058: brtrue.s IL_0065
IL_005a: ldstr bytearray (A5 62 4A 54 66 8B 7F 95 01 FF D1 53 B0 73 00 4E // .bJTf......S.s.N
EA 53 33 80 B5 4F 65 51 26 20 26 20 ) // .S3..OeQ& &
IL_005f: newobj instance void System.Exception::.ctor(string)
IL_0064: throw
IL_0065: ret
}
最后编译回去。
d:\temp> ilasm MyLib.il /dll /key=my.snk /optimize /out=MyLib.dll
<Module>.cctor 会在托管模块内任何成员或类型被调用前触发,所以完全能达到我们偷懒的目的。至于测试,就不需要我多说了吧。
页:
[1]