Ofuscação de código em Java

  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

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.