Ofuscación de código en Java

Mohammad Irfan 12 octubre 2023
  1. ¿Qué es la ofuscación?
  2. Técnicas de ofuscación de código
  3. Herramientas de ofuscación de código para Java
  4. Ofuscación en Java
  5. Resumen
Ofuscación de código en Java

Este tutorial presenta la ofuscación de código en Java y también enumera algunos códigos de ejemplo para comprender el tema.

La ofuscación es una técnica de programación utilizada para hacer que nuestro código sea difícil de entender. Está hecho para proteger nuestro código fuente de usuarios malintencionados. En este tutorial, aprenderemos más sobre la ofuscación.

¿Qué es la ofuscación?

  • La ofuscación es el proceso de hacer que un fragmento de código sea menos legible, difícil de entender y requiere más tiempo.
  • La ofuscación no afecta la ejecución del código, y el código seguirá haciendo lo que estaba destinado a hacer.
  • La ofuscación de código se utiliza para proteger nuestro código de piratas informáticos y atacantes. Estos usuarios malintencionados pueden intentar aplicar ingeniería inversa a nuestro código y aprovechar algunas deficiencias de nuestro código. La prevención de la ingeniería inversa de nuestro código es uno de los principales casos de uso de ofuscación.
  • Los piratas informáticos y atacantes también utilizan la ofuscación para evitar que las herramientas antivirus detecten su código hostil.

Técnicas de ofuscación de código

Existen muchas herramientas de ofuscación de código y utilizan una variedad de técnicas para prevenir ataques. En pocas palabras, la ofuscación utiliza lógica redundante y agrega código no deseado para desviar al lector o una herramienta de ingeniería inversa (como un descompilador).

Analicemos algunas de las técnicas de ofuscación de código más comunes.

  • Cambiar el nombre de las variables y los métodos: cambiar el nombre de las variables y los métodos a algunos nombres difíciles de leer o el uso de caracteres invisibles o no imprimibles es una técnica de ofuscación de uso común.
  • Cambio del flujo de control: las herramientas de ofuscación a menudo cambiarán el flujo de control del código. Esto hará que el código realice la misma tarea, pero será difícil de seguir.
  • Uso de código ficticio: algunas herramientas suelen incluir código falso que no tiene nada que ver con la lógica original. Esto dificultará la ingeniería inversa del código.
  • Alterar regularmente el código ofuscado: si actualizamos regularmente el código ofuscado, se perderá todo el progreso del hacker en el código anterior. El atacante tendrá que empezar desde cero con el nuevo código.
  • Cifrado: el cifrado de datos (como cadenas) evita que el pirata informático comprenda el verdadero significado del código. El descifrado se puede realizar cuando es necesario ejecutar el código.

Herramientas de ofuscación de código para Java

Java, al igual que cualquier otro lenguaje de programación, es propenso a sufrir ataques de ingeniería inversa. Hay descompiladores disponibles para Java que pueden realizar fácilmente ingeniería inversa del código de bytes compilado de Java. Las siguientes son algunas de las herramientas de ocultación de Java más populares.

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

Ofuscación en Java

Ofusquemos el código simple que se muestra a continuación usando una herramienta de ofuscación.

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

El código está oculto en el siguiente código. Las variables ficticias y la lógica no deseadas se colocan en el código ofuscado. La lógica del código original es difícil de entender.

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;
        }
      }
      }
    }
  }
}

Tenga en cuenta que es posible que el código anterior no se ejecute directamente en un IDE porque un ofuscador también produce archivos y dependencias adicionales que deben resolverse antes de ejecutar el programa.

Resumen

La ofuscación del código se realiza para prevenir ataques debido a la ingeniería inversa. Los atacantes y piratas informáticos también utilizan la ofuscación para engañar al software antivirus y otras herramientas de protección. Tenga en cuenta que la ingeniería inversa de un código no es imposible con las herramientas de ocultación de código. Lo hacen mucho más difícil y requiere mucho tiempo.