MinGW+GCCでImmunity Debugger Plugin開発

今度はImmunity Debuggerです。
OllyDbgを元にしているのでPlugin用のインターフェイスなどは
シンボル名を除いて大体同じです。


が、1.7系までと1.8系以降でエクスポートされている関数名などが変わっている
ため、対象バージョンによりプラグインを分ける必要があります。
更にPDKは1.7系の物までしか配布されていないので、1.8系は適当に書き換えて
使用します。一応問題無く動いているようですが何か変な可能性もあります。
(自分で使用してるAPIだけしか動作確認してないので)
なお、今回の動作や仕様確認はバージョン1.73と1.83で行っています。


PDKは以下の場所から取得できます。
もうメンテナンスされてないようなのでいつまで置かれているかは分かりませんが。


まずは差分を確認しましょう。
以下は、各バージョンのImmunity DebuggerとOllyDbgのプラグインでエクスポートする
必要のあるコールバック関数です(一部抜粋)。呼び出し規約はすべてcdeclです。

OllyDbg 1.10 Immunity Debugger 1.7X Immunity Debugger 1.8X
_ODBG_Pluginaction _IMMDBG_Pluginaction IMMDBG_Pluginaction
_ODBG_Pluginclose _IMMDBG_Pluginclose IMMDBG_Pluginclose
_ODBG_Plugindata _IMMDBG_Plugindata IMMDBG_Plugindata
_ODBG_Plugindestroy _IMMDBG_Plugindestroy IMMDBG_Plugindestroy
_ODBG_Plugininit _IMMDBG_Plugininit IMMDBG_Plugininit
_ODBG_Pluginmainloop _IMMDBG_Pluginmainloop IMMDBG_Pluginmainloop
_ODBG_Pluginmenu _IMMDBG_Pluginmenu IMMDBG_Pluginmenu
_ODBG_Pluginreset _IMMDBG_Pluginreset IMMDBG_Pluginreset

この通り1.7Xと1.8Xで関数先頭の_が除去されています。


更に以下がデバッガのexeがエクスポートしている関数です(一部抜粋)。
プラグインからはインポートする対象になります。
呼び出し規約は同様にすべてcdeclです。
Immunity DebuggerではPython拡張で用いたと思われるPy*のシンボル等が追加されていますが、
OllyDbg互換で書く分には特に気にする必要はありません。

OllyDbg 1.10 Immunity Debugger 1.7X Immunity Debugger 1.8X
_Addsorteddata _Addsorteddata Addsorteddata
_Addtolist _Addtolist Addtolist
_Analysecode _Analysecode Analysecode
_Assemble _Assemble Assemble
_Broadcast _Broadcast Broadcast
_Browsefilename _Browsefilename Browsefilename

こちらも同様で関数先頭の_が除去されています。


この辺の差違を吸収するため、1.7Xまで使えていたPDKのplugin.hを以下の通り修正します。
OllyDbgのPDKを元に書き換えても動くと思いますが、折角あるのでこちらを元にします。
コンパイル時に渡すバージョンでどちらの名前でエクスポートするか振り分けます。
Immunity Debugger専用にプラグインを書いている人はあまりいないと思うので
基本的にOllyDbgのシンボルに合わせてあります。

  // If you like Microsoft compiler, this will force byte alignment and verify
  // that character is set to unsigned.
  #ifdef _MSC_VER
  ...
  #endif
