この記事はKogakuin Univ Advent Calendar 2022 - Adventar 16日目です。

こんにちは。
12月にしか更新されないことで定評のあるブログです。

1年くらい前に学生団体(KogCoder)でGhidraのデバッガ関係入門講座みたいなものをやったのですが、未だに日本語情報があんまりなさそうなのでアドベントカレンダーのネタに流用共有したいと思います。

そもそもGhidraってなにさ?

A software reverse engineering (SRE) suite of tools developed by NSA's Research Directorate in support of the Cybersecurity mission

https://ghidra-sre.org/

NSA(アメリカ国家安全保障局)が作ったソフトウェアのリバースエンジニアリング用のソフトです。
OSSかつ無料で使うことができますが、有料ソフト並みの機能を備えています。
IDA Pro(クソ高リバースエンジニアリング用ソフト、機能はとても優秀)をお持ちでない貧民の心強い味方です。

Ghidra昔話(Ghidra 9.x系)

Ghidraは元々NSAがマルウェア解析用に内部で使っていたものを一般公開したものなので静的解析に特化した機能開発がされていました。
その関係上、一般公開された当初のGhidraにはデバッガがなかったのす。

ですが多くの希望があったのかGhidra 10.0の公開に合わせてデバッガ機能が実装されました。やったね。
これによって解析結果と今動いているコードを突き合わせる作業が楽になりました。

しかし、Ghidraリリース当初になかった機能であるため日本語の情報が大変少なくなっております。
今回の記事では実際に実行ファイルをデバッガで実行しながら最低限の操作説明をしたいと思います。
なお、今回はWindowsでやりますがLinuxでも大きく変わることはないと思います。

使う実行ファイル

これ

なお、解析作業は今回の本旨から逸れるためpdbファイルをセットで付けています。
exeファイルとpdbファイルを同じフォルダに置いた状態でGhidraに投げると関数名や変数名等がいい感じになります。

中身

だいたいこんなのです

const bool show_flag = false;

char flag[] = {172, 192, 161, 198, 189, 217, 234, 136, 253, 154, 253, 206, 188, 227, 141, 185, 215, 186, 138, 213, 162, 150, 253, 201, 187, 143, 225, 156};

void decrypt() {
    int key = 0xca;

    for (int i = 0; i < sizeof(flag); i++) {
        flag[i] ^= key;
        key ^= flag[i];
    }

    return;
}

int main() {
    if (show_flag) {
        puts("Congrats!");
        decrypt();
        puts(flag);
    }
    else {
        puts("You can't reach to flag. Hahaha");
    }
    _getch();
    return 0;
}

さてコードを読むとだいたい以下のことが分かると思います。

以降は、チェックをすっ飛ばしてflagを表示する処理に行くことを目標として進めていきます。

手順

新入生でも分かるを目標に作ってたのでかなりくどめの説明になってます
また、Ghidraに解析対象を登録して解析する作業は既に終わっているものとします。

1. デバッガを開く

まず、最初の画面から虫のマークを押してデバッガー画面を開きます。
h:440

開けたらこんな画面のはずです。
h:480

2. ファイルを開く

メニューバーの[File]→[Open...]を選択します。
h:480

インポートしたファイルを選択し[OK]を押します。
h:480

3. デバッガと接続する

この状態ではデバッガでどのファイルを開くかを指定しただけなので特に操作ができません。
なので、操作するデバッガと接続する必要があります。

まず、メニューバーの[Debugger]→[Debug [インポートした名前]]→[in dbgeng locally IN-VM]を選択します。

IN-VMとGADPの違いはデバッガを立ち上げるプロセスがGhidra本体か仲介用のソフトウェアを挟むかどうか(のはず)です。
所感としてはGADPだとたまに謎の不具合が出ますが、その代わりにデバッグ先が強制終了したときに巻き込まれてGhidra側に影響が出るようなことが少ないと感じました。

h:400

その後は、2回くらいなんかでてくるのでそのままOKを押します。
h:220

実行時引数を渡したい場合はここでどうにかして下さい。
h:140

4. ブレークポイントを設定する

さてこれでデバッグできるようになった訳ですが、このまま実行しても問答無用で終了するだけです。
なのでとりあえずmain開始時にブレークポイントを入れて処理を止めましょう。

Symbol Tree から[Functions]→[main]を選択します。
すると、Listing と Dynamic が表示するアドレスがmainのアドレスになります。
h:440

流石に機械語のまま読むのはつらいので、Dynamicのウィンドウの55 8b ec ..部分の55で右クリックし[Disassemble]を押します。

h:400

55 ..push rbp ; ..といった関数の開始地点によくある処理の機械語表現なので覚えておくと何かの役に立つかもしれません。

またASLRの関係でアドレスは毎回変化します。

次に、Dynamic のディスアセンブルした部分の最初(55 PUSH RBP)を右クリックし、[Set Breakpoint]→[SW_EXECUTE]を選択します。

h:400

SW_EXECUTEとHW_EXECUTEの違いはソフトウェアブレークポイントを使うかハードウェアブレークポイントを使うかどうかです。
ハードウェアブレークポイントのほうが何かと優秀なイメージですが設定可能な個数に上限があります。

ここはそのままOKで大丈夫です。
h:300

5. main関数まで実行

Objects ウィンドウのResumeボタンを押す、以上。
h:440

6. アセンブリ確認

さて、処理を飛ばすには「どこから」「どこに」飛ばすかを理解している必要があります。
それを調べるためにアセンブリを読みましょう。

h:480

それぞれのアセンブリはだいたいこんな意味です。

flagを表示させるのが目標なので、00401067に飛べばよさそうですね。

7. 処理を飛ばす

ということで実際に処理を飛ばしていきます。

まず、Interpreter ウィンドウの下にあるRegistersを押し、レジスタ一覧の表示に切り換えます。
h:440

次に、Enable editting of recorded register values ボタンを押し、レジスタの変更を許可します。
h:440

そして、RIP のValueをダブルクリックし、現在の値+7の値に書き換えます。
RIPは現在実行中のアドレスを示すレジスタです。
また、+7なのはmain関数の開始位置とflag表示処理の開始位置の差分が7であるためです。
書き変える際に値が16進数表記されていることに気を付けましょう。

h:340

8. いざ実行

Objects ウィンドウのResumeボタンを押します。
うまくいっていればflagが表示されるはずです。

flagはこれを書いてたときの僕の心の声です。

まとめ

いかがでしたか(定型文)
今回はGhidraのデバッガの使い方を紹介しました。

やったこと自体は他のデバッガでもできることですが、ディスアセンブラの優秀さの格が他のデバッガの比にならない(それはそう)なのでもっと大きなプログラムだと力を発揮しそうです。

また、日本語の情報がないと言いましたが英語の情報は公式ドキュメント含めけっこうあるので困ったらそっちを見るといいと思います。

では、明日のアドベントカレンダーの枠を誰かが埋めてくれることを信じつつ筆を置かせてもらいます。