
Plugin per la visualizzazione della copertura del codice binario per Ghidra
Dragon Dance è un plugin per Ghidra per visualizzare e manipolare i dati di copertura del codice binario. I dati di copertura possono essere importati da più fonti di copertura. Per ora il plugin supporta gli strumenti di strumentazione binaria Dynamorio e Intel Pin. Dynamorio ha un proprio modulo di raccolta della copertura chiamato "drcov". Intel Pin non fornisce un modulo di raccolta della copertura integrato. Per gestire la mancanza del modulo, ho dovuto scrivere il mio modulo di raccolta della copertura per Intel Pin. Quindi ho scritto un modulo di raccolta della copertura per Intel Pin chiamato ddph (Dragon Dance Pin Helper). Quindi puoi usarlo. Puoi vedere il codice sorgente di ddph da questo link. Se sei pigro per compilare da solo, puoi usare i binari compilati che ho fornito per Windows, macOS e Linux.

Dragon Dance può importare e utilizzare più dati di copertura nella stessa sessione. (Supporta anche multi-sessione ma per ora non è utilizzabile tramite GUI). E puoi passare da uno all'altro o applicare rapidamente operazioni di intersezione, differenza, distinzione o somma tra di loro.
Dragon Dance ti permette di visualizzare l'intensità delle istruzioni eseguite. Così puoi avere un'indicazione su quali istruzioni vengono eseguite con quale frequenza. Puoi anche visualizzare la copertura sulla finestra del grafico delle funzioni.

Dragon Dance supporta anche un proprio sistema di scripting.

