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);
