解説

インサイド .NET Framework [改訂版]

第5回 アセンブリのロードとセキュリティ

吉松 史彰
2003/07/30 改訂(改訂前の記事はこちら
Page1 Page2 Page3 Page4

 本稿は、2002/08/22に公開された記事を、.NET Frameworkの新しいバージョンである「.NET Framework 1.1」に対応させ、全面的に加筆・修正を行った改訂版です。

はじめに

Back Issue
1
マネージ・コード/アセンブリ/モジュール
2 アセンブリのアイデンティティ
3 アセンブリのロード
4 アセンブリとバージョン管理

 前回まで、厳密名付きアセンブリがロードされる手順を解説してきた。しかし、そもそもその手順は何らかのコードから型を参照し、その結果共通言語ランタイム(CLR)がアセンブリをロードする決断をして初めて行われるものだ。では、そもそもVisual Studio .NETなどで開発されたいわゆるEXEアプリケーションは、一番初めはどうやってメモリ上にロードされ、実行が始まるのだろうか。今回は、無の状態からCLRの環境を作り出すまでのWindows OSのローダーの仕組みと、そこから起動されるCLRの動作を解説し、その過程で行われるCLRのセキュリティ機能を概観する。

Windowsローダーの動作

 .NET FrameworkはWindows上で動作するアプリケーション実行環境だ。だが、Windows 2000とそれ以前のOSは、まだCLRの形がなかった時代に出荷されたOSだ。そのため、CLR上で動作するアプリケーション(アセンブリ)はWindowsの作法に従って起動されなければならない。CLRに対応するプログラミング言語のコンパイラは、その作法に従ったコードをモジュールに埋め込むようになっている(CLRの世界では、物理ファイルはモジュールであることを思い出してほしい)。

 Windowsの作法に従ったコードは、Portable Executable(PE)形式で出力されたファイルに入っている。この形式は、Windowsのローダーが読み込んでメモリに正しく配置する方法を知っている形式だ。このファイルの構造は、Microsoftが提供しているWindowsアプリケーションの開発キットであるPlatform SDKで配布されているWinnt.hにIMAGE_**_HEADERという構造体として定義されている。例えば、WindowsにロードできるPE形式のファイルの先頭には必ず“IMAGE_DOS_SIGNATURE”として定義されている定数が入っている(これは文字列にするとMZという2文字になる。MS-DOS 2.0のプログラム・マネージャであったMark Zbikowski氏の頭文字であるらしい)。何でもいいので手元にあるEXEファイルやDLLファイルをメモ帳で開いてみてほしい。先頭にMZが確認できるはずだ。

 PE形式のファイルには、決められたヘッダ構造がファイルの先頭に含まれている。32bitのWindows向けのPEファイルの場合、Visual C++またはVisual Studio .NETに付属しているdumpbin.exeというツールでヘッダ情報を見ることができる。例えば、「第3回 アセンブリのロード」で作成したuser.exeに対して、次のようなコマンドでPEファイルのヘッダを見ることができる。

% dumpbin.exe /headers user.exe

dumpbin.exe /headersの実行結果画面

 このヘッダでいろいろなことが分かるのだが、今回の議論でまず触れたいのは、「OPTIONAL HEADER VALUES」というセクションに入っている次の値だ。

230E entry point (0040230E)

dumpbin.exe /headers実行結果の「OPTIONAL HEADER VALUES」部分

 ここに書いてあるのは、(乱暴にいうと)Windowsのローダーがこのファイルをメモリ上に配置した後、最初に実行されるコードが入っているアドレスだ。Windowsのローダーはファイルをロードするとこのアドレスにジャンプして、そこから命令の実行を開始する。

 PE形式の大きな特徴は、ファイル上の配置と実行時のメモリ上の配置が(セクションごとに)同じになることだ。つまり、ファイルの中を見れば、メモリの中を見たのと同じことになる。それでは、user.exeで最初に実行されるコードはどんなものになっているのだろうか。それは、上記のアドレス(0x0040230E)に配置されるものを見れば分かる。先ほどのdumpbin.exe /headersの結果をたどっていくと、「SECTION HEADER #1」というセクション(セクションの名前は.text)には次のようなエントリがある。

314 virtual size
2000 virtual address (00402000 to 00402313)
400 size of raw data
200 file pointer to raw data (00000200 to 000005FF)

dumpbin.exe /headers実行結果の「SECTION HEADER #1」部分

 この内容は、メモリ上で0x00402000から0x00402313の間にロードされるコードは、このファイルの中の0x00000200バイト目から0x000005FFバイト目の間に入っているということを示している。先ほどの「entry point」に書いてあったアドレスは0x0040230Eなので、ファイル中の0x00000200バイト目から0x30Eバイトを足した場所、つまり0x0000050Eバイト目から書いてある機械語コードが、Windowsローダーが最初に実行するコードになる。user.exeの場合、ここは次のようなバイト列になっている。

FF 25 00 20 40 00

 これは、x86機械語命令で、アセンブリ言語に直すと次のようなコードを意味する。

jmp 0x00402000

 つまり、メモリ上の0x00402000番地に飛んで、そこからコードの実行を継続するというコードになっている。このコードが、かつて「.NETウイルス」が話題になったときによく紹介された「スタブ・コード」と呼ばれるものだ。この番地にロードされているコードは、次のようなコマンドで確認できる。

% dumpbin.exe /imports user.exe

dumpbin.exe /importsの実行結果画面

 つまり、0x00402000番地に入っているのはmscoree.dllに入っている_CorExeMainという関数である。mscoree.dllは.NET Frameworkのインストール時に%Systemroot%\System32に配置されるDLLで、user.exeの起動とともにメモリ上にロードされる。ロードされるアドレスは実行時に上書きされるため、実際には0x00402000にロードされるわけではない。実行時にどこにロードされるかは、bind.exeというプログラムで確認できる(このプログラムは通常、「\Program Files\Microsoft Visual Studio .NET\Common7\Tools\Bin」にインストールされる)。

% bind.exe -v user.exe

bind.exe -vの実行結果画面

 この場合、_CorExeMain関数は0x7917B865地点にロードされるということを示している。

 ここまでの動作をまとめると、次のようになる。

  1. (ダブル・クリックするなど、何らかの手順で)user.exeが起動される。Windowsはuser.exeをメモリ上に配置する。
  2. Windowsはuser.exeのヘッダを確認して、“entry point”のアドレスを確認する。
  3. Windowsはentry pointのコードを実行する。すると、すぐにmscoree.dllの_CorExeMain関数に飛ぶ。
  4. _CorExeMain関数の実行が始まる。

 なお、ここまでの解説はWindows XP以前のWindowsローダーが行う手順だ。Windows XPとWindows Server Familyの各OSは、CLRについてよく知っている。そのため、これらの新しいOSはスタブ・コードを必要としない。プログラムが開始されると、ローダーはまずPEファイルの中にCLRベースのプログラムであることを示す証拠を探し、それが見つかるとentry pointを無視してすぐにmscoree.dllをロードし、その_CorExeMain関数に処理を引き渡すようになっている。そのため、Windows XP上Windows Server 2003では上記のコードの動作は確認できない。また、これらのOSではスタブ・コードに感染するタイプの「ウイルス」は、感染はしても発症はしないこともここから分かる。

 ユーザー(あるいはほかのプログラム)から呼び出されたものがEXEではなくDLLだった場合、スタブ・コード(あるいはWindows XPローダー)が呼び出す関数は_CorDllMainという関数になる。この関数もやはりmscoree.dllに含まれていて、下記の手順をそっくりそのまま使ってCLRを呼び出す。


 INDEX
  解説 インサイド .NET Framework [改訂版]
  第5回 アセンブリのロードとセキュリティ
  1.Windowsローダーの動作
    2.CLRのロード
    3.署名の検証とプログラムの開始
    4.いくつものセキュリティ・チェック
 
インデックス・ページヘ  「解説:インサイド .NET Framework [改訂版]」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間