Ti offre un modo flessibile di giocare con i dati di copertura. Puoi eseguire operazioni di caricamento, eliminazione, visualizzazione, intersezione, differenza, distinzione e somma su di essi. La sezione seguente conterrà il sistema di scripting e l'API. Premi i tasti Alt + Invio per eseguire lo script.
Le funzioni integrate sono implementazioni delle operazioni interne di copertura per fornire un'interfaccia al sistema di scripting. Una funzione integrata può restituire una variabile oggetto di copertura o nulla. Le funzioni integrate possono avere alias. Accettano un argomento integrato come parametro. I parametri possono essere di lunghezza variabile.
L'argomento integrato è un riferimento per contenere diversi tipi di valore. Gli argomenti integrati vengono passati in ordine da sinistra a destra. L'argomento integrato può contenere i seguenti tipi di valore:
Le variabili sono responsabili di contenere solo l'oggetto di copertura. Possono essere caricate dalle funzioni integrate. Possono essere passate come parametro (Argomento integrato) alle funzioni integrate.
Esistono due tipi di oggetto di copertura.
Oggetto di copertura fisico e Oggetto di copertura logico
Oggetto di copertura fisico punta a un oggetto di copertura caricato direttamente dal file di copertura. Sono visibili nella tabella di copertura che si trova sulla GUI. Quindi puoi interagire con essi tramite le operazioni della GUI.
Oggetto di copertura logico punta a un oggetto di copertura che è stato elaborato in una funzione integrata e restituito da essa come risultato. Non sono visibili sulla GUI ma possono vivere in una variabile finché non vengono distrutti.
L'oggetto di copertura viene mantenuto automaticamente dall'oggetto Variabile per entrambi i tipi di oggetto di copertura. Per esempio;``` cov1 = load("firstcoverage.out") cov2 = load("secondcov.out")
cov1 = diff(cov1,cov2)
In questo esempio cov1 e cov2 sono variabili. Ed entrambe le variabili hanno un oggetto di copertura fisico. Il built-in diff prende entrambe le variabili e imposta il valore di ritorno sulla variabile cov1. Tale operazione di sovrascrittura imposterà l'oggetto di copertura risultante sulla variabile, ma non elimina l'oggetto di copertura perché si tratta di un oggetto di copertura fisico. Quei dati di copertura rimarranno nella sessione e anche nella tabella GUI. Pensiamo all'esempio precedente in questo modo;```
cov1 = load("first.out")
cov2 = load("second.out")
cov3 = load("third.out")
rvar = sum(cov1,cov2,cov3)
rvar = diff(rvar, cov2)
In questo esempio, tre variabili di copertura fisica entrano in un'operazione di somma e l'operazione di somma restituisce un oggetto di copertura come risultato logico. Quindi l'operazione diff prende una variabile logica e una fisica e sovrascrive la variabile chiamata rvar.
In questo caso, il risultato sarà impostato su rvar e il suo valore di copertura precedente viene immediatamente distrutto. Poiché questo era un oggetto logico, dovrebbe essere eliminato per evitare perdite di oggetti. Se vuoi distruggere una variabile che contiene un oggetto di copertura fisica, devi chiamare il built-in discard. Tutti i built-in saranno dettagliati di seguito.
Puoi scrivere script complessi usando chiamate nidificate ai built-in, puoi scrivere qualcosa del tipo:
cres = diff(intersect(a, load("another.log"), c, d), sum(e,f) )
Non devi scrivere la logica riga per riga.
Le seguenti documentazioni delle API e i loro comportamenti potrebbero cambiare fino al raggiungimento della versione finale.
clear()
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Nessuno |
| Conteggio minimo parametri | 0 |
| Conteggio massimo parametri | 0 |
| Descrizione | Questo built-in cancella la copertura visualizzata e imposta la copertura attiva a null. |
| Alias | Nessuno |
cwd( Stringa : directoryDiLavoro )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Nessuno |
| Conteggio minimo parametri | 1 |
| Conteggio massimo parametri | 1 |
| Descrizione | Imposta la directory di lavoro corrente con il percorso dato. Tutte le chiamate di importazione senza percorso assoluto dopo cwd cercheranno i file di copertura nella directory di lavoro attiva. |
| Alias | Nessuno |
diff( Variabile : var1, var2, ..... varN )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Variabile |
| Conteggio minimo parametri | 2 |
| Conteggio massimo parametri | Illimitato |
| Descrizione | Applica l'operazione di differenza alle variabili di lunghezza variabile fornite. E restituisce la variabile di copertura risultante. |
| Alias | Nessuno |
discard( Variabile : var1, var2, ..... varN )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Nessuno |
| Conteggio minimo parametri | 1 |
| Conteggio massimo parametri | Illimitato |
| Descrizione | Distrugge le variabili, siano esse fisiche o logiche. Distruggerà prima l'oggetto di copertura e poi deregistrerà il nome della variabile dall'elenco delle variabili. Dopo questa chiamata, tutte le variabili fornite diventano non definite. |
| Alias | del |
distinct( Variabile : var1, var2, ..... varN )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Variabile |
| Conteggio minimo parametri | 2 |
| Conteggio massimo parametri | Illimitato |
| Descrizione | Applica l'operazione di distinct (xor) alle variabili di lunghezza variabile fornite. E restituisce la variabile di copertura risultante. |
| Alias | xor |
goto( Intero : offset )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Nessuno |
| Conteggio minimo parametri | 1 |
| Conteggio massimo parametri | 1 |
| Descrizione | Individua la selezione dell'indirizzo corrente in base all'offset dato. Il valore dell'indirizzo reale viene calcolato aggiungendo il valore di offset al valore base dell'immagine. |
| Alias | Nessuno |
import( Stringa : percorsoFileONomeCopertura )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Variabile |
| Conteggio minimo parametri | 1 |
| Conteggio massimo parametri | 1 |
| Descrizione | Importa i dati di copertura dal file di copertura fisico. Accetta un percorso relativo o assoluto. Oppure un nome di copertura caricato fisicamente in precedenza. Se il percorso fornito è assoluto, import carica direttamente dal percorso. Altrimenti cerca nella directory di lavoro corrente per caricare. In entrambi i casi, import controlla se i dati di copertura sono già stati caricati utilizzando il percorso. Se già caricati, restituisce la variabile di copertura in cache. Oppure, se il valore fornito è un nome di una copertura fisica, cerca la mappa di copertura dalla sua sessione e restituisce l'oggetto di copertura se esiste. |
| Alias | get, load |
intersect( Variabile : var1, var2, ..... varN )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Variabile |
| Conteggio minimo parametri | 2 |
| Conteggio massimo parametri | Illimitato |
| Descrizione | Applica l'operazione di intersezione alle variabili di lunghezza variabile fornite. E restituisce la variabile di copertura risultante. |
| Alias | and |
show( Variabile : var )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Nessuno |
| Conteggio minimo parametri | 1 |
| Conteggio massimo parametri | 1 |
| Descrizione | Visualizza la variabile di copertura fornita. Se esiste un oggetto di copertura visualizzato attivamente e questo è logico, la funzione distrugge immediatamente l'oggetto di copertura precedente e mostra quello fornito. |
| Alias | Nessuno |
sum( Variabile : var1, var2, ..... varN )
| Proprietà | Descrizione |
|---|---|
| Valore di ritorno | Variabile |
| Conteggio minimo parametri | 1 |
| Conteggio massimo parametri | Illimitato |
| Descrizione | Applica l'operazione di somma alle variabili di lunghezza variabile fornite. E restituisce la variabile di copertura risultante. |
| Alias | or, union |
Fix Up
Dragon Dance può provare a correggere una situazione mal analizzata in Ghidra durante l'importazione del file di dati di copertura. Su alcuni binari, Ghidra non decompila le istruzioni di una funzione a causa di una generazione di codice inaspettata da parte del compilatore. Dragon Dance controlla l'immagine caricata e l'integrità dei dati di copertura. Se sono validi l'uno per l'altro e l'indirizzo appartiene a una sezione eseguibile ma manca la decompilazione delle istruzioni, il plugin chiede di correggere. Quindi cerca di correggere decompiando la sezione grezza.

