Como bloquear classes Java compiladas para evitar a descompilação?

96

Como faço para bloquear classes Java compiladas para evitar a descompilação?

Sei que deve ser um tema muito bem discutido na Internet, mas não consegui chegar a nenhuma conclusão após referi-los.

Muitas pessoas sugerem o ofuscador, mas eles apenas renomeiam classes, métodos e campos com sequências de caracteres difíceis de lembrar, mas e os valores constantes sensíveis?

Por exemplo, você desenvolveu o componente de criptografia e descriptografia com base em uma técnica de criptografia baseada em senha. Agora, neste caso, qualquer pessoa comum de Java pode usar JAD para descompilar o arquivo de classe e recuperar facilmente o valor da senha (definido como constante), bem como o salt e, por sua vez, pode descriptografar os dados escrevendo um pequeno programa independente!

Ou esses componentes sensíveis deveriam ser construídos em código nativo (por exemplo, VC ++) e chamá-los via JNI ?

Jatanp
fonte
Mesmo o código nativo desmontado é bastante legível para algumas pessoas, você provavelmente teria que usar algum ofuscador ou montagem feita à mão para torná-lo não óbvio (C ++ comum compilado com otimização é legível o suficiente). Qualquer código executado no dispositivo do usuário pode ser interceptado. Embora os requisitos de custo / habilidade de tal interceptação possam ser bastante altos, por exemplo, invadir o código de chips "à prova de violação" de cartão inteligente não é trivial, apenas factível com equipamento e habilidade de ponta. Com classes Java ... Eu não me incomodaria muito, você provavelmente pode criptografá-los o suficiente para afastar os alunos de script, não mais.
Ped7g

Respostas:

96

Alguns dos ofuscadores de bytecode Java mais avançados fazem muito mais do que apenas mutilar o nome da classe. Zelix KlassMaster , por exemplo, também pode embaralhar seu fluxo de código de uma forma que o torna realmente difícil de seguir e funciona como um excelente otimizador de código ...

Além disso, muitos dos obfuscators também são capazes de embaralhar suas constantes de string e remover o código não utilizado.

Outra solução possível (não necessariamente excluindo a ofuscação) é usar arquivos JAR criptografados e um carregador de classe customizado que faz a descriptografia (de preferência usando a biblioteca de tempo de execução nativa).

Terceiro (e possivelmente oferecendo a proteção mais forte) é usar compiladores antecipados nativos como GCC ou Excelsior JET , por exemplo, que compilam seu código Java diretamente para um binário nativo específico de plataforma.

Em qualquer caso, você deve se lembrar disso, como diz o ditado estoniano "As fechaduras são para animais". O que significa que cada bit de código está disponível (carregado na memória) durante o tempo de execução e com habilidade, determinação e motivação suficientes, as pessoas podem e irão descompilar, decodificar e hackear seu código ... Seu trabalho é simplesmente tornar o processo tão desconfortável quanto você pode e ainda mantém a coisa funcionando ...

Roland Tepp
fonte
13
+1 para "As fechaduras são para animais". Acho que o termo apropriado aqui seria script kiddies.
Antimônio
Você quer dizer GCJ, e está morto.
Marquês de Lorne
1
A criptografia do arquivo Componio jar também está morta. Use JarProtector em vez disso.
J.Profi
16

Contanto que eles tenham acesso aos dados criptografados e ao software que os descriptografa, basicamente não há como tornar isso totalmente seguro. Uma maneira de resolver isso antes é usar alguma forma de caixa preta externa para lidar com criptografia / descriptografia, como dongles, servidores de autenticação remota, etc. Mas mesmo assim, dado que o usuário tem acesso total ao seu próprio sistema, isso só torna as coisas difícil, não impossível - a menos que você possa vincular seu produto diretamente à funcionalidade armazenada na "caixa preta", como, digamos, servidores de jogos online.

Erlend Halvorsen
fonte
13

Isenção de responsabilidade: não sou um especialista em segurança.

Parece uma má ideia: você está permitindo que alguém criptografe as coisas com uma chave 'oculta' que você deu a ele. Não acho que isso possa ser seguro.

Talvez as chaves assimétricas possam funcionar:

  • implantar uma licença criptografada com uma chave pública para descriptografar
  • deixe o cliente criar uma nova licença e enviá-la para você para criptografia
  • envie uma nova licença de volta ao cliente.

Não tenho certeza, mas acredito que o cliente pode realmente criptografar a chave de licença com a chave pública que você deu a ele. Você pode então descriptografá-lo com sua chave privada e criptografá-lo novamente.

Você pode manter um par separado de chaves públicas / privadas por cliente para ter certeza de que está realmente recebendo as coisas do cliente certo - agora você é responsável pelas chaves ...

Daren Thomas
fonte
2
Já usei essa técnica antes e funciona bem. No entanto, de uma perspectiva de ataque, a primeira abordagem seria apenas modificar o código que executa a verificação de licença e removê-lo. Há coisas que você pode fazer para tornar esse vetor de ataque mais difícil, mas se você der ao invasor o controle do hardware, estará fadado ao fracasso com um invasor devidamente motivado e habilidoso. Na prática, o objetivo é apenas manter as pessoas honestas, em sua maioria, honestas.
Jim Rush
12

