代码迷惑技术如何保护Java免遭逆向工程
很少有问题比程序员遇到不访问无法利用的源代码就无法解决的漏洞更令人沮丧的了。
你是否在通过一个在线开源库修补代码,或正在调用常用操作系统例行程序;你可能每周都要花时间处理不是由你编写,因而也无法访问其源代码的代码。
因为Java字节码包含许多和原始代码相同的信息,所以很容易对Java类文件执行逆向工程。另外,Java程度以其“一旦编写,随处运行”特性而闻名。虽然并非Java语言的专利,但代码反编译从未在Java开发者之中得到如此公开或普遍地利用。反编译的对立面即为迷惑。
何为代码迷惑?
由于解译工具可以方便地从编译代码中提取源代码,因而我们很难对代码和其中的重大机密加以保护。和Java解译工具一样,Java迷惑工具也得到广泛应用,它实际上对你的代码使用障眼法。
代码迷惑是当前保护Java代码免遭逆向工程的最佳办法。代码迷惑使得软件难以理解,但在功能上仍然等同于原始代码。它还使得程序更难以理解,因而提高对逆向工程的抵抗力。
在图A中,一组类文件P通过迷惑工具变为另一组类文件P’。其结果是P的代码和P’的代码并不相同, P’代码比P代码更加难以理解,但二者的功能一样。
我们以从源代码(src1.java.txt)中提取的一段简单Java代码为例:
class OriginalHello {
public OriginalHello() {
int number=1;
}
public String getHello(String helloname){
return helloname;
}
使用最简单的迷惑工具(如KlassMaster)对这段代码执行代码迷惑后,这个类中的所有名称都变得杂乱无章,而且行号也不再存在。以下是执行代码迷惑后得到的代码(dst1.java.txt):
class a {
public static boolean a;
public a() {
int a=1;
}
public String a(String b){
return b;
}
从上面的例子中可以看到,Hello类已被代码迷惑工具KlassMaster变为a类,它们的方法,getHello(java.lang.String)也变成a(java.lang.String)。方法名a()比getHello()更难以理解。将最初的字节码和代码迷惑后的字节码进行比较,你还可以发现,代码迷惑后的字节码中已经没有行号。这为逆向工程提供更少的信息。
这个非常简单的代码迷惑实例仅打乱了标识符,去掉由编译工具生成的行号。现代的商业迷惑工具能够彻底地打乱代码的内容,使代码极其难以破解;但是,在这个领域,还有许多研究仍在进行之中。
代码迷惑技巧
除替换文字和取消行号外,各种迷惑工具还使用许多技巧。进一步利用无意义的字符串技巧是一种流行的迷惑方法,即用一个非法的字符串代替类文件中的一个符号。替代的内容可能是诸如private这样的关键字,或者更令人迷惑的,是一个完全没有任何意义的符号,如***。一些虚拟机,特别是在浏览器中,无法正常处理这样古怪的符号。技术上,变量以符号(如=)为名不符合Java规范;一些虚拟机会忽视这种情况。
一些迷惑工具使用的其它技巧一般主要针对特定的解译工具,如Mocha和JDOE。它将一个有害的指令注入代码中,这对代码运行不会造成任何影响,但它能够破坏解译工具。
我们以初始代码(解译后)为例来说明这样的有害指令:
Method void main(java.lang.String[])
0 new #4
3 invokespecial #10
6 return
以下是执行代码迷惑后的代码(为了简化,保留了相同的名称):
Method void main(java.lang.String[])
0 new #4
3 invokespecial #10
6 return
7 pop
注意,现在程序在返回(return)后增加了一个弹出(pop)指令。很明显,函数在返回后无法做其它事情——这就是诀窍所在。通过在返回语句后插入一个指令,可以保证语句绝不会被执行。这里的代码基本上无法解译。由于它不与任何可能的Java源代码相对应,因而也没有什么意义。
以下是另外一些常用的代码迷惑技巧:
- 布局迷惑用两个基本的方法修改程序的布局结构:更名标识符和删除调试信息。它们使得逆向工程无法了解更多有关程序代码的信息。大多数布局迷惑方法都无法破解,因为它们一般都使用修改标识符这类单向函数,如利用随机字符和删除内容、无用的方法、以及调试信息等。虽然布局迷惑无法阻止逆向工程查看执行代码迷惑后的代码,进而了解程序的内容,但它们至少增加了逆向工程的成本。布局迷惑是目前得到最多深入研究,使用最为广泛的代码迷惑技巧。几乎所有Java迷惑工具中都包含这种技巧。
- 控制迷惑改变程序的控制流程。其原理相当简单:对于例行程序A(),迷惑工具建立另外一个例行程序A_bug和一个”if”选择器,if (PREDICATE) then A_bug(); else A();。PREDICATE被有意设计为动态,以便它总是为假(但这样做是为了使它难以结束那个事实),而且总是选择例行程序A(),而不是充满漏洞的A_bug()。
- 数据迷惑打破程序使用的数据结构并对文字进行加密。这种方法包括修改继承关系、更改数组结构等。数据迷惑彻底改变一个程序的数据结构。它们使得执行代码迷惑后的代码变得非常复杂,因而无法重建最初的源代码。
结论
我们应该记住,至今还没有哪一种迷惑工具能够保证使代码完全免遭逆向工程;因此,迷惑工具并不能提供类似于现代加密方案的安全水平;如果安全问题极其重要,你应该结合使用其它保护方法。
最常见的软件逆向工程攻击目标拷贝保护方案。这些方案往往在很大程度上依赖于现有操作系统进程调用,因而使用在解译代码时用到的相同工具就可以轻松避开基本的代码迷惑技巧。此外,迷惑后的代码一般依赖于平台和编译器的特有特征,如果发生改变,就很难对其进行管理。
Peter V. Mikhalenko是一名通过Sun认证的专家,现在任德意志银行商业顾问。