Suggerimento Java 22: proteggi i tuoi bytecode da reverse engineering / decompilazione

Se stai scrivendo classi Java e le distribuisci su Internet, dovresti sapere che le persone possono decodificare, disassemblare o decompilare le tue classi in codice sorgente Java. Il decompilatore più utilizzato (almeno pubblicamente) è Mocha. Mocha legge uno o più file di bytecode (classi) e li converte di nuovo in codice sorgente Java. Sebbene il codice generato da Mocha non sia esattamente lo stesso del codice sorgente originale, è abbastanza vicino perché qualcuno lo possa capire e modificare. Se sei interessato a sviluppare classi Java e a distribuirle su Internet, e desideri proteggerle dalla decompilazione, continua a leggere.

Mocha: un esempio

Prima di introdurre Crema, esamineremo un esempio utilizzando Mocha. Il seguente semplice programma visualizza la stringa "Hi there" sullo schermo:

class test {public static void main (String argv []) {System.out.println ("Hi there"); }}

Se le quattro righe precedenti fossero salvate in un file, la test.javacompilazione test.javagenererebbe un nuovo file test.class, che contiene i bytecode Java che rappresentano quel codice sorgente Java. Ora eseguiamo Mocha sul file di classe e vediamo l'output di Mocha:

% java mocha.Decompiler test.class //% è il mio prompt della shell C su UNIX. 

Il comando precedente genera un file chiamato test.mocha, che contiene il codice sorgente Java generato da Mocha:

% more test.mocha / * Decompilato da Mocha da test.class * / / * Originariamente compilato da test.java * / import java.io.PrintStream; class test {public static void main (String astring []) {System.out.println ("Hi there"); } test () {}}

Come puoi vedere dall'esempio sopra, Mocha ci ha fornito un codice sorgente Java facile da leggere e capire. Se copi questo file in test.java, lo compili di nuovo e lo esegui, verrà compilato ed eseguito correttamente.

Crema in soccorso!

Quindi come puoi proteggere le tue classi dalla decompilazione? Una risposta è Crema. Crema rimescola le informazioni simboliche nei tuoi .classfile in modo che diventino meno vulnerabili alla decompilazione. Le informazioni simboliche che Crema rimescola includono il nome della classe, la sua superclasse, le interfacce, i nomi delle variabili, i metodi e così via. Questi nomi simbolici sono necessari alla JVM (Java virtual machine) per collegare le classi con i pacchetti della libreria. Crema rimescola questi nomi simbolici e fa riferimento ad essi allo stesso modo in modo che la JVM possa ancora ottenere il corretto collegamento tra classi e pacchetti.

Allora come funziona Crema? In pratica, prima di distribuire i file di classe su Internet, esegui Crema su di essi. Crema scrambierà le informazioni simboliche in esse contenute e inserirà ogni nuova classe nel fascicolo 1.crema. Il tuo compito quindi è rinominare 1.cremain qualcosa di simile filename.classprima di distribuirlo su Internet.

Eseguiamo Crema sul nostro test.classesempio mostrato sopra, quindi proviamo a decompilarlo con Mocha:

% java Crema -v test.class // -v è un'opzione per attivare la modalità dettagliata //. Ci sono molte altre opzioni. CREMA - The Java Obfuscator - VERSIONE DI VALUTAZIONE Copyright (c) 1996 Hanpeter van Vliet Caricamento test.class Test di offuscamento Salvataggio del test come 1.crema NOTA: Le classi elaborate con la versione di valutazione di Crema possono essere utilizzate solo localmente, poiché la maggior parte dei browser rifiuterà di farlo caricali. Per la versione completa di Crema, punta il tuo browser su: //www.inter.nl.net/users/HPvan.Vliet/crema.html (vedi Risorse)

Il comando precedente ha generato un nuovo file 1.crema, che contiene i bytecode con informazioni simboliche codificate. Notare che Crema ha molti parametri delle opzioni della riga di comando che è possibile utilizzare; per maggiori informazioni su Crema, consultare la sezione Risorse.

