Java 中的代码混淆

Mohammad Irfan 2023年10月12日
  1. 什么是混淆
  2. 代码混淆技术
  3. Java 代码混淆工具
  4. Java 中的混淆
  5. 总结
Java 中的代码混淆

本教程介绍了 Java 中的代码混淆,并列出了一些示例代码来理解该主题。

混淆是一种编程技术,用于使我们的代码难以理解。这样做是为了保护我们的源代码免受恶意用户的攻击。在本教程中,我们将了解有关混淆的更多信息。

什么是混淆

  • 混淆是使一段代码可读性较差、难以理解且耗时的过程。
  • 混淆不会影响代码的执行,代码将继续做它应该做的事情。
  • 代码混淆用于保护我们的代码免受黑客和攻击者的攻击。这些恶意用户可能会尝试对我们的代码进行逆向工程并利用我们代码的一些缺点。防止对我们的代码进行逆向工程是混淆的主要用例之一。
  • 黑客和攻击者还使用混淆来防止防病毒工具检测到他们的恶意代码。

代码混淆技术

存在许多代码混淆工具,它们使用各种技术来防止攻击。简而言之,混淆使用冗余逻辑并添加不需要的代码来误导读者或逆向工程工具(如反编译器)。

让我们讨论一些最常见的代码混淆技术。

  • 重命名变量和方法名称: 将变量和方法重命名为一些难以阅读的名称或使用不可打印或不可见的字符是一种常用的混淆技术。
  • 改变控制流:混淆工具经常会改变代码的控制流。这将使代码执行相同的任务,但变得难以遵循。
  • 使用虚拟代码: 一些工具通常会包含与原始逻辑无关的虚假代码。这将使代码难以逆向工程。
  • 定期更改混淆代码:如果我们定期更新混淆代码,那么黑客在之前代码上的所有进度都会丢失。攻击者必须从头开始编写新代码。
  • 加密:数据(如字符串)的加密可以防止黑客理解代码的真正含义。当需要执行代码时可以进行解密。

Java 代码混淆工具

Java,就像任何其他编程语言一样,容易受到逆向工程攻击。反编译器可用于 Java,它可以轻松地对已编译的 Java 字节码进行逆向工程。以下是一些最流行的 Java 混淆器工具。

  • ProGuard
  • JODE
  • JavaGuard
  • RetroGuard
  • jarg
  • yGuard

Java 中的混淆

让我们使用混淆工具混淆下面显示的简单代码。

public class StringAddition {
  public static void main(String args[]) {
    String s1 = "a";
    String s2 = "b";
    String s3 = s1 + s2;
  }
}

代码被混淆为以下代码。不需要的虚拟变量和逻辑被放置在混淆代码中。原代码逻辑难懂。

