Decompilazione
attività di ingegneria inversa Da Wikipedia, l'enciclopedia libera
attività di ingegneria inversa Da Wikipedia, l'enciclopedia libera
La decompilazione è l'attività di ingegneria inversa mediante la quale viene ricostruito il codice sorgente a partire da un file eseguibile in linguaggio macchina. Il decompilatore è un programma che tenta, attraverso alcune euristiche, di ricavare e ricostruire il codice sorgente.
Semplificando, la compilazione consiste nella traduzione del codice sorgente ad alto livello in codice oggetto a basso livello. Per mezzo di questo processo, oltre alla traduzione letterale delle istruzioni nelle equivalenti istruzioni per i processori, vengono rimpiazzate le funzioni contenute nelle librerie e rimossi i commenti inseriti dallo sviluppatore. Inoltre, il file eseguibile può essere composto da più moduli, aggregati nella fase di linking. La maggior parte delle volte, i compilatori moderni utilizzano alcune tecniche per l'ottimizzazione volte a rendere il programma più veloce e meno impattante in termini di risorse utilizzate.
L'operazione inversa, non potendo ad esempio conoscere a priori le librerie utilizzate, produce una rappresentazione attendibile ma meno leggibile dell'originale, dal momento che non sono presenti i riferimenti di alto livello nel codice binario. Informazioni molto importanti come i nomi delle variabili, i commenti originali e le strutture dati sono irrimediabilmente persi. Il linguaggio macchina, essendo a basso livello, non consente allo sviluppatore di esprimere tutte le caratteristiche espresse invece tramite il codice sorgente. Il motivo risulta dato dal fatto che ogni informazione non immediatamente necessaria alla CPU viene rimossa.[1]
Il processo di decompilazione non riguarda soltanto i file eseguibili di programma (ad esempio i file .EXE basati sul formato PE) ma qualunque file prodotto da un processo di compilazione (ovvero di traduzione esplicita), come ad esempio le animazioni in Adobe Flash. Gran parte delle tecniche illustrate e citate nella letteratura sono basate su metodi approssimativi chiamate euristiche.
La decompilazione può essere utile nei seguenti casi:
La nascita dei decompilatori è contemporanea a quella dei compilatori, ma il primo vero decompilatore fu scritto da Joel Donnelly nel 1960 al Naval Electronic Labs per decompilare il codice macchina dei programmi NELIAC su un computer Remington Rand Univac M-460 Countess, progetto visionato dal Prof. Maurice Halstead che lavorò sulla decompilazione tra gli anni '60 e '70, e pubblicò tecniche che stanno alla base dei compilatori odierni.[2]
Durante gli anni '60 la decompilazione fu usata nel processo di conversione dei programmi dalla seconda alla terza generazione; in questo modo i programmi per le macchine di terza generazione furono riscritti in maniera automatica.
Tra gli anni '70 e '80, la decompilazione fu adoperata per la portabilità dei software, documentazione, debugging, rielaborazione di codici sorgenti perduti, e modifica di file eseguibili già esistenti.[3]
A partire dagli anni '90 in poi tale tecnica è diventata uno strumento di reverse engineering capace di ausiliare gli utenti nel controllo di programmi per verificare la presenza di codice maligno, verificare che il compilatore generi un codice corretto, tradurre programmi binari da una macchina a un'altra e capire l'implementazione di una specifica funzione di libreria.
I decompilatori per le macchine reali possono essere:
Uno dei maggiori problemi nella decompilazione del codice sorgente è che non tutti i compilatori generano codice allo stesso modo perché ognuno effettua le proprie ottimizzazioni. Sul piano teorico, bisognerebbe attuare procedure di compilazione ben precise per ciascun compilatore, in modo da aver maggiori possibilità di capire il codice. Questo però è tecnicamente e praticamente complesso dal momento che neanche un compilatore nel corso della vita del software mantiene le stesse tecniche di ottimizzazione. L'operazione di traduzione "inversa" risulta impossibile per via di alcune impostazioni che consentono agli sviluppatori di applicare specifiche tecniche di ottimizzazione rispetto ad altre.
Tuttavia, si è notato come, reperendo diverse informazioni inserite all'interno del binario (debugging ma anche strutture dati del sistema operativo), fosse possibile rappresentare gran parte del funzionamento del software ad alto livello anche con il pattern matching. Seppur questa rappresentazione possa essere in qualche modo approssimativa, risulta di grande aiuto all'analista del software. Le tecniche di decompilazione da codice macchina tramite pattern matching sono oggetto ancora oggi di studio da parte di molte università informatiche.
Tra i decompilatori commerciali che utilizzano il pattern matching, troviamo il caso di IDA pro che basa l'operazione di decompilazione interrogando una base di dati che raggruppa diverse "firme" (note come binary signature) di svariati software open source e non. Sapendo a priori infatti che una certa sequenza di informazioni corrisponde ad una particolare libreria, è possibile agevolare il processo di decompilazione.
I ricercatori in questo campo hanno così lasciato alle spalle i metodi classici di decompilazione per intraprendere strade diverse (metodi statistici) i cui risultati non sono però stati resi noti.[senza fonte]
Esistono grandi differenze tra il codice macchina delle applicazioni per macchine reali (ad esempio Assembler) e il codice macchina delle applicazioni per macchine virtuali (ad esempio Bytecode). In particolare, tali differenze si riferiscono alle informazioni relative al codice sorgente che vengono conservate dentro il codice macchina.
Tra tutte le macchine virtuali la più famosa è la Java Virtual Machine il cui “codice macchina” si chiama bytecode. Il bytecode contiene molte più informazioni rispetto al codice macchina.
Tale operazione viene classificata dalla legge come una forma di "copia".
Normalmente numerosi software sono sotto copyright da parte degli autori. Questo significa che copiare la stessa idea su un altro programma è proibito dalla legge.
La decompilazione è lecita solamente in alcuni casi precisi descritti nella legge 633/1941 diritto d'autore italiano all'articolo 64 quater. In sintesi è possibile decompilare un eseguibile solamente se ciò è necessario per avere le informazioni utili al conseguimento dell'interoperabilità con un programma creato autonomamente, a patto che la decompilazione sia eseguita da chi possiede la licenza d'uso dell'eseguibile da decompilare e che le informazioni ricercate non siano già facilmente reperibili. Inoltre la decompilazione deve essere limitata alle parti indispensabili per l'interoperabilità. Le informazioni ottenute non possono essere utilizzate per fini diversi dall'interoperabilità né comunicate a terzi per altri fini.
La domanda circa l'utilizzazione del diritto di decompilazione prende sostanza nel momento in cui il codice sorgente originale è mantenuto segreto: per esempio, potrebbe essere necessario decompilare un sistema operativo per comprenderne il funzionamento, per poter scrivere un programma che funzioni su quella precisa piattaforma; o procedere alla decompilazione di un programma di un rivale commerciale affinché sia possibile comprendere come funzioni per poter creare un software che generi formati file in uscita compatibili.
Siamo di fronte ad un vero e proprio conflitto d'interessi. Da un lato, si ritiene importante, nell'interesse pubblico, la interoperabilità dei programmi tra loro. Dall'altro lato, la segretezza del codice sorgente è una prassi molto comune di mercato: è una forma di protezione dei propri programmi da modifiche illegittime, e di raccolta d'informazioni importanti dei propri concorrenti di mercato. La Direttiva, quindi, è stata disegnata per prevenire l'utilizzazione del diritto di decompilazione affinché non sia messa in pericolo la protezione conferita dalla segretezza.
Le condizioni e le limitazioni previste per il diritto di decompilazione sono tassative. La stesura risulta spesso poco chiara, ragion per cui c'è incertezza sull'interpretazione che forniranno i tribunali in questo caso "limite". Il diritto di decompilazione dovrà, in ogni caso, essere utilizzato con estrema cautela. Ci vuole un'adeguata consulenza giuridica per non cadere in errori.
Le condizioni più importanti da rispettare si traducono nel fatto che le informazioni ottenute attraverso l'utilizzazione del diritto di decompilazione potranno essere utilizzate solo con obiettivo di garantire la interoperabilità fra i programmi e non potranno essere cedute a terzi, tranne quando sia necessario al suddetto scopo. In pratica, l'unico modo per essere sicuri di ciò, è attraverso l'uso di una "stanza pulita". La procedura è la seguente:
Questo procedimento non può essere realizzato alla luce del sole. Nella pratica soltanto un'azienda con risorse notevoli sarà in grado di ricavare profitto dal diritto di decompilazione.
Proteggere totalmente il codice dalla decompilazione è un obiettivo difficilmente raggiungibile. Si possono tuttavia adottare degli espedienti opportuni, per limitare l'operazione di decompilazione da parte di utenti meno esperti o strumenti diffusi che decompilano automaticamente. Questi espedienti sono spesso offuscamenti, dal momento che riducono il grado di leggibilità delle istruzioni e dati all'interno di un decompilatore. Questo risulta abbastanza semplice per un motivo banale: la maggior parte dei compilatori basa il proprio funzionamento su alcune euristiche che talvolta funzionano, altre volte non riescono a restituire il dato sperato.
Prendiamo in esame Java, che a differenza degli altri linguaggi di programmazione ha come scopo primordiale funzionare su qualunque genere di hardware dotato di un'implementazione della Virtual Machine. In pratica quando compiliamo un listato Java, il .class che ricaviamo non è codificato nel linguaggio macchina di uno specifico processore, ma è "tradotto" in una sorta di "macro-linguaggio". Dunque a eseguire il file .class in questione non sarà il processore ma un software che interpreta i bytecode ed esegue le istruzioni codificate.
Analogamente a quanto avviene per qualsiasi altro linguaggio di programmazione, il codice generato dopo la compilazione può sempre essere disassemblato. Però, i file .class creati dal compilatore Java, e destinati ad una macchina virtuale, conservano un numero di informazioni relative al codice sorgente assai maggiore rispetto a un .exe tradizionale. Questo rende più facile la realizzazione di software che consentono un processo di reverse engineering molto accurato, che va ben oltre il processo di disassemblaggio. Infatti, esistono in rete diversi programmi sia freeware che commerciali, che consentono la decompilazione vera e propria dei file .class. Questi software sono capaci di ricreare un codice sorgente che differisce veramente di poco da quello originario.
Si ricorre generalmente all'offuscamento del codice. Tale tecnica consiste nel complicare il codice in fase di programmazione rendendone più difficile la comprensione degli algoritmi. Un esempio di offuscamento può essere ad esempio trasformare una semplice operazione come
c = a * b;
in
c = 0 while(b-- > 0) c = c + a;
Questa tecnica può almeno scoraggiare i cracker alle prime armi quando si troveranno a cercare di comprendere il listato. Un altro tipico esempio è la modifica dei nomi delle variabili e dei metodi con nomi senza senso.
Un'altra tecnica ancora adoperata per proteggersi dalla decompilazione è quella di modificare il bytecode dei class file in maniera tale da non comprometterne la funzionalità ma generare degli errori nei programmi di decompilazione. Questo per sfruttare i bachi dei decompilatori.
Ammettendo che questi ultimi svolgono un compito molto complesso, partiamo dal presupposto che presentino sempre dei bug e si cerca di individuarli e sfruttarli. Se tale decompilatore è un eseguibile, realizzato ad esempio in C o in altro linguaggio compilato, si può sfruttare uno dei principali punti deboli di tali linguaggi: gli overflow. Una prima idea che viene in mente, analizzando il linguaggio Java, è che quest'ultimo non pone limiti alla lunghezza dei nomi delle variabili. Inseriamo una variabile jolly, all'interno della classe da proteggere con un nome molto lungo. Inseriamo inoltre, all'interno della classe da proteggere, un metodo inutile che dichiara 514 variabili locali. Questo secondo espediente provoca un incremento del codice di poco superiore al Kbyte.
Esistono altre tecniche per offuscare i codici come ad esempio la criptazione delle classi. Tuttavia non è necessario ricorrere alla modifica manuale del bytecode poiché esistono programmi creati esclusivamente a questo scopo.
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.