P: Se eu criptografar meus arquivos .class e usar um carregador de classe personalizado para carregá-los e descriptografá-los na hora, isso impedirá a descompilação?
R: O problema de impedir a descompilação do byte-code Java é quase tão antigo quanto a própria linguagem. Apesar de uma série de ferramentas de ofuscação disponíveis no mercado, os programadores Java novatos continuam a pensar em maneiras novas e inteligentes de proteger sua propriedade intelectual. Neste artigo de perguntas e respostas do Java, eu desfaço alguns mitos em torno de uma ideia frequentemente refeita em fóruns de discussão.
A extrema facilidade com que os arquivos .class Java podem ser reconstruídos em fontes Java que se assemelham muito aos originais tem muito a ver com os objetivos e compensações de design de byte-code Java. Entre outras coisas, o byte code Java foi projetado para compactação, independência de plataforma, mobilidade de rede e facilidade de análise por interpretadores de byte-code e compiladores dinâmicos JIT (just-in-time) / HotSpot. Indiscutivelmente, os arquivos .class compilados expressam a intenção do programador de forma tão clara que podem ser mais fáceis de analisar do que o código-fonte original.
Várias coisas podem ser feitas, se não para evitar a descompilação completamente, pelo menos para torná-la mais difícil. Por exemplo, como uma etapa de pós-compilação, você pode massagear os dados .class para tornar o código de byte mais difícil de ler quando descompilado ou mais difícil de descompilar em código Java válido (ou ambos). Técnicas como executar sobrecarga extrema de nome de método funcionam bem para o primeiro, e manipular o fluxo de controle para criar estruturas de controle que não são possíveis de representar por meio da sintaxe Java funcionam bem para o último. Os ofuscadores comerciais mais bem-sucedidos usam uma combinação dessas e de outras técnicas.
Infelizmente, ambas as abordagens devem realmente alterar o código que a JVM executará, e muitos usuários temem (com razão) que essa transformação possa adicionar novos bugs em seus aplicativos. Além disso, a renomeação de método e campo pode fazer com que as chamadas de reflexão parem de funcionar. Alterar os nomes reais da classe e do pacote pode interromper várias outras APIs Java (JNDI (Java Naming and Directory Interface), provedores de URL, etc.). Além dos nomes alterados, se a associação entre os deslocamentos do código de byte de classe e os números da linha de origem for alterada, a recuperação dos rastreamentos de pilha de exceção originais pode se tornar difícil.
Depois, há a opção de ofuscar o código-fonte Java original. Mas, fundamentalmente, isso causa um conjunto semelhante de problemas. Criptografar, não ofuscar?
Talvez o acima tenha feito você pensar: "Bem, e se, em vez de manipular o código de byte, eu criptografar todas as minhas classes após a compilação e descriptografá-las instantaneamente dentro da JVM (o que pode ser feito com um carregador de classes personalizado)? Então a JVM executa meu código de byte original e ainda não há nada para descompilar ou fazer engenharia reversa, certo? "
Infelizmente, você estaria errado, tanto ao pensar que foi o primeiro a ter essa ideia quanto ao pensar que ela realmente funciona. E o motivo não tem nada a ver com a força do seu esquema de criptografia.