103

如何锁定已编译的 Java 类以防止反编译?

我知道这在互联网上一定是讨论得很好的话题,但是在参考他们之后我无法得出任何结论。

很多人确实建议使用混淆器,但他们只是用难以记住的字符序列重命名类、方法和字段,但是敏感的常量值呢?

例如,您开发了基于密码的加密技术的加密和解密组件。现在在这种情况下,任何普通的 Java 人都可以使用JAD反编译类文件并轻松检索密码值(定义为常量)以及salt,进而可以通过编写小型独立程序来解密数据!

还是应该在本机代码(例如 VC++)中构建此类敏感组件并通过JNI调用它们?

4

9 回答 9

100

一些更高级的 Java 字节码混淆器不仅仅是类名修饰。例如, Zelix KlassMaster还可以扰乱您的代码流,使其非常难以遵循,并且可以作为出色的代码优化器...

此外,许多混淆器还能够打乱您的字符串常量并删除未使用的代码。

另一种可能的解决方案(不一定排除混淆)是使用加密的 JAR 文件和执行解密的自定义类加载器(最好使用本机运行时库)。

第三(可能提供最强大的保护)是使用本地提前编译器,例如GCCExcelsior JET,例如,将您的 Java 代码直接编译为特定于平台的本地二进制文件。

无论如何,您必须记住,正如爱沙尼亚语中所说的“锁是为动物准备的”。这意味着在运行时每一段代码都是可用的(加载到内存中),只要有足够的技能、决心和动力,人们就可以并且将会反编译、解读和破解你的代码……你的工作就是让这个过程像你可以并且仍然保持工作......

于 2008-09-08T10:29:19.013 回答
17

只要他们可以访问加密数据和解密数据的软件,基本上就没有办法让这一切变得完全安全。之前解决这个问题的方法是使用某种形式的外部黑匣子来处理加密/解密,如加密狗、远程身份验证服务器等。但即便如此,鉴于用户可以完全访问自己的系统,这只会使事情变得更糟困难,并非不可能——除非您可以将您的产品直接与存储在“黑匣子”中的功能联系起来,例如在线游戏服务器。

于 2008-09-08T10:02:25.367 回答
14

免责声明:我不是安全专家。

这听起来是个坏主意:你让某人用你给他的“隐藏”密钥加密东西。我认为这不能保证安全。

也许非对称密钥可以工作:

  • 使用公钥部署加密许可证以进行解密
  • 让客户创建一个新的许可证并将其发送给您进行加密
  • 将新许可证发送回客户端。

我不确定,但我相信客户实际上可以用你给他的公钥加密许可证密钥。然后,您可以使用您的私钥对其进行解密并重新加密。

您可以为每个客户保留一个单独的公钥/私钥对,以确保您实际上从正确的客户那里获得了东西 - 现在负责密钥......

于 2008-09-08T09:56:33.307 回答
13

不管你做什么,它都可以被“反编译”。哎呀,你可以拆开它。或者查看内存转储以找到您的常量。你看,计算机需要知道它们,所以你的代码也需要。

该怎么办?

尽量不要在代码中将密钥作为硬编码常量发送:将其保留为每个用户的设置。让用户负责照看该密钥。

于 2008-09-08T09:43:53.227 回答
8

@jatanp:或者更好的是,他们可以反编译、删除许可代码并重新编译。对于 Java,我真的不认为有一个适当的、防黑客的解决方案来解决这个问题。即使是邪恶的小加密狗也无法通过 Java 阻止这种情况。

我自己的业务经理担心这个,我想太多了。但是话又说回来,我们将我们的应用程序出售给倾向于遵守许可条件的大公司——通常是一个安全的环境,这要归功于 bean 柜台和律师。如果您的许可证编写正确,反编译本身可能是非法的。

所以,我不得不问,你真的需要像你正在寻找你的应用程序那样的强化保护吗?您的客户群是什么样的?(企业?还是青少年游戏玩家群体,这在哪里会成为更大的问题?)

于 2008-09-08T10:02:08.190 回答
4

如果您正在寻找许可解决方案,可以查看TrueLicense API。它基于使用非对称密钥。但是,这并不意味着您的应用程序无法被破解。每个应用程序都可以通过足够的努力被破解。正如Stu 回答的那样,真正重要的是弄清楚您需要多强的保护。

