Article ID 835322

先にこちらの注意をお読み下さい(id:NyaRuRu:20040924#p1).

グローバルに提供される side-by-side アセンブリを適用しないアプリケーションは、マイクロソフトのソフトウェア更新プログラムで修正される問題に対して脆弱になる可能性がある
http://support.microsoft.com/default.aspx?scid=kb;ja;835322

このkbは米国で9月14日に公開され,日本にも9月16日にはスピード翻訳されています.タイミング的にMS04-028[緊急]:JPEG 処理 (GDI+) のバッファ オーバーランにより、コードが実行されると無関係とは言えないでしょう.
さて上のkbによれば特殊な設定ファイルを使うことで敢えて古いSxS DLLにリダイレクトできるとのことです.興味があって試してみたんですがこれが中々大変で,成功するまでには案外苦労が必要でした.そもそも手元の環境ではWindows Server 2003でしかうまくいっていません.Windows XPで管理者権限でアプリケーション構成ファイルを設定するというのが,具体的にどのあたりで「管理者権限」を使うのかまだ分かっていなかったりします.
Windows Server 2003Windows UpdateでMS04-028の修正がインストールされますが,このとき新しいSxS版GDIPlus.dllとともに発行元構成ファイルというものがインストールされます.発行元構成とはアセンブリの開発者・販売元が提供する設定ファイルで,実際C:\WINDOWS\WinSxS\Policies\以下に見ることができるでしょう.以下はGdiplus.dllバージョン5.2.3790.136に関する発行者構成ファイルです.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity
      type="win32-policy"
      name="policy.1.0.Microsoft.Windows.GdiPlus"
      version="5.1.3790.136"
      processorArchitecture="x86"
      publicKeyToken="6595b64144ccf1df"/>
   <dependency>
      <dependentAssembly>
         <assemblyIdentity type="win32" name="Microsoft.Windows.GdiPlus" processorArchitecture="x86"/>
         <bindingRedirect oldVersion="1.0.0.0-1.0.3790.136" newVersion="1.0.3790.136"/>
      </dependentAssembly>
   </dependency>
</assembly>

このファイルが意図するのはGdiplus.dllバージョン1.0.0.0から1.0.3790.136までのアセンブリ参照を全て1.0.3790.136にリダイレクトせよというものです.
このようなバージョンリダイレクトポリシーは他にも存在し,それぞれ「マシン構成ファイル」と「アプリケーション構成ファイル」と呼ばれています.SxSを利用するアプリケーションはアプリケーションマニフェストで依存するアセンブリとその識別情報(バージョン,公開キートークン,カルチャ情報など)を記述することができますが,実際にはこのとき要請されたアセンブリは

  1. アプリケーション構成ファイル
  2. 発行元構成ファイル
  3. マシン構成ファイル

の順にリダイレクトが行われ,結果的には要請されたバージョンとは異なるDLLが実行さる,ということも十分に起こりえます.ここで「アプリケーション構成ファイル」はその名の通りアプリケーションごとに作成できて,拡張子は"exe.config"となります.さてこの「アプリケーション構成ファイル」で敢えて古いDLLにリダイレクトしようとしても,結局はそれ以後の構成ファイルで最新版のGDIPlus.dllに振り向けられてしまうことになります.しかしこれには抜け道が存在します.

<publisherPolicy apply="no" />

アプリケーション構成ファイルに上のような記述を行うことで発行元構成をバイパス出来て,これによってアプリケーションマニフェストに記された本来のDLLを参照できる可能性が生まれてきます.
このような強力な迂回機能が許される背景に,不具合を修正するためだけのはずのマイナーアップデートが簡単に互換性を破壊してしまうという過去の教訓があります.コンポーネントをアップグレードしたため特定アプリケーションで問題が発生した場合,暫定的にそのアプリケーションが以前のバージョンのコンポーネントを使用することを許可出来ます.
しかしながら迂回機能はセキュリティ上の弱点にもなりかねません.そこでこのバイバス設定はMicrosoftまたは管理者しか行うことができなくなっています.さらにWindows Server 2003では条件が強化されていて,予めアプリケーション互換性データベースに登録された一部のアプリケーションにしかバイパスは許可されなくなっています.アプリケーション互換性データベースは一般的にはMicrosoftが作成するものですが,Compatibility Administratorを利用すれば管理者権限で独自のエントリを追加することもできます(id:NyaRuRu:20040703#p1).実際にテスト用に作ったアプリケーションがEnableAppConfigフラグの適用を受けるようなエントリを作成してみました.

EnableAppConfigを設定

ここまで来てやっと完成です.あとはGDI+を用いるサンプルプログラムのフォルダに以下の設定ファイルを用意して実行することで,Windows Server 2003にてGDIPlus.dllバージョン1.0.0.0がロードされることを確認しました.

アプリケーションマニフェスト
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
      version="1.0.0.0"
      processorArchitecture="X86"
      name="GDIPlusTest"
      type="win32"/>
    <description>Description</description>
    <dependency>
      <dependentAssembly>
        <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.GdiPlus"
          version="1.0.0.0"
          processorArchitecture="x86"
          publicKeyToken="6595b64144ccf1df"
          language="*"/>
      </dependentAssembly>
    </dependency>
</assembly>
アプリケーション構成ファイル
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
  <windows>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <publisherPolicy apply="no"/>
    </assemblyBinding>
  </windows>
</configuration>
次回は別の観点からプライベートにdeployされたGDIPlus.dllがロードされる条件について調べてみたいと思います*1

*1:ここのとこDirectXネタを全然やってなくてすみません.次回はちょっぴりゲームプログラミング(というよりDLL Injectionネタ)に絡みます