
Plugin de visualisation de couverture de code binaire pour Ghidra
Dragon Dance est un plugin pour Ghidra permettant de visualiser et de manipuler les données de couverture de code binaire. Les données de couverture peuvent être importées à partir de multiples sources de couverture. Pour l'instant, le plugin prend en charge les outils d'instrumentation binaire Dynamorio et Intel Pin. Dynamorio possède son propre module de collecte de couverture appelé « drcov ». Intel Pin ne fournit pas de module de collecte de couverture intégré. Pour pallier ce manque, j'ai dû écrire mon propre module de collecte de couverture pour Intel Pin. J'ai donc écrit un module de collecte de couverture pour Intel Pin nommé ddph (Dragon Dance Pin Helper). Vous pouvez donc l'utiliser. Vous pouvez consulter le source de ddph via ce lien. Si vous êtes trop paresseux pour compiler vous-même, vous pouvez utiliser les binaires compilés que j'ai fournis pour Windows, macOS et Linux.

Dragon Dance peut importer et utiliser plusieurs données de couverture dans la même session. (Il prend également en charge les sessions multiples, mais pour l'instant ce n'est pas utilisable via l'interface graphique). Et vous pouvez basculer entre elles ou appliquer rapidement des opérations d'intersection, de différence, distinct ou de somme entre elles.
Dragon Dance vous permet de visualiser l'intensité des instructions exécutées. Ainsi, vous pouvez avoir une indication sur la fréquence d'exécution des instructions. Vous pouvez également visualiser la couverture sur la fenêtre du graphe de fonctions.


Il vous offre une manière flexible de jouer avec les données de couverture. Vous pouvez effectuer les opérations de chargement, suppression, affichage, intersection, diff, distinct, somme sur celles-ci. La section suivante contiendra le système de script et l'API. Appuyez sur les touches Alt + Entrée pour exécuter le script.
Les fonctions intégrées sont des implémentations des opérations internes de couverture visant à fournir une interface au système de script. Une fonction intégrée peut retourner une variable d'objet de couverture ou rien. Les fonctions intégrées peuvent avoir des alias.
Elles acceptent un Built-in Arg comme paramètre. Les paramètres peuvent être de longueur variable.
Built-in Arg est une référence qui permet de contenir différents types de valeurs. Les Built-in Args sont passés dans l'ordre de gauche à droite. Un Built-in Arg peut contenir les types de valeurs suivants :
Les variables sont chargées de contenir uniquement l'objet de couverture. Elles peuvent être chargées par les fonctions intégrées. Elles peuvent être passées en tant que paramètre (Built-in Arg) aux fonctions intégrées.
Il existe deux types d'objets de couverture.
Objet de couverture physique et Objet de couverture logique
L'objet de couverture physique fait référence à un objet de couverture chargé directement depuis le fichier de couverture. Ils sont visibles dans la table de couverture de l'interface graphique. Vous pouvez donc interagir avec eux via les opérations de l'interface.
L'objet de couverture logique fait référence à un objet de couverture qui a été traité dans une fonction intégrée et renvoyé comme résultat. Ils ne sont pas visibles dans l'interface graphique mais peuvent vivre dans une variable jusqu'à ce qu'ils soient détruits.
L'objet de couverture est géré automatiquement par l'objet Variable pour les deux types d'objets de couverture. Par exemple ;``` cov1 = load("firstcoverage.out") cov2 = load("secondcov.out")
cov1 = diff(cov1,cov2)
Dans cet exemple, cov1 et cov2 sont des variables. Et les deux variables possèdent un objet de couverture physique. La fonction intégrée `diff` prend les deux variables et affecte la valeur de retour à la variable cov1. Cette opération de remplacement affecte l'objet de couverture résultat à la variable mais ne supprime pas l'objet de couverture car il s'agit d'un objet de couverture physique. Ces données de couverture resteront dans la session ainsi que dans le tableau de l'interface graphique.
Pensons à l'exemple précédent comme ceci ;```
cov1 = load("first.out")
cov2 = load("second.out")
cov3 = load("third.out")
rvar = sum(cov1,cov2,cov3)
rvar = diff(rvar, cov2)
Dans cet exemple, trois variables de couverture physique entrent dans une opération de somme et l'opération de somme retourne un objet de couverture de résultat logique. Ensuite, l'opération de différence prend une variable logique et une variable physique et écrase la variable nommée rvar.
Dans ce cas, le résultat sera défini sur rvar et sa valeur de couverture précédente sera immédiatement détruite. Comme il s'agissait d'un objet logique, il doit être supprimé pour éviter une fuite d'objet. Si vous souhaitez détruire une variable qui contient un objet de couverture physique, vous devez appeler la fonction intégrée discard pour le faire. Toutes les fonctions intégrées seront détaillées ci-dessous.
Vous pouvez écrire des scripts complexes en utilisant des appels de fonctions intégrées imbriqués, comme ceci :
cres = diff(intersect(a, load("another.log"), c, d), sum(e,f) )
vous n'avez pas besoin d'écrire la logique ligne par ligne.
Les documentations d'API suivantes et leurs comportements peuvent changer jusqu'à atteindre la version finale.
clear()
| Propriété | Description |
|---|---|
| Valeur de retour | Aucune |
| Nombre minimal de paramètres | 0 |
| Nombre maximal de paramètres | 0 |
| Description | Cette fonction intégrée efface la couverture en cours de visualisation et définit la couverture active sur null. |
| Alias | Aucun |
cwd( String : workingDirectory )
| Propriété | Description |
|---|---|
| Valeur de retour | Aucune |
| Nombre minimal de paramètres | 1 |
| Nombre maximal de paramètres | 1 |
| Description | Définit le répertoire de travail actuel avec le chemin donné. Tous les appels d'importation sans chemin absolu après cwd rechercheront les fichiers de couverture dans le répertoire de travail actif. |
| Alias | Aucun |
diff( Variable : var1, var2, ..... varN )