+ #ifdef __GNUC__
+   #pragma pack(1)
+   #ifndef cdecl
+     #define cdecl __cdecl
+   #endif
+   #undef _export
+   #if IMMDBGVER >= 180
+     #define ODBG_Plugindata      IMMDBG_Plugindata
+     #define ODBG_Plugininit      IMMDBG_Plugininit
+     #define ODBG_Pluginmainloop  IMMDBG_Pluginmainloop
+     #define ODBG_Pluginsaveudd   IMMDBG_Pluginsaveudd
+     #define ODBG_Pluginuddrecord IMMDBG_Pluginuddrecord
+     #define ODBG_Pluginmenu      IMMDBG_Pluginmenu
+     #define ODBG_Pluginaction    IMMDBG_Pluginaction
+     #define ODBG_Pluginshortcut  IMMDBG_Pluginshortcut
+     #define ODBG_Pluginreset     IMMDBG_Pluginreset
+     #define ODBG_Pluginclose     IMMDBG_Pluginclose
+     #define ODBG_Plugindestroy   IMMDBG_Plugindestroy
+     #define ODBG_Paused          IMMDBG_Paused
+     #define ODBG_Pausedex        IMMDBG_Pausedex
+     #define ODBG_Plugincmd       IMMDBG_Plugincmd
+   #else
+     #define IMMDBG_Plugindata      _IMMDBG_Plugindata
+     #define IMMDBG_Plugininit      _IMMDBG_Plugininit
+     #define IMMDBG_Pluginmainloop  _IMMDBG_Pluginmainloop
+     #define IMMDBG_Pluginsaveudd   _IMMDBG_Pluginsaveudd
+     #define IMMDBG_Pluginuddrecord _IMMDBG_Pluginuddrecord
+     #define IMMDBG_Pluginmenu      _IMMDBG_Pluginmenu
+     #define IMMDBG_Pluginaction    _IMMDBG_Pluginaction
+     #define IMMDBG_Pluginshortcut  _IMMDBG_Pluginshortcut
+     #define IMMDBG_Pluginreset     _IMMDBG_Pluginreset
+     #define IMMDBG_Pluginclose     _IMMDBG_Pluginclose
+     #define IMMDBG_Plugindestroy   _IMMDBG_Plugindestroy
+     #define IMMDBG_Paused          _IMMDBG_Paused
+     #define IMMDBG_Pausedex        _IMMDBG_Pausedex
+     #define IMMDBG_Plugincmd       _IMMDBG_Plugincmd
+
+     #define ODBG_Plugindata      _IMMDBG_Plugindata
+     #define ODBG_Plugininit      _IMMDBG_Plugininit
+     #define ODBG_Pluginmainloop  _IMMDBG_Pluginmainloop
+     #define ODBG_Pluginsaveudd   _IMMDBG_Pluginsaveudd
+     #define ODBG_Pluginuddrecord _IMMDBG_Pluginuddrecord
+     #define ODBG_Pluginmenu      _IMMDBG_Pluginmenu
+     #define ODBG_Pluginaction    _IMMDBG_Pluginaction
+     #define ODBG_Pluginshortcut  _IMMDBG_Pluginshortcut
+     #define ODBG_Pluginreset     _IMMDBG_Pluginreset
+     #define ODBG_Pluginclose     _IMMDBG_Pluginclose
+     #define ODBG_Plugindestroy   _IMMDBG_Plugindestroy
+     #define ODBG_Paused          _IMMDBG_Paused
+     #define ODBG_Pausedex        _IMMDBG_Pausedex
+     #define ODBG_Plugincmd       _IMMDBG_Plugincmd
+   #endif
+ #endif


ImmunityDebugger.defをOllyDbgの時と同様に変換します。
1.7Xと1.8X用のインポートテーブル分を別々に生成します。(差分は-Uオプション)

# dlltoolが処理できない不要な宣言削除、 関数名の_を外す
$ sed -e 's/^CODE.*//' -e 's/^EXETYPE.*//' -e 's/^DATA.*//' -e '/^$/d' -e 's/_//' ImmunityDebugger.def \
   > ImmunityDebugger.def.mingw
# gccで扱える形式に変換 (1.7X用)
$ i686-pc-mingw32-dlltool -U --dllname ImmunityDebugger.exe --input-def ImmunityDebugger.def.mingw --output-lib immdbg17.a
# gccで扱える形式に変換 (1.8X用)
$ i686-pc-mingw32-dlltool --dllname ImmunityDebugger.exe --input-def ImmunityDebugger.def.mingw --output-lib immdbg18.a


プラグインのソースをコンパイルする際にバーションを引数で渡して振り分けます。

# 1.7X用オブジェクトファイル生成
$ i686-pc-mingw32-gcc -funsigned-char -mwindows -c Bookmark.c -DIMMDBGVER=173 -o Bookmark_17.o
# 1.8X用オブジェクトファイル生成
$ i686-pc-mingw32-gcc -funsigned-char -mwindows -c Bookmark.c -DIMMDBGVER=183 -o Bookmark_18.o


リンク時にそれぞれのバージョンのライブラリにリンクします。

# 1.7X用DLL生成
$ i686-pc-mingw32-gcc -funsigned-char -mwindows Bookmark_17.o immdbg17.a \
    -o Bookmark_17.dll -W -shared -Wl,--dll
# 1.8X用DLL生成
$ i686-pc-mingw32-gcc -funsigned-char -mwindows Bookmark_18.o immdbg18.a \
    -o Bookmark_18.dll -W -shared -Wl,--dll


インクルードファイルとリンクするライブラリを振り分けてあげれば
一つのソースコードでOllyDbg 1.10も含む各バージョンで動くプラグイン
が生成できます。


既にバイナリになっている物はImmunity Debugger Plugin Fixer Tool等で
書き換えるしか無いですが、折角プラグインを作るなら最初からそれぞれで
動くバージョンを用意した方が良いんじゃ無いでしょうか。
ほとんど差が無いのでそれほど手間も掛かりませんし。