Progettare un Decompilatore - Strumenti di Sviluppo

 

Indietro: introduzione

Strumenti di Sviluppo (compilatori, assemblatori, linker)

Gli strumenti di sviluppo sono programmi che convertono un programma da un livello di astrazione incentrato sulle persone, ad un livello di astrazione centrato sulla macchina.

Per molti programmatori, l'interfaccia principale con la macchina è l'ambiente di compilazione.

L'ambiente di compilazione prende come input uno o più file in un linguaggio di alto livello come il C o Java, più una serie di file di supporto come file risorse e librerie, e converte il tutto in un file eseguibile per uno specifico ambiente di esecuzione, per esempio Linux o Windows (non consideriamo rappresentazioni in linguaggi a livello ancora più elevato come linguaggi di modellazione UML o linguaggi per domini specifici, sebbene anche loro possano essere oggetto di decompilazione).

La compilazione avviene tramine un certo numero di passi che coinvolgono strumenti specifici:

  • Ogni linguaggio ad alto livello viene compilato in un file in formato assembler da un compilatore per quel linguaggio specifico.
  • Ogni file in formato assembler, che sia stato creato da un compilatore o direttamente dal programmatore, è convertito in un file oggetto rilocabile da un programma assemblatore. L'assemblatore non è interessato a quale fosse il linguaggio ad alto livello originale. L'unica cosa di cui si preoccupa è di quale processore eseguirà il codice, in modo da convertire le linee testo in sequenze di byte che possano essere eseguite dal processore. Questo è il primo stadio in cui vengono perse delle informazioni, poichè l'assemblatore può ignorare molte delle informazioni che possono essere utili al programmatore, come variabili locali e i loro tipi di dati.
  • Ogni file oggetto rilocabile è combinato insieme ad un numero di librerie che forniscono il supporto all'ambiente di esecuzione da un programma chiamato "linker". Al linker può non importare quale processore andrà a eseguire il programma. In teoria tutto quello che interessa al linker è come formattare il programma in modo che possa essere caricato in memoria ed eseguito dal sistema operativo. Perciò il linker può decidere di rimuovere informazioni dal file binario generato quando pensa che tali informazioni non sono necessarie all'esecuzione del programma.

Come si vede, ad ogni stadio si perdono delle informazioni che erano vitali al programmatore quando ha scritto il programma, ma che non sono necessarie quando il programma viene eseguito dalla macchina.

Ancora peggio, lo stesso programmatore può istruire gli strumenti di sviluppo ad aggiungere o rimuovere informazioni utili. Quando si usa un compilatore, l'utente può decidere di:

  • generare informazioni aggiuntive per migliorare la debuggabilità del codice (l'opzione -g nei compilatori Unix è di solito usata per questo scopo).
  • generare codice che è più difficile da capire per gli umani, ma è eseguito più efficientemente dai processori; cioè generare codice ottimizzato tramite la specifica del livello di ottimizzazione tramite le opzioni -O1, -O2 ecc. I compilatori ottimizzanti, tramite un numero di trasformazioni che eseguono sulle istruzioni generate, rendono il codice finale meno leggibile anche quando le informazioni di debugging sono presenti nel file finale, ed il file sorgente rimane disponibile per l'ispezione tramite un debugger. Il debugging di codice ottimizzato è un'area di ricerca di interesse a se, e non sarà considerata in questo documento, sebbene molte delle tecniche possono essere applicate a un "debugger de-ottimizzatore".

Anche quando si usa uno degli altri strumenti l'utente può influire sull'esecuzione di un decompilatore, per esempio dicendo al linker di rimuovere tutte le informazioni simboliche dal file binario.

Da questo punto in avanti, ogni strumento che possiamo usare per analizzare il programma può essere considerato uno strumento di ingenieria inversa.


Avanti: Strumenti di Ingenieria Inversa

 
Traduzioni

  Française
by Mary Orban

  Inglese