Ofuscação de código em Java

Mohammad Irfan 12 outubro 2023
  1. O que é ofuscação?
  2. Técnicas de Ofuscação de Código
  3. Ferramentas de ofuscação de código para Java
  4. Ofuscação em Java
  5. Resumo
Ofuscação de código em Java

Este tutorial apresenta a ofuscação de código em Java e também lista alguns códigos de exemplo para entender o tópico.

Ofuscação é uma técnica de programação usada para tornar nosso código difícil de entender. Isso é feito para proteger nosso código-fonte de usuários mal-intencionados. Neste tutorial, aprenderemos mais sobre ofuscação.

O que é ofuscação?

  • Ofuscação é o processo de tornar um trecho de código menos legível, difícil de entender e demorado.
  • A ofuscação não afeta a execução do código, e o código continuará a fazer o que deveria.
  • A ofuscação de código é usada para proteger nosso código de hackers e atacantes. Esses usuários mal-intencionados podem tentar fazer engenharia reversa em nosso código e tirar proveito de algumas deficiências de nosso código. Impedir a engenharia reversa de nosso código é um dos principais casos de uso de ofuscação.
  • Hackers e atacantes também usam ofuscação para evitar que ferramentas antivírus detectem seu código hostil.

Técnicas de Ofuscação de Código

Existem muitas ferramentas de ofuscação de código e elas usam uma variedade de técnicas para evitar ataques. Em suma, a ofuscação usa lógica redundante e adiciona código indesejado para desviar o leitor ou uma ferramenta de engenharia reversa (como um descompilador).

Vamos discutir algumas das técnicas de ofuscação de código mais comuns.

  • Renomeando variáveis ​​e nomes de métodos: renomear variáveis ​​e métodos em alguns nomes de difícil leitura ou usando caracteres não imprimíveis ou invisíveis é uma técnica de ofuscação comumente usada.
  • Alterar o fluxo de controle: as ferramentas de ofuscação geralmente alteram o fluxo de controle do código. Isso fará com que o código execute a mesma tarefa, mas se torna difícil de seguir.
  • Usando código fictício: algumas ferramentas geralmente incluem código falso que não tem nada a ver com a lógica original. Isso tornará o código difícil de fazer engenharia reversa.
  • Alterar regularmente o código ofuscado: se atualizarmos regularmente o código ofuscado, todo o progresso do hacker no código anterior será perdido. O invasor terá que começar do zero no novo código.
  • Criptografia: a criptografia de dados (como strings) evita que o hacker entenda o verdadeiro significado do código. A descriptografia pode ser realizada quando o código precisa ser executado.

Ferramentas de ofuscação de código para Java

Java, assim como qualquer outra linguagem de programação, está sujeito a ataques de engenharia reversa. Os descompiladores estão disponíveis para Java que podem facilmente fazer engenharia reversa do byte-code Java compilado. A seguir estão algumas das ferramentas de obfuscator Java mais populares.

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

Ofuscação em Java

Vamos ofuscar o código simples mostrado abaixo usando uma ferramenta de ofuscação.

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

O código é ofuscado no código a seguir. Variáveis ​​fictícias indesejadas e lógica são colocadas no código ofuscado. A lógica do código original é 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;
        }
      }
      }
    }
  }
}

Observe que o código acima pode não ser executado diretamente em um IDE porque um ofuscador também produz arquivos e dependências adicionais que precisam ser resolvidos antes de executar o programa.

Resumo

A ofuscação de código é feita para evitar ataques devido à engenharia reversa. Os invasores e hackers também usam a ofuscação para enganar o software antivírus e outras ferramentas de proteção. Observe que a engenharia reversa de um código não é impossibilitada por ferramentas de ofuscador de código. Eles tornam isso muito difícil e demorado.