如何封锁您的(或打开别人的) Java 代码 | ![]() | ![]() | ||
![]() | ||||
![]() |
![]() |
Greg Travis (mito@panix.com) 无论是修改许多网上开放源代码库中的代码,还是调用常见的操作系统例行程序,您免不了要花一些时间去琢磨您没有编写过的代码,而且您还可能没有这些代码的源文件。在开始调试代码时,您需要有一个好的 Java 反编译器,并了解正确使用它的技术。同时,您还要知道如何保护您自己的代码不被窥视。为此,您还需了解有关代码模糊处理的问题。在这篇有关打开和封锁 Java 代码的初学者指南中, Greg Travis 使用 Mocha、HoseMocha、jmangle 和 JODE 等流行工具中的示例,来循序渐进地教你有关反汇编、反编译和 Java 代码模糊处理的基础知识。 没有比发现一个错误,却没有源代码就不能修改更令人沮丧的了。正是这个原因导致了 Java 反编译器的出现,它可以把编译后的字节码完全转回成源代码。尽管代码反编译器不只是针对 Java 语言,但它从来没有象在 Java 开发人员中那样被公开地或广泛地使用。 与反编译针锋相对的是模糊处理。假设反编译人员能很容易从编译后的代码中设法得到源代码,那么要保护您的代码和有价值的技术秘密就不是那么简单了。随着 Java 反编译器的普遍使用, Java 模糊处理器也同样被普及,它的作用就好像放一块烟幕在您的代码前面。反编译和模糊处理在商业开发领域中引起了一场争论 -- 争论中的大部分都集中在了 Java 语言上。 在本文中,我将让您了解代码反编译和模糊处理的具体过程,讨论在这两种技术之后的理论问题,同时简要地谈到它们在商业编程领域中所引起的争论。我还将介绍一些比较有名的反编译器和模糊处理器(有商业的,也有开放源代码的),并随着文章的深入使用它们来创建一些实例。 什么是反编译? 反编译是困难的 作为该公司的新雇员,您可能会问下属他或她在做些什么,并得到回答,“我在安装新的 XML 数据库。”从这句话中,您不可能推断出其最终目的是最大程度地提高技术生产能力。毕竟,最终目标不尽相同,例如可能是分离供应链或累积消费者的数据。 然而,如果属于好奇心特强的那类人,您可能会再多问几个问题,并让公司中不同级别的下属回答您的问题。最后,当把所有的答案汇总后,您可能会猜到企业更大的目标是最大程度地提高技术生产能力。 如果您把计算机程序的工作方式看作类似一个公司的组织结构,那么对于为什么反编译代码不是无关紧要的,以上的这个比方就会给你一个直接的感受。从比较理论化的角度来看,这儿要引用在该领域的杰出研究员 Cristina Cifuentes 对反编译过程的描述:
任何一个二进制改造工程都需要对存储在二进制文件中的代码进行反汇编。从理论上说,分离 von Neumann 上的数据和代码就好象停机问题,因此完全的静态翻译是不可能的。然而,实际上可以使用不同技术来提高可被静态翻译的代码的所占比例,或者采取可在运行中被使用的动态翻译技术。 把目标代码转换成源代码并不是反编译时碰到的唯一问题。一个 Java 类文件潜在包含了一些不同类型的信息。知道类文件中可能包含了哪类信息对于了解您如何利用该信息以及对于信息作何种处理都是很重要的。这其实就是 Java 反汇编器所要做的。 反汇编一个类文件
请注意,清单 2 所示的并不是源代码。该清单的第一部分列出了方法的局部变量;第二部分是汇编代码,它也是人们可读的目标代码。 一个类文件中的元素
既然对 Java 类文件的内部情况已有所了解,让我们看一下如何能转换这些信息来达到我们的目的。 使用反编译器 一些比较新的反编译器有精致的图形界面。但在一开始所举的例子中,我们将使用的是 Mocha,它是第一个公开的可利用的反编译器。在本文的最后,我会讨论一下在 GPL 下一个较新的反编译器。(请参阅参考资料,下载 Mocha 并获取 Java 反编译器的清单。) 让我们假设在目录中有一个名为 Foo.class 的类文件。用 Mocha 对它进行反编译非常简单,只要键入以下命令:
这会生成一个新的名为 Foo.mocha 的文件(Mocha 使用 Foo.mocha 这个名字以避免覆盖原文件的源代码)。这个新文件就是 Java 的源文件,并且假设一切顺利的话,您现在就能正常地编译它。只需把它重命名为 Foo.java 就可以开始了。 但是这儿有个问题:如果在一些您已经有所改动的代码上运行 Mocha,您会注意到它生成的代码和源代码不是完全一样的。我举个例子,这样您能明白我的意思。清单 3 所示的原始源代码是来自一个名为 Foo.java 的测试程序。
以下是 Mocha 生成的代码
这两个代码片段的成员变量 反编译是困难的:不断重复
撇开 Mocha 的问题不谈,反编译器在通常情况下还是能比较准确地翻译出源代码。一旦知道了某一反编译器的弱点,您可以手工分析和转换反编译后的代码,以使它们能较准确地符合原始源代码。随着反编译器正变得越来越出色,我们又碰到了另外一个问题:如果您不想让任何人能反编译您的代码,那该怎么办呢? 反编译和对安全的威胁 就语言本身而言, 由于其相对简单的 Java 虚拟机(与真实的微处理器相比)和其写得很规范的字节码格式, Java 代码非常容易反汇编。而这随着 Java 语言在 Web 开发平台上的日益普及,已经在商业开发领域引起了很多争议。自从 Mocha 于 1996 年首次发布以来,一些在保护它们的源代码方面有过投资的公司和个人一直在为 Java 反编译器大吵大闹。 实际上,当 Mocha 第一次发布时,它的作者 Hanpeter van Vliet 曾被一些公司的诉讼威胁过(请参阅参考资料)。起初,他把反编译器从他的网站上移去,但是他后来以 Crema 的形式提供了一个更好的解决方案。Crema 是一个 Java 模糊处理器,它完全对立于 Mocha。 自 Crema 发布以来,许多 Java 模糊处理器开始出现,其中一些是商业的,也有一些是开放源代码的。正如您看到的那样,一个好的 Java 模糊处理器可以在很大程度上保护您的 Java 代码。 针锋相对的代码模糊处理 让我们看一下当反汇编器遇到经过模糊处理后的代码会发生什么情况。清单 6 显示了 Mocha 在尝试反汇编被一种名为 jmangle 的工具模糊处理的 Java 代码后的结果。请注意以下的一小段程序和我们在前面清单中使用的是相同的,尽管乍一看,您肯定不会这么认为。
象 jmangle 这样的模糊处理器把许多变量名和方法名(有时甚至是类名和包的名称)转换成没有意义的字符串。这样就使得人们难以阅读程序,但对于 JVM 来说,其在本质上和原来的程序是一样的。 变得卑鄙 一种常用的模糊处理代码的方法是用一个非法的字符串来替代类文件中的标记,这比使用没有意义的字符串更进了一步。替代的有可能是一个关键字,例如 Crema 放置炸弹 可惜,Crema 已经没有了,但有一种名为 HoseMocha 的工具是专门为关闭 Mocha 而设计的。为了了解 HoseMocha 是如何工作的,我们将使用 javap,这个值得信赖的反汇编器。清单 7 所示的是 HoseMocha 放置炸弹前的代码。
以下是 HoseMocha 处理后的代码。
您看到那颗炸弹吗?请注意现在这个程序在返回后面有一条 pop 语句。等一下 -- 一个函数在返回之后还能做什么吗?很显然,它不能,而这就是关键所在。在返回语句后放一条指令确保了它不会被执行。您这儿所见的是根本不可能被反汇编的。因为它没有对应任何可能的 Java 源代码,所以也就没有任何意义。 但为什么这一个小小的障碍就能导致 Mocha 崩溃呢? Mocha 可以只是简单地忽略它,或发一条警告信息并继续下去。尽管 Mocha 对于此类炸弹的脆弱性可以被认为是一个程序错误,但更有可能的是 van Vliet 为了回应对 Mocha 的攻击而故意设置的。 到此为止,我们已经了解了较老的反汇编工具和模糊处理工具 -- 虽然有点过时,但还是比较出色的。但是,类似工具在这几年已经变得更加成熟,尤其在图形界面方面更是如此。在本文的最后,我们看一下一个较新的反汇编器,仅仅让您有个大致的概念。 这一领域的新成员 JODE (Java 优化和反编译环境)就是这样一个程序。在命令行中键入 .jar 文件的名称, JODE 就会允许您图形化地浏览它的类,并自动反汇编每个类以让您查看。这特别有助于通过 Java SDK 提供的库来查找源代码。简单地键入以下命令:
您就会得到如图 1 所示的对文件的完整翻译。 请参阅参考资料,获取更有用的工具的清单。 结论
|