wivern ロゴ

サイバーセキュリティ研究所

サイバーセキュリティを中心に、軍事、防犯、サーバーの管理と監視、その他、最新技術を研究しています。


「リバースエンジニアリングバイブル」勉強メモ#7

MFC リバーシング

「リバースエンジニアリングバイブル」#7

前回に続き、「リバースエンジニアリングバイブル」の勉強メモ第7弾です。 実行環境は、Windows7(64bit版)です。実行環境は、Windows7(64bit版)です。

7章 MFC リバーシング
[リバースエンジニアリングに必要な MFC の構造]
・メッセージ処理の仕組み
 プロシージャに対応するハンドラーを見つけることがリバースエンジニアリングをするときに最も重要である。
・初期化ルーチン探索
・rdata + 仮想関数
・MFC フレームワーク  MFC で作成されたプログラムからリバーサーが必要な機能を見つけ出すには、MFC の簡単な構造を理解し、メッセージハンドラーや仮想関数などの位置を把握する作業が必要である。
 通常は、特定のボタンを押したときや初期化ルーチンが解析対象になるので、その流れに沿って行えばよい。

WinMain() のメッセージループ部分は、MFC クラスの集合体である MFC32.DLL に入っている。
新しいメッセージがあるかどうかを頻繁にチェックしていて、キューにメッセージが入ってくると、そのアドレスを確認して EXE 上の当該アドレスにジャンプさせた後で機能を有効化する。

EXE ファイルを逆アセンブルしたときに、MFC で開発されたかどうかを確認することは難しくない。
MFC 関連の DLL をインポートしたかどうかを、PE ヘッダーの中身で確認すれば簡単にわかる。
→PE 関連ツールで対象の EXE ファイルの[Imported Function] の部分を見る( DLL ファイル名を確認)。
→逆アセンブルすると、MFC のクラスが .rdata セクションに保管されえている。

MFC リバースエンジニアリングをするときには、まず MFC ライブラリの登録をしておかなければならない。
→シンボルに該当する lib ファイルを OllyDBG に設定するだけでいい。
[誤植]
p.149 2 行目
誤:「まず MFC ライブラリの登録しておかなければならない。」
正:「まず MFC ライブラリの登録しておかなければならない。」
PE Tools の Import を見て、どのような MFC のバージョンで作られているかを確認し、OllyDBG に登録されていない lib ファイルであれば、リバースエンジニアリングする前に、必ず登録の手順を行ったほうがよい。

[MFC の初期化ルーチン探索]
基本的にダイアログプロジェクトだと仮定した場合、MFC の初期化ルーチンは OnInitDialog() で行われる。
作成した OnInitDialog() は、CDialog の OnInitDialog() から始まっている。
CDialog は MFC の基本クラスであり、lib ファイルを利用して位置が把握できる。
CDialog::OnInitDialog が呼び出されている場所を把握すれば、作成したクラスの OnInitDialog() が見つかる。
[誤植]
p.151 下から 2 行目
誤:「作成したクラスの InitDialog() が見つかるというわけだ。」
正:「作成したクラスの OnInitDialog() が見つかるというわけだ。」
[ボタンハンドラーを見つける]
MFC のハンドラーで生成された関数はほとんどが仮想関数である。
そのため、.rdata セクションにテーブルが保管される。
継承の機構により、関数のエントリポイントが「push ebp」などで始まらない場合もある(呼び出された場所が決まっていないから)。
つまり、バイナリに変換されたとき、コンパイラはこの関数を「完全な関数」として取り扱わない。
したがって、関数のエントリポイントのパターンを考えて関数の開始場所を探そうとすると、そのハンドラーは永遠に見つからない可能性がある。

[メッセージマップの探索]
メッセージマップの構造体(.rdata にある)
struct AFX_MSGMAP_ENTRY
{
  UNINT nMessage ;  // ウィンドウメッセージ(WM_PAINT、WM_CREATE など)
  UNINT nCode ;    // 制御コードまたは WM_NOTIFY コード(MFC 3.0 以降)
  UNINT nID ;     // メッセージを発生させたコントロール ID (Windows のメッセージの場合は 0)
  UNINT nLastID ;   // コントロール ID の範囲を表すエントリ(MFC 3.0 以降)
  UNINT_PTR nSig ;  // nMessage に対応する関数のシグネチャ
  AFX_PMSG pfn ;   // nMessage を処理する関数のポインター(ハンドラーのアドレス)
} ;

ハンドラーを1つ作るごとに、この構造体に相当するものが生成される。
最終的な目標は、一番最後の項目(ハンドラーのアドレス)の場所を見つけること。
nID と nLastID には、プログラマがボタンを作成したときのリソース ID が入るので、必要なボタンに対するリソース ID を手に入れた後で、そのリソース ID を持つ構造体を見つけて「AFX_PMSG pfn」の値(ハンドラーのアドレス)を取得することを最終的な目標とすればよい。
リソース ID は、開発時に resource.h ファイルに含まれているインデックス。
「メッセージマップ構造体はリソース ID で連続した2つのフィールドを持っている。」
→nID と nLastID には同じ値が入っているという意味か?
[ヘッダーファイルの活用]
コードを作成するときにインクルードするプラットフォーム SDK の多くのヘッダーファイルを、逆アセンブルするときの補助として利用すれば、それぞれの値が何を指し、数値が何のフラグなのかを簡単に解析できる。
SendMessage() のメッセージである WM_ 定数の値は、WinUser.h ファイルに宣言されている。
API 関数に見えても実際には SendMEssage() で実装されたものは多い。


記述に際しては、細心の注意をしたつもりですが、間違いやご指摘がありましたら、こちらからお知らせいただけると幸いです。


→CTF for ビギナーズ 2016 博多に参加しました
←「リバースエンジニアリングバイブル」勉強メモ#6


« 戻る