您的位置:首页 > 编程语言

To De or Not to De?(关于.NET代码安全,反编译和混淆加密)

2007-09-29 10:49 483 查看
转自: http://www.microsoft.com/taiwan/msdn/columns/DoNet/ToDeoNottoDe.htm
大内高手专栏:
To De or Not to De?
作者:蔡学镛

2005 年 2 月

莎士比亚(Shakespeare)剧作中的哈姆雷特(Hamlet)对于该不该杀了叔父感到犹豫不决,To be or not to be?欲望城市(Sex and the City)影集中的凯莉(Carrie Bradshaw)对于男友要求 pee 在他身上的动作犹豫不决(Season 3, Episode 2),To pee or not to pee?身为程序员的我们对于该不该反编译(decompile)别人的 .NET 与 Java 程序感到犹豫不决,To de or not to de?

.NET 与 Java 程序的诸多特质,使得程序特别容易被反编译。本文章中,我会介绍反编译相关的技术与议题,包括反组译(disassembler)、反编译器、混淆器(obfuscator)、以及法律。看完这篇文章后,To de or not to de,答案就由你自己决定了。

反组译

.NET Framework SDK 提供了 ILDASM.exe,可以把 Managed PE 档反组译成 MSIL 汇编语言。.NET Framework SDK 同时也提供了 ILASM.exe,可以把 MSIL 汇编语言组译(assemble)成 Managed PE 檔。你可以在 Serge Lidin 所著的《Inside Microsoft .NET IL Assembler》(Microsoft Press)一书中看到 MSIL 汇编语言的定义。

我比较不喜欢使用 ILDASM,而偏好使用 RemoteSoft 推出的 .NET Explorer,如图 1 所示,我建议读者去下载 .NET Explorer 试用版回来使用。.NET Explorer 试用版可以反组译,但是反编译功能被 disable(需要花钱购买)。

图 1:RemoteSoft .NET Explorer 可以用来进行 .NET 反组译。
Java 2 SDK 也提供了 javap,以为 Java Class File 的反组译工具。但是 Sun 并未定义 Java 的汇编语言,所以当然也不会提供 Java 的组译工具。除了 javap 之外,还有许多其它的 Java 反组译工具:

ClassfileToXML
IceBreaker
ClassNavigator
JavaDump
Jasmin

我自己也写了两套 Java 反组译工具:

Vitamin J(我用 Java Swing 写的一套 GUI-mode 反组译工具,曾在 JavaTwo 2002 研讨会上展示过)
Jasm4Jvm5(我用 REBOL 语言写的一套 Console-mode 反组译工具,支持最新的 Java Class File 格式)

为何 .NET 与 Java 的程序容易被反编译?

简而言之,.NET 与 Java 的程序容易被反编译,是因为:

采用两阶段式编译(2-phase compilation),中间码(Intermediate Language)和源码(Source Code)之间非常接近,所以容易反编译
Java Class File 与 .NET Managed PE 文件保留相当多符号(Symbol)信息以及 metadata。
使用的指令 Opcode 不多,且重复性太高。Java 一直以来只有约 202 个 Opcode,.NET 1.0/1.1 只有 213 个 Opcode(.NET 2.0 估计会多出几个)。
Java 和 .NET的Opcode 都相当高阶(High-Level)。
两者都是使用 Stack-Based 的架构,使得指令之间的关系简单,容易反向推导。
许多程序没有使用混淆器。
现代程序的架构良好,模块切割得当。

反编译工具

Java 的反编译工具包括了:

JAD
JODE
Mocha
SourceAgain
Jive
WingDis
HomeBrew
JReveal
Decafe
JReverse
jAscii

.NET 的反编译工具包括了:

Reflector for .NET
Anakrino
RemoteSoft Salamander

因应之道

如何保护你的 Java 或 .NET 程序,可能的方法包括了:

法律。后面会有更详细的说明。
将程序放在 Server 上执行,不要让公司以外的人接触到。并非所有的程序都适合使用这种方式。
混淆(obfuscation)。后面会有更详细的说明。
编译成原生码(Native Code),但是会因此失去跨平台的优点,且这种作法并不普遍,销售 Java 原生译器的厂商似乎都不再继续此项业务。我也似乎没有看到真正广为使用的 .NET 原生编译器。(RemoteSoft 的Salamander .NET Native Compiler 虽然号称是 .NET Native Compiler,但其实是类似 Thinstall 的作法,并不是真正的 Native Compiler。)
包装并加密,但是会失去跨平台的优点。
程序关键处使用 native code,以提高反编译的难度,但是会失去跨平台的优点。.NET 这方便比 Java 简单,特别是,如果使用 Managed C++,会更简单。Java 呼叫 native code 必须透过 JNI(Java Native Interface)。我觉得『JNI 超难用!』
基于反编译很不容易阻挡,有些公司干脆卖起源码了,许多 Java 和 .NET 组件厂商在卖组件的同时,会有另一个版本连源码一起卖,并允许修改。

混淆器

混淆器(Obfuscator)的目的是,将原来的执行档经过处理之后,转成另一个执行文件,程序依然可以执行,但是不容易被反编译。混淆器的设计方针应该是:

