Dragon Dance 是一个 Ghidra 插件,用于可视化和操作二进制代码覆盖率数据。覆盖率数据可以从多个覆盖率源导入。目前该插件支持 Dynamorio 和 Intel Pin 二进制插桩工具。Dynamorio 有自己的覆盖率收集模块,名为 "drcov"。Intel Pin 没有提供内置的覆盖率收集器模块。为了处理这种缺乏模块的情况,我不得不为 Intel Pin 编写自己的覆盖率收集模块。所以我为 Intel Pin 编写了一个名为 ddph(Dragon Dance Pin Helper)的覆盖率收集模块。你可以使用它。你可以通过这个 链接 查看 ddph 的源代码。如果你懒得自己编译,可以使用我提供的适用于 Windows、macOS 和 Linux 的预编译二进制文件。

Dragon Dance 可以在同一会话中导入并使用多个覆盖率数据。(它还支持多会话,但目前 GUI 无法使用该功能)。您可以快速切换这些数据,或者对它们进行交集、差集、对称差集或求和运算。
Dragon Dance 允许您查看已执行指令的强度。这样您可以了解哪些指令执行得有多频繁。您还可以在函数图窗口中查看覆盖率可视化。

Dragon Dance 还支持自己的脚本系统。

它为您提供了一种灵活的方式来操作覆盖率数据。您可以对它们执行加载、删除、显示、交集、差集、对称差集、求和等操作。以下部分将包含脚本系统和 API。按 Alt + Enter 键执行脚本。
内置函数是内部覆盖率操作的实现,为脚本系统提供接口。内置函数可以返回覆盖率对象变量,也可以不返回。内置函数可能具有别名。
它们接受 Built-in Arg 作为参数。参数可以是可变长度的。
Built-in Arg 是一个用于保存不同类型值的引用。Built-in Args 按从左到右的顺序传递。Built-in Arg 可以保存以下值类型:
变量仅负责保存覆盖率对象。它们可以由内置函数加载。它们可以作为参数(Built-in Arg)传递给内置函数。
有两种类型的覆盖率对象:
物理覆盖率对象 和 逻辑覆盖率对象
物理覆盖率对象指向直接从覆盖率文件加载的覆盖率对象。它们在 GUI 上的覆盖率表中可见。因此,您可以通过 GUI 操作与它们交互。
逻辑覆盖率对象指向经过内置函数处理并返回结果的覆盖率对象。它们在 GUI 上不可见,但可以存在于变量中,直到被销毁。
覆盖率对象由 Variable 对象自动维护,适用于这两种类型的覆盖率对象。例如:``` cov1 = load("firstcoverage.out") cov2 = load("secondcov.out")
cov1 = diff(cov1,cov2)
在此示例中,`cov1` 和 `cov2` 是变量,且两个变量都具有物理覆盖率对象。`diff` 内置函数接收这两个变量,并将返回值赋给 `cov1` 变量。该覆盖操作会将结果覆盖率对象存入变量,但不会删除原覆盖率对象,因为它是物理覆盖率对象,其覆盖率数据将保留在会话以及 GUI 表格中。
让我们按如下方式思考前面的示例;```
cov1 = load("first.out")
cov2 = load("second.out")
cov3 = load("third.out")
rvar = sum(cov1,cov2,cov3)
rvar = diff(rvar, cov2)
在这个例子中,三个物理覆盖率变量进入求和操作,求和操作返回逻辑结果覆盖率对象。然后,diff操作接受一个逻辑变量和一个物理变量,并覆盖名为rvar的变量。
在这种情况下,结果将被设置到rvar中,其之前的覆盖率值立即被销毁。因为这是一个逻辑对象,应该被删除以防止对象泄漏。如果你想要销毁一个包含物理覆盖率对象的变量,你必须调用内置的discard函数。所有内置函数将在下面详细介绍。
你可以使用嵌套的内置函数调用来编写复杂的脚本,例如可以这样写:
cres = diff(intersect(a, load("another.log"), c, d), sum(e,f) )
你不必逐行编写逻辑。
以下API文档及其行为在最终版本发布前可能会发生变化。
clear()
| 属性 | 描述 |
|---|---|
| 返回值 | 无 |
| 最小参数数量 | 0 |
| 最大参数数量 | 0 |
| 描述 | 此内置函数清除当前可视化的覆盖率,并将活动覆盖率设置为空。 |
| 别名 | 无 |
cwd( String : workingDirectory )
| 属性 | 描述 |
|---|---|
| 返回值 | 无 |
| 最小参数数量 | 1 |
| 最大参数数量 | 1 |
| 描述 | 使用给定路径设置当前工作目录。在cwd之后的所有没有绝对路径的导入调用,覆盖率文件将在当前工作目录中搜索。 |
| 别名 | 无 |
diff( Variable : var1, var2, ..... varN )
| 属性 | 描述 |
|---|---|
| 返回值 | Variable |
| 最小参数数量 | 2 |
| 最大参数数量 | 不限 |
| 描述 | 对给定可变长度的变量应用差集操作。并返回结果覆盖率变量。 |
| 别名 | 无 |
discard( Variable : var1, var2, ..... varN )
| 属性 | 描述 |
|---|---|
| 返回值 | 无 |
| 最小参数数量 | 1 |
| 最大参数数量 | 不限 |
| 描述 | 销毁变量,无论物理还是逻辑。它将首先销毁覆盖率对象,然后从变量列表中取消注册变量名称。此调用后,所有给定的变量变为未定义。 |
| 别名 | del |
distinct( Variable : var1, var2, ..... varN )
| 属性 | 描述 |
|---|---|
| 返回值 | Variable |
| 最小参数数量 | 2 |
| 最大参数数量 | 不限 |
| 描述 | 对给定可变长度的变量应用异或(xor)操作。并返回结果覆盖率变量。 |
| 别名 | xor |
goto( Integer : offset )
| 属性 | 描述 |
|---|---|
| 返回值 | 无 |
| 最小参数数量 | 1 |
| 最大参数数量 | 1 |
| 描述 | 通过给定偏移定位当前地址选择。实际地址值通过将偏移值加到镜像基址计算得出。 |
| 别名 | 无 |
import( String : filePathOrCoverageName )
| 属性 | 描述 |
|---|---|
| 返回值 | Variable |
| 最小参数数量 | 1 |
| 最大参数数量 | 1 |
| 描述 | 从物理覆盖率文件导入覆盖率数据。接受相对或绝对路径。或者之前物理加载的覆盖率名称。如果给定路径是绝对路径,import直接从该路径加载。否则,它会在当前工作目录下查找加载。在这两种情况下,import都会检查该覆盖率数据是否已经加载(通过路径)。如果已加载,返回缓存的覆盖率变量。或者如果给定值是一个物理覆盖率的名称,它从其会话中查找覆盖率映射,如果存在则返回覆盖率对象。 |
| 别名 | get, load |
intersect( Variable : var1, var2, ..... varN )
| 属性 | 描述 |
|---|---|
| 返回值 | Variable |
| 最小参数数量 | 2 |
| 最大参数数量 | 不限 |
| 描述 | 对给定可变长度的变量应用交集操作。并返回结果覆盖率变量。 |
| 别名 | and |
show( Variable : var )
| 属性 | 描述 |
|---|---|
| 返回值 | 无 |
| 最小参数数量 | 1 |
| 最大参数数量 | 1 |
| 描述 | 可视化给定的覆盖率变量。如果存在一个活动可视化的覆盖率对象且是逻辑的,该函数立即销毁之前的覆盖率对象并显示给定的对象。 |
| 别名 | 无 |
sum( Variable : var1, var2, ..... varN )