于 2008-09-08T11:20:44.290 回答
4

您可以毫无顾忌地使用字节码加密。

事实上,上面引用的论文“Cracking Java byte-code encryption”包含一个逻辑谬误。论文的主要主张是在运行所有类之前必须解密并传递给ClassLoader.defineClass(...)方法。但是这是错误的。

这里遗漏的假设是假设它们在真实的或标准的 java 运行时环境中运行。没有什么可以强制受保护的 java 应用程序不仅启动这些类,甚至解密并将它们传递给ClassLoader. 换句话说,如果您在标准 JRE 中,则无法拦截defineClass(...)方法,因为标准 java 没有用于此目的的 API,如果您使用带有补丁ClassLoader或任何其他“黑客技巧”的修改过的 JRE,您将无法这样做,因为受保护java 应用程序根本无法工作,因此您将没有什么可以拦截的。使用哪个“补丁查找器”或黑客使用哪个技巧绝对无关紧要。这些技术细节是一个完全不同的故事。

于 2012-03-07T09:03:04.167 回答
3

我认为不存在任何有效的离线反盗版方法。电子游戏行业多次尝试发现,他们的程序总是被破解。唯一的解决方案是该程序必须与您的服务器连接在线运行,以便您可以验证 lincense 密钥,并且一次只有一个被许可人的活动连接。这就是魔兽世界暗黑破坏神的运作方式。更难的是,为他们开发了私有服务器来绕过安全性。

话虽如此,我不相信大中型公司会使用非法复制的软件,因为与他们相比,他们的许可证成本是最低的(也许,我不知道你要为你的程序收取多少费用)试用版的费用。

于 2008-09-08T13:00:57.107 回答
0

问:如果我加密我的 .class 文件并使用自定义类加载器即时加载和解密它们,这会阻止反编译吗?

A:防止Java 字节码反编译的问题几乎与语言本身一样古老。尽管市场上有一系列混淆工具可供使用,但 Java 新手程序员仍在不断思考新的巧妙方法来保护他们的知识产权。在这期 Java 问答部分中,我消除了一些关于论坛中经常重复讨论的想法的神话。

Java .class 文件可以极其容易地重构为与原始文件非常相似的 Java 源代码,这与 Java 字节码设计目标和权衡取舍有很大关系。除其他外,Java 字节码旨在实现紧凑性、平台独立性、网络移动性以及字节码解释器和 JIT(即时)/HotSpot 动态编译器易于分析。可以说,编译后的 .class 文件如此清楚地表达了程序员的意图,它们比原始源代码更容易分析。

可以做几件事,如果不是为了完全防止反编译,至少要让它变得更加困难。例如,作为编译后的步骤,您可以对 .class 数据进行处理,以使字节码在反编译时更难阅读,或者更难反编译成有效的 Java 代码(或两者兼而有之)。执行极端方法名称重载等技术适用于前者,而操纵控制流以创建无法通过 Java 语法表示的控制结构则适用于后者。更成功的商业混淆器使用这些技术和其他技术的混合。

不幸的是,这两种方法都必须实际更改 JVM 将运行的代码,并且许多用户担心(理所当然地)这种转换可能会给他们的应用程序添加新的错误。此外,方法和字段重命名可能会导致反射调用停止工作。更改实际的类和包名称可能会破坏其他几个 Java API(JNDI(Java 命名和目录接口)、URL 提供程序等)。除了更改名称之外,如果类字节码偏移量和源行号之间的关联发生更改,恢复原始异常堆栈跟踪可能会变得困难。

然后是混淆原始 Java 源代码的选项。但从根本上说,这会导致一系列类似的问题。加密,而不是混淆?

也许上面的内容让你想到,“好吧,如果我在编译后加密我所有的类并在 JVM 中动态解密它们(这可以通过自定义类加载器完成)而不是操作字节码怎么办?然后 JVM 执行我的原始字节码,但没有什么可以反编译或逆向工程,对吧?”

不幸的是,你错了,你认为你是第一个提出这个想法的人,并且认为它确实有效。原因与您的加密方案的强度无关。

于 2015-09-18T11:52:24.430 回答