Nelle versioni future del plugin, potrebbe contenere più fix up o soluzioni alternative per l'immagine.
L'installazione è abbastanza semplice.
Avvia Ghidra.
Fai clic sul menu "File" e poi seleziona "Install Extensions.."
Fai clic sull'icona del segno più verde in alto a destra della finestra
Seleziona il pacchetto zip del plugin e conferma con Ok.
Seleziona dragondance dall'elenco
Fai clic su Ok e riavvia Ghidra
Durante il primo caricamento di un binario in Ghidra dopo l'installazione del plugin, Ghidra dovrebbe chiederti se vuoi configurare il plugin appena installato o meno.
Se fai clic sul pulsante Sì, il plugin DragonDance dovrebbe apparire immediatamente.
Se fai clic sul pulsante No, devi attivarlo manualmente tu stesso.
Per attivarlo manualmente,
Fai clic sul menu "File" e poi seleziona Configura dalla finestra Disassembly (CodeBrowser)
Fai clic sulla piccola icona a spina nell'angolo in alto a destra della finestra di Configurazione dello strumento.
Trova l'elemento DragonDance dall'elenco dei plugin e abilita la sua casella di spunta, quindi fai clic su Ok
La finestra di Dragon Dance dovrebbe apparire.
Dopo l'attivazione, dovresti essere in grado di vedere l'elemento Dragon Dance nel menu Window.
Come descritto in precedenza, Dragon Dance può importare dati di copertura da Dynamorio e Intel Pin (per ora). In realtà questi sono strumenti generici di strumentazione binaria. Devi usare un modulo appropriato con essi per raccogliere i dati di copertura. Dynamorio ha il proprio modulo di copertura chiamato drcov. Puoi usare quel modulo integrato per raccogliere la copertura.
Utilizzo di Dynamorio
Puoi raccogliere dati di copertura da Dynamorio usando il seguente comando:
drrun -t drcov -logdir [PERCORSO_DIRECTORY_OUTPUT_COPERTURA] -- [PERCORSO_ESEGUIBILE_DA_ESAMINARE] [ARGOMENTI_ESEGUIBILE]
L'output verrà inserito nella directory fornita con il formato drcov.[NOME_ESEGUIBILE].[ID].proc.log.
Utilizzo di Intel Pin
Come accennato in precedenza, Intel Pin non fornisce alcun modulo di raccolta della copertura integrato. Devi usare un modulo Pin personalizzato. Fortunatamente, ne ho creato uno mio per raccogliere la copertura da Pin. Questo ci offre alcuni vantaggi. Posso estenderlo quando necessario o aggiungere funzionalità extra e opzioni.
Anche se le versioni successive potrebbero funzionare, solo Intel PIN 3.7 è supportato. Questi non sono immediatamente disponibili sulla pagina Intel PIN, quindi ecco i link di download diretto:
Puoi accedere al codice sorgente di ddph qui. Condividerò i binari per Windows, macOS e Linux. Oppure puoi creare il tuo binario usando il suo script shell di build.
Per raccogliere dati di copertura da Intel Pin usa il seguente comando:
pin -t ddph.[so,dylib,dll] [opzioni ddph] -- [PERCORSO_ESEGUIBILE_DA_ESAMINARE] [ARGOMENTI_ESEGUIBILE]
ddph ha alcune opzioni per la raccolta.
-o: con questa opzione puoi specificare il nome del file di output della copertura. (default: ddph.out)
-l: puoi specificare il nome del file di log delle operazioni. se passi "no" a questa opzione, ddph non eseguirà l'operazione di log. (default: ddph.log)
-p: livello di dettaglio della cattura. questa opzione può essere reduced o high. il livello high cattura tutte le istruzioni una per una e crea blocchi di esecuzione pre-elaborati. Questo ti darà un output di copertura più intensivo ma è più lento di reduced. Il livello reduced utilizza i blocchi di traccia di pin, quindi è molto più veloce del livello high. Ma ciò non causerà enormi differenze sul diverso livello di output di copertura. Se non vuoi fare cose specifiche, considera l'uso del livello reduced. (default: reduced)
Per gli utenti macOS: A partire da macOS 10.11 (OS X El Capitan), il sistema operativo include un livello di sicurezza chiamato System Integrity Protection SIP. Questo impedisce ai processi in modalità utente che tentano di eseguire iniezione o modifica su un altro processo anche se stai eseguendo con privilegi di root.
Per superare questa prevenzione, devi disabilitarlo. Per fare ciò, segui i passaggi seguenti.
Riavvia macOS
Durante il processo di avvio, tieni premuti i tasti Comando + R.
Il sistema operativo alla fine entra in modalità di ripristino (Recovery Mode)
Apri un terminale dalla sezione Utility (Utilities)
digita csrutil status e premi invio. Dovresti vedere che SIP è abilitato.
digita csrutil disable e premi invio.
digita csrutil status di nuovo per assicurarti che sia disabilitato o meno. Quindi riavvia il sistema operativo e lascia che si avvii normalmente. Ora sei pronto per utilizzare gli strumenti di strumentazione binaria.