Não importa o que você faça, ele pode ser 'descompilado'. Heck, você pode simplesmente desmontá-lo. Ou observe um despejo de memória para encontrar suas constantes. Você vê, o computador precisa conhecê-los, então seu código também precisará.

O que fazer em relação a isto?

Tente não enviar a chave como uma constante codificada em seu código: mantenha-a como uma configuração por usuário. Torne o usuário responsável por cuidar dessa chave.

Daren Thomas
fonte
6

@jatanp: ou melhor ainda, eles podem descompilar, remover o código de licença e recompilar. Com Java, realmente não acho que haja uma solução adequada e à prova de hack para esse problema. Nem mesmo um pequeno dongle do mal poderia impedir isso com Java.

Meus próprios gerentes de negócios se preocupam com isso, e eu penso demais. Mas, novamente, vendemos nosso aplicativo para grandes empresas que tendem a respeitar as condições de licenciamento - geralmente um ambiente seguro graças aos contadores de grãos e advogados. O próprio ato de descompilar pode ser ilegal se sua licença for escrita corretamente.

Portanto, devo perguntar: você realmente precisa de proteção reforçada como está procurando para sua aplicação? Como é sua base de clientes? (Corporações? Ou as massas de jogadores adolescentes, onde isso seria mais um problema?)

Stu Thompson
fonte
Na verdade, existe uma solução adequada e à prova de hack que btw parece um dongle: excelsior-usa.com/blog/excelsior-jet/…
Dmitry Leskov
@DmitryLeskov 'resistente a hack', talvez. Mas é apenas um obstáculo para quem deseja o código. No final do dia, o código de byte precisa ser executado em uma plataforma host não criptografada. Ponto final.
Stu Thompson
Você não leu o post que eu vinculei. O bytecode é convertido para o código da CPU do dongle e criptografado. Quando o usuário final executa o aplicativo protegido, esse código criptografado é transferido para o "dongle". O dongle descriptografa e executa em sua CPU.
Dmitry Leskov
2
Jogadores adolescentes corporativos.
odiszapc
3

Se você está procurando uma solução de licenciamento, pode verificar a API TrueLicense . Baseia-se no uso de chaves assimétricas. No entanto, isso não significa que seu aplicativo não pode ser crackeado. Cada aplicativo pode ser quebrado com bastante esforço. O que é realmente importante, como Stu respondeu , é descobrir o quão forte você precisa de proteção.

hakan
fonte
2

Não acho que exista nenhum método antipirataria offline eficaz. A indústria de videogames tentou descobrir isso muitas vezes e seus programas sempre foram quebrados. A única solução é que o programa seja executado online ligado aos seus servidores, para que possa verificar a chave do lincense, e que exista apenas uma ligação ativa do licenciado de cada vez. É assim que World of Warcraft ou Diablo funcionam. Mesmo difícil, existem servidores privados desenvolvidos para contornar a segurança.

Dito isso, não acredito que médias / grandes corporações usem software copiado ilegal, porque o custo da licença para elas é mínimo (talvez, não sei quanto você vai cobrar por seu programa) em comparação com o custo de uma versão de teste.

Telcontar
fonte
2

Você pode usar a criptografia por código de bytes sem medo.

O fato é que o artigo citado acima “Cracking Java byte-code encryption” contém uma falácia lógica. A principal reivindicação do artigo é que antes de executar todas as classes, todas as classes devem ser descriptografadas e passadas para o ClassLoader.defineClass(...)método . mas isso não é verdade.

A suposição perdida aqui é fornecida que eles estão sendo executados em um ambiente de tempo de execução Java autêntico ou padrão . Nada pode obrigar o aplicativo Java protegido não apenas a iniciar essas classes, mas até mesmo descriptografá-las e passá-las para ClassLoader. Em outras palavras, se você está no JRE padrão, você não pode interceptar o defineClass(...)método porque o java padrão não tem API para este propósito, e se você usa JRE modificado com patch ClassLoaderou qualquer outro “truque de hacker”, não pode fazê-lo porque está protegido O aplicativo java não funcionará de todo e, portanto, você não terá nada para interceptar. E absolutamente não importa qual “localizador de patch” é usado ou qual truque é usado pelos hackers. Esses detalhes técnicos são uma história bem diferente.

Yury Bendersky
fonte
Como exatamente você pretende detectar uma JVM corrigida? De qualquer forma, tudo isso torna as coisas um pouco mais difíceis.
Antimônio
Você não consegue simplesmente encontrar uma chamada para defineClass () em seu inicializador de aplicativo? Quando você faz essa chamada, precisa entregar uma matriz de bytes descriptografados de qualquer maneira. Não é esse outro ponto onde a fonte original pode vazar?
Ascendant
4
Eu realmente não concordo com esta resposta. Para mim, isso soa como: "Pergunta: Qual é a maneira mais fácil de encontrar Pi? Resposta: Pegue 2 * Pi e divida por dois." Não estou discordando da ideia, mas você poderia incluir mais detalhes? Por exemplo, você espera que o programa principal seja escrito em java puro? Isso inclui o código que está procurando modificações?
Patrick M
-1

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.

S Harish Morampudi
fonte