Java:反编译后的奇怪结果

发布于 2024-10-07 11:23:50 字数 457 浏览 0 评论 0原文

我有一个奇怪的 jar 文件,它包含一些类,当我使用 JD Decompiler 时,它显示这样的段:

public final void a(ak aa) {
    this.jdField_a_of_type_Ak = aa;
}

public final void a(cn ccn) {
  this.jdField_a_of_type_Cn = ccn;
}

public final cN a() {
  return this.jdField_a_of_type_CN;
}

public final void a() {
  super.b();
}

public final boolean a() {
    return this.jdField_a_of_type_Boolean;
}

我只是想知道为什么编译器/混淆器可以生成这样的类字节代码,我的意思是方法签名。有人知道混淆器可以做到这一点吗?

I have a strange jar file, it contains some class that when I use JD Decompiler, it show a segment like this:

public final void a(ak aa) {
    this.jdField_a_of_type_Ak = aa;
}

public final void a(cn ccn) {
  this.jdField_a_of_type_Cn = ccn;
}

public final cN a() {
  return this.jdField_a_of_type_CN;
}

public final void a() {
  super.b();
}

public final boolean a() {
    return this.jdField_a_of_type_Boolean;
}

I just wonder why a/an compiler/obfuscator can product a class byte code like that, I mean the method signature. Did any one know a obfuscator can do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

忘你却要生生世世 2024-10-14 11:23:50

Java 字节码支持在 Java 源代码中无效的构造。混淆器通过修改字节码以使用这些结构来利用这一事实(同时仍然给出与未混淆的字节码相同的结果)。

The Java bytecode supports constructs that are not valid in Java source code. Obfuscators exploit that fact by modifying bytecode to use those constructs (while still giving the same result as the un-obfuscated bytecode).

南薇 2024-10-14 11:23:50

正如 @Joachim Sauer 正确指出的:JVM 规范对方法的限制较少字节码中的重载比 JLS 在 Java 程序中的重载要多。

来自 JVM 规范(第 4.6 节,方法)

一个类文件中的两个方法不能具有相同的名称和描述符(第 4.3.3 节)。

并且方法描述符包括返回类型:(4.3.3 方法描述符)

方法描述符:
    ( ParameterDescriptor* ) ReturnDescriptor

您在问题中提到的方法都有不同的描述符,所以它们没问题:

public final void a(ak aa)     ->     (Lsomepkg1/ak;)V
public final void a(cn ccn)    ->     (Lsomepkg2/ccn;)V
public final cN a()            ->     ()Lsomepkg3/cN;
public final void a()          ->     ()V
public final boolean a()       ->     ()Z

这是混淆器巧妙利用的。有效的字节码程序不再具有“直接对应的”Java 程序。 ProGuard 例如就是这样做的。这是他们手册中的一个片段:

-积极过载

指定在混淆时应用积极的重载。然后,多个字段和方法可以获得相同的名称,只要它们的参数和返回类型不同(不仅仅是它们的参数)

还有其他类似的技术,例如使用 jsr 字节码指令或使用 Java 语言中保留字的变量标识符。 这里是一个列出了一些技术的网页。


回答显而易见的后续问题:JVM 如何知道在调用站点调用哪个方法?

调用指令要求您指定对要调用的完整方法签名(包括方法的返回类型)的引用。

As @Joachim Sauer correctly points out: The JVM specification poses less constraints on method overloading in the bytecode than the JLS does on Java programs.

From the JVM Specification (Section 4.6, Methods):

No two methods in one class file may have the same name and descriptor (§4.3.3).

And a method descriptor includes the return type: (4.3.3 Method Descriptors)

MethodDescriptor:
    ( ParameterDescriptor* ) ReturnDescriptor

The methods you mentioned in your question all have distinct descriptors, so they are okay:

public final void a(ak aa)     ->     (Lsomepkg1/ak;)V
public final void a(cn ccn)    ->     (Lsomepkg2/ccn;)V
public final cN a()            ->     ()Lsomepkg3/cN;
public final void a()          ->     ()V
public final boolean a()       ->     ()Z

This is cleverly exploited by obfuscators. A valid bytecode-program no longer has a "directly corresponding" Java program. ProGuard does this for instance. Here is a snippet from their manual:

-overloadaggressively

Specifies to apply aggressive overloading while obfuscating. Multiple fields and methods can then get the same names, as long as their arguments and return types are different (not just their arguments).

There are other similar techniques using for instance the jsr bytecode instruction or using variable identifiers that are reserved words in the Java language. Here is a webpage listing a few techniques.


To answer the obvious follow-up question: How does the JVM know which method to call at the call-site?

The invoke-instructions require you to specify a reference to a complete method signature, (including the return type of the method) that is to be called.

扭转时空 2024-10-14 11:23:50

...混淆器产生这样的方法名称/签名,因为这是它的工作。任何混淆器都应该为此目的而工作。

... An obfuscator produces method names/signatures like this because that's its job. Any obfuscator should work for this purpose.

总攻大人 2024-10-14 11:23:50

该类已编译,没有调试信息(至少缺少局部变量信息),并在稍后进行混淆。

一种基本的混淆策略是用新的、无意义的名称替换(几乎)所有包、类和方法名称,这样人们就无法理解反编译的代码。

其他策略包括混淆字符串和添加无法反编译为 java 代码的字节码结构。

您仍然可以为混淆的类文件创建等效的 java 源代码,但需要付出很大的努力。

The class has been compiled without debugging information (at least local variable information is missing) and obfuscated later.

One basic obfuscating strategy is to replace (nearly) all package, class and methodnames by new, senseless names, so that one can't understand the decompiled code.

Additional strategies are obfuscating strings and adding bytecode constructs that can't be decompiled to java code.

You'll still be able to create a java source equivalent for the obfuscated class file but only with great effort.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文