Dragon Dance Plugin Main Interface
Dragon Dance Plugin Main Interface

Dragon Dance: Visualize Binary Code Coverage in Ghidra

Unveiling Dragon Dance: A Ghidra Plugin for Code Coverage Visualization

Dragon Dance is a powerful plugin designed for Ghidra, the popular reverse engineering framework. This plugin brings binary code coverage data to life, allowing users to visualize and interact with it directly within Ghidra. By importing coverage data from various sources, Dragon Dance offers invaluable insights into the execution paths of binary code. Currently, it seamlessly integrates with Dynamorio and Intel Pin, two leading binary instrumentation tools.

For Intel Pin users, Dragon Dance provides ddph (Dragon Dance Pin Helper), a custom-built coverage collection module addressing the absence of a native module in Pin. The source code for ddph is readily available here. Pre-compiled binaries for Windows, macOS, and Linux are also provided for ease of use.

Dragon Dance Plugin Main InterfaceDragon Dance Plugin Main Interface

Dragon Dance stands out by supporting the import and simultaneous utilization of multiple coverage datasets within a single Ghidra session. Users can effortlessly switch between these datasets or perform operations like intersection, difference, distinct, and sum to analyze coverage from different perspectives.

The plugin visually represents the intensity of instruction execution, offering hints about frequently executed code paths. Furthermore, coverage visualization is integrated into Ghidra’s function graph window, providing a contextual understanding of code execution within function boundaries.

Dragon Dance Coverage Visualization on Function GraphDragon Dance Coverage Visualization on Function Graph

Scripting Capabilities for Advanced Coverage Analysis

Dragon Dance extends its functionality with a built-in scripting system, empowering users with flexible control over coverage data manipulation.

Dragon Dance Scripting System InterfaceDragon Dance Scripting System Interface

This scripting environment allows for loading, deleting, displaying, and performing set operations (intersect, diff, distinct, sum) on coverage data. The scripting system and its API are detailed in the following sections. Scripts are executed by pressing Alt + Enter.

Built-in Functions: Core Coverage Operations

Built-in functions are the foundation of Dragon Dance’s scripting, providing access to internal coverage operations. These functions, which may have aliases, can return coverage objects or perform actions without returning a value. They accept “Built-in Arg” parameters, which can be coverage data objects, strings, or integers.

Understanding Built-in Arg

A “Built-in Arg” acts as a versatile parameter type, capable of holding:

  • Coverage Data Objects (returned by built-in functions)
  • Strings
  • Integers (decimal, hexadecimal, or octal formats)

Variables: Managing Coverage Objects

Variables in Dragon Dance are specifically designed to store coverage objects. These objects, loaded via built-in functions, can then be used as parameters in other built-in function calls.

There are two primary types of coverage objects:

  • Physical Coverage Objects: These represent coverage data loaded directly from files and are visible in the plugin’s GUI coverage table. They can be interacted with through GUI operations.

  • Logical Coverage Objects: These are the result of operations performed by built-in functions. They are not directly visible in the GUI but can be stored in variables for further scripting operations.

Dragon Dance automatically manages both types of coverage objects through variables.

Example Scenarios:

cov1 = load("firstcoverage.out")
cov2 = load("secondcov.out")
cov1 = diff(cov1,cov2)

In this example, cov1 and cov2 are variables holding physical coverage objects. The diff function calculates the difference between cov1 and cov2, and the result is assigned back to cov1. The original physical coverage object associated with cov1 remains in the session and GUI table, even though the variable cov1 now points to the result of the diff operation.

Consider a more complex example:

cov1 = load("first.out")
cov2 = load("second.out")
cov3 = load("third.out")
rvar = sum(cov1,cov2,cov3)
rvar = diff(rvar, cov2)

Here, cov1, cov2, and cov3 hold physical coverage objects. The sum function combines these into a logical coverage object, assigned to rvar. Subsequently, diff calculates the difference between this logical object and cov2, again updating rvar. In this case, the previous logical coverage object held by rvar is automatically destroyed to prevent memory leaks. To explicitly remove a physical coverage object and its associated variable, the discard built-in function must be used.

Nested built-in function calls are also supported, enabling complex, concise scripts:

