.NET 源代码的安全性
[ 2005-05-30 11:32:53 | 作者: admin ]
作者 Jason.NET
2005-04-22 13:02
.NET开发效率的确是高,也非常简单,比起Delphi,BC,Vc来说,在某些方面有很多的优势。我已经打算开
始做一位共享软件的作者,看看共享软件市场上有多少.net写的东西,非常抱歉基本上没有,为什么
?因为MSIL实在太容易反编译了。。。
关于.net源代码的安全性,关于如何保护您的知识产权,关于市场上流动的混淆工具以及最新的混淆
加密工具,我将给您展示一下,全新的保护手段,也能让您使用.net开发共享程序并保护自己的知识
产权。
.NET 源代码的安全性(源代码工具真正比拼) (論)(1)
.NET开发效率的确是高,也非常简单,比起Delphi,BC,Vc来说,在某些方面有很多的优势。
我已经打算开始做一位共享软件的作者,看看共享软件市场上有多少.net写的东西,非常抱歉基本上
没有,为什么?因为MSIL实在太容易反编译了。。。
我时常听到朋友的抱怨,跨什么平台嘛,跨了平台吗?还没有,那为什么不能将.net编译成本机代码
?我真不敢相信拿到2002正式版的时候,那种到处找本地编译选项的心情,直到VS2003,我已经放弃
了本机代码的愿望……我知道这一切不可能了,.NET是为企业而设计的,不过企业的软件就真的完全
没有这样的问题了吗?。 呵呵,相信你也有这种抱怨吧。
关于.net源代码的安全性,关于如何保护您的知识产权,关于市场上流动的混淆工具以及最新的混淆
加密工具,我将给您展示一下,全新的保护手段,也能让您使用.net开发共享程序并保护自己的知识
产权。
.NET 源代码的安全性(源代码工具真正比拼) (論)(2)
今天被屠宰的对象就是 我们公司使用vb.net开发的一套管理系统。名字就不说了,此系统主程序约3M
,其中共有约 3000个方法,事件,等等。是一个中大型的程序,源代码约有 5万行左右(去掉一些自
动生成的代码计算后),其中有底层硬件操作类,也有水晶报表操作,数据库操作。我们最关心的是
其中的一块,注册类,以及一些核心代码。而我们演示的时候将会把注册代码拿出来演示(当然,不
是原来的注册代码。。。那个拿出去,BOSS要杀我的)
而代码保护工具的主角是:
1. Dotfuscator Community Edition //微软推荐的东东,我个人认为非常烂
2. XeonCode //最近最好的混淆器,有可取点.它号称安全,我们今
//天来看看是不是真的安全
3. MaxtoCode //一个不同于混淆的.NET源码工具 MaxtoCode
//目前普及版只支持 WindowsApplication的加密
//以后开放 WEB APPlication and Class Library
我们的反编译目标分为二种:
1. C#语言
2. MSIL语言
使用的全是市场上流通的免费反编译工具,我知道收费的反编译工具还会自动还源混淆.收费的我也没
有条件去测试
好,开始测试:
我们分别使用上面三样对代码进行混淆或加密。
完毕,测试程序所有功能:
1. Dotfuscator Community Edition 我最不喜欢的混淆器,由于是1.2免费版,所以必须混淆所
有模块,导致部分功能不能使用。但我们不关心那些,我们一会将展现被混淆的注册代码(因为类和
方法都被混淆,所以必须要花点时间)
2. XeonCode 一个相对强大的混淆工具,我一直认为他与某著名的反编译工具捆绑,导至其它工
具能反编译的代码而它不能反编译。我们把它的强度开到最大,勾上所有能勾的选项。(它混淆的Win
dows Application可以正常运行)
3. MaxtoCode 这是一个加密器,可以与XeonCode叠加使用,并且在2.0版中,将会加入自混淆功
能,成为混淆加密一体化的工具。(它混淆的 Windows Applicaton 可以正常运行)
我们最关心的是混淆或加密的结果。OK,请不要急,让我们先看看源代码,再比较一下反编译后的
代码。
Private Function Encrypt(ByVal inStr As String) As String
Dim key As String = "a#2151336fdaghksfges"
Dim out As String
Dim j, i, s As Integer
i = inStr.Length
s = 0
For j = 0 To i - 1
out = out + inStr.Substring(j, 1) + key.Substring(s, 1)
s = s + 1
If s >= 20 Then s = 0
Next
Return out
End Function
Private Function Register(ByVal instr As String) As String
Dim pRsa As New System.Security.Cryptography.RSACryptoServiceProvider
Dim en As New System.Text.ASCIIEncoding
Return en.GetString(pRsa.Encrypt(en.GetBytes(Encrypt(instr)), False))
'Dim a As String
'a = Encrypt(instr)
'Dim b() As Byte
'Dim c() As Byte
'b = en.GetBytes(a)
'c = pRsa.Encrypt(b, False)
'Return en.GetString(c)
这里怕大家看不清楚,特别加了展开的代码
End Function
源代码很清楚,即是对字符串进行插入,然后使用 RSA 进行加密(这里省去了RSA的KEY)
好,那么我们首先来看看使用上面三种工具混淆后的C#代码
1. Dotfuscator Community Edition
private string b(string A_0)
{
string text3;
string text2 = "a#2151336fdaghksfges";
int num1 = A_0.Length;
int num3 = 0;
int num4 = num1 - 1;
for (int num2 = 0; num2 <= num4; num2++)
{
text3 = text3 + A_0.Substring(num2, 1) + text2.Substring(num3, 1);
num3++;
if (num3 >= 20)
{
num3 = 0;
}
}
return text3;
}
private string a(string A_0)
{
RSACryptoServiceProvider provider1 = new RSACryptoServiceProvider();
ASCIIEncoding encoding1 = new ASCIIEncoding();
return encoding1.GetString(provider1.Encrypt(encoding1.GetBytes(this.b(A_0)),
false));
评价:跟原代码一模一样,只是混淆了类和方法名称,代码中根本未进行混淆,由于是免费版,也只能提供
这么多功能.毫无意义
2. XeonCode
private string x246b032720dd4c0d(string x96c91b85a03f00b0)
{
string str2;
string str3;
int k;
int j;
str2 =
String.Intern(x1110bdd110cdcea4._d574bb1a8f3e9cbc("uec3buf2faufa06u0102u0803u0efcu15fbu1c
f8u23f8u2b25u3220u391au401du471bu4e1bu5520u5c10u630eu6a09u7114", 281144282));
int i1 = x96c91b85a03f00b0.Length;
k = 0;
i2 = i1 - 1;
j = 0;
goto IL_003c;
VariableExp: k
ConstantExp: 20
IL_0029: blt.s IL_0038 //自动的跳转混合
k = 0;
goto IL_0038; //花指令
ConstantExp: 1
IL_0031: add.ovf
IL_0032: stloc.s 5
VariableExp: k
int j;
int i2;
IL_0038:
j++;
if (j > i2)
{
return str3;
}
str3 = String.Concat(str3, x96c91b85a03f00b0.Substring(j, 1),
str2.Substring(k, 1));
}
private string x2a0cb95ab84ba877(string x5b3e4cba383dedd9)
{
RSACryptoServiceProvider provider1 = new RSACryptoServiceProvider();
ASCIIEncoding encoding1 = new ASCIIEncoding();
return
encoding1.GetString(provider1.Encrypt(encoding1.GetBytes(this.x246b032720dd4c0d(x5b3e4cba
383dedd9)), false));
}
评价:功能强大许多,对于较长的代码会加入花指令进行混淆,从而达到混淆反编译器的目地.这种混淆
已经是XeonCode的最大混淆较果,虽然有达到混淆反编译的较果,但耐心仔细的看,并结合IL代码一起阅
读,想得到其其算法是一件很容易的事情,最重要的是,有某些反编译器竟然可以去掉花指令,从而达反
混淆的较果.所以使用它来保护你的知识版权是不是还不够强大呢?
3. MaxtoCode
private string Encrypt(string inStr)
{
}
private string Register(string instr)
{
}
评价:代码呢????代码已经不见了……不过这就是MaxtoCode的功效,它是加密,不是混淆,你看不到代码
了,你完全无法入手去分析,那怕一点珠丝马迹都没有.您还可以结合XeonCode一起使用,达到双重的保
护(因为现版本的MaxtoCode不提供混淆功能,您能清楚的看到类及方法的正确名称)
好,刚刚展现的是反编译出来的C#代码,那么,我们再深入一点,真接反汇编为MSIL代码看看能达到什么
较果
.NET 源代码的安全性(源代码工具真正比拼) (論)(3)
上面我们展现了反编译技术对混淆过后代码的比较,我们清楚的看到了三个工具的强大性.从C#的反编
译结果上来说,无疑MaxtoCode是最优秀的,它已经完全的杜绝你的源代码外泄.
但C#只是一种高级语言,我们希望更底层一点,希望读到更深层的代码MSIL,那么,现在让我们来用各种
工具取得它的MSIL代码吧.
源文件的MSIL代码如下:
.method private instance string Encrypt(string inStr) cil managed
{
// Code Size: 84 byte(s)
.maxstack 5
.locals (
string text1,
int32 num1,
int32 num2,
string text2,
string text3,
int32 num3,
int32 num4)
L_0000: nop
L_0001: ldstr "a#2151336fdaghksfges"
L_0006: stloc.3
L_0007: ldarg.1
L_0008: callvirt instance int32 string::get_Length()
L_000d: stloc.1
L_000e: ldc.i4.0
L_000f: stloc.s num3
L_0011: ldc.i4.0
L_0012: ldloc.1
L_0013: ldc.i4.1
L_0014: sub.ovf
L_0015: stloc.s num4
L_0017: stloc.2
L_0018: br.s L_0048
L_001a: ldloc.s text3
L_001c: ldarg.1
L_001d: ldloc.2
L_001e: ldc.i4.1
L_001f: callvirt instance string string::Substring(int32, int32)
L_0024: ldloc.3
L_0025: ldloc.s num3
L_0027: ldc.i4.1
L_0028: callvirt instance string string::Substring(int32, int32)
L_002d: call string string::Concat(string, string, string)
L_0032: stloc.s text3
L_0034: ldloc.s num3
L_0036: ldc.i4.1
L_0037: add.ovf
L_0038: stloc.s num3
L_003a: ldloc.s num3
L_003c: ldc.i4.s 20
L_003e: blt.s L_0043
L_0040: ldc.i4.0
L_0041: stloc.s num3
L_0043: nop
L_0044: ldloc.2
L_0045: ldc.i4.1
L_0046: add.ovf
L_0047: stloc.2
L_0048: ldloc.2
L_0049: ldloc.s num4
L_004b: ble.s L_001a
L_004d: ldloc.s text3
L_004f: stloc.0
L_0050: br.s L_0052
L_0052: ldloc.0
L_0053: ret
}
.method private instance string Register(string instr) cil managed
{
// Code Size: 44 byte(s)
.maxstack 5
.locals (
[mscorlib]System.Text.ASCIIEncoding encoding1,
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider provider1,
string text1)
L_0000: nop
L_0001: newobj instance void
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider::.ctor()
L_0006: stloc.1
L_0007: newobj instance void [mscorlib]System.Text.ASCIIEncoding::.ctor()
L_000c: stloc.0
L_000d: ldloc.0
L_000e: ldloc.1
L_000f: ldloc.0
L_0010: ldarg.0
L_0011: ldarg.1
L_0012: callvirt instance string TestRegister.Form1::Encrypt(string)
L_0017: callvirt instance unsigned int8[]
[mscorlib]System.Text.Encoding::GetBytes(string)
L_001c: ldc.i4.0
L_001d: callvirt instance unsigned int8[]
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider::Encrypt(unsigned int8[],
bool)
L_0022: callvirt instance string
[mscorlib]System.Text.ASCIIEncoding::GetString(unsigned int8[])
L_0027: stloc.2
L_0028: br.s L_002a
L_002a: ldloc.2
L_002b: ret
}
1. Dotfuscator Community Edition MSIL
.method private instance string b(string A_0) cil managed
{
// Code Size: 84 byte(s)
.maxstack 5
.locals (
string text1,
int32 num1,
int32 num2,
string text2,
string text3,
int32 num3,
int32 num4)
L_0000: nop
L_0001: ldstr "a#2151336fdaghksfges"
L_0006: stloc.3
L_0007: ldarg.1
L_0008: callvirt instance int32 string::get_Length()
L_000d: stloc.1
L_000e: ldc.i4.0
L_000f: stloc.s num3
L_0011: ldc.i4.0
L_0012: ldloc.1
L_0013: ldc.i4.1
L_0014: sub.ovf
L_0015: stloc.s num4
L_0017: stloc.2
L_0018: br.s L_0048
L_001a: ldloc.s text3
L_001c: ldarg.1
L_001d: ldloc.2
L_001e: ldc.i4.1
L_001f: callvirt instance string string::Substring(int32, int32)
L_0024: ldloc.3
L_0025: ldloc.s num3
L_0027: ldc.i4.1
L_0028: callvirt instance string string::Substring(int32, int32)
L_002d: call string string::Concat(string, string, string)
L_0032: stloc.s text3
L_0034: ldloc.s num3
L_0036: ldc.i4.1
L_0037: add.ovf
L_0038: stloc.s num3
L_003a: ldloc.s num3
L_003c: ldc.i4.s 20
L_003e: blt.s L_0043
L_0040: ldc.i4.0
L_0041: stloc.s num3
L_0043: nop
L_0044: ldloc.2
L_0045: ldc.i4.1
L_0046: add.ovf
L_0047: stloc.2
L_0048: ldloc.2
L_0049: ldloc.s num4
L_004b: ble.s L_001a
L_004d: ldloc.s text3
L_004f: stloc.0
L_0050: br.s L_0052
L_0052: ldloc.0
L_0053: ret
}
.method private instance string a(string A_0) cil managed
{
// Code Size: 44 byte(s)
.maxstack 5
.locals (
[mscorlib]System.Text.ASCIIEncoding encoding1,
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider provider1,
string text1)
L_0000: nop
L_0001: newobj instance void
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider::.ctor()
L_0006: stloc.1
L_0007: newobj instance void [mscorlib]System.Text.ASCIIEncoding::.ctor()
L_000c: stloc.0
L_000d: ldloc.0
L_000e: ldloc.1
L_000f: ldloc.0
L_0010: ldarg.0
L_0011: ldarg.1
L_0012: callvirt instance string c::b(string)
L_0017: callvirt instance unsigned int8[]
[mscorlib]System.Text.Encoding::GetBytes(string)
L_001c: ldc.i4.0
L_001d: callvirt instance unsigned int8[]
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider::Encrypt(unsigned int8[],
bool)
L_0022: callvirt instance string
[mscorlib]System.Text.ASCIIEncoding::GetString(unsigned int8[])
L_0027: stloc.2
L_0028: br.s L_002a
L_002a: ldloc.2
L_002b: ret
}
评价:还是和源代码基本上一样…我不知道把这种东西给我们有什么意思
2. XeonCode MSIL
.method private instance string x246b032720dd4c0d(string x96c91b85a03f00b0) cil managed
{
// Code Size: 105 byte(s)
.maxstack 6
.locals (
string text1,
int32 num1,
int32 num2,
string text2,
string text3,
int32 num3,
int32 num4)
L_0000: ldstr
"uec3buf2faufa06u0102u0803u0efcu15fbu1cf8u23f8u2b25u3220u391au401du471bu4e1bu5520u5c10u63
0eu6a09u7114"
L_0005: ldc.i4 281144282
L_000a: call string x2715eff98365d956.x1110bdd110cdcea4::_d574bb1a8f3e9cbc(string,
int32)
L_000f: call string string::Intern(string)
L_0014: stloc.3
L_0015: ldarg.1
L_0016: callvirt instance int32 string::get_Length()
L_001b: stloc.1
L_001c: ldc.i4.0
L_001d: stloc.s num3
L_001f: br.s L_0043
L_0021: stloc.s text3
L_0023: ldloc.s num3
L_0025: br.s L_0030
L_0027: ldc.i4.s 20
L_0029: blt.s L_0038
L_002b: ldc.i4.0
L_002c: stloc.s num3
L_002e: br.s L_0038
L_0030: ldc.i4.1
L_0031: add.ovf
L_0032: stloc.s num3
L_0034: ldloc.s num3
L_0036: br.s L_0027
L_0038: ldloc.2
L_0039: ldc.i4.1
L_003a: add.ovf
L_003b: stloc.2
L_003c: ldloc.2
L_003d: ldloc.s num4
L_003f: ble.s L_004c
L_0041: br.s L_0066
L_0043: ldc.i4.0
L_0044: ldloc.1
L_0045: ldc.i4.1
L_0046: sub.ovf
L_0047: stloc.s num4
L_0049: stloc.2
L_004a: br.s L_003c
L_004c: ldloc.s text3
L_004e: ldarg.1
L_004f: ldloc.2
L_0050: ldc.i4.1
L_0051: callvirt instance string string::Substring(int32, int32)
L_0056: ldloc.3
L_0057: ldloc.s num3
L_0059: ldc.i4.1
L_005a: callvirt instance string string::Substring(int32, int32)
L_005f: call string string::Concat(string, string, string)
L_0064: br.s L_0021
L_0066: ldloc.s text3
L_0068: ret
}
.method private instance string x2a0cb95ab84ba877(string x5b3e4cba383dedd9) cil managed
{
// Code Size: 39 byte(s)
.maxstack 6
.locals (
[mscorlib]System.Text.ASCIIEncoding encoding1,
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider provider1,
string text1)
L_0000: newobj instance void
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider::.ctor()
L_0005: stloc.1
L_0006: newobj instance void [mscorlib]System.Text.ASCIIEncoding::.ctor()
L_000b: stloc.0
L_000c: ldloc.0
L_000d: ldloc.1
L_000e: ldloc.0
L_000f: ldarg.0
L_0010: ldarg.1
L_0011: callvirt instance string
x9324a7f62e6a3ae4.xaa4f033827d75b4d::x246b032720dd4c0d(string)
L_0016: callvirt instance unsigned int8[]
[mscorlib]System.Text.Encoding::GetBytes(string)
L_001b: ldc.i4.0
L_001c: callvirt instance unsigned int8[]
[mscorlib]System.Security.Cryptography.RSACryptoServiceProvider::Encrypt(unsigned int8[],
bool)
L_0021: callvirt instance string
[mscorlib]System.Text.ASCIIEncoding::GetString(unsigned int8[])
L_0026: ret
}
评价:有点花指令,不过在MSIL的状态下,源码的逻辑尽显无疑,也可以畅读一番,不太理想
3. MaxtoCode MSIL
.method private instance string Encrypt(string inStr) cil managed
{
}
.method private instance string Register(string instr) cil managed
{
}
评价:依然无法看到代码,那怕是较底层的MSIL也无法看见.这下应该安全了.
以上的比较让您更清楚谁对您的知识产权保护更完美了吧。
排除代码层面上的因素,我们再看看其它方面吧。
.NET 源代码的安全性(源代码工具真正比拼) (論)(4)
1. Dotfuscator Community Edition
不知道Microsoft为什么推荐这个东东,是不是出来的最早还是别的什么,反正它的功能是最弱的,唯
一不同的是,它将类名,方法名缩成1~2个字符,从而缩小了整个文件的尺寸,虽然缩小尺寸并不多,
不过我可能也只是它唯一的优点了(免费版,高强度的收费版可达数千美元,用不起啊)
2. XeonCode
这个东东破解版很多,功能也很强大。它可以集成整个FrameWork到你的程序中……我实在没有想出来
这有什么用,除了使程序的启动速度巨慢以及尺寸变得大。
它还可以不让ilDasm以及一些使用Ms提供的API的反编译器查看
它会在你的程序中加足使Method的数量变为0x2000个,从而混淆某个重要标志的位数
它会在你的代码中加入花指指令及其它的跳转指令,从而混淆代码,同时降低执行率
总的来说,它的混淆强度已经算是不错了
3. MaxtoCode
最新版的它目前还不提供混淆功能,它计划在推出WEB application and Class Library加密功能后才
推出加密混淆一体化的功能。
它使用的技术手段是利用ASM对MSIL代码加密,从而不用禁止任何反编译工具也可以达到无法反编译的
功能,上面的例子已经很清楚的展现给你们了。
它的执行效率:在普及版中,我们的用例程序大约3000个方法需要加密的情况下,加密耗时 < 1秒。
您根本感觉不到与正常使用有什么区别
它有一个缺点,就是加过密后的程序,只能运行在WINDOWS系列平台上。如果您的程序目前只运行在WI
NDOWS平台上,那么MaxtoCode将是你知识产权的最佳武器(.NET的设计是可以跨平台的,不过Microso
ft并没有去考虑跨平台的工作)。如果您的程序需要运行在其它系统上,您可以只用MaxtoCode的混淆
功能在以后的版本中。
总的来的,我只是从客观的因素上对以上工具进行评价,你也可以有自己的见解。
评论Feed: http://blog.xg98.com/feed.asp?q=comment&id=83
这篇日志没有评论。
此日志不可发表评论。