Prima, scarica Ghidra (ultima versione, attualmente 9.1.2) e dragondance``` $ wget https://ghidra-sre.org/ghidra_9.1.2_PUBLIC_20200212.zip $ wget https://github.com/0ffffffffh/dragondance/archive/master.zip $ unzip ghidra_9.1.2_PUBLIC_20200212.zip $ unzip master.zip
Successivamente installa gradle e jdk```
$ sudo apt install openjdk-11-jdk
$ wget https://services.gradle.org/distributions/gradle-5.2.1-bin.zip
$ sudo unzip -d /opt/gradle gradle-5.2.1-bin.zip
Crea nuovo file profilo``` $ sudo vi /etc/profile.d/gradle.sh
e aggiungi quanto segue per aggiungere gradle al PATH in ogni successivo login.```
export GRADLE_HOME=/opt/gradle/gradle-5.2.1
export PATH=${GRADLE_HOME}/bin:${PATH}
per farlo immediatamente, senza dover fare logout``` $ source /etc/profile.d/gradle.sh
possiamo ora procedere a costruire dragondance```
$ cd dragondance-master/
$ gradle -PGHIDRA_INSTALL_DIR=/home/ubuntu/ghidra_9.1.2_PUBLIC
> Task :buildExtension
Created ghidra_9.1.2_PUBLIC_20200506_dragondance-master.zip in /home/ubuntu/dragondance-master/dist
BUILD SUCCESSFUL in 29s
5 actionable tasks: 5 executed
ubuntu@ubuntu:~/dragondance-master$
dove potrebbe essere necessario regolare il percorso in cui hai scaricato ghidra. Puoi ora trovare l'estensione compilata in dragondance-master/dist
Esecuzione di script riga per riga in stile prompt dei comandi.
Consapevolezza del cambio di contesto
Visualizzazione della copertura basata su funzioni (routine)
Consapevolezza del flusso di esecuzione
Più funzioni integrate per lo scripting
Un proprio formato di database di copertura per salvare e caricare più velocemente e mantenere le ultime modifiche della sessione.
Colorazione del pseudo-codice. (Ghidra non fornisce un'API per questo. Quindi devo studiare il codice sorgente di Ghidra per trovare un modo o un workaround per realizzarlo.)
Miglioramenti dell'interfaccia utente
Oğuz Kartal (@0ffffffffh)