Con linguaggi di macchine virtuali basati su bytecode come Java, VB.NET, C #, ActionScript 3.0, ecc., senti a volte quanto sia facile scaricare un decompilatore da Internet, eseguire il bytecode tramite tempo e, spesso, escogitare qualcosa non troppo lontano dal codice sorgente originale in pochi secondi. Presumibilmente questo tipo di linguaggio è particolarmente vulnerabile a questo.
Recentemente ho iniziato a chiedermi perché non si ascolti di più riguardo al codice binario nativo, quando almeno conosci la lingua in cui è stato scritto in origine (e quindi quale lingua cercare di decompilare). Per molto tempo ho pensato che fosse solo perché il linguaggio macchina nativo è molto più folle e complesso del tipico codice byte.
Ma che aspetto ha il bytecode? Assomiglia a questo:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
E come appare il codice macchina nativo (in hex)? Ovviamente, assomiglia a questo:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
E le istruzioni provengono da uno stato d'animo in qualche modo simile:
1000: mov EAX, 20
1001: mov EBX, loc1
1002: mul EAX, EBX
1003: push ECX
Quindi, dato il linguaggio per provare a decompilare un binario nativo in, ad esempio in C ++, cosa c'è di così difficile? Le uniche due idee che vengono subito in mente sono 1) è davvero molto più intricata di bytecode, o 2) qualcosa sul fatto che i sistemi operativi tendono a impaginare i programmi e spargere i loro pezzi causa troppi problemi. Se una di queste possibilità è corretta, per favore spiega. Ma in entrambi i casi, perché non ne hai mai sentito parlare sostanzialmente?
Nota
Sto per accettare una delle risposte, ma prima voglio menzionare qualcosa. Quasi tutti si riferiscono al fatto che pezzi diversi del codice sorgente originale potrebbero essere associati allo stesso codice macchina; i nomi delle variabili locali sono persi, non si sa quale tipo di loop è stato originariamente utilizzato, ecc.
Tuttavia esempi come i due che sono stati appena menzionati sono una specie di banale nei miei occhi. Alcune delle risposte tendono a dire che la differenza tra il codice macchina e la fonte originale è drasticamente molto più di qualcosa di così banale.
Però, per esempio, quando si tratta di cose come nomi di variabili locali e tipi di loop, bytecode perde anche queste informazioni (almeno per ActionScript 3.0). Ho rimosso quella roba da un decompilatore in precedenza, e non mi importava davvero se una variabile fosse chiamata strMyLocalString:String
o loc1
. Potrei ancora guardare in quel piccolo ambito locale e vedere come viene usato senza troppi problemi. E un ciclo for
è praticamente la stessa cosa di un ciclo while
, se ci pensi. Inoltre, anche quando eseguivo la sorgente tramite irrFuscator (che, a differenza di secureSWF, non fa molto più che randomizzare nomi di variabili e funzioni membro), sembrava ancora che si potesse iniziare ad isolare determinate variabili e funzioni in classi più piccole, come vengono utilizzati, assegna loro i tuoi nomi e lavora da lì.
Affinché questo sia un grosso problema, il codice della macchina dovrebbe perdere molte più informazioni di quello, e alcune delle risposte sono utili.