import b.n;
public class StringAddition {
  private static final String[] d;
  public static void main(final String[] array) {
  Label_0054: {
    break Label_0054;
  Label_0003:
    while (true) {
      final String str = StringAddition.d[Integer.parseInt("1", 22) << 10017];
      try {
        new StringBuilder()
            .append(str)
            .append(StringAddition.d[24 << Integer.parseInt("9k1", 30)])
            .toString();
        return;
        final String x = StringAddition.d[8388608 >>> 4183];
        System.out.println(x);
      }
      // iftrue(Label_0003:, x.hashCode() == 1033634479)
      // monitorenter(array)
      // monitorenter(array)
      catch (EnumConstantNotPresentException i) {
        throw n.i = i;
      }
    }
  }
  }
  static {
    final char[] charArray =
        "\u0e47\u0e59\u0093\u0cc1¥£®£®\u00d1®®®®½£®A\u00da\u00dc®£®®\u00d1®®®\u00c2\u00dc®A\u00da£\u00d1£®®®\u00d1®®\u00c2£\u00d1A\u00da£®\u00dc®®®®\u00d1®\u00c2£®>\u00da£®£\u00d1®®®®\u00d1\u00c2£®A¥£®£®®®®®®½\u00f6\u00d1b\u00f9\u009e\u00d1\u0080\u008d\u008d\u0083\u00d1\u008d\u008d\u00e1\u009d\u00d1b\u00f9\u0080\u009f\u00dc\u008d\u008d\u008d\u0092\u00d1\u008d\u00e1\u0080\u0099>\u00f9\u0080\u008d\u0090\u00d1\u008d\u008d\u008d\u009e\u00d1\u00e1\u0080\u008d}¥\u0080\u008d\u0080\u009a\u00d1\u008d\u008d\u008d\u00df½\u0080\u008db\u00eb\u00dc\u008d\u0080\u008d\u0094\u00d1\u008d\u008d\u008d\u00e9\u00dc\u008db\u00f9\u00dc\u00d1\u00dc\u00d1\u00d1\u00d1\u00d1\u008d\u008d\u0097\u00dc\u008db\u00da£\u008d\u0080\u008d®®\u008d\u008d\u008d\u00c2£\u008db\u00f9£®\u0080\u008d\u008d®®\u008d\u008d\u00e1£®b\u00f9\u0080®£\u008d\u008d\u008d®®\u008d\u00e1\u0080®A\u00f9\u0080\u008d£®\u008d\u008d\u008d®®\u00e1\u0080\u008dA\u00da\u0080\u008d\u0080®®\u008d\u008d\u008d®\u00c2\u0080\u008db\u00da£®£®®®\u008d\u008d\u00fb½\u0080\u00deA\u00da \u008d\u00d3®®\u00ad\u008d\u00de®\u00c2 \u008d1\u00da£\u00ad\u0080\u00de®®\u00ad\u008d\u00de\u00c2£\u00adbª£® \u008d\u00de®®\u00ad\u008d²£®B\u00f9\u00d3®£\u00ad\u008d\u00de®®\u00ad\u00e1\u00d3®A\u00d9\u0080\u00de£®\u00ad\u008d\u00de®®\u00c1\u0080\u00deA\u00da£®£®®\u00ad\u008d\u00fb\u00d1½£®A\u00da\u00dc®£®®\u00d1®®®\u00c2\u00dc®A\u00da£\u00d1£®®®\u00d1®®\u00c2£\u00d1A\u00da£®\u00dc®®®®\u00d1®\u00c2£®>\u00da£®£\u00d1®®®®\u00d1\u00c2£®A¥£®£®\u00d1®®®®½£®A\u00da\u00dc®£®®\u00d1\u00fb\u00d1\u008d\u00e1\u0093\u00d1b\u00f9\u0080\u0093\u00dc\u008d\u008d\u008d\u0097\u00d1\u008d\u00e1\u0080\u0084>\u00f9\u0080\u008d\u008f\u00d1\u008d\u008d\u008d\u0092\u00d1\u00e1\u0080\u008d\u007f¥\u0080\u008d\u0080\u0085\u00d1\u008d\u008d\u008d\u009e½\u0080\u008db\u00f7\u00dc\u008d\u0080\u008d\u00dc\u00d1\u008d\u008d\u008d\u00e9\u00dc\u008db\u00f9\u008e\u00d1\u0080\u008d\u008d\u0098\u00d1\u008d\u008d\u00e1\u009d\u00d1b\u00f9\u0080\u009d\u00dc\u008d\u008d\u00fb\u00d1\u008d\u008d\u00c2£\u008db\u00f9£®\u0080\u008d\u008d®®\u008d\u008d\u00e1£®b\u00f9\u0080®£\u008d\u008d\u008d®®\u008d\u00e1\u0080®A\u00f9\u0080\u008d£®\u008d\u008d\u008d®®\u00e1\u0080\u008dA\u00da\u0080\u008d\u0080®®\u008d\u008d\u008d®\u00c2\u0080\u008db\u00da£\u008d\u0080\u008d®®\u008d\u008d\u008d\u00c2£\u008db\u00f9£®\u0080\u008d\u00fb\u00d1\u008d\u00de®\u00c2 \u008d1\u00da£\u00ad\u0080\u00de®®\u00ad\u008d\u00de\u00c2£\u00adbª£® \u008d\u00de®®\u00ad\u008d²£®B\u00f9\u00d3®£\u00ad\u008d\u00de®®\u00ad\u00e1\u00d3®A\u00d9\u0080\u00de£®\u00ad\u008d\u00de®®\u00c1\u0080\u00deA\u00da \u008d\u00d3®®\u00ad\u008d\u00de®\u00c2 \u008d1\u00da£\u00ad\u0080\u00fb\u00d1\u00fb\u0e59\u0090"
            .toCharArray();
    int n = 64 << 5658;
    final StackTraceElement stackTraceElement;
    final int n2 = (stackTraceElement = new Throwable().getStackTrace()[107 >>> 11463])
                       .getMethodName()
                       .hashCode()
        & Integer.parseInt("171h3c0", 22) - 149806781;
    final char[] charArray2 = stackTraceElement.getClassName().toCharArray();
    final char[] array = charArray;
    final int n3 = 101 >>> 10951;
    ++n;
    d = new String[array[n3] ^ Integer.parseInt("1g1nna6", 28) - 758393825 ^ n2];
    int n4 = 26 >>> Integer.parseInt("gk9", 31);
  Label_0101:
    while (true) {
      int n5;
      final char[] value =
          new char[n5 = (charArray[n++] ^ Integer.parseInt("ifjj061", 22) - 2122795820 ^ n2)];
      int n6 = 72 << 11485;
    Label_0334_Outer:
      while (true) {
      Label_0272: {
        if (n5 <= 0) {
          break Label_0272;
        }
        int n7 = charArray[n];
      Label_0334:
        while (true) {
        Label_0388: {
          switch (charArray2[n % charArray2.length] ^ (0xC999A060 ^ 0xC999A0AE)) {
            case 160: {
              break Label_0334;
            }
            case 162: {
              break Label_0334;
            }
            case 131: {
              break Label_0334;
            }
            case 163: {
              break Label_0334;
            }
            case 167: {
              break Label_0334;
            }
            case 136: {
              break Label_0388;
            }
            case 170: {
              break Label_0388;
            }
            case 186: {
              break Label_0388;
            }
            case 139: {
              break Label_0388;
            }
            case 171: {
              break Label_0388;
            }
          }
          while (true) {
            value[n6] = (char) n7;
            try {
              ++n6;
              ++n;
              --n5;
              // monitorexit(charArray)
              n7 ^= -400944133 + 400944374;
              continue Label_0334_Outer;
              n7 ^= Integer.parseInt("83bdf8k", 22) - 925222572;
              continue Label_0334_Outer;
              continue Label_0334;
              StringAddition.d[n4++] = new String(value).intern();
              // iftrue(Label_0101:, n < charArray.length)
              return;
              n7 ^= Integer.parseInt("6j", 23) << 3840;
              continue Label_0334_Outer;
              n7 ^= 61440 >>> 11787;
              continue Label_0334_Outer;
              n7 ^= 364965749 - 364965616;
            } catch (Throwable t) {
              break;
            }
          }
        } break;
        }
      }
      }
    }
  }
}

请注意,上面的代码可能不会直接在 IDE 中运行,因为混淆器还会产生额外的文件和依赖项,这些文件和依赖项在执行程序之前需要解决。

总结

代码混淆是为了防止由于逆向工程造成的攻击。攻击者和黑客还使用混淆来欺骗防病毒软件和其他保护工具。请注意,代码混淆器工具并非无法对代码进行逆向工程。它们使它变得非常困难和耗时。