Domanda:
Cos'è un corretto disassemblatore?
perror
2013-03-25 22:02:44 UTC
view on stackexchange narkive permalink

Si suppone che un disassemblatore produca una rappresentazione leggibile dall'uomo del programma binario. Ma le tecniche più conosciute: sweep lineare e traversal ricorsivo (vedi questo commento per ulteriori informazioni) sono note per essere facilmente fuorviate da trucchi specifici. Una volta ingannati, produrranno codice che non verrà mai eseguito dal programma reale.

Si pensava che esistessero nuove tecniche e nuovi strumenti più preoccupati della correttezza (ad es. Jakstab, McVeto, ...), la nozione di correttezza dell'output non è mai stata adeguatamente definita, per quanto ne so, per i disassemblatori.

Quale sarebbe un buona definizione di disassemblatore, quale sarebbe una corretta definizione di correttezza per il suo output e come classificheresti i disassemblatori esistenti rispetto a questa definizione di correzione ?

Mi sembra che tu stia praticamente rispondendo alla domanda. Inoltre, la tua metrica di correttezza è probabilmente del tutto soggettiva. Dal momento che non puoi eseguire una decompilazione esatta, ciò che fai da lì dipende solo.
Sembra più un post sul blog che una domanda :)
Sì, per favore suddividilo in una domanda e risposta. * Puoi * rispondere alle tue domande, non è un problema. Inoltre, la domanda alla fine non corrisponde al titolo.
@Emmanuel: si prega di dividere la risposta dalla domanda, prima che venga chiusa.
fatto (e scusa per il disordine). Si spera che ci sarà una risposta migliore ...
Tuttavia, questo non è adatto per domande e risposte. È soggettivo. Votazioni per chiudere.
Direi che le due domande che stai ponendo ("Quale sarebbe una buona _definizione_ di un disassemblatore" e "quale sarebbe una corretta _definizione_ di _correttezza_ per il suo output"), potrebbero adattarsi meglio a [cs.se] (http: //cs.stackexchange.com/)?
@Jesper: Sì, hai riassunto le due domande che non sono riuscito a porre. Ma, poiché un disassemblatore è davvero una pietra angolare nel reverse engineering del software, perché non dovrebbe essere collocato qui?
@Emmanuel, La mia impressione della maggior parte delle persone RE (almeno dal punto di vista del software, attenzione alle opinioni colorate) è che sono auto-pensate e dannatamente brave in quello che fanno. Tuttavia, per questo motivo non sono ben fondati negli accademici e avrebbero problemi a fornire una definizione adeguata di correttezza. Qui vedo una maggiore enfasi sulla definizione e le parti di correttezza, e la parte di disassemblatore è solo un esempio di cosa definire la correttezza, ecc. Questo dovrebbe trasformarsi in un dibattito o essere visto come un tentativo di imbrattare le persone.
@Jesper, probabilmente hai ragione (anche se non era affatto il mio intento precedente). Teniamo chiusa questa domanda, allora. Sono d'accordo con questa decisione.
Ho dimenticato alcune negazioni. Ovviamente volevo scrivere qualcosa del tipo: "Questo ** non ** intende trasformarsi in un dibattito o dovrebbe essere visto come un tentativo di diffamare le persone".
Due risposte:
#1
+16
endeavor
2013-03-26 00:02:55 UTC
view on stackexchange narkive permalink

Sono l'autore di rdis e ho riflettuto un po 'su questo problema. Ti consiglio di dare un'occhiata al mio blog se hai altre domande dopo questo.

Ti rimando anche al post sul blog di Andrew Ruef Binary Analysis Isn't. Il punto chiave è che spesso tentiamo di comprendere i nostri programmi con il contesto dei compilatori, e non necessariamente come un continuum di istruzioni. Conia il termine "Analisi dell'output del compilatore", che è più o meno ciò che cerchiamo di ottenere con i nostri disassemblatori.

Termini e definizioni

Ricomincia con le tue definizioni di termini comuni smontare. Abbiamo dati, o stati, che possono essere composti da memoria, registri, tutte le cose buone. Abbiamo il codice, che è un'etichetta che applichiamo ai dati che ci aspettiamo che la macchina esegua (torneremo al codice). Abbiamo un programma, che è un algoritmo codificato nei dati che, quando interpretato da una macchina, fa sì che i dati vengano manipolati in un certo modo. Abbiamo una macchina che mappa uno stato in un altro. Abbiamo istruzioni che, per i nostri scopi, esistono in un singolo momento e sono composte da parti specifiche di dati che controllano il modo in cui la nostra macchina manipola i dati.

Spesso crediamo che il nostro obiettivo sia la trasformazione di codice, i dati che ci aspettiamo vengano eseguiti dalla macchina, in un disassemblaggio leggibile. Credo che lo facciamo a causa della nostra divisione dell'analisi del programma tra analisi del flusso di controllo (codice) e analisi del flusso di dati (dati). Nell'analisi del programma, il nostro codice è privo di stato e i nostri dati hanno uno stato. In realtà, il nostro codice è solo dati, tutto ha uno stato.

Ripristino del programma

Invece, il nostro obiettivo dovrebbe essere il ripristino del programma mediante l'osservazione o la previsione della macchina. In altre parole, non ci interessa trasformare i dati in un disassemblaggio leggibile, ma scoprire le istruzioni che verranno interpretate dalla nostra macchina.

Inoltre, la nostra rappresentazione del programma dovrebbe essere memorizzata separatamente dalla nostra rappresentazione senza stato dei dati, che di solito è il layout di memoria iniziale fornito dal nostro file eseguibile (ELF / PE / MACH-O /eccetera). In realtà, dovrebbe essere memorizzato in un grafico diretto. Quando vedo una rappresentazione lineare della memoria con più posizioni etichettate come istruzioni, scatto. Non lo sai ancora!

Credo che il passo successivo nello smontaggio coinvolga processi che fanno previsioni migliori sulle macchine consentendo cambiamenti di stato durante il processo di disassemblaggio. Credo che avremo sia il disassemblaggio emulato che il disassemblaggio astratto. Alcune persone lo stanno già facendo più o meno, anche se non sono sicuro che qualcuno lo stia facendo espressamente allo scopo di creare "ripristini del programma" utilizzabili e comprensibili.

Puoi vedere un esempio della differenza tra un disassemblaggio ricorsivo di un programma e un disassemblaggio emulato di un programma qui.

Cos'è un disassemblatore corretto?

Quindi, ora per rispondere alla tua domanda , "Che cos'è un corretto disassemblatore?" Credo che un corretto disassemblatore sia quello che definisce chiaramente il comportamento del suo processo di ripristino del programma e aderisce a questa definizione. Una volta che otteniamo disassemblatori che lo fanno, i migliori disassemblatori saranno quelli le cui definizioni prevedono meglio il comportamento delle macchine per le quali ripristinano i programmi.

#2
+1
perror
2013-03-25 23:29:05 UTC
view on stackexchange narkive permalink

Cos'è un disassemblatore?

Decompongo un disassemblatore in due parti, prima un decodificatore che accetta un codice esadecimale e genera un'istruzione di assemblaggio (possibilmente con la lunghezza di l'istruzione decodificata se il linguaggio assembly ha istruzioni di lunghezza variabile). E poi un algoritmo di disassemblaggio che utilizzerà il decodificatore per navigare nel codice eseguibile.

