D: Se crittografo i miei file .class e utilizzo un classloader personalizzato per caricarli e decrittografarli al volo, questo impedirà la decompilazione?
R: Il problema di impedire la decompilazione del codice byte Java è vecchio quasi quanto il linguaggio stesso. Nonostante una gamma di strumenti di offuscamento disponibili sul mercato, i programmatori Java alle prime armi continuano a pensare a modi nuovi e intelligenti per proteggere la loro proprietà intellettuale. In questa puntata di domande e risposte su Java, sfoglio alcuni miti su un'idea che viene spesso rimossa nei forum di discussione.
L'estrema facilità con cui i file Java .class possono essere ricostruiti in sorgenti Java che assomigliano molto agli originali ha molto a che fare con gli obiettivi di progettazione del codice byte Java e con i compromessi. Tra le altre cose, il codice byte Java è stato progettato per compattezza, indipendenza dalla piattaforma, mobilità di rete e facilità di analisi da parte di interpreti di codice byte e compilatori dinamici JIT (just-in-time) / HotSpot. Probabilmente, i file .class compilati esprimono l'intento del programmatore in modo così chiaro che potrebbero essere più facili da analizzare rispetto al codice sorgente originale.
Si possono fare diverse cose, se non per impedire completamente la decompilazione, almeno per renderla più difficile. Ad esempio, come passaggio di post-compilazione è possibile massaggiare i dati .class per rendere il codice byte più difficile da leggere quando decompilato o più difficile da decompilare in codice Java valido (o entrambi). Tecniche come l'esecuzione di un sovraccarico estremo del nome del metodo funzionano bene per il primo e la manipolazione del flusso di controllo per creare strutture di controllo che non è possibile rappresentare tramite la sintassi Java funziona bene per il secondo. Gli offuscatori commerciali di maggior successo utilizzano un mix di queste e altre tecniche.
Sfortunatamente, entrambi gli approcci devono effettivamente modificare il codice che verrà eseguito dalla JVM e molti utenti temono (giustamente) che questa trasformazione possa aggiungere nuovi bug alle loro applicazioni. Inoltre, la ridenominazione di metodi e campi può causare l'interruzione del funzionamento delle chiamate di riflessione. La modifica dei nomi delle classi e dei pacchetti effettivi può interrompere diverse altre API Java (JNDI (Java Naming and Directory Interface), provider di URL, ecc.). Oltre ai nomi alterati, se l'associazione tra gli offset del codice byte di classe e i numeri di riga di origine viene alterata, il ripristino delle tracce dello stack di eccezioni originali potrebbe diventare difficile.
Poi c'è la possibilità di offuscare il codice sorgente Java originale. Ma fondamentalmente questo causa una serie simile di problemi. Crittografare, non offuscare?
Forse quanto sopra ti ha fatto pensare: "Bene, e se invece di manipolare il codice a byte crittografassi tutte le mie classi dopo la compilazione e le decifrassi al volo all'interno della JVM (cosa che può essere eseguita con un caricatore di classi personalizzato)? Allora la JVM esegue il mio codice byte originale e tuttavia non c'è nulla da decompilare o da decodificare, giusto? "
Sfortunatamente, sbaglieresti, sia nel pensare di essere stato il primo a proporre questa idea sia nel pensare che funzioni davvero. E il motivo non ha nulla a che fare con la forza del tuo schema di crittografia.