遅延読み込みのインポート

libを使い、静的にリンクするようにビルドされたバイナリは、WinMainやDllMainなどよりも早い段階で、そのDLLのロードが行われる。DLLが見つからない場合、警告ダイアログを表示して起動に失敗する。この辺の、俗にローダと呼ばれる処理についての理解が浅く、詳しいことは書けないのだけれど、本日は少しこれでハマった。

detoured.dll を静的にリンクしている Hook.dll がある。このDLLをSetWinwodwsHookExを利用して広く読み込ませるとき、SetWinwodwsHookExを実行するプロセスは Hook.dll と detoured.dll 両方をロードできる場所に無ければいけない。これは、まぁ当然のこと。

CreateRemoteThreadで Hook.dll をロードさせる場合も同じ。ロードするプロセスは detoured.dll をロードする必要がある。しかし、これはどうしたらいいのだろう。


Hook.dll はフルパスを渡してロードするが、detoured.dllはプロセスの環境変数*1からパスを検索し、ロードしようとする。そのためパスを渡すことができない。対応として考えた結果の選択肢。

  1. detoured.dll を %PATH% などのディレクトリに置く
  2. ロードするプロセスに事前に SetCurrentDirectory を使わせ、detoured.dll のあるディレクトリにカレントを移動させておく
  3. ロードするプロセスに事前に detoured.dll を事前に読み込ませておく

この辺だろうが、どれも残念な感じがする。ロードするプロセスがひとつであれば3は良さそうだが、CreateRemoteThread→SetWinwodwsHookExで配信したい場合は結構胡散臭い実装が必要になる*2

問題なのは、ユーザコードが走る機会が無いまま静的DLLのロードを実行し、失敗してしまうことで、これが回避できればHook.dllのDllMainでdetoured.dllを読むことができる。


で、今回の場合、Hook.dll に手を加えることができるので、/DELAYLOAD (遅延読み込みのインポート)というリンカオプションで何とかなるようだ。これを使うと必要になるまでDLLをロードしなくなる。

*1:正確なところは知らない。PEB::RTL_USER_PROCESS_PARAMETERS::DllPathかなと思って書き換えてもみたけど、関係なさそうだった。

*2:たぶん、まずダミーDLLを配信して、ダミーDLLのDllMainでdetoured.dllをロードするような格好になると思う。