L'obiettivo generale di un disassemblatore, a mio modesto parere, sarebbe quello di recuperare tutto il possibili esecuzioni che possono essere costruite da un dato eseguibile e presentarlo in un formato conciso e leggibile dall'uomo.

Problemi di disassemblaggio

Ci sono molti problemi che un disassemblatore può incontrare durante lo smontaggio un programma binario. Uno dei più difficili sarebbe gestire il codice di auto-modifica . In effetti, fino ad ora non esiste una vera rappresentazione leggibile dall'uomo per il programma auto-modificante. Quindi, tutti i disassemblatori quando affrontano un codice che si auto-modifica falliscono miseramente nell'output di qualcosa di chiaramente comprensibile.

Il secondo problema che può fermare un disassemblatore è che di tanto in tanto il programma binario salta in un altro posto per eseguirne alcuni codice (chiamate di funzione, if-then-else, switch, ...). E, se la maggior parte di questi salti sono statici (l'indirizzo dove saltare è codificato staticamente nel codice), ci sono alcuni salti che dipendono dal contesto dell'esecuzione. Di solito chiamiamo questi salti salti dinamici (al contrario di salti statici ). Questi salti dinamici costringono il disassemblatore a tenere traccia non solo della sintassi delle istruzioni ma anche della loro semantica per non perdersi quando la incontra.

Infine un ultimo problema è che non tutti i programmi binari possono essere assunti per seguire una precisa ABI (Application Binary Interface), definendo una precisa interfaccia per le chiamate di funzione o un modo per gestire le strutture dati. In effetti, alcuni programmi binari sono realizzati a mano o con un compilatore modificato che tenterà di fuorviare i disassemblatori. Pertanto, il disassemblatore dovrà riconoscere una chiamata di funzione dalla sua semantica e non solo dalla sua sintassi.

Correttezza di un disassemblatore

Come abbiamo affermato in precedenza, l'obiettivo finale di un disassemblatore è ricostruire tutte le possibili tracce di esecuzione da un programma binario. Ovviamente, il più delle volte questo è estremamente difficile, quindi possiamo definire tre tipi di disassemblatori:

  • Disassemblatore esatto : ipoteticamente, dovrebbe emettere tutte le tracce corrette che può essere eseguito sul programma binario e solo queste tracce.
  • Disassemblatore eccessivamente approssimato : l'output di questo dovrebbe includere tutto il possibile tracce, possibilmente con alcune in più.
  • Disassemblatore poco approssimato : l'output di questo dovrebbe essere incluso nelle possibili tracce ma non fornire non fattibili.

Tecniche esistenti e dove classificarle

Per ora, le due tecniche più popolari sono sweep lineare e attraversamento ricorsivo (vedi qui per maggiori dettagli).

Entrambi sono abbastanza ampiamente utilizzati in natura da numerosi reverse-engineer. Ma, in realtà, nessuna di queste tecniche è né esatta, né eccessiva, né sotto-approssimativa. Entrambi producono qualcosa che non è niente di ciò che abbiamo appena visto prima (a volte inventeranno un percorso che non sarà mai raggiunto, ea volte se ne dimenticheranno un altro).

Esistono tecniche più avanzate con maggiore preoccupazione per la correttezza (ad esempio Jakstab, McVeto, McVeto sul codice auto-modificante, ... ), ma la ricerca del recupero esatto è sicuramente fuori portata.

Quindi, scegliere tra sotto e iperapprossimazione dipende da quale sarà l'utilizzo dell'output del disassembler.



Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...