必须符合 VM 的规格书,以免造成程序无法执行
不能让程序执行结果有出入
被混淆过的程序,效率通常会变差,但是不可以受到太大的影响
让程序尽量无法被现有的反编译器反编译成功
即使被反编译成功,也会造成程序不容易被程序员阅读与修改

Java 的混淆器(obfuscator)包括了:

Crema(奇怪的是,当初 Borland 从 Hanpeter Van Vliet 买进 Crema 版权,后来似乎弃之不顾了。)
DashO,这是最多人使用的产品
SourceGuard
Zelix KlassMaster

Microsoft Visual Studio .NET 2003/2005 附上 Preemptive Dotfuscator。Borland C# Builder 附上 Wise Owl 的Demeanor。C# Builder『似乎』因为销售状况不佳,已经不再推出新版本了,而是被整合进 Delphi 2005 中。目前 Delphi 2005 没有内建混淆器。

各种混淆的方法

混淆的方法包括了:

Scramble Identifiers:把有意义的名称(类别名称、变量名称…)移除,改用没有意义的名称取代,如此一来,就算被反编译成功,也不容易阅读理解。通常的作法是让名称缩短,以节省档案空间。
Insert dead code or irrelevant code:程序中穿插一些永远不会被执行的程序代码,以为欺敌之计。
Extend loop condition:在循环条件中加入一些没有实际效果的条件。
Reducible to non-reducible:Java 语言只能表达出一部份的 Java Bytecode 语法。C# 语言也只能表达出一部份的 MSIL。利用这个特点,将程序的 Bytecode 与 PE 改成无法被反编译的语法。
Add redundant operands:加入一些冗赘的运算。例如:「int a = 10;」改成「int b = 2; int c = 5; int a = b * c;」
我在清华大学硕士论文中用的方法,利用 Exception 制造出跳跃。

我认为读者应该对这些混淆器实作上的琐碎细节不感兴趣,所以以下只列出名称,不作说明:

Parallelize code
Inline and Outline methods
Interleave methods
Clone methods
Loop transformations
Reorder statements
Reorder loops
Reorder expressions
Change encoding
Split variables
Convert static to procedure data
Aggregation
Merge Scalar variables
Factor Class
Insert Bogus class
Refactor Class
Split Array
Merge Arrays
Fold Array
Flatten Array
Ordering
Reorder methods and Instance variables
Reorder Arrays

关于这些混淆方式,有一些值得注意的地方:

如果你看过 Martin Fowler 的《Refactoring》一书,你可能发现上面列出的某些名称似曾相识。基本上,refactoring 一书中所介绍的方法都可以用来混淆,只不过是『反方向使用』。
字符串的混淆,可以让程序代码变小。流程的混淆,会让程序代码变大。
上面列出来的方法虽然多,但是大多数的混淆器只有使用其中一小部分的方法。
许多 VM 实作上会基于某些假设,因此,混淆过的程序很有可能会造成某些 VM 当机。所以,有些人不愿意使用混淆器,免得不但反编译器被混淆而无法反编译,连 VM 也被混淆而无法执行。
打开大门,表示欢迎大家进来;锁了门,不代表小偷就不会进门,但是不锁门,就会增加被偷的风险。同样的道理:开放源码,表示欢迎大家阅读并利用源码;把程序混淆过,不代表程序就无法被反编译,但是不将程序混淆过,就会增加被反编译的风险。

To De or Not to De? That’s a Legal Question.

软件可以利用下面的法律来保护,禁止被反编译。

专利法:申请专利来保护程序内的特殊技巧。
版权法:抄袭程序(虽然作了修改)仍然会侵犯版权。
授权协议:可以在软件合约内容中加上禁止逆向工程(Reverse Engineering)条款
其它法律:例如美国的 Digital Millennium Copyright Act

逆向工程(反编译与反组译)并非一律被禁止,有些逆向工程的动作属于该软件的「合理使用」范围内。大多数的法律认定,利用反编译来取得未公开的 API 是合法的,其它用途则属非法。所以,如果你购买了某个 .NET 或 Java 软件(或控件),且该软件 / 控件没有足够的使用说明,你可以利用逆向工程技术来取得该软件的操作接口(User Interface)与 API。以前相当流行挖掘 Windows OS 的 Undocumented API,这正是属于「合理使用」的范围。但是,不同的国家与区域对此可能有不同的规范,请特别注意。

许多销售反编译器的厂商,往往宣称他们的产品可以帮助你「将遗失的源码找回来」,他们绝对不会宣称该产品可以帮助你用来「偷窃别人的源码」。提供反编译工具并不具有法律上的疑义,但是这些厂商仍然对此做出道德上的宣示,可见反编译是一个多么敏感的话题。我想,这或许是为什么 Godfrey Nolan 早在 1999 或 2000 年就该出版的《Decompiling Java》一书,一直拖延到 2004 年才出版的,而出版社也从 McGraw Hill 换成了 APress。

最后,我归纳的结论是:想要反编译别人的程序,必须考虑到法律、道德、和技术三个层面。

意见与支持

您有任何问题、意见或建议吗?您可以透过下列电子邮件与作者连络:
xy.cai@msa.hinet.net
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