PE インスペクター: Windows EXE・DLL ビューア
dumpbin や pefile と同じことを、バイト列から直接、ブラウザの中だけで行います。先頭の MS-DOS ヘッダーは化石で、意味を持つ フィールドはオフセット 0x3C の e_lfanew だけです。そこから本物の "PE\0\0" 署名と COFF ヘッダーが始まります。以降、マシン種別(x86、x86-64、ARM64、ARM64EC、Itanium、RISC-V ほか)、オプションヘッダーが PE32 か PE32+ か、ファイルヘッダーに DLL 属性があるか、ローダーがどのサブシステムで起動するか(Windows コンソール、Windows GUI、ネイティブ、EFI 系)が分かります。堅牢化の行は ELF 側の checksec に当たるもので、DllCharacteristics から読み取ります。ASLR(DYNAMIC_BASE)、高エントロピー ASLR(64 ビットイメージの再配置のランダム性を 8 ビットから 17 ビット以上へ引き上げます)、DEP/NX(NX_COMPAT)、Control Flow Guard(GUARD_CF)、構造化例外処理を抑止したか(NO_SEH)、整合性の強制、そして証明書テーブルがあるかどうかです。セクションは仮想アドレス、仮想サイズと実データサイズ、デコードした属性とともに並び、書き込みと実行の両方が可能なものには印を 付けます。パッカーやインストーラー、自己書き換えコード以外にその組み合わせが必要な理由はまずないからです。インポートは DLL ごとに まとめて関数名を示し、序数でインポートされて名前が記録されていない項目は #序数 と表示します。エクスポートはライブラリが自ら名乗る 名前、序数ベース(1 でないことがよくあります)、すべてのエクスポート名を示します。限界もはっきり書いておきます。証明書 ディレクトリが空でないのは、ファイルが署名を持っているという意味だけで、その署名が有効だとも信頼できるとも言っていません。Authenticode の検証には証明書チェーンとタイムスタンプ局が要り、ここでは扱いません。リンク時刻もリンカーが書いたフィールドに すぎず、再現可能ビルドでは定数やハッシュが入ります。壊れたファイルは中途半端に読まずに拒否します。MZ マジックが違う場合、DOS ヘッダーが PE 署名を指していない場合、オプションヘッダーのマジックが不明な場合、セクションがファイルにないデータを要求する 場合、それぞれ名前の付いたエラーになります。
使い方
- ファイルを枠にドロップします。.exe、.dll、.sys ドライバー、.ocx コントロール、.efi バイナリはどれも同じように解析されます。拡張子で決まるわけではありません。
- ヘッダーのタイルでアーキテクチャ、PE32 か PE32+ か、EXE か DLL か、サブシステムを確認します。同じプログラムのコンソール版と GUI 版はここだけが違います。
- 堅牢化のチップを見ます。現行の Windows ツールチェーンは ASLR、DEP、CFG を既定で有効にするため、新しいバイナリで黄色が出たら配布前に理由を説明できるようにしておきます。
- インポートから必要な機能を探します。WS2_32 はソケット、WININET や WINHTTP はウェブ通信、ADVAPI32 はレジストリやサービス、CRYPT32 は証明書を意味します。
- セクション表で書き込みと実行が同時に可能なものを、データディレクトリで証明書・デバッグ項目・CLR ランタイムヘッダーを確認します。最後のものは他の読み方そのものを変えます。
よくある質問
- 署名のチップが出ています。安全なファイルということですか。
- いいえ、そこは区別が要ります。このチップは証明書データディレクトリが空でないこと、つまり最後のセクションの後ろに Authenticode のブロブが付いていることだけを示します。そのブロブが前のバイト列と一致するか、署名証明書が Windows の信頼するルートまでつながるか、署名時点で証明書が有効だったか、署名者がファイルの主張どおりの相手かは何も分かりません。自己発行の証明書で署名し直しても、署名後に中身を書き換えてハッシュが合わなくなっても、ディレクトリは埋まったままです。きちんと検証するにはチェーン全体と失効情報、多くの場合は信頼できるタイムスタンプの副署名まで必要で、これはネットワーク処理であり、ファイルを絶対にアップロードしないと約束したブラウザツールの範囲外です。
- 高エントロピー ASLR は通常の ASLR と何が違うのですか。
- 通常の ASLR も読み込み時にイメージをランダムなベースへ動かしますが、32 ビット Windows では実際にランダムなビットが 8 ビット程度しかなく、総当たりで抜けてしまいます。HIGH_ENTROPY_VA は 64 ビットのアドレス空間のどこに置いても安全だとローダーに伝えるもので、ランダムなビットが 17 ビット以上になり推測が現実的でなくなります。この旗は DYNAMIC_BASE も立っている PE32+ イメージでのみ意味を持ち、さらにプログラムがポインタを 32 ビットに切り詰めないことが前提です。リンカーが /HIGHENTROPYVA の後ろに置いている理由であり、32 ビット時代のコードを引き継いだ 64 ビットバイナリがあえて外していることがある理由でもあります。
- インポートした関数が名前ではなく #12 と表示されるのはなぜですか。
- そのファイルが序数でインポートしているからです。サンク配列の各要素は最上位ビットがフラグのマシンワードです。そのビットが立っていれば下位 16 ビットが序数で、インポート側のファイルには名前がどこにも保存されません。ビットが下りていれば残りはヒント/名前ペアを指す RVA で、名前がそのまま入っています。序数インポートは解決がわずかに速く古いコードでよく使われました。どの API を呼んでいるか隠したい場合にも現れます。このツールは推測せずに生の序数を出します。序数と名前の対応はエクスポート側の DLL にあり、インストールされているバージョンによって変わるからです。
- 書き込みと実行の両方が可能なセクションがあります。どの程度まずいのですか。
- 目を留める価値はありますが、それだけでは証拠になりません。通常のコンパイラとリンカーは .text を読み取りと実行、.data を読み取りと書き込みにし、両方を重ねません。DEP はまさにその組み合わせを例外にするために存在します。IMAGE_SCN_MEM_WRITE と IMAGE_SCN_MEM_EXECUTE が同時に立つセクションは、たいてい実行時にコードを自分のセクションへ展開してそこへ飛ぶランタイムパッカーによるものです。UPX はそのセクションを UPX1 と名付けます。古いインストーラー、自己書き換え型の保護、イメージ内に確保する JIT も原因になります。どれも想定していないなら、動かす前に理由を理解しておくべきです。
- リンク時刻が 1970 年や未来の日付になっています。壊れているのですか。
- まず違います。TimeDateStamp はリンカーが埋める 1970 年起点の 32 ビット秒フィールドですが、現代のツールチェーンはこれを日付として扱わなくなりました。再現可能ビルドは同じソースから常に同一のバイト列が出るようにゼロや固定値を入れますし、MSVC の /Brepro は入力のハッシュを書くので、暦の上では任意の、しばしば遠い未来の点に落ちます。ツールチェーンを推し量るなら Rich ヘッダーやデバッグディレクトリのほうが手がかりになりますし、どれもファイルが実際にいつ配布されたかの証拠ではありません。日付の形をしたメタデータだと考えてください。
- .NET アセンブリなのに、インポートとエントリポイントがほとんど空に見えます。
- マネージドアセンブリでは PE の部分が殻だからです。CLR ランタイムデータディレクトリ(14 番、COM ディスクリプタ)が空でないとき、本体は .NET メタデータの中の IL であり、ネイティブヘッダーは主に Windows ローダーを満足させるために存在します。典型的な C# の実行ファイルはインポートが mscoree.dll の _CorExeMain ひとつだけ、エントリポイントはそこへ飛ぶ二命令、.text セクションは機械語よりメタデータが大半です。そのためアーキテクチャ欄が Intel 386 でも 64 ビットで問題なく動きますし、セクションのサイズはプログラムの規模を表しません。中身を読むには ildasm や ILSpy のような IL 逆アセンブラが必要です。
関連ツール
Mach-O バイナリ検査ツール
macOS・iOS のバイナリをブラウザで解析。ユニバーサルの各アーキテクチャ、セグメントとセクション、依存 dylib、rpath、UUID、コード署名を表示します。
ELF バイナリ検査ツール
Linux の実行ファイル・.so・.o をブラウザーで開きます。アーキテクチャ、必要な共有ライブラリ、ビルド ID、PIE・NX・RELRO の有無まで。
Java クラスファイル解析ツール
コンパイル済みの .class ファイルをブラウザーで解析します。クラスファイルバージョンと Java リリース、アクセスフラグ、定数プール、フィールド、メソッド、参照クラスを表示します。
gzip(.gz)インスペクター
.gz のヘッダーを項目ごとに読み、全メンバーをたどり、ブラウザ内で CRC32 と実サイズを再計算してトレーラーを検証します。
xz(.xz)ストリーム解析ツール
xz コンテナを読み取ります。ストリームヘッダー、各ブロックのヘッダーとフィルターチェーン、インデックス、フッター、そして展開せずに分かる本当のサイズまで。
テキストエンコーディング変換
Shift_JIS・EUC-JP・Windows-1252 など非 UTF-8 のテキストを UTF-8 で読める形に。