Decompilazione

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

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.

Principi di base[modifica | modifica wikitesto]

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.

Applicazioni[modifica | modifica wikitesto]

La decompilazione può essere utile nei seguenti casi:

  • Recupero di codici sorgenti perduti, avendo la necessità di modificare l'eseguibile;
  • Traduzione di codice scritto in linguaggi obsoleti non più supportati dagli strumenti di compilazione attuali;
  • Determinazione dell'esistenza di virus o software maligno nei programmi;
  • Analisi del codice per risalire agli algoritmi utilizzati.
  • Eliminazione delle protezioni degli shareware (cracking)

Storia[modifica | modifica wikitesto]

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.

Tecniche di decompilazione[modifica | modifica wikitesto]

Decompilazione per le macchine reali[modifica | modifica wikitesto]

I decompilatori per le macchine reali possono essere:

  • basati sul Pattern matching (dipendono da un particolare compilatore)
  • Indipendenti dal compilatore

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]

Decompilazione per le macchine virtuali[modifica | modifica wikitesto]

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.

Implicazioni legali[modifica | modifica wikitesto]

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:

  1. La società individua una stanza determinata come "stanza pulita". I computer sono installati nella stanza, senza collegamento ad Internet.
  2. Vengono incaricati alcuni soggetti dello staff per svolgere le attività lavorative svolte nella stanza pulita. Soltanto questi, ed altro personale specificamente autorizzato, sono legittimati ad entrare nella stanza. La stanza è utilizzata solo per decomporre ed analizzare il software. Nessuna informazione riguardante la decompilazione può essere trasmessa al di fuori della stanza (sia in forma cartacea che digitale).
  3. Una volta estratta l'informazione necessaria alla interoperabilità, lo staff realizzatore della decompilazione prepara un rapporto, nel quale vengono riportate soltanto le informazioni utili alla interoperabilità. Ciò può essere rimosso con sicurezza dalla stanza.
  4. Concluso il lavoro soddisfacente realizzato dalla società che realizza la decomposizione, vengono distrutti tutti i documenti e vengono formattate memorie e dischi dei computer. Soltanto a conclusione di tali operazioni la stanza pulita può tornare ad essere utilizzata senza le citate limitazioni.

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.

Protezione dalla decompilazione[modifica | modifica wikitesto]

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.

Note bibliografiche[modifica | modifica wikitesto]

  1. ^ SerHack, 2. L'ambiente di compilazione, su SerHack – Security Research, 13 aprile 2023. URL consultato il 13 aprile 2023.
  2. ^ Program Transformation Wiki / History Of Decompilation 1, su www.program-transformation.org. URL consultato il 13 aprile 2023.
  3. ^ Program Transformation Wiki / History Of Decompilation 2, su www.program-transformation.org. URL consultato il 13 aprile 2023.

Voci correlate[modifica | modifica wikitesto]

Controllo di autoritàLCCN (ENsh2006006021 · J9U (ENHE987007530511605171
  Portale Informatica: accedi alle voci di Wikipedia che trattano di Informatica