Sithere的DotNet学习历程
DotNet Studing
博客园
社区
首页
新随笔
联系
管理
订阅
随笔- 3 文章- 0 评论- 0
DLL 简易保护方法【转】
"怎样保护我程序中的 DLL 不被别人盗用……"
当我看到这个问题的第一反应不是保护,而是满脑子的 IL 反编译代码
。说实话,无论你用什么方法,都不能避免被有心人 "破解"。混淆也好,加壳也罢,不过是让别人多费些功夫而已。当然,这会大大缩减 "有心人" 的范围。要保护自己的 DLL 不被别人盗用,最好的办法是把它合并到 Entry EXE 中,然后无论是 public 还是 protected 都统统混淆(再加 n 层壳 ),这样自然没有 DLL 可供盗用了。(很遗憾,还是可以盗用的。使用 脱壳器 + dump 还原程序集,然后将 EXE 作为一个 Assembly 反射 Load 到程序域中调用执行,只不过要花点时间对付那些怪莫怪样的类型名称而已)
"你说的那些太复杂了,而且我不大熟悉混淆工具,听说容易出错…… 有没有简单点的方法…… 阻止普通人就行了…… 破解高手就算了吧……"
OK! 简单是吧?本文教的方法都简单到了极点,权作无聊时玩玩吧。
方法一. 调用验证
所谓调用验证,就是在 DLL 相关类型中验证调用者是不是某个特定的程序集,如果不是则抛出异常进行阻止。
核对 Entry Assembly Name,最好使用强签名,这样就不能简单通过相同程序集名冒充。
Code
1
public
class
MyClass
2
{
3
static
MyClass()
4
{
5
if
(Assembly.GetEntryAssembly().FullName
!=
"
MyExe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
"
)
6
throw
new
Exception(
"
报告警长!发现一只耳侵入……
"
);
7
}
8
9
public
void
Test()
10
{
11
Console.WriteLine(
"
MyClass.Test
"
);
12
}
13
}
另外一种情况是,这个 DLL 会被公司 n 个已知或未知的程序使用。我们自然不能把所有的 FullName 都写进去。解决方法是:判断 PublicKeyToken。通常一个公司的所有产品都会使用同一个签名密钥文件(snk/pfx),所生成的程序集 PublicKeyToken 都会相同。不要告诉我,你每个程序集的签名文件都是临时生成的……
Code
1
public
class
MyClass
2
{
3
static
MyClass()
4
{
5
var bytes
=
Assembly.GetEntryAssembly().GetName().GetPublicKeyToken();
6
var publicToken
=
new
StringBuilder();
7
Array.ForEach(bytes, b
=>
publicToken.Append(b.ToString(
"
x
"
)));
8
9
if
(publicToken.ToString()
!=
"
fcc8632e2470ea51
"
)
10
throw
new
Exception(
"
报告警长!发现一只耳侵入……
"
);
11
}
12
13
public
void
Test()
14
{
15
Console.WriteLine(
"
MyClass.Test
"
);
16
}
17
}
这样,非法调用者无论是通过 "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,注意删除)。
Code
1
.method
private
hidebysig specialname rtspecialname
static
void
.cctor()
2
{
3
//
Code size 102 (0x66)
4
.maxstack
3
5
.locals init ([
0
] uint8[] bytes,
6
[
1
]
class
[mscorlib]System.Text.StringBuilder publicToken,
7
[
2
] int32 i,
8
[
3
]
bool
CS$
4
$
0000
)
9
IL_0000: nop
10
IL_0001: call
class
[mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::GetEntryAssembly()
11
IL_0006: callvirt instance
class
[mscorlib]System.Reflection.AssemblyName [mscorlib]System.Reflection.Assembly::GetName()
12
IL_000b: callvirt instance uint8[] [mscorlib]System.Reflection.AssemblyName::GetPublicKeyToken()
13
IL_0010: stloc.
0
14
IL_0011: newobj instance
void
[mscorlib]System.Text.StringBuilder::.ctor()
15
IL_0016: stloc.
1
16
IL_0017: ldc.i4.
0
17
IL_0018: stloc.
2
18
IL_0019: br.s IL_0039
19
20
IL_001b: nop
21
IL_001c: ldloc.
1
22
IL_001d: ldloc.
0
23
IL_001e: ldloc.
2
24
IL_001f: ldelema [mscorlib]System.Byte
25
IL_0024: ldstr
"
x
"
26
IL_0029: call instance
string
[mscorlib]System.Byte::ToString(
string
)
27
IL_002e: callvirt instance
class
[mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(
string
)
28
IL_0033: pop
29
IL_0034: nop
30
IL_0035: ldloc.
2
31
IL_0036: ldc.i4.
1
32
IL_0037: add
33
IL_0038: stloc.
2
34
IL_0039: ldloc.
2
35
IL_003a: ldloc.
0
36
IL_003b: ldlen
37
IL_003c: conv.i4
38
IL_003d: clt
39
IL_003f: stloc.
3
40
IL_0040: ldloc.
3
41
IL_0041: brtrue.s IL_001b
42
43
IL_0043: ldloc.
1
44
IL_0044: callvirt instance
string
[mscorlib]System.Object::ToString()
45
IL_0049: ldstr
"
fcc8632e2470ea51
"
46
IL_004e: call
bool
[mscorlib]System.String::op_Inequality(
string
,
47
string
)
48
IL_0053: ldc.i4.
0
49
IL_0054: ceq
50
IL_0056: stloc.
3
51
IL_0057: ldloc.
3
52
IL_0058: brtrue.s IL_0065
53
54
IL_005a: ldstr bytearray (A5
62
4A
54
66
8B 7F
95
01
FF D1
53
B0
73
00
4E
//
.bJTf
S.s.N
55
EA
53
33
80
B5 4F
65
51
26
20
26
20
)
//
.S3..OeQ& &
56
IL_005f: newobj instance
void
[mscorlib]System.Exception::.ctor(
string
)
57
IL_0064:
throw
58
59
IL_0065: ret
60
}
最后编译回去。
d:\temp
>
ilasm MyLib.il
/
dll
/
key
=
my.snk
/
optimize
/
out
=
MyLib.dll
<Module>.cctor 会在托管模块内任何成员或类型被调用前触发,所以完全能达到我们偷懒的目的。至于测试,就不需要我多说了吧。
我们既可以用
ILMerge
将 DLL 和 EXE 合并到一个 "标准程序集" 里面,也可以用类似
.NETZ
这样工具将 EXE 和 DLL 打包到到一个 "壳" 中。(有关 .NETZ 的原理,可参考
《.NETZ 原理分析 》
)
ILMerge 演示
将 MyExe.exe 和 MyLib.dll 合并,输出新的程序集文件 MyExe2.exe
d:\temp
>
ILMerge
/
out
:MyEXE2.exe
/
keyfile:my.snk MyExe.exe MyLib.dll
.NETZ 演示
.NETZ 的好处是 "壳" + "压缩",详细细节可参考软件帮助,或者源码。
Code
1
d:\temp
>
netz
-
s
-
so
-
z MyExe.exe
-
d MyLib.dll
2
3
.NETZ
-
.NET Executables Compressor
4
Copyright (C)
2004
-
2008
Vasian Cepa
5
[v0.
4.0
] Email: mailbox_200@yahoo.com
6
7
PE subsystem : CUI
8
.NET Runtime :
2.0
.
50727.1433
9
Output directory: d:\temp\MyExe.netz
10
Added : d:\program\tools\netz\zip.dll [
65536
byte
(s)
~
64KB]
11
Processing :
2
file(s)
12
13
1
|
d:\temp\MyExe.exe
14
[
15360
byte
(s)
~
15KB]
->
[
5760
byte
(s)
~
5KB]
-
63
%
15
!
Warning:
1002
Icon Can
'
t find resource.
16
17
2
|
d:\temp\MyLib.dll
18
[
4608
byte
(s)
~
4KB]
->
[
1887
byte
(s)
~
1KB]
-
60
%
19
20
Done [
00
:
00
:
00.343
]
21
作者:
Q.yuhen
posted @ 2008-05-11 17:59
SitHere
阅读(67)
评论(0)
编辑
收藏
所属分类:
Asp.net(c#)
、
C#
新用户注册
刷新评论列表
标题
姓名
主页
Email
(博主才能看到)
验证码
*
看不清,换一张
[
登录
][
注册
]
内容(请不要发表任何与政治相关的内容)
网站首页
新闻频道
社区
小组
博问
网摘
闪存
找找看
Remember Me?
登录
使用高级评论
新用户注册
返回页首
恢复上次提交
[使用Ctrl+Enter键可以直接提交]
该文被作者在 2008-06-03 19:20 编辑过
相关文章:
Asp.net(C#)实现验证码功能
C#字符串分割
一个绝对有效的治疗“寻常疣(干日疮,瘊子)”的方法
电脑故障处理小经验—IE无法打开链接的处理方法
c# 非托管DLL 平台调用复杂结构体 指针
Asp.net的部分类跟c#类有什么不一样?
C# 来这里学习C#吧
C# 委托
相关链接:
所属分类的其他文章:
在类库内部判断代码执行环境
DLL 简易保护方法【转】
最新IT新闻:
百度网络交易平台定名“有啊”
全屏触摸力反馈 黑莓Storm发布
微软推出Touchless:无需触屏的触屏技术
AMD剥离芯片制造业务 获60亿美元投资
Google向Wine贡献更多代码
<
2008年5月
>
日
一
二
三
四
五
六
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
与我联系
发短消息
搜索
常用链接
我的随笔
我的空间
我的短信
我的评论
更多链接
我的参与
我的新闻
我的标签
留言簿
给我留言
查看留言
我参加的小组
ASP.NET
随笔分类
Asp.net(c#)(2)
(rss)
C#(2)
(rss)
MS SQL
(rss)
随笔档案
2008年6月 (1)
2008年5月 (2)
Blog I Interested
jackielin
(rss)
jillzhang
(rss)
最新评论
阅读排行榜
1. DLL 简易保护方法【转】(67)
2. 如何在SQL Server中查询本日 本周 本月 本季 本年的记录【转并改】(66)
3. 在类库内部判断代码执行环境(12)
评论排行榜
1. DLL 简易保护方法【转】(0)
2. 在类库内部判断代码执行环境(0)
3. 如何在SQL Server中查询本日 本周 本月 本季 本年的记录【转并改】(0)