
Plugin visualizador de cobertura de código binario para Ghidra
Dragon Dance es un plugin para Ghidra para visualizar y manipular los datos de cobertura de código binario. Los datos de cobertura pueden importarse de múltiples fuentes de cobertura. Por ahora, el plugin es compatible con las herramientas de instrumentación binaria Dynamorio e Intel Pin. Dynamorio tiene su propio módulo de recolección de cobertura llamado "drcov". Intel Pin no proporciona un módulo de recolección de cobertura incorporado. Para manejar la falta de dicho módulo, tuve que escribir mi propio módulo de recolección de cobertura para Intel Pin. Así que escribí un módulo de recolección de cobertura para Intel Pin llamado ddph (Dragon Dance Pin Helper). Así que puedes usarlo. Puedes ver el código fuente de ddph en este enlace. Si eres demasiado perezoso para compilarlo por tu cuenta, puedes usar los binarios compilados que proporcioné para Windows, macOS y Linux.

Dragon Dance puede importar y usar múltiples datos de cobertura en la misma sesión. (También admite múltiples sesiones, pero por ahora eso no es usable desde la GUI). Y puedes cambiar entre ellas o aplicar operaciones de intersection, difference, distinct o sum entre ellas rápidamente.
Dragon Dance te permite ver la intensidad de las instrucciones ejecutadas. Así que puedes obtener una pista sobre qué instrucciones se ejecutan con qué frecuencia. También puedes ver la visualización de cobertura en la ventana del grafo de funciones.

Dragon Dance también admite su propio sistema de scripting.