cres = diff(intersect(a, load("another.log"), c, d), sum(e,f) )

Built-in Function Reference

The following section details the available built-in functions in Dragon Dance’s scripting system. Note that API and behaviors are subject to change in future versions.

clear()

Property Description
Return Value None
Minimum Parameter Count 0
Maximum Parameter Count 0
Description Clears the currently visualized coverage and resets the active coverage to null, effectively removing the coverage overlay.
Aliases None

cwd( String : workingDirectory )

Property Description
Return Value None
Minimum Parameter Count 1
Maximum Parameter Count 1
Description Sets the current working directory for subsequent file operations. Relative paths used in import calls will be resolved against this directory.
Aliases None

diff( Variable : var1, var2, ….. varN )

Property Description
Return Value Variable
Minimum Parameter Count 2
Maximum Parameter Count Unlimited
Description Calculates the set difference between the provided coverage variables. Returns a new coverage variable representing the result.
Aliases None

discard( Variable : var1, var2, ….. varN )

Property Description
Return Value None
Minimum Parameter Count 1
Maximum Parameter Count Unlimited
Description Removes the specified coverage variables. This destroys the associated coverage objects (physical or logical) and unregisters the variable names, making them undefined.
Aliases del

distinct( Variable : var1, var2, ….. varN )

Property Description
Return Value Variable
Minimum Parameter Count 2
Maximum Parameter Count Unlimited
Description Performs a distinct (XOR – exclusive OR) operation on the given coverage variables. Returns a new coverage variable containing the result.
Aliases xor

goto( Integer : offset )

Property Description
Return Value None
Minimum Parameter Count 1
Maximum Parameter Count 1
Description Jumps to the address calculated by adding the provided offset to the image base address in Ghidra, allowing for quick navigation within the binary.
Aliases None

import( String : filePathOrCoverageName )

Property Description
Return Value Variable
Minimum Parameter Count 1
Maximum Parameter Count 1
Description Loads coverage data from a file. Accepts absolute or relative file paths, or the name of a previously loaded physical coverage. If the data is already loaded, it returns the cached coverage variable.
Aliases get, load

intersect( Variable : var1, var2, ….. varN )

Property Description
Return Value Variable
Minimum Parameter Count 2
Maximum Parameter Count Unlimited
Description Computes the intersection of the provided coverage variables. Returns a new variable holding the resulting coverage.
Aliases and

show( Variable : var )

Property Description
Return Value None
Minimum Parameter Count 1
Maximum Parameter Count 1
Description Visualizes the coverage data associated with the given variable. If another coverage is currently visualized and it is logical, it will be replaced by the new one.
Aliases None

sum( Variable : var1, var2, ….. varN )

Property Description
Return Value Variable
Minimum Parameter Count 2
Maximum Parameter Count Unlimited
Description Calculates the union (sum) of the given coverage variables. Returns a new coverage variable representing the combined coverage.
Aliases or, union

Fix Ups: Addressing Ghidra Analysis Limitations

Dragon Dance incorporates a “Fix Ups” mechanism to handle situations where Ghidra’s analysis might be incomplete. This is particularly useful when dealing with binaries where compilers generate unexpected code, leading to decompilation failures. During coverage data import, Dragon Dance validates the loaded image and coverage data. If inconsistencies are detected, and executable sections lack decompiled instructions, the plugin offers to attempt a fix by decompiling the raw section.

Dragon Dance Fix Up Prompt ExampleDragon Dance Fix Up Prompt Example

Future versions of Dragon Dance are planned to include more advanced fix-up strategies and workarounds to enhance compatibility and analysis accuracy.

Installation Guide

Installing Dragon Dance is a straightforward process:

  1. Launch Ghidra.
  2. Navigate to File > Install Extensions…
  3. Click the Green Plus icon in the top-right corner.
  4. Select the Dragon Dance plugin zip package and click Ok.
  5. Choose dragondance from the extension list.
  6. Click Ok and restart Ghidra.

Launching Dragon Dance Plugin

