
Ghidra用バイナリコードカバレッジ可視化プラグイン
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 を使用すると、実行された命令の強度を表示できます。これにより、どの命令がどのくらいの頻度で実行されたかのヒントを得られます。また、関数グラフウィンドウでカバレッジの可視化を表示することもできます。


これにより、カバレッジデータを柔軟に操作できます。データの読み込み、削除、表示、交差、差分、排他、合計演算を実行できます。次のセクションでは、スクリプティングシステムとAPIについて説明します。Alt + Enter キーを押してスクリプトを実行します。
組み込み関数は、スクリプティングシステムにインターフェースを提供するための内部カバレッジ操作の実装です。組み込み関数は、カバレッジオブジェクト変数を返すか、何も返さないことができます。組み込み関数にはエイリアスがある場合があります。
これらはパラメータとして組み込み引数(Built-in Arg)を受け入れます。パラメータは可変長です。
組み込み引数は、異なるタイプの値を保持するための参照です。組み込み引数は左から右の順序で渡されます。組み込み引数は以下の値タイプを保持できます:
変数はカバレッジオブジェクトのみを保持する役割を持ちます。組み込み関数によってロードされ、組み込み関数へのパラメータ(組み込み引数)として渡すことができます。
カバレッジオブジェクトには2つのタイプがあります。物理カバレッジオブジェクト と 論理カバレッジオブジェクト です。
物理カバレッジオブジェクトは、カバレッジファイルから直接ロードされたカバレッジオブジェクトを指します。これらはGUI上のカバレッジテーブルに表示され、GUI操作で操作できます。
論理カバレッジオブジェクトは、組み込み関数で処理され、その結果として返されたカバレッジオブジェクトを指します。これらはGUIには表示されませんが、破棄されるまで変数内に存在できます。
カバレッジオブジェクトは、両方のタイプのカバレッジオブジェクトに対して変数オブジェクトによって自動的に管理されます。例えば;``` 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)
この例では、3つの物理的なカバレッジ変数がsum演算に投入され、sum演算が論理的な結果カバレッジオブジェクトを返します。次に、diff演算が論理変数と物理変数を取り、rvarという名前の変数を上書きします。
この場合、結果はrvarに設定され、以前のカバレッジ値は即座に破棄されます。これは論理オブジェクトであり、オブジェクトリークを防ぐために削除される必要があるためです。物理的なカバレッジオブジェクトを含む変数を破棄したい場合は、discard組み込み関数を呼び出す必要があります。すべての組み込み関数については後述します。
ネストされた組み込み関数呼び出しを使用して複雑なスクリプトを作成できます。次のように記述できます。
cres = diff(intersect(a, load("another.log"), c, d), sum(e,f) )
ロジックを1行ずつ記述する必要はありません。
以下のAPIドキュメントとその動作は、最終バージョンに達するまで変更される可能性があります。
clear()
| プロパティ | 説明 |
|---|---|
| 戻り値 | なし |
| 最小パラメータ数 | 0 |
| 最大パラメータ数 | 0 |
| 説明 | この組み込み関数は、表示中のカバレッジをクリアし、アクティブなカバレッジをnullに設定します。 |
| エイリアス |
cwd( String : workingDirectory )
| プロパティ | 説明 |
|---|---|
| 戻り値 | なし |
diff( Variable : var1, var2, ..... varN )
| プロパティ | 説明 |
|---|---|
| 戻り値 | 変数 |
| 最小パラメータ数 | 2 |
| 最大パラメータ数 | 無制限 |
| 説明 | 指定された可変長の変数に対して差分演算を適用します。そして、結果のカバレッジ変数を返します。 |
| エイリアス |
discard( Variable : var1, var2, ..... varN )
| プロパティ | 説明 |
|---|---|
| 戻り値 | なし |
| 最小パラメータ数 | 1 |
| 最大パラメータ数 | 無制限 |
| 説明 | 変数(物理または論理)を破棄します。最初にカバレッジオブジェクトを破棄し、次に変数リストから変数名を登録解除します。この呼び出し後、指定されたすべての変数は未定義になります。 |
distinct( Variable : var1, var2, ..... varN )
| プロパティ | 説明 |
|---|---|
| 戻り値 | 変数 |
| 最小パラメータ数 | 2 |
| 最大パラメータ数 | 無制限 |
| 説明 | 指定された可変長の変数に対して排他的論理和(xor)演算を適用します。そして、結果のカバレッジ変数を返します。 |
goto( Integer : offset )
| プロパティ | 説明 |
|---|---|
| 戻り値 | なし |
| 最小パラメータ数 | 1 |
| 最大パラメータ数 | 1 |
| 説明 | 指定されたオフセットによって現在のアドレス選択を位置付けます。実際のアドレス値は、オフセット値をイメージベース値に加算して計算されます。 |
import( String : filePathOrCoverageName )
| プロパティ | 説明 |
|---|---|
| 戻り値 | 変数 |
intersect( Variable : var1, var2, ..... varN )
| プロパティ | 説明 |
|---|---|
| 戻り値 | 変数 |
| 最小パラメータ数 | 2 |
| 最大パラメータ数 | 無制限 |
| 説明 | 指定された可変長の変数に対して積集合演算を適用します。そして、結果のカバレッジ変数を返します。 |
| エイリアス |
show( Variable : var )
| プロパティ | 説明 |
|---|---|
| 戻り値 | なし |
| 最小パラメータ数 | 1 |
| 最大パラメータ数 | 1 |
| 説明 | 指定されたカバレッジ変数を可視化します。アクティブに可視化されているカバレッジオブジェクトが存在し、それが論理オブジェクトである場合、関数は以前のカバレッジオブジェクトを即座に破棄し、指定されたものを表示します。 |
sum( Variable : var1, var2, ..... varN )
| プロパティ | 説明 |
|---|---|
| 戻り値 | 変数 |
| 最小パラメータ数 | 1 |
| 最大パラメータ数 | 無制限 |
| 説明 | 指定された可変長の変数に対して和演算を適用します。そして、結果のカバレッジ変数を返します。 |
| エイリアス |
Fix Ups
Dragon Danceは、カバレッジデータファイルをインポートする際に、ghidraにおける誤解析された状況を修正しようと試みることができます。一部のバイナリでは、コンパイラによる予期しないコード生成のため、ghidraが関数の命令を逆コンパイルしません。Dragon Danceは、ロードされたイメージとカバレッジデータの整合性をチェックします。それらが互いに有効であり、アドレスが実行可能セクションに属しているが、命令の逆コンパイルが不足している場合、プラグインは修正を求めます。その後、生のセクションを逆コンパイルして修正を試みます。

今後のプラグインのバージョンでは、イメージに対するさらなる修正や回避策が含まれる可能性があります。
インストールは非常に簡単です。
Ghidraを起動します。
"File"メニューをクリックし、"Install Extensions.."を選択します。
ウィンドウの右上にある緑のプラスアイコンをクリックします。
プラグインのzipパッケージを選択し、Okを選択します。
リストからdragondanceを選択します。
Okをクリックし、ghidraを再起動します。
プラグインインストール後、最初にバイナリをGhidraにロードする際、Ghidraは新しくインストールされたプラグインを設定するかどうかを尋ねます。
"はい"ボタンをクリックすると、DragonDanceプラグインがすぐに表示されます。
"いいえ"ボタンをクリックした場合は、手動で自分でアクティブにする必要があります。
手動でアクティブにするには、
"File"メニューをクリックし、逆アセンブルウィンドウ(CodeBrowser)からConfigureを選択します。
Configure Toolウィンドウの右上隅にある小さなプラグアイコンをクリックします。
プラグインリストからDragonDance項目を見つけ、そのチェックボックスを有効にしてOkをクリックします。
Dragon Danceウィンドウが表示されます。
アクティブ化後、WindowメニューにDragon Dance項目が表示されるはずです。
先に述べたように、Dragon DanceはDynamorioとIntel Pinからカバレッジデータをインポートできます。(現時点では)これらは実際には汎用のバイナリ計測ツールです。カバレッジデータを収集するには、適切なモジュールをそれらとともに使用する必要があります。Dynamorioにはdrcovという独自のカバレッジモジュールがあります。その組み込みモジュールを使用してカバレッジを収集できます。
Dynamorioの使用
Dynamorioからカバレッジデータを収集するには、次のコマンドを使用します。
drrun -t drcov -logdir [COVERAGE_OUTPUT_DIRECTORY_PATH] -- [EXECUTABLE_PATH_TO_EXAMINE] [EXECUTABLE_ARGUMENTS]
出力は、指定されたディレクトリに drcov.[EXECUTABLE_NAME].[ID].proc.log 形式で配置されます。
Intel Pinの使用
先に述べたように、Intel Pinには組み込みのカバレッジコレクタモジュールはありません。カスタムのPinモジュールを使用する必要があります。幸い、私はPinからカバレッジを収集するために独自のものを制作しました。これにより、いくつかの利点がもたらされます。必要に応じて拡張したり、追加機能やオプションを追加したりできます。
後のバージョンでも動作する可能性がありますが、Intel PIN 3.7のみがサポートされています。これらはIntel PINページですぐに提供されているわけではないため、以下に直接ダウンロードリンクを示します。
ddphのソースにはこちらからアクセスできます。Windows、macOS、Linux用のバイナリを共有します。または、ビルドシェルスクリプトを使用して、独自のバイナリをビルドすることもできます。
Intel Pinからカバレッジデータを収集するには、次のコマンドを使用します。
pin -t ddph.[so,dylib,dll] [ddph options] -- [EXECUTABLE_PATH_TO_EXAMINE] [EXECUTABLE_ARGUMENTS]
ddphには収集用のいくつかのオプションがあります。
-o: このオプションでカバレッジ出力ファイル名を指定できます。(デフォルト: ddph.out)
-l: 操作ログファイル名を指定できます。このオプションに"no"を渡すと、ddphはログ操作を実行しません。(デフォルト: ddph.log)
-p: キャプチャ詳細レベル。このオプションは reduced または high に設定できます。highレベルはすべての命令を1つずつキャプチャし、前処理された実行ブロックを構築します。これにより、より集中的なカバレッジ出力が得られますが、reducedよりも低速です。reducedレベルはpinのトレースブロックを使用するため、highレベルよりもはるかに高速です。ただし、異なるレベルのカバレッジ出力に大きな違いは生じません。特定のことを行いたい場合を除き、reducedレベルの使用を検討してください。(デフォルト: reduced)
macOSユーザー向け: macOS 10.11(OS X El Capitan)以降、OSには System Integrity Protection SIP というセキュリティレイヤが付属しています。これは、たとえroot権限で実行していても、別のプロセスに対して注入や変更を試みるユーザーモードプロセスを防ぎます。
この防止策を回避するには、無効にする必要があります。そのためには、以下の手順に従ってください。
macOSを再起動します。
起動中にCommand + Rキーを押し続けます。
OSは最終的にリカバリモードに入ります。
Utilitiesセクションからターミナルを開きます。
csrutil status と入力してEnterキーを押します。SIPが有効になっていることを確認してください。
csrutil disable と入力してEnterキーを押します。
再度 csrutil status と入力して、無効になっているかどうかを確認します。その後OSを再起動し、通常通り起動させます。これでバイナリ計測ツールを使用する準備が整いました。



まず、ghidra(最新バージョン、現在は9.1.2)と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
次に gradle と 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
新しいプロフィールファイルを作成``` $ sudo vi /etc/profile.d/gradle.sh
そして、以下のようにして、以降のログインごとに gradle を PATH に追加します。```
export GRADLE_HOME=/opt/gradle/gradle-5.2.1
export PATH=${GRADLE_HOME}/bin:${PATH}
すぐに実行するために、ログアウトする必要なく``` $ source /etc/profile.d/gradle.sh
これで 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$
Ghidraをダウンロードしたパスに合わせて調整する必要があるかもしれません。ビルドされた拡張機能は dragondance-master/dist にあります。
コマンドプロンプトスタイルの行単位スクリプト実行。
コンテキスト変更の認識
関数(ルーチン)ベースのカバレッジ可視化
実行フローの認識
スクリプトへのビルトイン機能の追加
独自のカバレッジデータベース形式により、高速な保存・読み込みとセッション上の最新変更の保持。
疑似コードのペインティング。(Ghidraはこれに対するAPIを提供していません。そのため、Ghidraのソースコードを調査して、実現する方法や回避策を見つける必要があります。)
UIの拡張
Oğuz Kartal (@0ffffffffh)
| なし |
| 最小パラメータ数 |
| 1 |
| 最大パラメータ数 | 1 |
| 説明 | 指定されたパスで現在の作業ディレクトリを設定します。 cwd以降の絶対パスを持たないすべてのインポート呼び出しでは、カバレッジファイルはアクティブな作業ディレクトリ内で検索されます。 |
| エイリアス | なし |
| なし |
| エイリアス | del |
| エイリアス |
| xor |
| エイリアス | なし |
| 最小パラメータ数 |
| 1 |
| 最大パラメータ数 | 1 |
| 説明 | 物理的なカバレッジファイルからカバレッジデータをインポートします。相対パスまたは絶対パスを取ります。または、以前に物理的にロードされたカバレッジ名。指定されたパスが絶対パスの場合、インポートはそのパスから直接ロードします。それ以外の場合は、現在の作業ディレクトリ以下を検索してロードします。どちらの場合も、インポートはカバレッジデータがすでにロードされているかどうかをそのパスを使用して確認します。すでにロードされている場合は、キャッシュされたカバレッジ変数を返します。または、指定された値が物理カバレッジの名前である場合、セッション内のカバレッジマップを検索し、存在すればカバレッジオブジェクトを返します。 |
| エイリアス | get, load |
| and |
| エイリアス | なし |
| or, union |