Te permite una forma flexible de jugar con los datos de cobertura. Puedes realizar operaciones de load, delete, show, intersect, diff, distinct, sum sobre ellos. La siguiente sección contendrá el sistema de scripting y la API. Presiona las teclas Alt + Enter para ejecutar el script.
Las funciones incorporadas son implementaciones de las operaciones internas de cobertura para proporcionar una interfaz al sistema de scripting. Una función incorporada puede devolver una variable de objeto de cobertura o nada. Las funciones incorporadas pueden tener alias.
Aceptan un Built-in Arg como parámetro. Los parámetros pueden tener longitud variable.
Built-in Arg es una referencia para contener diferentes tipos de valor. Los Built-in Args se pasan en orden de izquierda a derecha. Built-in Arg puede contener los siguientes tipos de valor:
Las variables son responsables de contener únicamente el objeto de cobertura. Pueden ser cargadas por funciones incorporadas. Pueden pasarse como un parámetro (Built-in Arg) a las Funciones Incorporadas.
Hay dos tipos de objeto de cobertura.
Physical Coverage Object y Logical Coverage Object
Physical Coverage Object apunta a un objeto de cobertura que se carga directamente desde el archivo de cobertura. Son visibles en la tabla de cobertura que está en la GUI. Por lo tanto, puedes interactuar con ellos mediante las operaciones de la GUI.
Logical Coverage Object apunta a un objeto de cobertura que ha sido procesado en una función incorporada y devuelto como resultado. No son visibles en la GUI, pero pueden vivir en una Variable hasta que sean destruidos.
El objeto de cobertura es mantenido automáticamente por el objeto Variable para ambos tipos de objeto de cobertura. Por ejemplo;``` cov1 = load("firstcoverage.out") cov2 = load("secondcov.out")
cov1 = diff(cov1,cov2)
En este ejemplo, cov1 y cov2 son variables. Y ambas variables tienen un objeto de cobertura física. La función incorporada diff toma ambas variables y asigna el valor de retorno a la variable cov1. Esa operación de sobrescritura asignará el objeto de cobertura resultante a la variable, pero no elimina el objeto de cobertura porque es un objeto de cobertura física. Esos datos de cobertura permanecerán en la sesión y también en la tabla GUI. Pensemos en el ejemplo anterior de esta manera;```
cov1 = load("first.out")
cov2 = load("second.out")
cov3 = load("third.out")
rvar = sum(cov1,cov2,cov3)
rvar = diff(rvar, cov2)
En este ejemplo, tres variables de cobertura física entran en una operación de suma y la operación de suma devuelve un objeto de cobertura de resultado lógico. Luego, la operación de diferencia toma una variable lógica y una física y sobrescribe la variable llamada rvar.
En este caso, el resultado se asignará a rvar y su valor de cobertura anterior se destruirá inmediatamente. Debido a que era un objeto lógico y debe eliminarse para evitar fugas de objetos. Si desea destruir una variable que contenía un objeto de cobertura física, debe llamar al integrado discard para hacerlo. Todos los integrados se detallarán a continuación.
Puede escribir scripts complejos utilizando llamadas integradas anidadas, puede escribir algo como:
cres = diff(intersect(a, load("another.log"), c, d), sum(e,f) )
no tiene que escribir la lógica línea por línea.
Los siguientes documentos de API y sus comportamientos pueden cambiar hasta que se alcance la versión final.
clear()
| Propiedad | Descripción |
|---|---|
| Valor de retorno | Ninguno |
| Número mínimo de parámetros | 0 |
cwd( String : workingDirectory )
diff( Variable : var1, var2, ..... varN )
| Propiedad | Descripción |
|---|---|
| Valor de retorno | Variable |
| Número mínimo de parámetros |
discard( Variable : var1, var2, ..... varN )
| Propiedad | Descripción |
|---|---|
| Valor de retorno |
distinct( Variable : var1, var2, ..... varN )
| Propiedad | Descripción |
|---|---|
| Valor de retorno | Variable |
| Número mínimo de parámetros | 2 |
goto( Integer : offset )
| Propiedad | Descripción |
|---|---|
| Valor de retorno | Ninguno |
| Número mínimo de parámetros |
import( String : filePathOrCoverageName )
intersect( Variable : var1, var2, ..... varN )
| Propiedad | Descripción |
|---|---|
| Valor de retorno | Variable |
| Número mínimo de parámetros | 2 |
show( Variable : var )
| Propiedad | Descripción |
|---|---|
| Valor de retorno | Ninguno |
sum( Variable : var1, var2, ..... varN )
| Propiedad | Descripción |
|---|---|
| Valor de retorno | Variable |
| Número mínimo de parámetros | 1 |
Correcciones
Dragon Dance puede intentar corregir una situación mal analizada en Ghidra durante la importación del archivo de datos de cobertura. En algunos binarios, Ghidra no descompila las instrucciones de una función debido a una generación de código inesperada por parte del compilador. Dragon Dance verifica la imagen cargada y la integridad de los datos de cobertura. Si son válidos entre sí y la dirección pertenece a una sección ejecutable pero hay falta de descompilación de instrucciones, el plugin pregunta si se desea corregir. Luego intenta corregir descompilando la sección sin procesar.