Upon the first binary load in Ghidra after installation, you will be prompted to configure the newly installed plugin.

  • Clicking Yes will automatically launch Dragon Dance.
  • Clicking No requires manual activation:
    1. Go to File > Configure within the Disassembly Window (CodeBrowser).
    2. Click the plug icon at the top-right of the Configure Tool window.
    3. Locate DragonDance in the plugin list, enable its checkbox, and click Ok.
    4. The Dragon Dance window should now appear.

After activation, “Dragon Dance” will be accessible in the Window menu.

Collecting Coverage Data for Dragon Dance

Dragon Dance supports importing coverage data generated by Dynamorio and Intel Pin. These tools require specific modules to collect coverage information.

Using Dynamorio for Coverage Collection

Use the following command to collect coverage data with Dynamorio:

drrun -t drcov -logdir [COVERAGE_OUTPUT_DIRECTORY_PATH] -- [EXECUTABLE_PATH_TO_EXAMINE] [EXECUTABLE_ARGUMENTS]

Coverage logs will be saved in the specified directory with the format drcov.[EXECUTABLE_NAME].[ID].proc.log.

Using Intel Pin and ddph for Coverage Collection

Intel Pin requires a custom module for coverage collection. ddph (Dragon Dance Pin Helper) is provided for this purpose, offering flexibility and potential for future feature additions.

Important: While later versions may work, Intel PIN 3.7 is the officially supported version. Download links are provided below:

Access the ddph source code here. Pre-built binaries are available, or you can build your own using the provided build script.

Collect coverage data using Intel Pin with the following command:

pin -t ddph.[so,dylib,dll] [ddph options] -- [EXECUTABLE_PATH_TO_EXAMINE] [EXECUTABLE_ARGUMENTS]

ddph Options:

  • -o: Specify the coverage output filename (default: ddph.out).
  • -l: Specify the operation log filename. Use -l no to disable logging (default: ddph.log).
  • -p: Set the capture detail level: reduced (faster, trace blocks) or high (slower, instruction-by-instruction, pre-process execution blocks) (default: reduced).

macOS SIP (System Integrity Protection) Configuration:

macOS users may need to disable System Integrity Protection (SIP) to use binary instrumentation tools like Intel Pin.

Disabling SIP:

  1. Restart macOS.
  2. Hold Command + R during boot to enter Recovery Mode.
  3. Open Terminal from the Utilities menu.
  4. Check SIP status: csrutil status
  5. Disable SIP: csrutil disable
  6. Verify status again: csrutil status
  7. Restart the OS.

macOS Recovery Mode TerminalmacOS Recovery Mode Terminal

macOS SIP Status CheckmacOS SIP Status Check

macOS SIP Disable CommandmacOS SIP Disable Command

Build Instructions for Dragon Dance Plugin

To build the Dragon Dance Ghidra plugin from source:

  1. Download Ghidra (latest version, e.g., 9.1.2) and Dragon Dance source code:

    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
  2. Install Gradle and 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
  3. Configure Gradle Environment: Create /etc/profile.d/gradle.sh with the following content:

    export GRADLE_HOME=/opt/gradle/gradle-5.2.1
    export PATH=${GRADLE_HOME}/bin:${PATH}

    Apply the changes:

    source /etc/profile.d/gradle.sh
  4. Build Dragon Dance:

    cd dragondance-master/
    gradle -PGHIDRA_INSTALL_DIR=/path/to/ghidra_9.1.2_PUBLIC > Task :buildExtension

    Replace /path/to/ghidra_9.1.2_PUBLIC with the actual path to your Ghidra installation directory.

    The built extension zip file (ghidra_9.1.2_PUBLIC_20200506_dragondance-master.zip) will be located in the dragondance-master/dist directory.

Future Enhancements for Dragon Dance

Planned features for future versions of Dragon Dance include:

  • Command prompt style, line-by-line script execution within the plugin.
  • Context change awareness for more dynamic coverage analysis.
  • Function (Routine) based coverage visualization for function-level insights.
  • Execution flow awareness to understand code execution paths in detail.
  • Expansion of the scripting API with more built-in functions.
  • A dedicated coverage database format for faster loading and session persistence.
  • Pseudo-code painting (pending Ghidra API availability or workaround development).
  • UI enhancements for improved user experience.

Project Author

Dragon Dance is developed by Oğuz Kartal (@0ffffffffh).

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *