12: DbgViewでデバッグを楽にする
概要
前回の記事で上手くいかなかったり、その他インジェクションする DLL関連で上手く動かなかった場合、Printデバッグもできないため結構めんどくさい。MessageBox
は使えるが、変数展開ができないので不便。今回は、DbgViewというツールを使ってDLLをデバッグする方法を紹介します。
Windows対象で、
C++
を言語として用いる。ソースコードはまとめてページの一番下にある。
DbgViewを使ったシンプルなデバッグ
DbgViewというのはツールの名前で、このサイトからダウンロードする事ができます。 ダウンロードするとDbgView.exeがあるので、それを起動すると以下のような真っ白い画面が出てくると思います。 DbgViewというのは、Windowsのマシンのデバッグ出力を拾って出力してくれるツールです。つまり、自分たちのDLLのデバッグ出力に関わらず、自分のPC上の全デバッグ出力がここに表示されます(もちろんフィルタする事もできます)。
デバッグ出力は、
OutputDebugString
というWindowsAPIにより出力する事ができます。試しに、何か適当なDLLに
OutputDebugString
を入れて何か出力してみましょう。自分は
前回
作成した、プレイヤー無敵化兼敵へのダメージ倍増DLLに以下のように追加して実行してみます。
すると、以下のようにDbgViewの方にその内容が出力されていることがわかります。
変数展開のしやすいデバッグ出力にする
これで文字列は表示できましたが、例えばint hoge=10; printf("%d", hoge)
みたいな変数展開ができません。しかし、以下のように
OutputDebugString
をラップして関数を作ってしまえば、DbgPrint("%d", hoge)
のようにして、
変数展開ができるデバッグ出力をすることができます。
行数は少ないですがトリッキーなコードなので説明すると、まず引数のconst char* fmt, ...
の...
というのは、
可変長引数を表す記号です。C言語で同じみのprintf
も、printf(const char* fmt, ...)
のように内部では定義されているらしいです。続いて、
va_list v1;
va_start(v1, fmt);
ですが、これはva_list
型の変数v1
を最初の行で用意し、va_start
マクロを使って、
第二引数の可変長引数の先頭へのポインタ(今回はfmt
)を、第一引数のv1
の一つの変数にまとめています。変数をまとめると言っても、
va_list
は実質、char *
なのでポインタをそのまま渡しているだけではありますが、
va_list
型の変数にまとめる事により、次のvsnprintf
などの特殊な関数が使えるようになります。次の、
vsnprintf
の前に、snprintf
関数がどんな挙動なのかを説明すると、以下のような感じです。
char buf[100];
snprintf(buf, sizeof(buf), "1+2=%d", 3); // bufに"1+2=3"という文字列が入る
つまり、第三引数以降のフォーマットストリング(変数展開できる文字列)を、第一引数のchar配列に格納する関数がsnprintf
です。しかし、今回のケースだと
snprintf
の第三引数にはfmt
を入れれば良いですが、第四引数はva_list
型の
引数が一つにまとめられた特殊な値になっています。この第四引数にva_list
型の変数を指定できるようになっているのが、
vsnprintf
というわけです。よって、vsnprintf(buf, sizeof(buf), fmt, v1)
により、buf
に変数展開された文字列が入る事がわかります。最後に、可変長引数を使い終わったタイミングで、
va_end
マクロを呼び出す必要があります。関数呼び出しをアセンブラ単位で考えると、リターンアドレスと同時に引数もスタックに積むので、引数が可変長だとリターンアドレスがどこにあるのか よくわからなくなってしまいます。これを解決するために、すなわちちゃんと関数からリターンするために
va_end
を呼び出す
必要がありますが、具体的にどんな処理をしているのかまでは自分はわかりません。後は、通常通り
OutputDebugString(buf)
をするだけです。では実際に以下のようにコードを書いて、DbgViewにアドレスの情報を出力してみましょう。
(ちなみに、
%I32X
というのは32bitの16進数を表すフォーマットです。)
これで、ビルドしてインジェクトすると、以下のように変数展開されて表示されていることがわかります。
DbgViewでデバッグ出力をフィルタ掛け
先程からデバッグ出力を見てわかる通り、例えばCheatEngineのデバッグ出力とかが混じって見にくいです。DbgView上で、
Ctrl-l
を押すとフィルタをかけるようのウィンドウが出てくるので、ここに例えばゲームのプロセスID
を入れれば、DLLのデバッグ出力だけがキレイに表示されたりします。
ソースコード
変数展開可能なデバッグ出力関数
void DbgPrint(const char* fmt, ...) {
char buf[256];
va_list v1;
va_start(v1, fmt);
vsnprintf(buf, sizeof(buf), fmt, v1);
va_end(v1);
OutputDebugString(buf);
}
// 使用例: DbgPrint("GameLogic.dll Address: 0x%I32X", baseAddr);