En versiones futuras del plugin, puede contener más correcciones o soluciones alternativas para la imagen.
La instalación es bastante sencilla.
Inicie Ghidra.
Haga clic en el menú "File" y luego seleccione "Install Extensions.."
Haga clic en el icono del signo más verde en la esquina superior derecha de la ventana.
Seleccione el paquete zip del plugin y seleccione Aceptar.
Seleccione dragondance de la lista.
Haga clic en Aceptar y reinicie Ghidra.
Durante la primera carga de un binario en Ghidra después de la instalación del plugin, Ghidra debería preguntarle si desea configurar el plugin recién instalado o no.
Si hace clic en el botón Sí, el plugin DragonDance debería aparecer inmediatamente.
Si hace clic en el botón No, debe activarlo manualmente usted mismo.
Para activarlo manualmente,
Haga clic en el menú "File" y luego seleccione Configure desde la ventana de Desensamblaje (CodeBrowser).
Haga clic en el pequeño icono de enchufe en la esquina superior derecha de la ventana Configure Tool.
Busque el elemento DragonDance en la lista de plugins y active su casilla de verificación, luego haga clic en Aceptar.
La ventana de Dragon Dance debería aparecer.
Después de la activación, debería poder ver el elemento Dragon Dance en el menú Window.
Como describí antes, Dragon Dance puede importar datos de cobertura de Dynamorio e Intel Pin (por ahora). En realidad, estas son herramientas genéricas de instrumentación binaria. Debe usar un módulo adecuado con ellas para recopilar datos de cobertura. Dynamorio tiene su propio módulo de cobertura llamado drcov. Puede usar ese módulo integrado para recopilar cobertura.
Uso de Dynamorio
Puede recopilar datos de cobertura de Dynamorio usando el siguiente comando:
drrun -t drcov -logdir [COVERAGE_OUTPUT_DIRECTORY_PATH] -- [EXECUTABLE_PATH_TO_EXAMINE] [EXECUTABLE_ARGUMENTS]
La salida se colocará en el directorio dado con el formato drcov.[EXECUTABLE_NAME].[ID].proc.log.
Uso de Intel Pin
Como mencioné antes, Intel Pin no proporciona ningún módulo de recopilación de cobertura integrado. Debe usar un módulo Pin personalizado. Afortunadamente, creé el mío propio para recopilar cobertura de Pin. Eso nos brinda algunas ventajas. Puedo extenderlo cuando sea necesario o agregar características y opciones adicionales.
Si bien las versiones posteriores pueden funcionar, solo se admite Intel PIN 3.7. Estos no se ofrecen de inmediato en la página de Intel PIN, por lo que aquí hay enlaces de descarga directa:
Puede acceder al código fuente de ddph aquí. Compartiré binarios para Windows, macOS y Linux. O puede crear su propio binario usando su script de shell de compilación.
Para recopilar datos de cobertura de Intel Pin, use el siguiente comando:
pin -t ddph.[so,dylib,dll] [ddph options] -- [EXECUTABLE_PATH_TO_EXAMINE] [EXECUTABLE_ARGUMENTS]
ddph tiene algunas opciones para la recopilación.
-o: con esta opción puede especificar el nombre del archivo de salida de cobertura. (predeterminado: ddph.out)
-l: puede especificar el nombre del archivo de registro de operación. si pasa "no" a esta opción, ddph no realizará la operación de registro. (predeterminado: ddph.log)
-p: nivel de detalle de captura. esta opción puede ser reduced o high. el nivel alto captura todas las instrucciones una por una y construye bloques de ejecución preprocesados. Esto le dará una salida de cobertura más intensiva, pero es más lento que reduced. el nivel reducido usa los bloques de seguimiento de pin, por lo que es mucho más rápido que el nivel alto. Pero eso no causará grandes diferencias en el nivel de salida de cobertura. Si no desea hacer cosas específicas, considere usar el nivel reducido. (predeterminado: reduced)
Para usuarios de macOS: A partir de macOS 10.11 (OS X El Capitan), el sistema operativo incluye una capa de seguridad llamada System Integrity Protection SIP. Esto evita que los procesos en modo de usuario intenten inyectar o modificar otro proceso, incluso si se ejecutan con privilegios de root.
Para superar esta prevención, debe deshabilitarlo. Para hacerlo, siga estos pasos a continuación.
Reinicie macOS.
Durante el proceso de arranque, mantenga presionadas las teclas Comando + R.
El sistema operativo eventualmente entrará en el Modo de Recuperación.
Abra una terminal desde la sección Utilidades.
Escriba csrutil status y presione enter. Debe ver que SIP está habilitado.
Escriba csrutil disable y presione enter.
Escriba csrutil status nuevamente para asegurarse de que está deshabilitado o no. Luego reinicie el sistema operativo y déjelo arrancar normalmente. Ahora está listo para usar herramientas de instrumentación binaria.



