VirtualDLLの仕組み

VirtualDLLというものを知っているだろうか?
自分も由来などは知らないが、asciiの記事によるとWindows7から導入されたDLLのAPI呼び出しを別のDLLへ転送するものとか。
ちょっと気になったので仕組みを色々調べてみた。

※ちなみにMSDNをVirtualDllで検索しても何も出てこない
なのでVirtualDllというのは非公式な造語の可能性がある
が、他にそれらしい名称が見つからなかったので本記事ではVirtualDllと呼称することにする。

VirtualDLLの本体はどこか

まずAPIの転送と聞くと、.defファイルに「#{API名} = #{転送先DLL名}.#{転送先API名}」と記述することで行えるエクスポート転送が思い当たる。
実際Kernel32.dllなどを覗いてみるとapi-ms-win-base-bootconfig-l1-1-0.dll(Windows8の場合)など、見覚えのない数個のDLLへの転送が行われている。
だが、実際に転送先のdllを覗いてみてもさらに自分自身へ転送して無限ループしているだけで実体に行き着かない。
実際にLoadLibraryしてみるとadvapi32.dllがロードされるのだが、api-ms-win-base-bootconfig-l1-1-0.dllの中にはadvapi32.dllなどという文字列は出てこないうえ、暗号化の類を施している形跡もない。
それどころか、中にはファイルとして存在しないdllもあり、通常のエクスポート転送だけではないことがうかがえる。

上記のasciiの記事によればapisetschema.dllを介して転送されるとある。
中をのぞいてみると、確かにms-win-base-bootconfig-l1-1-0(なぜか最初のapi-が削られている)や、advapi32.dllという文字列が見える。
どういうことかは不明だが、これを介して特殊な転送が行われているらしいことは確かなようだ。

※apisetschema.dllをwindows8 64bit上で32bitアプリから開く場合C:\windows\sysnative\apisetschema.dllでないと開けないことに注意
windows7ならばSystem32とSysWOW64の両方に存在するので問題はない

どのように転送しているか

apisetschema.dllを実際に調べてみると.textや.rsrcなどの通常のセクションに混ざって.apisetという見覚えのないセクションが含まれているのがわかる。
結論から言ってしまうとここがまさにVirtualDllの要なのだが、解析した道筋を書いていくと無駄に長いうえ対して得るものもないので
コードだけ示す。

ReadApiSetSchema.zip

簡単にいうと、転送元dll名と転送先dll名のペアがいくつも格納されているだけというシンプルな構造。
格納の仕方にいくつか癖があるが、それは重要な部分ではないので、気になる方は実際のソースコードを読んでみてください。

ちなみに、最初四文字がapi-かext-だと決まっているようで、apisetschema.dll内では省略されている。
別にそんなところ節約したってしょうがないのだから含めてしまえばいいと思うのだが、なにか別の事情があるのだろうか……?

まとめ

先頭がapi-かext-で始まるdllは、まずapisetschema内のテーブルを参照し
転送先が記述されている場合はそちらのdllをロード。
そうでない場合に通常のdllロードが行われる。
それがVirtualDllの仕組みのようだ。

逆にいえば、apisetschemaを書き換えることができれば、一部処理を自作dllに向けることも可能。
全アプリケーションに対してdllインジェクションしようとするなら、割と視野に入るかもしれない。
もっとも、そんなアプリケーションはよっぽどのことがない限りユーザーに嫌われますが。