Ora spostiamo di nuovo quel file test.classe decompiliamolo usando Mocha:

% mv 1.crema test.class% java mocha.Decompiler test.class java.lang.NullPointerException SIGSEGV 11 * violazione della segmentazione si_signo [11]: SIGSEGV 11 * violazione della segmentazione si_errno [0]: Errore 0 si_code [1]: SEGV_ACCERR [ addr: 0x0] stackbase = EFFFF35C, stackpointer = EFFFF040 Full thread dump: "Finalizer thread" (TID: 0xee3003b0, sys_thread_t: 0xef490de0) prio = 1 "Async Garbage Collector" (TID: 0xee300368, sys_thread_cio0 "0xef priode0) thread "(TID: 0xee300320, sys_thread_t: 0xef4f0de0) prio = 0" clock handler "(TID: 0xee3001f8, sys_thread_t: 0xef5b0de0) prio = 11" main "(TID: 0xee3000a0, sys_threada35 * threada035) corrente .lang.Throwable.printStackTrace (Throwable.java) java.lang.ThreadGroup.uncaughtException (ThreadGroup.java) java.lang.ThreadGroup.uncaughtException (ThreadGroup.java) Monitoraggio Cache Dump: Registrato Monitor Dump: Finalizzami blocco coda: senza proprietario Blocco coda thread: senza proprietario Blocco classe: senza proprietario Blocco stack Java: senza proprietario Blocco riscrittura codice: senza proprietario Blocco heap: senza proprietario Ha blocco coda di finalizzazione: senza proprietario Blocco IO monitor: senza proprietario Monitoraggio morte bambino: senza proprietario Monitor eventi: senza proprietario Monitor I / O: senza proprietario Monitor allarme: senza proprietario In attesa di una notifica: "gestore dell'orologio" Blocco Sbrk: senza proprietario Monitoraggio blocco cache: senza proprietario Monitor registro: monitor proprietario: "principale" Allarme thread D: Interrompi (core dump)senza proprietario Monitoraggio allarme: senza proprietario In attesa di una notifica: "gestore dell'orologio" Blocco Sbrk: senza proprietario Blocco cache monitor: senza proprietario Registro di sistema monitor: proprietario monitor: "principale" Allarme thread Q: Interruzione (core dump)senza proprietario Monitoraggio allarme: senza proprietario In attesa di una notifica: "gestore dell'orologio" Blocco Sbrk: senza proprietario Blocco cache monitor: senza proprietario Registro di sistema monitor: proprietario monitor: "principale" Allarme thread Q: Interruzione (core dump)

Come puoi vedere nel codice sopra, la prima cosa di cui Mocha si lamenta è NullPointerExceptionperché era confuso riguardo alle informazioni simboliche. Quindi, il nostro obiettivo di rendere difficile la decompilazione del nostro codice è stato raggiunto.

Va notato che l'autore di Mocha, Hanpeter van Vliet, è anche l'autore di Crema! Mocha è distribuito gratuitamente. Una copia di valutazione di Crema è disponibile gratuitamente, ma la versione completa è un prodotto commerciale.

Quando si distribuiscono classi Java su Internet, è possibile proteggere il bytecode Java dal rischio di essere sottoposto a reverse engineering. Gli esempi di codice sopra mostrano come Mocha viene utilizzato per effettuare la decompilazione e come Crema può venire in soccorso prevenendo tale attività.

Qusay H. Mahmoud è uno studente laureato in informatica presso l'Università del New Brunswick, campus di Saint John, Canada.

Ulteriori informazioni su questo argomento

  • Nota del redattore Dopo la morte (di cancro) del signor van Vliet, i siti da lui creati per la distribuzione di Mocha e Crema hanno cessato di esistere.
  • Sito di distribuzione Mocha di Eric Smith //www.brouhaha.com/~eric/computers/mocha.html
  • Crema sul sito del CERN //java.cern.ch:80/CremaE1/DOC/quickstart.html

Questa storia, "Java Tip 22: Proteggi i tuoi bytecode da reverse engineering / decompilazione" è stata originariamente pubblicata da JavaWorld.