Primero, descargue Ghidra (última versión, actualmente 9.1.2) y 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
A continuación instala gradle y 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
crear nuevo archivo de perfil``` $ sudo vi /etc/profile.d/gradle.sh
y agrega lo siguiente para agregar gradle al PATH en cada inicio de sesión posterior.```
export GRADLE_HOME=/opt/gradle/gradle-5.2.1
export PATH=${GRADLE_HOME}/bin:${PATH}
hacerlo inmediatamente, sin tener que cerrar sesión``` $ source /etc/profile.d/gradle.sh
ahora podemos proceder a construir 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$
donde es posible que tengas que ajustar la ruta a donde descargaste ghidra. Ahora puedes encontrar la extensión compilada en dragondance-master/dist
Ejecución de scripts línea por línea al estilo de la consola de comandos.
Conciencia de cambio de contexto
Visualización de cobertura basada en funciones (rutinas)
Conciencia del flujo de ejecución
Más funciones integradas para la escritura de scripts
Su propio formato de base de datos de cobertura para guardar y cargar más rápido y mantener los últimos cambios en la sesión.
Pintado de pseudocódigo. (Ghidra no proporciona una API para esto. Por lo tanto, tengo que estudiar el código fuente de Ghidra para encontrar una forma o solución alternativa para lograrlo).
Mejoras en la interfaz de usuario
Oğuz Kartal (@0ffffffffh)
| Número máximo de parámetros |
| 0 |
| Descripción | Este integrado limpia la cobertura que se está visualizando y establece la cobertura activa en nulo. |
| Alias | Ninguno |
| Propiedad | Descripción |
|---|
| Valor de retorno | Ninguno |
| Número mínimo de parámetros | 1 |
| Número máximo de parámetros | 1 |
| Descripción | Establece el directorio de trabajo actual con la ruta dada. Todas las llamadas de importación sin ruta absoluta después de cwd buscarán los archivos de cobertura en el directorio de trabajo activo. |
| Alias | Ninguno |
| 2 |
| Número máximo de parámetros | Ilimitado |
| Descripción | Aplica la operación de diferencia a un número variable de Variables proporcionadas. Y devuelve la variable de cobertura resultante. |
| Alias | Ninguno |
| Ninguno |
| Número mínimo de parámetros | 1 |
| Número máximo de parámetros | Ilimitado |
| Descripción | Destruye las variables, sean físicas o lógicas. Primero destruye el objeto de cobertura y luego anula el registro del nombre de la variable de la lista de variables. Después de esta llamada, todas las variables dadas quedan indefinidas. |
| Alias | del |
| Número máximo de parámetros | Ilimitado |
| Descripción | Aplica la operación distinct (xor) a un número variable de variables. Y devuelve la variable de cobertura resultante. |
| Alias | xor |
| 1 |
| Número máximo de parámetros | 1 |
| Descripción | Localiza la selección de dirección actual mediante el desplazamiento dado. El valor de dirección real se calcula sumando el valor de desplazamiento al valor de la base de la imagen. |
| Alias | Ninguno |
| Propiedad | Descripción |
|---|
| Valor de retorno | Variable |
| Número mínimo de parámetros | 1 |
| Número máximo de parámetros | 1 |
| Descripción | Importa datos de cobertura desde el archivo de cobertura física. Toma ruta relativa o absoluta. O nombre de cobertura que se cargó físicamente antes. Si la ruta dada es absoluta, import carga directamente desde la ruta. De lo contrario, busca en el directorio de trabajo actual para cargarlo. En ambos casos, import comprobará si los datos de cobertura ya se cargaron usando su ruta. Si ya está cargado, devuelve la variable de cobertura en caché. O si el valor dado es un nombre de una cobertura física, busca el mapa de cobertura desde su sesión y devuelve el objeto de cobertura si existe. |
| Alias | get, load |
| Número máximo de parámetros | Ilimitado |
| Descripción | Aplica la operación de intersección a un número variable de variables. Y devuelve la variable de cobertura resultante. |
| Alias | and |
| Número mínimo de parámetros |
| 1 |
| Número máximo de parámetros | 1 |
| Descripción | Visualiza la variable de cobertura dada. Si hay un objeto de cobertura visualizado activamente y es lógico, la función destruye el objeto de cobertura anterior inmediatamente y muestra el dado. |
| Alias | Ninguno |
| Número máximo de parámetros | Ilimitado |
| Descripción | Aplica la operación de suma a un número variable de variables. Y devuelve la variable de cobertura resultante. |
| Alias | or, union |