Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2014年05月25日] [更新 2014年06月09日] Delphi サンプルプログラム集
   
006_究極の選択 - アセンブリ言語と PE ヘッダ
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
006_Assembler_exe.zip [3,195 KB] 2014年06月09日版 (EXE 同梱)



  • 2014年06月06日
  • DLL 内のエクスポート関数名の列挙のサンプルに,ファイルバージョン取得のコードを追加




究極の選択  -  アセンブリ言語


究極の選択,それはアセンブリ言語を使用することです.

前のページで,実行時パッケージを使用して開発すると,実行ファイルのサイズが小さくなることを示しました.EXE のサイズを小さくする究極の方法は,アセンブリ言語を使用して,アセンブラを使用することです.
アセンブリ言語のニーモニック (ニモニック) と呼ばれる命令語は,コンピュータが直接理解可能な 0 と 1 の組み合わせである機械語に,ほぼ 1 対 1 に対応しています.無駄がありません.

本ページは,このアセンブリ言語を使用する開発に必要な環境についての記事です.また,EXE ファイルの内容に関するテストをしてみました.

本ページのサンプルのコードでは,64 ビット版の EXE や DLL の情報は正しく取得できないものがあります.64 ビット用の構造体は,32 ビット用とは異なる構造体があります.
32 ビットアプリケーションからでも,64 ビット版の EXE, DLL の情報は取得できます.読み込んだ EXE, DLL が 64 ビット版の場合は,64 ビット版用の構造体を使用するように修正,あるいはコードを追加する必要があります.
Delphi XE 以前の Delphi には,64 ビット用の構造体の定義はありません.





01_アセンブラを用意してコンパイル


アセンブリ言語を使用して,アプリケーションを開発するには,コンパイラが必要です.Delphi 言語で書いたプログラムのコードからアプリを作成する,つまり,EXE を作成するには,Delphi のコンパイラを使用します.それと同じです.
アセンブリ言語で書いたプログラムコードをコンパイルするコンパイラを一般に,アセンブラと言っています.

ここでは,アセンブラを使用して,下図のようなダイアログを表示するプログラムを作成します.これ以上のことはやりません.興味ある方は,本ページの参考リンクの記事や参考書で調べたり,あるいはネットで検索してください.


図1
作成したアプリケーションの実行結果




01-01_開発に必要なアセンブラとリンカとライブラリ


今回は,Netwide Assembler (NASM) というアセンブラを使用します.これは,下の参考リンクのページから,[DWONLOAD] [2.11.04/] [Win32] の順にたどり, nasm-2.11.04-win32.zip をダンロードして解凍しました.D ドライブに nasm というフォルダを作成して,そこに解凍しました.
この他にリンカとライブラリが必要です.

Delphi で開発していると,意識しなくても,コンパイルすると EXE ファイルができます.
一般的に,アセンブラの命令は,CPU の操作コードです.CPU が画面の操作やダイアログを表示する機能を持っているわけではありません.それらの機能は OS が提供しています.
プログラムのソースコードをコンパイルすると,オブジェクトファイルを生成します.そのオブジェクトファイルと,実行に必要なライブラリファイルをリンクさせて EXE ファイルを生成します.このライブラリファイルに,OS の機能を使用するため実際のコードがあります.
オブジェクトファイルとライブラリのリンクに必要なのがリンカです.

基本的なライブラリは OS が提供しています.そのため,リンカとライブラリ類も必要です.
今回は,Microsoft Visual Studio Express 2013 for Windows (以後,VS 2013) を使用しました.これにリンカとライブラリがあります.これの入手方法とインストール方法は [タイプライブラリの作成] の記事を参考にしてください.


図2
ダウンロードしたアセンブラ
図3
D:\nasm フォルダ内に解凍
  • nasm.exe がアセンブラの本体




01-02_ソースコードとバッチファイルを作成


NASM を解凍して,VS 2013 をイントールしたら,プログラムのコードを作成します.
メモ帳等のテキストエディタで,下のコードを入力して,hello2.asm という名前で保存します.今回は,D ドライブに 000 というフォルダを作成して,そこに保存しました.
コードの意味の説明は省略します.各自で調べてください.


図4
hello2.asm という名前で保存

リスト1
アセンブリ言語のプログラムの例
global _WinMain@16
extern _MessageBoxA@16

section .text
_WinMain@16:
    PUSH EBP
    MOV EBP, ESP

    PUSH 0
    PUSH caption
    PUSH msg
    PUSH 0
    CALL _MessageBoxA@16

    MOV EAX, 2

    MOV ESP, EBP
    POP EBP
    RET 16

section .data

caption:
    db "テスト", 0

msg:
    db `Win32 Application\r\nHello World!`, 0


コンパイルとリンクを実行するためのバッチファイルを作成しておくことにします.
下のコードをメモ帳等のテキストエディタで作成し,nasmbat.bat という名前で保存します.保存先は nasm.exe があるフォルダとします.今回のプログラムでは,user32.lib と msvcrt.lib というライブラリを使用します.そこで,これを追加しています.


nasm -f win32 %1.asm
link /OUT:%1.exe %1.obj user32.lib msvcrt.lib


図5
バッチファイルを作成して保存




01-03_コンパイルして実行


準備ができたらコンパイルします.
[開発者コマンドプロンプト for VS2013] を管理者権限で起動します.起動したら,nasm.exe があるフォルダに移動します.そして,前項で作成したバッチファイルの引数に,作成したアセンブラのコードが書いてあるファイルを指定して,バッチファイルを実行します.今回の場合,コマンドラインは次のようになります.


nasmbat d:\000\Hello2


図6
バッチファイルを実行

コンパイルとリンクに成功すると,次のように,Hello2.exe ができます.わずか 6 KB しかないアプリケーションとなっています.
フォルダ内の msvcr120.dll は,VS 2013 が提供している DLL で,作成したアプリの実行に必要な (再配布可能) DLL です.実行環境に VS 2013 がインストールされていれば必要ありません.
今回は VS 2013 で開発したので,msvcr120.dll が必要ですが,他のバージョンの Visual Studio を使用した場合は,別の DLL が必要となることがあります.


図7
Hello2.exe ができる

作成したアプリである Hello2.exe を起動すると,本ページの最初の図のようなダイアログが表示されることが確認できます.
タスクマネージャーで確認すると,確かに Hello2.exe (メモリ使用量 1,132 KB) となっていて,6 KB しかない EXE がメモリ上で展開しているこが分かります.VCL_Delphi_XE_32bit.exe (メモリ使用量 1,376 KB) は,Delphi XE で作成した,フォームが 1 つだけのアプリです.

確かに,アセンブリ言語を使用すれば,EXE のサイズが小さなアプリケーションを作成することができます.しかし,実用的なアプリケーションを開発するのは容易ではないでしょう.


図8
タスクマネージャで確認




02_EXE ファイルのバイナリダンプ


作成した EXE の中身を見てみることにします.
1 と 0 の機械語では見にくいので,一般的には,16 進数の文字で表示することが行われています.その結果が下図です.一見すると,意味不明の記号の羅列に見えます.これをバイナリのダンプと言っています.
本サンプルのコードは,EXE のサイズが大きくなると,処理に時間がかかります.本格的に処理するのであれば,工夫が必要です.

遙か昔のプログラマは,このような値,実際には 1 と 0, あるいは 16 進数を,メモリやディスクに直接記録していきました.その後,アセンブラのような道具が開発され,更に Delphi のような総合開発環境が整備されていきました.
これらは,人類の知識と技術の蓄積が生み出した産物と言えるでしょう.

このようなバイナリダンプ表示のユーティリティ類は,ネット上でも入手できます.編集を可能にしたバイナリエディタと言うものもあります.


図9
設計時画面
図10
Hello2.exe のバイナリダンプ

16 進数文字でのダンプ表示は EXE だけとは限りません.下図は,'テスト' という文字列を保存したファイルを 16 進数表示したものです.
Shift-JIS の場合は,1 文字は 2 バイトです.したがって,全部で 6 バイトあります.一方,ユニコードである UTF-8 の場合は 12 バイトあることが分かります.


図11
文字列のバイナリダンプ
  • 'テスト' という文字列の場合
  • 上が SHIFT-JIS
  • 下が UTF-8

リスト2
ファイルの内容をバイナリダンプ表示する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Memo1: TMemo;
    Memo2: TMemo;
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Name   := 'Courier New';
  Memo1.Font.Size   := 10;
  Memo1.WordWrap    := False;

  Panel1.Font := Memo1.Font;
  Memo2.Font  := Memo1.Font;
  Memo2.Lines.Clear;
  Memo2.Lines.Add('       00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F');
end;

//=============================================================================
//  ファイルのデータを16進バイナリダンプ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath : String;
  FStream      : TFileStream;
  SL           : TStringList;
  BinArray     : TBytes;
  CntReadBytes : Integer;
  StrLine      : String;
  i            : Integer;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;

  if OpenDialog1.Execute then begin
    Screen.Cursor := crHourGlass;
    FileFullPath  := OpenDialog1.FileName;
    InitDir       := ExtractFileDir(FileFullPath);

    Panel1.Caption := ExtractFileName(FileFullPath);
    Memo1.Clear;
    Memo1.Update;
    Memo1.Lines.BeginUpdate;

    //ファイルストリームを生成
    FStream := TFilestream.Create(FileFullPath, fmOpenread or fmShareDenyNone);
    SL      := TStringList.Create;
    try
      //バイト配列のサイズを設定
      SetLength(BinArray, fStream.Size);
      //バイト配列に読み出す
      CntReadBytes := fStream.Read(BinArray[0], Length(BinArray));

      StrLine := IntToHex(0, 6) + ' ' + IntToHex(BinArray[0], 2) + ' ';
      for i := 1 to CntReadBytes - 1 do begin
        if (i mod 16) = 0 then begin
          SL.Add(StrLine);
          StrLine := IntToHex(i, 6) + ' ';
        end else
        if (i mod 8) = 0 then begin
          StrLine := StrLine + ' ';
        end;
        StrLine := StrLine + IntToHex(BinArray[i], 2) + ' ';

        Application.ProcessMessages;
      end;
      if StrLine <> '' then SL.Add(StrLine);

      Memo1.Text := SL.Text;
    finally
      FStream.Free;
      SL.Free;
    end;

    Memo1.Lines.EndUpdate;
    Screen.Cursor := crDefault;
  end;
end;

end.


TMemoryStream を使用して,ファイルを読み込んで処理する場合は,例えば次のようにできます.
メモリからデータを 1 バイトづつ読み込んで処理しています.
処理速度も,使用するメモリ量も,上のコードの場合と大差ありません.いずれにしても,表示の処理に時間がかかります.本格的に処理するのであれば工夫が必要です.


//=============================================================================
//  ファイルのデータを16進バイナリダンプ - TMemory Streamを使用する場合の例 
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath : String;
  FStream      : TMemoryStream;
  SL           : TStringList;
  pStream      : PByte;
  StrLine      : String;
  i            : Integer;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;

  if OpenDialog1.Execute then begin
    Screen.Cursor := crHourGlass;
    FileFullPath  := OpenDialog1.FileName;
    InitDir       := ExtractFileDir(FileFullPath);

    Panel1.Caption := ExtractFileName(FileFullPath);
    Memo1.Clear;
    Memo1.Update;
    Memo1.Lines.BeginUpdate;

    //メモリストリームを生成
    FStream := TMemoryStream.Create;
    SL      := TStringList.Create;
    try
      //メモリストリームにファイルの内容を読み込み
      FStream.LoadFromFile(FileFullPath);
      //メモリに直接アクセスする
      pStream := FStream.Memory;

      StrLine := IntToHex(0, 6) + ' ' + IntToHex(pStream^, 2) + ' ';
      inc(pStream);
      for i := 1 to FStream.Size - 1 do begin
        if (i mod 16) = 0 then begin
          SL.Add(StrLine);
          StrLine := IntToHex(i, 6) + ' ';
        end else
        if (i mod 8) = 0 then begin
          StrLine := StrLine + ' ';
        end;
        StrLine := StrLine + IntToHex(pStream^, 2) + ' ';
        inc(pStream);

        Application.ProcessMessages;
      end;
      if StrLine <> '' then SL.Add(StrLine);

      Memo1.Text := SL.Text;
    finally
      FStream.Free;
      SL.Free;
    end;

    Memo1.Lines.EndUpdate;
    Screen.Cursor := crDefault;
  end;
end;




03_DUMPBIN を使用した逆アセンブル


VS 2013 に DUMPBIN というプログラムが付属しています.これは,文字通り,バイナリダンプ用のプログラムです.実際には,バイナリダンプ以外にも,次項のサンプルの結果のような,EXE ファイルの他の情報も出力することができます.

VS 2013 の [開発者コマンドプロンプト for VS2013] を起動して,作成した Hello2.exe のフォルダに移動します.そして,次のコマンを実行すると,dump.txt に結果が出力されます.

dumpbin /disasm /out:dump.txt Hello2.exe



図12
DUMPBIN を実行

下のリストが実行結果です.バイナリダンプの他に,アセンブリ言語の,CPU の命令語であるニモニックも出力されています.このように,バイナリからアセンブリ言語のコードを出力するのを,逆アセンブルと言っています.ニモニックの 1 行 1 行が機械語の命令に対応しています.

最初に書いたアセンブリのコードよりも非常に長いコードになっています.これは,元のソースコードをコンパイルした結果としての,ネイティブコードになっています.
このコードの左端の値は,EXE がメモリ上に展開する際のメモリのアドレスです.このアドレスは,物理的なメモリのアドレスではなく,プログラマから見たメモリ空間の「仮想アドレス」です.EXE 内での開始位置は,今回作成した Hello2.exe のバイナリダンプでは $000400 からです.この位置は,後述の PE ヘッダの情報から計算できます.

Delphi でプログラムの開発中,あるいはデバック中に,CPU ウィンドウで,このような表示を見たことがあるも知れません.


[備考]
下のコードは,俗に言う「コンパイラが吐き出したコード」です.EXE から元のソースコードを取得することは基本的にはできません.これはどの開発言語で作成した EXE でも同じです.
EXE を作成した開発言語のコードとの変換テーブル等を作成すれば可能かも知れませんが,個人的には意味がある作業とは考えられません.
以下のサイトに,Delphi 用の逆コンパイル (デコンパイル) ツールがあります.筆者は使用したことがありませんので,どこまで解析するのかは不明です. .NET の開発言語には,中間コードを利用した逆コンパイル用のツールがあるようです.


リスト3
Hello2.exe を逆アセンブルした結果
EXE に記録されているのは,55 89 E5 6A 00 68 00 30 40 00 68 07 ... という値
Dump of file Hello2.exe

File Type: EXECUTABLE IMAGE

  00401000: 55                 push        ebp
  00401001: 89 E5              mov         ebp,esp
  00401003: 6A 00              push        0
  00401005: 68 00 30 40 00     push        403000h
  0040100A: 68 07 30 40 00     push        403007h
  0040100F: 6A 00              push        0
  00401011: E8 1A 00 00 00     call        00401030
  00401016: B8 02 00 00 00     mov         eax,2
  0040101B: 89 EC              mov         esp,ebp
  0040101D: 5D                 pop         ebp
  0040101E: C2 10 00           ret         10h
  00401021: CC                 int         3
  00401022: CC                 int         3
  00401023: CC                 int         3
  00401024: CC                 int         3
  00401025: CC                 int         3
  00401026: CC                 int         3
  00401027: CC                 int         3
  00401028: CC                 int         3
  00401029: CC                 int         3
  0040102A: CC                 int         3
  0040102B: CC                 int         3
  0040102C: CC                 int         3
  0040102D: CC                 int         3
  0040102E: CC                 int         3
  0040102F: CC                 int         3
  00401030: FF 25 9C 20 40 00  jmp         dword ptr ds:[0040209Ch]
  00401036: B8 4D 5A 00 00     mov         eax,5A4Dh
  0040103B: 66 39 05 00 00 40  cmp         word ptr ds:[00400000h],ax
            00
  00401042: 74 04              je          00401048
  00401044: 33 C0              xor         eax,eax
  00401046: EB 34              jmp         0040107C
  00401048: 8B 0D 3C 00 40 00  mov         ecx,dword ptr ds:[0040003Ch]
  0040104E: 81 B9 00 00 40 00  cmp         dword ptr [ecx+00400000h],4550h
            50 45 00 00
  00401058: 75 EA              jne         00401044
  0040105A: B8 0B 01 00 00     mov         eax,10Bh
  0040105F: 66 39 81 18 00 40  cmp         word ptr [ecx+00400018h],ax
            00
  00401066: 75 DC              jne         00401044
  00401068: 33 C0              xor         eax,eax
  0040106A: 83 B9 74 00 40 00  cmp         dword ptr [ecx+00400074h],0Eh
            0E
  00401071: 76 09              jbe         0040107C
  00401073: 39 81 E8 00 40 00  cmp         dword ptr [ecx+004000E8h],eax
  00401079: 0F 95 C0           setne       al
  0040107C: 6A 02              push        2
  0040107E: A3 48 30 40 00     mov         dword ptr ds:[00403048h],eax
  00401083: FF 15 80 20 40 00  call        dword ptr ds:[00402080h]
  00401089: 59                 pop         ecx
  0040108A: 6A FF              push        0FFFFFFFFh
  0040108C: FF 15 18 20 40 00  call        dword ptr ds:[00402018h]
  00401092: 8B 0D 40 20 40 00  mov         ecx,dword ptr ds:[00402040h]
  00401098: A3 98 33 40 00     mov         dword ptr ds:[00403398h],eax
  0040109D: A3 9C 33 40 00     mov         dword ptr ds:[0040339Ch],eax
  004010A2: A1 6C 30 40 00     mov         eax,dword ptr ds:[0040306Ch]
  004010A7: 89 01              mov         dword ptr [ecx],eax
  004010A9: 8B 0D 44 20 40 00  mov         ecx,dword ptr ds:[00402044h]
  004010AF: A1 60 30 40 00     mov         eax,dword ptr ds:[00403060h]
  004010B4: 89 01              mov         dword ptr [ecx],eax
  004010B6: E8 7C 05 00 00     call        00401637
  004010BB: E8 94 02 00 00     call        00401354
  004010C0: 83 3D 28 30 40 00  cmp         dword ptr ds:[00403028h],0
            00
  004010C7: 75 0C              jne         004010D5
  004010C9: 68 54 13 40 00     push        401354h
  004010CE: FF 15 30 20 40 00  call        dword ptr ds:[00402030h]
  004010D4: 59                 pop         ecx
  004010D5: E8 9D 05 00 00     call        00401677
  004010DA: 83 3D 30 30 40 00  cmp         dword ptr ds:[00403030h],0FFFFFFFFh
            FF
  004010E1: 75 09              jne         004010EC
  004010E3: 6A FF              push        0FFFFFFFFh
  004010E5: FF 15 2C 20 40 00  call        dword ptr ds:[0040202Ch]
  004010EB: 59                 pop         ecx
  004010EC: 33 C0              xor         eax,eax
  004010EE: C3                 ret
  004010EF: 68 57 16 40 00     push        401657h
  004010F4: E8 29 05 00 00     call        00401622
  004010F9: A1 68 30 40 00     mov         eax,dword ptr ds:[00403068h]
  004010FE: C7 04 24 5C 30 40  mov         dword ptr [esp],40305Ch
            00
  00401105: FF 35 64 30 40 00  push        dword ptr ds:[00403064h]
  0040110B: A3 5C 30 40 00     mov         dword ptr ds:[0040305Ch],eax
  00401110: 68 54 30 40 00     push        403054h
  00401115: 68 50 30 40 00     push        403050h
  0040111A: 68 4C 30 40 00     push        40304Ch
  0040111F: FF 15 84 20 40 00  call        dword ptr ds:[00402084h]
  00401125: 83 C4 14           add         esp,14h
  00401128: A3 58 30 40 00     mov         dword ptr ds:[00403058h],eax
  0040112D: 85 C0              test        eax,eax
  0040112F: 79 08              jns         00401139
  00401131: 6A 08              push        8
  00401133: E8 16 02 00 00     call        0040134E
  00401138: 59                 pop         ecx
  00401139: C3                 ret
  0040113A: E8 62 03 00 00     call        004014A1
  0040113F: E9 00 00 00 00     jmp         00401144
  00401144: 6A 14              push        14h
  00401146: 68 38 21 40 00     push        402138h
  0040114B: E8 60 05 00 00     call        004016B0
  00401150: 33 F6              xor         esi,esi
  00401152: 89 75 E4           mov         dword ptr [ebp-1Ch],esi
  00401155: 89 75 E0           mov         dword ptr [ebp-20h],esi
  00401158: FF 15 8C 20 40 00  call        dword ptr ds:[0040208Ch]
  0040115E: 0F B7 D8           movzx       ebx,ax
  00401161: 89 75 FC           mov         dword ptr [ebp-4],esi
  00401164: 64 A1 18 00 00 00  mov         eax,dword ptr fs:[00000018h]
  0040116A: 8B 50 04           mov         edx,dword ptr [eax+4]
  0040116D: 8B FE              mov         edi,esi
  0040116F: BE 90 33 40 00     mov         esi,403390h
  00401174: 8B CA              mov         ecx,edx
  00401176: 33 C0              xor         eax,eax
  00401178: F0 0F B1 0E        lock cmpxchg dword ptr [esi],ecx
  0040117C: 85 C0              test        eax,eax
  0040117E: 74 0B              je          0040118B
  00401180: 3B C2              cmp         eax,edx
  00401182: 75 F0              jne         00401174
  00401184: 33 F6              xor         esi,esi
  00401186: 46                 inc         esi
  00401187: 8B FE              mov         edi,esi
  00401189: EB 03              jmp         0040118E
  0040118B: 33 F6              xor         esi,esi
  0040118D: 46                 inc         esi
  0040118E: 39 35 94 33 40 00  cmp         dword ptr ds:[00403394h],esi
  00401194: 75 0A              jne         004011A0
  00401196: 6A 1F              push        1Fh
  00401198: E8 B1 01 00 00     call        0040134E
  0040119D: 59                 pop         ecx
  0040119E: EB 3B              jmp         004011DB
  004011A0: 83 3D 94 33 40 00  cmp         dword ptr ds:[00403394h],0
            00
  004011A7: 75 2C              jne         004011D5
  004011A9: 89 35 94 33 40 00  mov         dword ptr ds:[00403394h],esi
  004011AF: 68 C0 20 40 00     push        4020C0h
  004011B4: 68 B0 20 40 00     push        4020B0h
  004011B9: E8 E0 04 00 00     call        0040169E
  004011BE: 59                 pop         ecx
  004011BF: 59                 pop         ecx
  004011C0: 85 C0              test        eax,eax
  004011C2: 74 17              je          004011DB
  004011C4: C7 45 FC FE FF FF  mov         dword ptr [ebp-4],0FFFFFFFEh
            FF
  004011CB: B8 FF 00 00 00     mov         eax,0FFh
  004011D0: E9 1D 01 00 00     jmp         004012F2
  004011D5: 89 35 40 30 40 00  mov         dword ptr ds:[00403040h],esi
  004011DB: 39 35 94 33 40 00  cmp         dword ptr ds:[00403394h],esi
  004011E1: 75 1B              jne         004011FE
  004011E3: 68 AC 20 40 00     push        4020ACh
  004011E8: 68 A4 20 40 00     push        4020A4h
  004011ED: E8 B2 04 00 00     call        004016A4
  004011F2: 59                 pop         ecx
  004011F3: 59                 pop         ecx
  004011F4: C7 05 94 33 40 00  mov         dword ptr ds:[00403394h],2
            02 00 00 00
  004011FE: 85 FF              test        edi,edi
  00401200: 75 09              jne         0040120B
  00401202: 33 C0              xor         eax,eax
  00401204: B9 90 33 40 00     mov         ecx,403390h
  00401209: 87 01              xchg        eax,dword ptr [ecx]
  0040120B: 83 3D A0 33 40 00  cmp         dword ptr ds:[004033A0h],0
            00
  00401212: 74 1B              je          0040122F
  00401214: 68 A0 33 40 00     push        4033A0h
  00401219: E8 92 01 00 00     call        004013B0
  0040121E: 59                 pop         ecx
  0040121F: 85 C0              test        eax,eax
  00401221: 74 0C              je          0040122F
  00401223: 6A 00              push        0
  00401225: 6A 02              push        2
  00401227: 6A 00              push        0
  00401229: FF 15 A0 33 40 00  call        dword ptr ds:[004033A0h]
  0040122F: A1 3C 20 40 00     mov         eax,dword ptr ds:[0040203Ch]
  00401234: 8B 30              mov         esi,dword ptr [eax]
  00401236: 89 75 E4           mov         dword ptr [ebp-1Ch],esi
  00401239: 8B 7D E0           mov         edi,dword ptr [ebp-20h]
  0040123C: 8A 0E              mov         cl,byte ptr [esi]
  0040123E: 80 F9 20           cmp         cl,20h
  00401241: 77 3B              ja          0040127E
  00401243: 84 C9              test        cl,cl
  00401245: 74 04              je          0040124B
  00401247: 85 FF              test        edi,edi
  00401249: 75 33              jne         0040127E
  0040124B: 8A 06              mov         al,byte ptr [esi]
  0040124D: 84 C0              test        al,al
  0040124F: 74 0A              je          0040125B
  00401251: 3C 20              cmp         al,20h
  00401253: 77 06              ja          0040125B
  00401255: 46                 inc         esi
  00401256: 89 75 E4           mov         dword ptr [ebp-1Ch],esi
  00401259: EB F0              jmp         0040124B
  0040125B: 53                 push        ebx
  0040125C: 56                 push        esi
  0040125D: 6A 00              push        0
  0040125F: 68 00 00 40 00     push        400000h
  00401264: E8 97 FD FF FF     call        00401000
  00401269: A3 44 30 40 00     mov         dword ptr ds:[00403044h],eax
  0040126E: 83 3D 48 30 40 00  cmp         dword ptr ds:[00403048h],0
            00
  00401275: 75 60              jne         004012D7
  00401277: 50                 push        eax
  00401278: FF 15 7C 20 40 00  call        dword ptr ds:[0040207Ch]
  0040127E: 80 F9 22           cmp         cl,22h
  00401281: 75 0C              jne         0040128F
  00401283: 33 C0              xor         eax,eax
  00401285: 85 FF              test        edi,edi
  00401287: 0F 94 C0           sete        al
  0040128A: 8B F8              mov         edi,eax
  0040128C: 89 7D E0           mov         dword ptr [ebp-20h],edi
  0040128F: 0F B6 C1           movzx       eax,cl
  00401292: 50                 push        eax
  00401293: FF 15 28 20 40 00  call        dword ptr ds:[00402028h]
  00401299: 59                 pop         ecx
  0040129A: 85 C0              test        eax,eax
  0040129C: 74 04              je          004012A2
  0040129E: 46                 inc         esi
  0040129F: 89 75 E4           mov         dword ptr [ebp-1Ch],esi
  004012A2: 46                 inc         esi
  004012A3: 89 75 E4           mov         dword ptr [ebp-1Ch],esi
  004012A6: EB 94              jmp         0040123C
  004012A8: 8B 4D EC           mov         ecx,dword ptr [ebp-14h]
  004012AB: 8B 01              mov         eax,dword ptr [ecx]
  004012AD: 8B 00              mov         eax,dword ptr [eax]
  004012AF: 89 45 DC           mov         dword ptr [ebp-24h],eax
  004012B2: 51                 push        ecx
  004012B3: 50                 push        eax
  004012B4: E8 8F 00 00 00     call        00401348
  004012B9: 59                 pop         ecx
  004012BA: 59                 pop         ecx
  004012BB: C3                 ret
  004012BC: 8B 65 E8           mov         esp,dword ptr [ebp-18h]
  004012BF: 8B 45 DC           mov         eax,dword ptr [ebp-24h]
  004012C2: A3 44 30 40 00     mov         dword ptr ds:[00403044h],eax
  004012C7: 83 3D 48 30 40 00  cmp         dword ptr ds:[00403048h],0
            00
  004012CE: 75 07              jne         004012D7
  004012D0: 50                 push        eax
  004012D1: FF 15 4C 20 40 00  call        dword ptr ds:[0040204Ch]
  004012D7: 83 3D 40 30 40 00  cmp         dword ptr ds:[00403040h],0
            00
  004012DE: 75 0B              jne         004012EB
  004012E0: FF 15 24 20 40 00  call        dword ptr ds:[00402024h]
  004012E6: A1 44 30 40 00     mov         eax,dword ptr ds:[00403044h]
  004012EB: C7 45 FC FE FF FF  mov         dword ptr [ebp-4],0FFFFFFFEh
            FF
  004012F2: E8 FE 03 00 00     call        004016F5
  004012F7: C3                 ret
  004012F8: 55                 push        ebp
  004012F9: 8B EC              mov         ebp,esp
  004012FB: 8B 45 08           mov         eax,dword ptr [ebp+8]
  004012FE: 8B 00              mov         eax,dword ptr [eax]
  00401300: 81 38 63 73 6D E0  cmp         dword ptr [eax],0E06D7363h
  00401306: 75 25              jne         0040132D
  00401308: 83 78 10 03        cmp         dword ptr [eax+10h],3
  0040130C: 75 1F              jne         0040132D
  0040130E: 8B 40 14           mov         eax,dword ptr [eax+14h]
  00401311: 3D 20 05 93 19     cmp         eax,19930520h
  00401316: 74 1B              je          00401333
  00401318: 3D 21 05 93 19     cmp         eax,19930521h
  0040131D: 74 14              je          00401333
  0040131F: 3D 22 05 93 19     cmp         eax,19930522h
  00401324: 74 0D              je          00401333
  00401326: 3D 00 40 99 01     cmp         eax,1994000h
  0040132B: 74 06              je          00401333
  0040132D: 33 C0              xor         eax,eax
  0040132F: 5D                 pop         ebp
  00401330: C2 04 00           ret         4
  00401333: E8 F4 03 00 00     call        0040172C
  00401338: CC                 int         3
  00401339: 68 F8 12 40 00     push        4012F8h
  0040133E: E8 EF 03 00 00     call        00401732
  00401343: 59                 pop         ecx
  00401344: 33 C0              xor         eax,eax
  00401346: C3                 ret
  00401347: CC                 int         3
  00401348: FF 25 90 20 40 00  jmp         dword ptr ds:[00402090h]
  0040134E: FF 25 88 20 40 00  jmp         dword ptr ds:[00402088h]
  00401354: 33 C0              xor         eax,eax
  00401356: C3                 ret
  00401357: CC                 int         3
  00401358: CC                 int         3
  00401359: CC                 int         3
  0040135A: CC                 int         3
  0040135B: CC                 int         3
  0040135C: CC                 int         3
  0040135D: CC                 int         3
  0040135E: CC                 int         3
  0040135F: CC                 int         3
  00401360: 55                 push        ebp
  00401361: 8B EC              mov         ebp,esp
  00401363: 8B 45 08           mov         eax,dword ptr [ebp+8]
  00401366: 33 D2              xor         edx,edx
  00401368: 53                 push        ebx
  00401369: 56                 push        esi
  0040136A: 57                 push        edi
  0040136B: 8B 48 3C           mov         ecx,dword ptr [eax+3Ch]
  0040136E: 03 C8              add         ecx,eax
  00401370: 0F B7 41 14        movzx       eax,word ptr [ecx+14h]
  00401374: 0F B7 59 06        movzx       ebx,word ptr [ecx+6]
  00401378: 83 C0 18           add         eax,18h
  0040137B: 03 C1              add         eax,ecx
  0040137D: 85 DB              test        ebx,ebx
  0040137F: 74 1B              je          0040139C
  00401381: 8B 7D 0C           mov         edi,dword ptr [ebp+0Ch]
  00401384: 8B 70 0C           mov         esi,dword ptr [eax+0Ch]
  00401387: 3B FE              cmp         edi,esi
  00401389: 72 09              jb          00401394
  0040138B: 8B 48 08           mov         ecx,dword ptr [eax+8]
  0040138E: 03 CE              add         ecx,esi
  00401390: 3B F9              cmp         edi,ecx
  00401392: 72 0A              jb          0040139E
  00401394: 42                 inc         edx
  00401395: 83 C0 28           add         eax,28h
  00401398: 3B D3              cmp         edx,ebx
  0040139A: 72 E8              jb          00401384
  0040139C: 33 C0              xor         eax,eax
  0040139E: 5F                 pop         edi
  0040139F: 5E                 pop         esi
  004013A0: 5B                 pop         ebx
  004013A1: 5D                 pop         ebp
  004013A2: C3                 ret
  004013A3: CC                 int         3
  004013A4: CC                 int         3
  004013A5: CC                 int         3
  004013A6: CC                 int         3
  004013A7: CC                 int         3
  004013A8: CC                 int         3
  004013A9: CC                 int         3
  004013AA: CC                 int         3
  004013AB: CC                 int         3
  004013AC: CC                 int         3
  004013AD: CC                 int         3
  004013AE: CC                 int         3
  004013AF: CC                 int         3
  004013B0: 55                 push        ebp
  004013B1: 8B EC              mov         ebp,esp
  004013B3: 6A FE              push        0FFFFFFFEh
  004013B5: 68 58 21 40 00     push        402158h
  004013BA: 68 09 17 40 00     push        401709h
  004013BF: 64 A1 00 00 00 00  mov         eax,dword ptr fs:[00000000h]
  004013C5: 50                 push        eax
  004013C6: 83 EC 08           sub         esp,8
  004013C9: 53                 push        ebx
  004013CA: 56                 push        esi
  004013CB: 57                 push        edi
  004013CC: A1 38 30 40 00     mov         eax,dword ptr ds:[00403038h]
  004013D1: 31 45 F8           xor         dword ptr [ebp-8],eax
  004013D4: 33 C5              xor         eax,ebp
  004013D6: 50                 push        eax
  004013D7: 8D 45 F0           lea         eax,[ebp-10h]
  004013DA: 64 A3 00 00 00 00  mov         dword ptr fs:[00000000h],eax
  004013E0: 89 65 E8           mov         dword ptr [ebp-18h],esp
  004013E3: C7 45 FC 00 00 00  mov         dword ptr [ebp-4],0
            00
  004013EA: 68 00 00 40 00     push        400000h
  004013EF: E8 7C 00 00 00     call        00401470
  004013F4: 83 C4 04           add         esp,4
  004013F7: 85 C0              test        eax,eax
  004013F9: 74 54              je          0040144F
  004013FB: 8B 45 08           mov         eax,dword ptr [ebp+8]
  004013FE: 2D 00 00 40 00     sub         eax,400000h
  00401403: 50                 push        eax
  00401404: 68 00 00 40 00     push        400000h
  00401409: E8 52 FF FF FF     call        00401360
  0040140E: 83 C4 08           add         esp,8
  00401411: 85 C0              test        eax,eax
  00401413: 74 3A              je          0040144F
  00401415: 8B 40 24           mov         eax,dword ptr [eax+24h]
  00401418: C1 E8 1F           shr         eax,1Fh
  0040141B: F7 D0              not         eax
  0040141D: 83 E0 01           and         eax,1
  00401420: C7 45 FC FE FF FF  mov         dword ptr [ebp-4],0FFFFFFFEh
            FF
  00401427: 8B 4D F0           mov         ecx,dword ptr [ebp-10h]
  0040142A: 64 89 0D 00 00 00  mov         dword ptr fs:[00000000h],ecx
            00
  00401431: 59                 pop         ecx
  00401432: 5F                 pop         edi
  00401433: 5E                 pop         esi
  00401434: 5B                 pop         ebx
  00401435: 8B E5              mov         esp,ebp
  00401437: 5D                 pop         ebp
  00401438: C3                 ret
  00401439: 8B 45 EC           mov         eax,dword ptr [ebp-14h]
  0040143C: 8B 00              mov         eax,dword ptr [eax]
  0040143E: 33 C9              xor         ecx,ecx
  00401440: 81 38 05 00 00 C0  cmp         dword ptr [eax],0C0000005h
  00401446: 0F 94 C1           sete        cl
  00401449: 8B C1              mov         eax,ecx
  0040144B: C3                 ret
  0040144C: 8B 65 E8           mov         esp,dword ptr [ebp-18h]
  0040144F: C7 45 FC FE FF FF  mov         dword ptr [ebp-4],0FFFFFFFEh
            FF
  00401456: 33 C0              xor         eax,eax
  00401458: 8B 4D F0           mov         ecx,dword ptr [ebp-10h]
  0040145B: 64 89 0D 00 00 00  mov         dword ptr fs:[00000000h],ecx
            00
  00401462: 59                 pop         ecx
  00401463: 5F                 pop         edi
  00401464: 5E                 pop         esi
  00401465: 5B                 pop         ebx
  00401466: 8B E5              mov         esp,ebp
  00401468: 5D                 pop         ebp
  00401469: C3                 ret
  0040146A: CC                 int         3
  0040146B: CC                 int         3
  0040146C: CC                 int         3
  0040146D: CC                 int         3
  0040146E: CC                 int         3
  0040146F: CC                 int         3
  00401470: 55                 push        ebp
  00401471: 8B EC              mov         ebp,esp
  00401473: 8B 45 08           mov         eax,dword ptr [ebp+8]
  00401476: B9 4D 5A 00 00     mov         ecx,5A4Dh
  0040147B: 66 39 08           cmp         word ptr [eax],cx
  0040147E: 74 04              je          00401484
  00401480: 33 C0              xor         eax,eax
  00401482: 5D                 pop         ebp
  00401483: C3                 ret
  00401484: 8B 48 3C           mov         ecx,dword ptr [eax+3Ch]
  00401487: 03 C8              add         ecx,eax
  00401489: 33 C0              xor         eax,eax
  0040148B: 81 39 50 45 00 00  cmp         dword ptr [ecx],4550h
  00401491: 75 0C              jne         0040149F
  00401493: BA 0B 01 00 00     mov         edx,10Bh
  00401498: 66 39 51 18        cmp         word ptr [ecx+18h],dx
  0040149C: 0F 94 C0           sete        al
  0040149F: 5D                 pop         ebp
  004014A0: C3                 ret
  004014A1: 55                 push        ebp
  004014A2: 8B EC              mov         ebp,esp
  004014A4: 83 EC 14           sub         esp,14h
  004014A7: 83 65 F4 00        and         dword ptr [ebp-0Ch],0
  004014AB: 83 65 F8 00        and         dword ptr [ebp-8],0
  004014AF: A1 38 30 40 00     mov         eax,dword ptr ds:[00403038h]
  004014B4: 56                 push        esi
  004014B5: 57                 push        edi
  004014B6: BF 4E E6 40 BB     mov         edi,0BB40E64Eh
  004014BB: BE 00 00 FF FF     mov         esi,0FFFF0000h
  004014C0: 3B C7              cmp         eax,edi
  004014C2: 74 0D              je          004014D1
  004014C4: 85 C6              test        esi,eax
  004014C6: 74 09              je          004014D1
  004014C8: F7 D0              not         eax
  004014CA: A3 3C 30 40 00     mov         dword ptr ds:[0040303Ch],eax
  004014CF: EB 66              jmp         00401537
  004014D1: 8D 45 F4           lea         eax,[ebp-0Ch]
  004014D4: 50                 push        eax
  004014D5: FF 15 08 20 40 00  call        dword ptr ds:[00402008h]
  004014DB: 8B 45 F8           mov         eax,dword ptr [ebp-8]
  004014DE: 33 45 F4           xor         eax,dword ptr [ebp-0Ch]
  004014E1: 89 45 FC           mov         dword ptr [ebp-4],eax
  004014E4: FF 15 0C 20 40 00  call        dword ptr ds:[0040200Ch]
  004014EA: 31 45 FC           xor         dword ptr [ebp-4],eax
  004014ED: FF 15 10 20 40 00  call        dword ptr ds:[00402010h]
  004014F3: 31 45 FC           xor         dword ptr [ebp-4],eax
  004014F6: 8D 45 EC           lea         eax,[ebp-14h]
  004014F9: 50                 push        eax
  004014FA: FF 15 14 20 40 00  call        dword ptr ds:[00402014h]
  00401500: 8B 4D F0           mov         ecx,dword ptr [ebp-10h]
  00401503: 8D 45 FC           lea         eax,[ebp-4]
  00401506: 33 4D EC           xor         ecx,dword ptr [ebp-14h]
  00401509: 33 4D FC           xor         ecx,dword ptr [ebp-4]
  0040150C: 33 C8              xor         ecx,eax
  0040150E: 3B CF              cmp         ecx,edi
  00401510: 75 07              jne         00401519
  00401512: B9 4F E6 40 BB     mov         ecx,0BB40E64Fh
  00401517: EB 10              jmp         00401529
  00401519: 85 CE              test        esi,ecx
  0040151B: 75 0C              jne         00401529
  0040151D: 8B C1              mov         eax,ecx
  0040151F: 0D 11 47 00 00     or          eax,4711h
  00401524: C1 E0 10           shl         eax,10h
  00401527: 0B C8              or          ecx,eax
  00401529: 89 0D 38 30 40 00  mov         dword ptr ds:[00403038h],ecx
  0040152F: F7 D1              not         ecx
  00401531: 89 0D 3C 30 40 00  mov         dword ptr ds:[0040303Ch],ecx
  00401537: 5F                 pop         edi
  00401538: 5E                 pop         esi
  00401539: 8B E5              mov         esp,ebp
  0040153B: 5D                 pop         ebp
  0040153C: C3                 ret
  0040153D: 83 3D 9C 33 40 00  cmp         dword ptr ds:[0040339Ch],0
            00
  00401544: 74 03              je          00401549
  00401546: 33 C0              xor         eax,eax
  00401548: C3                 ret
  00401549: 56                 push        esi
  0040154A: 6A 04              push        4
  0040154C: 6A 20              push        20h
  0040154E: FF 15 58 20 40 00  call        dword ptr ds:[00402058h]
  00401554: 59                 pop         ecx
  00401555: 59                 pop         ecx
  00401556: 8B F0              mov         esi,eax
  00401558: 56                 push        esi
  00401559: FF 15 18 20 40 00  call        dword ptr ds:[00402018h]
  0040155F: A3 9C 33 40 00     mov         dword ptr ds:[0040339Ch],eax
  00401564: A3 98 33 40 00     mov         dword ptr ds:[00403398h],eax
  00401569: 85 F6              test        esi,esi
  0040156B: 75 05              jne         00401572
  0040156D: 6A 18              push        18h
  0040156F: 58                 pop         eax
  00401570: 5E                 pop         esi
  00401571: C3                 ret
  00401572: 83 26 00           and         dword ptr [esi],0
  00401575: 33 C0              xor         eax,eax
  00401577: 5E                 pop         esi
  00401578: C3                 ret
  00401579: 6A 14              push        14h
  0040157B: 68 78 21 40 00     push        402178h
  00401580: E8 2B 01 00 00     call        004016B0
  00401585: 83 65 DC 00        and         dword ptr [ebp-24h],0
  00401589: FF 35 9C 33 40 00  push        dword ptr ds:[0040339Ch]
  0040158F: 8B 35 04 20 40 00  mov         esi,dword ptr ds:[00402004h]
  00401595: FF D6              call        esi
  00401597: 89 45 E4           mov         dword ptr [ebp-1Ch],eax
  0040159A: 83 F8 FF           cmp         eax,0FFFFFFFFh
  0040159D: 75 0C              jne         004015AB
  0040159F: FF 75 08           push        dword ptr [ebp+8]
  004015A2: FF 15 60 20 40 00  call        dword ptr ds:[00402060h]
  004015A8: 59                 pop         ecx
  004015A9: EB 65              jmp         00401610
  004015AB: 6A 08              push        8
  004015AD: E8 86 01 00 00     call        00401738
  004015B2: 59                 pop         ecx
  004015B3: 83 65 FC 00        and         dword ptr [ebp-4],0
  004015B7: FF 35 9C 33 40 00  push        dword ptr ds:[0040339Ch]
  004015BD: FF D6              call        esi
  004015BF: 89 45 E4           mov         dword ptr [ebp-1Ch],eax
  004015C2: FF 35 98 33 40 00  push        dword ptr ds:[00403398h]
  004015C8: FF D6              call        esi
  004015CA: 89 45 E0           mov         dword ptr [ebp-20h],eax
  004015CD: 8D 45 E0           lea         eax,[ebp-20h]
  004015D0: 50                 push        eax
  004015D1: 8D 45 E4           lea         eax,[ebp-1Ch]
  004015D4: 50                 push        eax
  004015D5: FF 75 08           push        dword ptr [ebp+8]
  004015D8: 8B 35 18 20 40 00  mov         esi,dword ptr ds:[00402018h]
  004015DE: FF D6              call        esi
  004015E0: 50                 push        eax
  004015E1: E8 5E 01 00 00     call        00401744
  004015E6: 83 C4 0C           add         esp,0Ch
  004015E9: 8B F8              mov         edi,eax
  004015EB: 89 7D DC           mov         dword ptr [ebp-24h],edi
  004015EE: FF 75 E4           push        dword ptr [ebp-1Ch]
  004015F1: FF D6              call        esi
  004015F3: A3 9C 33 40 00     mov         dword ptr ds:[0040339Ch],eax
  004015F8: FF 75 E0           push        dword ptr [ebp-20h]
  004015FB: FF D6              call        esi
  004015FD: A3 98 33 40 00     mov         dword ptr ds:[00403398h],eax
  00401602: C7 45 FC FE FF FF  mov         dword ptr [ebp-4],0FFFFFFFEh
            FF
  00401609: E8 0B 00 00 00     call        00401619
  0040160E: 8B C7              mov         eax,edi
  00401610: E8 E0 00 00 00     call        004016F5
  00401615: C3                 ret
  00401616: 8B 7D DC           mov         edi,dword ptr [ebp-24h]
  00401619: 6A 08              push        8
  0040161B: E8 1E 01 00 00     call        0040173E
  00401620: 59                 pop         ecx
  00401621: C3                 ret
  00401622: 55                 push        ebp
  00401623: 8B EC              mov         ebp,esp
  00401625: FF 75 08           push        dword ptr [ebp+8]
  00401628: E8 4C FF FF FF     call        00401579
  0040162D: F7 D8              neg         eax
  0040162F: 59                 pop         ecx
  00401630: 1B C0              sbb         eax,eax
  00401632: F7 D8              neg         eax
  00401634: 48                 dec         eax
  00401635: 5D                 pop         ebp
  00401636: C3                 ret
  00401637: 56                 push        esi
  00401638: 57                 push        edi
  00401639: BE 28 21 40 00     mov         esi,402128h
  0040163E: BF 28 21 40 00     mov         edi,402128h
  00401643: EB 0B              jmp         00401650
  00401645: 8B 06              mov         eax,dword ptr [esi]
  00401647: 85 C0              test        eax,eax
  00401649: 74 02              je          0040164D
  0040164B: FF D0              call        eax
  0040164D: 83 C6 04           add         esi,4
  00401650: 3B F7              cmp         esi,edi
  00401652: 72 F1              jb          00401645
  00401654: 5F                 pop         edi
  00401655: 5E                 pop         esi
  00401656: C3                 ret
  00401657: 56                 push        esi
  00401658: 57                 push        edi
  00401659: BE 30 21 40 00     mov         esi,402130h
  0040165E: BF 30 21 40 00     mov         edi,402130h
  00401663: EB 0B              jmp         00401670
  00401665: 8B 06              mov         eax,dword ptr [esi]
  00401667: 85 C0              test        eax,eax
  00401669: 74 02              je          0040166D
  0040166B: FF D0              call        eax
  0040166D: 83 C6 04           add         esi,4
  00401670: 3B F7              cmp         esi,edi
  00401672: 72 F1              jb          00401665
  00401674: 5F                 pop         edi
  00401675: 5E                 pop         esi
  00401676: C3                 ret
  00401677: 56                 push        esi
  00401678: 68 00 00 03 00     push        30000h
  0040167D: 68 00 00 01 00     push        10000h
  00401682: 33 F6              xor         esi,esi
  00401684: 56                 push        esi
  00401685: E8 C6 00 00 00     call        00401750
  0040168A: 83 C4 0C           add         esp,0Ch
  0040168D: 85 C0              test        eax,eax
  0040168F: 75 02              jne         00401693
  00401691: 5E                 pop         esi
  00401692: C3                 ret
  00401693: 56                 push        esi
  00401694: 56                 push        esi
  00401695: 56                 push        esi
  00401696: 56                 push        esi
  00401697: 56                 push        esi
  00401698: E8 AD 00 00 00     call        0040174A
  0040169D: CC                 int         3
  0040169E: FF 25 34 20 40 00  jmp         dword ptr ds:[00402034h]
  004016A4: FF 25 38 20 40 00  jmp         dword ptr ds:[00402038h]
  004016AA: CC                 int         3
  004016AB: CC                 int         3
  004016AC: CC                 int         3
  004016AD: CC                 int         3
  004016AE: CC                 int         3
  004016AF: CC                 int         3
  004016B0: 68 09 17 40 00     push        401709h
  004016B5: 64 FF 35 00 00 00  push        dword ptr fs:[00000000h]
            00
  004016BC: 8B 44 24 10        mov         eax,dword ptr [esp+10h]
  004016C0: 89 6C 24 10        mov         dword ptr [esp+10h],ebp
  004016C4: 8D 6C 24 10        lea         ebp,[esp+10h]
  004016C8: 2B E0              sub         esp,eax
  004016CA: 53                 push        ebx
  004016CB: 56                 push        esi
  004016CC: 57                 push        edi
  004016CD: A1 38 30 40 00     mov         eax,dword ptr ds:[00403038h]
  004016D2: 31 45 FC           xor         dword ptr [ebp-4],eax
  004016D5: 33 C5              xor         eax,ebp
  004016D7: 50                 push        eax
  004016D8: 89 65 E8           mov         dword ptr [ebp-18h],esp
  004016DB: FF 75 F8           push        dword ptr [ebp-8]
  004016DE: 8B 45 FC           mov         eax,dword ptr [ebp-4]
  004016E1: C7 45 FC FE FF FF  mov         dword ptr [ebp-4],0FFFFFFFEh
            FF
  004016E8: 89 45 F8           mov         dword ptr [ebp-8],eax
  004016EB: 8D 45 F0           lea         eax,[ebp-10h]
  004016EE: 64 A3 00 00 00 00  mov         dword ptr fs:[00000000h],eax
  004016F4: C3                 ret
  004016F5: 8B 4D F0           mov         ecx,dword ptr [ebp-10h]
  004016F8: 64 89 0D 00 00 00  mov         dword ptr fs:[00000000h],ecx
            00
  004016FF: 59                 pop         ecx
  00401700: 5F                 pop         edi
  00401701: 5F                 pop         edi
  00401702: 5E                 pop         esi
  00401703: 5B                 pop         ebx
  00401704: 8B E5              mov         esp,ebp
  00401706: 5D                 pop         ebp
  00401707: 51                 push        ecx
  00401708: C3                 ret
  00401709: 55                 push        ebp
  0040170A: 8B EC              mov         ebp,esp
  0040170C: FF 75 14           push        dword ptr [ebp+14h]
  0040170F: FF 75 10           push        dword ptr [ebp+10h]
  00401712: FF 75 0C           push        dword ptr [ebp+0Ch]
  00401715: FF 75 08           push        dword ptr [ebp+8]
  00401718: 68 56 17 40 00     push        401756h
  0040171D: 68 38 30 40 00     push        403038h
  00401722: E8 3F 00 00 00     call        00401766
  00401727: 83 C4 18           add         esp,18h
  0040172A: 5D                 pop         ebp
  0040172B: C3                 ret
  0040172C: FF 25 48 20 40 00  jmp         dword ptr ds:[00402048h]
  00401732: FF 25 94 20 40 00  jmp         dword ptr ds:[00402094h]
  00401738: FF 25 50 20 40 00  jmp         dword ptr ds:[00402050h]
  0040173E: FF 25 54 20 40 00  jmp         dword ptr ds:[00402054h]
  00401744: FF 25 5C 20 40 00  jmp         dword ptr ds:[0040205Ch]
  0040174A: FF 25 64 20 40 00  jmp         dword ptr ds:[00402064h]
  00401750: FF 25 68 20 40 00  jmp         dword ptr ds:[00402068h]
  00401756: 3B 0D 38 30 40 00  cmp         ecx,dword ptr ds:[00403038h]
  0040175C: 75 02              jne         00401760
  0040175E: F3 C3              rep ret
  00401760: E9 44 00 00 00     jmp         004017A9
  00401765: CC                 int         3
  00401766: FF 25 6C 20 40 00  jmp         dword ptr ds:[0040206Ch]
  0040176C: 55                 push        ebp
  0040176D: 8B EC              mov         ebp,esp
  0040176F: FF 15 1C 20 40 00  call        dword ptr ds:[0040201Ch]
  00401775: 6A 01              push        1
  00401777: A3 8C 33 40 00     mov         dword ptr ds:[0040338Ch],eax
  0040177C: E8 23 01 00 00     call        004018A4
  00401781: FF 75 08           push        dword ptr [ebp+8]
  00401784: E8 21 01 00 00     call        004018AA
  00401789: 83 3D 8C 33 40 00  cmp         dword ptr ds:[0040338Ch],0
            00
  00401790: 59                 pop         ecx
  00401791: 59                 pop         ecx
  00401792: 75 08              jne         0040179C
  00401794: 6A 01              push        1
  00401796: E8 09 01 00 00     call        004018A4
  0040179B: 59                 pop         ecx
  0040179C: 68 09 04 00 C0     push        0C0000409h
  004017A1: E8 0A 01 00 00     call        004018B0
  004017A6: 59                 pop         ecx
  004017A7: 5D                 pop         ebp
  004017A8: C3                 ret
  004017A9: 55                 push        ebp
  004017AA: 8B EC              mov         ebp,esp
  004017AC: 81 EC 24 03 00 00  sub         esp,324h
  004017B2: 6A 17              push        17h
  004017B4: E8 FD 00 00 00     call        004018B6
  004017B9: 85 C0              test        eax,eax
  004017BB: 74 05              je          004017C2
  004017BD: 6A 02              push        2
  004017BF: 59                 pop         ecx
  004017C0: CD 29              int         29h
  004017C2: A3 70 31 40 00     mov         dword ptr ds:[00403170h],eax
  004017C7: 89 0D 6C 31 40 00  mov         dword ptr ds:[0040316Ch],ecx
  004017CD: 89 15 68 31 40 00  mov         dword ptr ds:[00403168h],edx
  004017D3: 89 1D 64 31 40 00  mov         dword ptr ds:[00403164h],ebx
  004017D9: 89 35 60 31 40 00  mov         dword ptr ds:[00403160h],esi
  004017DF: 89 3D 5C 31 40 00  mov         dword ptr ds:[0040315Ch],edi
  004017E5: 66 8C 15 88 31 40  mov         word ptr ds:[00403188h],ss
            00
  004017EC: 66 8C 0D 7C 31 40  mov         word ptr ds:[0040317Ch],cs
            00
  004017F3: 66 8C 1D 58 31 40  mov         word ptr ds:[00403158h],ds
            00
  004017FA: 66 8C 05 54 31 40  mov         word ptr ds:[00403154h],es
            00
  00401801: 66 8C 25 50 31 40  mov         word ptr ds:[00403150h],fs
            00
  00401808: 66 8C 2D 4C 31 40  mov         word ptr ds:[0040314Ch],gs
            00
  0040180F: 9C                 pushfd
  00401810: 8F 05 80 31 40 00  pop         dword ptr ds:[00403180h]
  00401816: 8B 45 00           mov         eax,dword ptr [ebp]
  00401819: A3 74 31 40 00     mov         dword ptr ds:[00403174h],eax
  0040181E: 8B 45 04           mov         eax,dword ptr [ebp+4]
  00401821: A3 78 31 40 00     mov         dword ptr ds:[00403178h],eax
  00401826: 8D 45 08           lea         eax,[ebp+8]
  00401829: A3 84 31 40 00     mov         dword ptr ds:[00403184h],eax
  0040182E: 8B 85 DC FC FF FF  mov         eax,dword ptr [ebp+FFFFFCDCh]
  00401834: C7 05 C0 30 40 00  mov         dword ptr ds:[004030C0h],10001h
            01 00 01 00
  0040183E: A1 78 31 40 00     mov         eax,dword ptr ds:[00403178h]
  00401843: A3 7C 30 40 00     mov         dword ptr ds:[0040307Ch],eax
  00401848: C7 05 70 30 40 00  mov         dword ptr ds:[00403070h],0C0000409h
            09 04 00 C0
  00401852: C7 05 74 30 40 00  mov         dword ptr ds:[00403074h],1
            01 00 00 00
  0040185C: C7 05 80 30 40 00  mov         dword ptr ds:[00403080h],1
            01 00 00 00
  00401866: 6A 04              push        4
  00401868: 58                 pop         eax
  00401869: 6B C0 00           imul        eax,eax,0
  0040186C: C7 80 84 30 40 00  mov         dword ptr [eax+00403084h],2
            02 00 00 00
  00401876: 6A 04              push        4
  00401878: 58                 pop         eax
  00401879: 6B C0 00           imul        eax,eax,0
  0040187C: 8B 0D 38 30 40 00  mov         ecx,dword ptr ds:[00403038h]
  00401882: 89 4C 05 F8        mov         dword ptr [ebp+eax-8],ecx
  00401886: 6A 04              push        4
  00401888: 58                 pop         eax
  00401889: C1 E0 00           shl         eax,0
  0040188C: 8B 0D 3C 30 40 00  mov         ecx,dword ptr ds:[0040303Ch]
  00401892: 89 4C 05 F8        mov         dword ptr [ebp+eax-8],ecx
  00401896: 68 C8 20 40 00     push        4020C8h
  0040189B: E8 CC FE FF FF     call        0040176C
  004018A0: 8B E5              mov         esp,ebp
  004018A2: 5D                 pop         ebp
  004018A3: C3                 ret
  004018A4: FF 25 70 20 40 00  jmp         dword ptr ds:[00402070h]
  004018AA: FF 25 74 20 40 00  jmp         dword ptr ds:[00402074h]
  004018B0: FF 25 78 20 40 00  jmp         dword ptr ds:[00402078h]
  004018B6: FF 25 00 20 40 00  jmp         dword ptr ds:[00402000h]

  Summary

        1000 .data
        1000 .rdata
        1000 .reloc
        1000 .text




04_PE ヘッダ内の各構造体のメンバーの値を列挙


EXE や DLL の実行可能形式のファイルを PE ファイルと言っています.PE は Portable Executable の略です.PE ファイルのヘッダ部分が PE ヘッダということになります.

EXE のバイナリダンプをみると,一見意味のない数値の羅列ですが,PE ヘッダは複数の構造体で構成されています.詳細は参考リンクの記事で確認してください.
以下は,この PE ヘッダの情報を取得するサンプルです.まず,TFileStream でファイルストリームを作成します.そのストリームに,構造体を順番に読み込んで,各構造体のメンバーの値を取得します.
PE ヘッダの内容は,前項で説明した DUMPBIN でも取得することができます.

特定の構造体の,特定のメンバーの値だけを取得したい場合は,ImageNtHeader 関数を使用すると便利です.この関数の使用方法は,参考リンクの記事,または次項のサンプルを参考にしてください.

PE ヘッダからは,対象のファイルが 32 ビット版であるか 64 ビット版であるか,DLL か EXE かも知ることができます.また,GUI アプリかコンソールアプリであるかの情報も格納されています.
下の図は,今回作成した Hello2.exe の PE ヘッダ情報の取得結果です.

このサンプルは,32 ビット版の EXE, DLL 用のコードとなっています.


図13
設計時画面
図14
実行結果

上の図の最後の行の 6144 という値は,PE ヘッダの値から計算したファイルのサイズです.
一方,下の図のファイルのプロパティダイアログでも,ファイルのサイズが 6,144 バイトとなっていて,同じになっていることが確認できます.


図15
ファイルのプロパティダイアログでファイのサイズを確認

リスト4
PE ファイルのヘッダ部の情報を取得して表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Memo1: TMemo;
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
    procedure IMAGE_DOS_HEADER_Read(const Header: IMAGE_DOS_HEADER);
    procedure IMAGE_NT_HEADERS_Read(const Header: IMAGE_NT_HEADERS);
    procedure IMAGE_FILE_HEADER_Read(const Header: IMAGE_FILE_HEADER);
    procedure IMAGE_OPTIONAL_HEADER_Read(const Header: IMAGE_OPTIONAL_HEADER);
    procedure IMAGE_SECTION_HEADERS_Read(const Header: IMAGE_SECTION_HEADER);
    procedure DisplayValueToMemo(CntChar: Integer; DigitValue: Integer;
      DispText: String; DispValue: Integer);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses DateUtils;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Name   := 'Courier New';
  Memo1.Font.Size   := 10;
  Memo1.WordWrap    := False;

  Panel1.Caption := '';
end;

//=============================================================================
//  PEヘッダ内の各構造体のメンバーの値を列挙
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath   : String;
  FStream        : TFilestream;
  DummyData      : DWORD;
  HInfo_DOS      : IMAGE_DOS_HEADER;
  HInfo_NTHEADER : IMAGE_NT_HEADERS;
  HInfo_FILE     : IMAGE_FILE_HEADER;
  HInfo_OPTIONAL : IMAGE_OPTIONAL_HEADER;
  HInfo_SECTION  : IMAGE_SECTION_HEADER;
  CountSections  : Integer;
  i              : Integer;
  MemPos         : Integer;
  EndSectionPos  : Integer;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    Memo1.Clear;
    Memo1.Lines.BeginUpdate;

    //ファイルストリームを生成
    FStream := TFilestream.Create(FileFullPath, fmOpenread or fmShareDenyNone);
    try
      Memo1.Lines.Add('FileName : ' + ExtractFileName(FileFullPath));
      Memo1.Lines.Add(' ');


      //IMAGE_DOS_HEADERの情報をHeaderInfo_DOSレコード型に読み込む
      MemPos := FStream.Position;
      FStream.Read(HInfo_DOS, SizeOf(HInfo_DOS));
      Memo1.Lines.Add('IMAGE_DOS_HEADER ');
      Memo1.Lines.Add('[Memory Position]' + Format('  $%.8x', [MemPos]));
      Memo1.Lines.Add('[Size of Header]' + ' ' + IntToStr(SizeOf(HInfo_DOS)));
      Memo1.Lines.Add('--------------------------------------------------------');
      IMAGE_DOS_HEADER_Read(HInfo_DOS);


      //IMAGE_NT_HEADER部の先頭に移動するための位置移動
      FStream.Seek(HInfo_DOS._lfanew, soFromBeginning);

      //IMAGE_NT_HEADERSの情報を読み込む
      MemPos := FStream.Position;
      FStream.Read(HInfo_NTHEADER, SizeOf(HInfo_NTHEADER));
      //その情報をTMemoに表示する
      Memo1.Lines.Add(' ');
      Memo1.Lines.Add('IMAGE_NT_HEADER (PE HEADER)');
      Memo1.Lines.Add('[Memory Position]' + Format('  $%.8x', [MemPos]));
      Memo1.Lines.Add('[Size of Header]' + ' ' + IntToStr(SizeOf(HInfo_NTHEADER)));
      Memo1.Lines.Add('--------------------------------------------------------');
      IMAGE_NT_HEADERS_Read(HInfo_NTHEADER);


      //IMAGE_FILE_HEADERレコード型の先頭に移動
      FStream.Seek(HInfo_DOS._lfanew, soFromBeginning);
      FStream.Read(DummyData, SizeOf(DummyData));

      //IMAGE_FILE_HEADERの情報を読み込む
      MemPos := FStream.Position;
      FStream.Read(HInfo_FILE, SizeOf(HInfo_FILE));
      //その情報をTMemoに表示する
      Memo1.Lines.Add(' ');
      Memo1.Lines.Add('IMAGE_NT_HEADER - IMAGE_FILE_HEADER');
      Memo1.Lines.Add('[Memory Position]' + Format('  $%.8x', [MemPos]));
      Memo1.Lines.Add('[Size of Header]' + ' ' + IntToStr(SizeOf(HInfo_FILE)));
      Memo1.Lines.Add('--------------------------------------------------------');
      IMAGE_FILE_HEADER_Read(HInfo_FILE);

      //セクション数を取得しておく
      CountSections := HInfo_FILE.NumberOfSections;


      //IMAGE_OPTIONAL_HEADER部の情報を読み込む
      MemPos := FStream.Position;
      FStream.Read( HInfo_OPTIONAL, SizeOf(HInfo_OPTIONAL));
      //その情報をTMemoに表示する
      Memo1.Lines.Add(' ');
      Memo1.Lines.Add('IMAGE_NT_HEADER - IMAGE_OPTIONAL_HEADER');
      Memo1.Lines.Add('[Memory Position]' + Format('  $%.8x', [MemPos]));
      Memo1.Lines.Add('[Size of Header]' + ' ' + IntToStr(SizeOf(HInfo_OPTIONAL)));
      Memo1.Lines.Add('--------------------------------------------------------');
      IMAGE_OPTIONAL_HEADER_Read(HInfo_OPTIONAL);

      for i := 0 to CountSections - 1 do begin
        //IMAGE_SECTION_HEADERの情報を読み込む
        MemPos := FStream.Position;
        FStream.Read(HInfo_SECTION, SizeOf(HInfo_SECTION));

        //その情報をTMemoに表示する
        Memo1.Lines.Add(' ');
        Memo1.Lines.Add('IMAGE_SECTION_HEADER - SECTION[' + IntToStr(i) + ']');
        Memo1.Lines.Add('[Memory Position]' + Format('  $%.8x', [MemPos]));
        Memo1.Lines.Add('[Size of Header]' + ' ' + IntToStr(SizeOf(HInfo_SECTION)));
        Memo1.Lines.Add('--------------------------------------------------------');
        //その情報をTMemoに表示する
        IMAGE_SECTION_HEADERS_Read(HInfo_SECTION);
      end;

      //最後ののセクションの最後の位置を計算
      EndSectionPos := HInfo_SECTION.PointerToRawData + HInfo_SECTION.SizeOfRawData;
      //16進文字の表示にはIntToHeX関数も使用可能
      Memo1.Lines.Add('--------------------------------------------------------');
      Memo1.Lines.Add('FileSize =  $' + IntToHex(EndSectionPos, 8)
                     + ' (' + IntToStr(EndSectionPos) + ') bytes');
    finally
      FStream.Free;
    end;

    Memo1.Lines.EndUpdate;
  end;
end;

//-----------------------------------------------------------------------------
//  IMAGE_DOS_HEADER構造体の各メンバの値を取得して表示
//  Windowsでは使用されていない
//  Windowsで意味があるのは以下の2つのみ
//
//  e_magic : DWORD MZというシグネチャ文字で,実行可能ファイルを意味する
//  lfanew  : LONG
//            新しい形式のヘッダへのファイル内オフセット
//            新しい形式のヘッダとはPE ファイルフォーマットならNTヘッダのこと
//            NTヘッダがPEファイルの真のヘッダ
//-----------------------------------------------------------------------------
procedure TForm1.IMAGE_DOS_HEADER_Read(const Header: IMAGE_DOS_HEADER);
var
  magic   : WORD;
  lfanew  : Integer;
  StrText : String;
begin
  magic  := Header.e_magic;
  lfanew := Header._lfanew;

  //このシグネチャはIMAGE_DOS_SIGNATURE($5A4D = 'MZ'という定数がある
  StrText := Char(Lo(magic)) + Char(Hi(magic));
  Memo1.Lines.Add(Format('%-10s %-3s',  ['e_magic : ', StrText]));
  Memo1.Lines.Add(Format('%-10s $%.8x', ['lfanew  : ', lfanew]));
end;

//-----------------------------------------------------------------------------
//  IMAGE_NT_HEADERS構造体の各メンバの値を取得して表示
//  この構造体には,次の構造体が階層下にある
//  IMAGE_FILE_HEADER
//  IMAGE_OPTIONAL_HEADER(この中にIMAGE_DATA_DIRECTORY構造体がある)
//-----------------------------------------------------------------------------
procedure TForm1.IMAGE_NT_HEADERS_Read(const Header: IMAGE_NT_HEADERS);
var
  Signature : DWORD;
  StrText   : String;
begin
  Signature := Header.Signature;
  StrText := String(PAnsiChar(@Signature));

  //16進数はバイトオーダーがリトルリトルエンディアンになっているので注意
  //このシグネチャはIMAGE_NT_SIGNATURE($00004550 = 'PE00'という定数がある
  StrText :=  'Signature : ' + StrText + ' (' + Format('$%.4x',  [Signature]) + ')';
  Memo1.Lines.Add(StrText);
end;

//-----------------------------------------------------------------------------
//  PEのIMAGE_FILE_HEADER構造体の各メンバーの値を取得して表示
//-----------------------------------------------------------------------------
procedure TForm1.IMAGE_FILE_HEADER_Read(const Header: IMAGE_FILE_HEADER);

  //---------------------------------------------------------------------------
  //  UTC(世界協定時刻)とローカルとの時刻差を求める関数
  //  この差は分単位となっている
  //  日本の場合は常に9:00:00となる
  //---------------------------------------------------------------------------
  function UTCTimeDateTimeOffset: TDateTime;
  var
    tZone : TTimeZoneInformation;
  begin
    GetTimeZoneInformation(tZone);
    Result := EncodeTime(ABS(tZone.Bias) div 60, ABS(tZone.Bias) mod 60, 0, 0);
  end;
  //---------------------------------------------------------------------------

const
  IMAGE_FILE_MACHINE_IA64  = $0200;
  IMAGE_FILE_MACHINE_AMD64 = $8664;
var
  StrText  : String;
  CntChars : Integer;
  StrFmt   : String;
  TempText : String;
  DateTime : TDateTime;
  SL       : TStringList;
  i        : Integer;
begin
  CntChars := 20;
  StrFmt   := '%-' + IntTostr(CntChars) + 's : ';

  //マシンのタイプ
  StrText := 'Machine';
  case Header.Machine of
    IMAGE_FILE_MACHINE_I386    : TempText := 'IMAGE_FILE_MACHINE_I386';
    IMAGE_FILE_MACHINE_IA64    : TempText := 'IMAGE_FILE_MACHINE_IA64';
    IMAGE_FILE_MACHINE_AMD64   : TempText := 'IMAGE_FILE_MACHINE_AMD64';
    IMAGE_FILE_MACHINE_R3000   : TempText := 'IMAGE_FILE_MACHINE_R3000';
    IMAGE_FILE_MACHINE_R4000   : TempText := 'IMAGE_FILE_MACHINE_R4000';
    IMAGE_FILE_MACHINE_R10000  : TempText := 'IMAGE_FILE_MACHINE_R10000';
    IMAGE_FILE_MACHINE_ALPHA   : TempText := 'IMAGE_FILE_MACHINE_ALPHA';
    IMAGE_FILE_MACHINE_POWERPC : TempText := 'IMAGE_FILE_MACHINE_POWERPC';
  else
    TempText := 'Unknown';
  end;
  Memo1.Lines.Add(Format(StrFmt, [StrText]) + TempText);

  StrText := 'NumberOfSections';
  DisplayValueToMemo(CntChars, 4, StrText, Header.NumberOfSections);

  StrText  := 'TimeDateStamp';
  DateTime := UnixToDateTime(Header.TimeDateStamp) + UTCTimeDateTimeOffset;
  TempText := FormatDateTime('yyyy/mm/dd hh:nn:ss', DateTime);
  Memo1.Lines.Add(Format(StrFmt, [StrText]) + TempText);

  StrText  := 'PointerToSymbolTable';
  DisplayValueToMemo(CntChars, 8, StrText, Header.PointerToSymbolTable);

  StrText  := 'NumberOfSymbols';
  DisplayValueToMemo(CntChars, 8, StrText, Header.NumberOfSymbols);


  //ファイルの属性値のフラグの組み合わせ
  //DLLの時は,IMAGE_FILE_DLLがある
  //EXEの時は,IMAGE_FILE_EXECUTABLE_IMAGEがある
  StrText  := 'SizeOfOptionalHeader';
  DisplayValueToMemo(CntChars, 4, StrText, Header.SizeOfOptionalHeader);

  SL := TStringList.Create;
  try
    StrText  := 'Characteristics';
    if (IMAGE_FILE_RELOCS_STRIPPED and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_RELOCS_STRIPPED');
    if (IMAGE_FILE_EXECUTABLE_IMAGE and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_EXECUTABLE_IMAGE');
    if (IMAGE_FILE_LINE_NUMS_STRIPPED and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_LINE_NUMS_STRIPPED');
    if (IMAGE_FILE_LOCAL_SYMS_STRIPPED and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_LOCAL_SYMS_STRIPPED');
    if (IMAGE_FILE_AGGRESIVE_WS_TRIM and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_AGGRESIVE_WS_TRIM');
    if (IMAGE_FILE_LARGE_ADDRESS_AWARE and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_LARGE_ADDRESS_AWARE');
    if (IMAGE_FILE_BYTES_REVERSED_LO and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_BYTES_REVERSED_LO');
    if (IMAGE_FILE_32BIT_MACHINE and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_32BIT_MACHINE');
    if (IMAGE_FILE_DEBUG_STRIPPED and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_DEBUG_STRIPPED');
    if (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP');
    if (IMAGE_FILE_NET_RUN_FROM_SWAP and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_NET_RUN_FROM_SWAP');
    if (IMAGE_FILE_SYSTEM and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_SYSTEM');
    if (IMAGE_FILE_DLL and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_DLL');
    if (IMAGE_FILE_UP_SYSTEM_ONLY and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_UP_SYSTEM_ONLY');
    if (IMAGE_FILE_BYTES_REVERSED_HI and Header.Characteristics) <> 0 then
      SL.Add('IMAGE_FILE_BYTES_REVERSED_HI');

    Memo1.Lines.Add(Format(StrFmt, [StrText]) + SL[0]);
    StrText  := '';
    for i := 1 to SL.Count - 1 do begin
      Memo1.Lines.Add(Format(StrFmt, [StrText]) + SL[i]);
    end;
  finally
    FreeAndNil(SL);
  end;
end;

//-----------------------------------------------------------------------------
//  IMAGE_OPTIONAL_HEADER構造体の各メンバーの値を取得して表示
//-----------------------------------------------------------------------------
procedure TForm1.IMAGE_OPTIONAL_HEADER_Read(const Header: IMAGE_OPTIONAL_HEADER);
const
  IMAGE_NT_OPTIONAL_HDR64_MAGIC  = $020B;
  IMAGE_NT_OPTIONAL_HDR32_MAGIC  = $010B;
  IMAGE_ROM_OPTIONAL_HDR_MAGIC   = $0107;
  IMAGE_SUBSYSTEM_WINDOWS_CE_GUI           = 9;
  IMAGE_SUBSYSTEM_EFI_APPLICATION          = 10;
  IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER  = 11;
  IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER       = 12;
  IMAGE_SUBSYSTEM_EFI_ROM                  = 13;
  IMAGE_SUBSYSTEM_XBOX                     = 14;
  IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16;
  IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE    = $0040;
  IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY = $0080;
  IMAGE_DLLCHARACTERISTICS_NX_COMPAT       = $0100;
  IMAGE_DLLCHARACTERISTICS_NO_ISOLATION    = $0200;
  IMAGE_DLLCHARACTERISTICS_NO_SEH          = $0400;
var
  StrText  : String;
  TempText : String;
  TempWord : WORD;
  CntChars : Integer;
  StrFmt   : String;
  SL       : TStringList;
  ImageDir : TImageDataDirectory;
  i        : Integer;
begin
  CntChars := 27;
  StrFmt   := '%-' + IntTostr(CntChars) + 's : ';

  //バイナリの実行環境が32ビットか64ビットか等
  StrText := 'Magic';
  case Header.Magic of
    IMAGE_NT_OPTIONAL_HDR32_MAGIC : TempText := 'IMAGE_NT_OPTIONAL_HDR32_MAGIC';
    IMAGE_NT_OPTIONAL_HDR64_MAGIC : TempText := 'IMAGE_NT_OPTIONAL_HDR64_MAGIC';
    IMAGE_ROM_OPTIONAL_HDR_MAGIC  : TempText := 'IMAGE_ROM_OPTIONAL_HDR_MAGIC';
  else
    TempText := 'Unknown';
  end;
  Memo1.Lines.Add(Format(StrFmt, [StrText]) + TempText);

  StrText := 'MajorLinkerVersion';
  DisplayValueToMemo(CntChars, 2, StrText, Header.MajorLinkerVersion);

  StrText := 'MinorLinkerVersion';
  DisplayValueToMemo(CntChars, 2, StrText, Header.MinorLinkerVersion);

  StrText := 'SizeOfCode';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfCode);

  StrText := 'SizeOfInitializedData';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfInitializedData);

  StrText := 'SizeOfUninitializedData';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfUninitializedData);

  StrText := 'AddressOfEntryPoint';
  DisplayValueToMemo(CntChars, 8, StrText, Header.AddressOfEntryPoint);

  StrText := 'BaseOfCode';
  DisplayValueToMemo(CntChars, 8, StrText, Header.BaseOfCode);

  StrText := 'BaseOfData';
  DisplayValueToMemo(CntChars, 8, StrText, Header.BaseOfData);

  StrText := 'ImageBase';
  DisplayValueToMemo(CntChars, 8, StrText, Header.ImageBase);

  StrText := 'SectionAlignment';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SectionAlignment);

  StrText := 'FileAlignment';
  DisplayValueToMemo(CntChars, 8, StrText, Header.FileAlignment);

  StrText := 'MajorOperatingSystemVersion';
  DisplayValueToMemo(CntChars, 4, StrText, Header.MajorOperatingSystemVersion);

  StrText := 'MinorOperatingSystemVersion';
  DisplayValueToMemo(CntChars, 4, StrText, Header.MinorOperatingSystemVersion);

  StrText := 'MajorImageVersion';
  DisplayValueToMemo(CntChars, 4, StrText, Header.MajorImageVersion);

  StrText := 'MinorImageVersion';
  DisplayValueToMemo(CntChars, 4, StrText, Header.MinorImageVersion);

  StrText := 'MajorSubsystemVersion';
  DisplayValueToMemo(CntChars, 4, StrText, Header.MajorSubsystemVersion);

  StrText := 'MinorSubsystemVersion';
  DisplayValueToMemo(CntChars, 4, StrText, Header.MinorSubsystemVersion);

  StrText := 'Win32VersionValue';
  DisplayValueToMemo(CntChars, 8, StrText, Header.Win32VersionValue);

  StrText := 'SizeOfImage';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfImage);

  StrText := 'SizeOfHeaders';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfHeaders);

  StrText := 'CheckSum';
  DisplayValueToMemo(CntChars, 8, StrText, Header.CheckSum);

  //アプリの特性
  //Windowsアプリは,IMAGE_SUBSYSTEM_WINDOWS_GUI(2)
  //コンソールアプリは,IMAGE_SUBSYSTEM_WINDOWS_CUI(3)
  //デバイスドライバは,IMAGE_SUBSYSTEM_NATIVE(1)
  StrText := 'Subsystem';
  case Header.Subsystem of
    IMAGE_SUBSYSTEM_UNKNOWN :
      TempText := 'IMAGE_SUBSYSTEM_UNKNOWN';
    IMAGE_SUBSYSTEM_NATIVE :
      TempText := 'IMAGE_SUBSYSTEM_NATIVE';
    IMAGE_SUBSYSTEM_WINDOWS_GUI :
      TempText := 'IMAGE_SUBSYSTEM_WINDOWS_GUI';
    IMAGE_SUBSYSTEM_WINDOWS_CUI :
      TempText := 'IMAGE_SUBSYSTEM_WINDOWS_CUI';
    IMAGE_SUBSYSTEM_OS2_CUI :
      TempText := 'IMAGE_SUBSYSTEM_OS2_CUI';
    IMAGE_SUBSYSTEM_POSIX_CUI :
      TempText := 'IMAGE_SUBSYSTEM_POSIX_CUI';
    IMAGE_SUBSYSTEM_WINDOWS_CE_GUI :
      TempText := 'IMAGE_SUBSYSTEM_WINDOWS_CE_GUI';
    IMAGE_SUBSYSTEM_EFI_APPLICATION :
      TempText := 'IMAGE_SUBSYSTEM_EFI_APPLICATION';
    IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER :
      TempText := 'IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER';
    IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER :
      TempText := 'IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER';
    IMAGE_SUBSYSTEM_EFI_ROM :
      TempText := 'IMAGE_SUBSYSTEM_EFI_ROM';
    IMAGE_SUBSYSTEM_XBOX :
      TempText := 'IMAGE_SUBSYSTEM_XBOX';
    IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION :
      TempText := 'IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION';
  else
    TempText := 'Unknown';
  end;
  Memo1.Lines.Add(Format(StrFmt, [StrText]) + TempText);

  //DLLの特性に関する値(一般的には意味がないらしい)
  StrText  := 'DllCharacteristics';
  TempWord := Header.DllCharacteristics;
  SL := TStringList.Create;
  try
    if (TempWord and IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE');
    if (TempWord and IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY');
    if (TempWord and IMAGE_DLLCHARACTERISTICS_NX_COMPAT) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_NX_COMPAT');
    if (TempWord and IMAGE_DLLCHARACTERISTICS_NO_ISOLATION) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_NO_ISOLATION');
    if (TempWord and IMAGE_DLLCHARACTERISTICS_NO_SEH) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_NO_SEH');
    if (TempWord and IMAGE_DLLCHARACTERISTICS_NO_BIND) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_NO_BIND');
    if (TempWord and IMAGE_DLLCHARACTERISTICS_WDM_DRIVER) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_WDM_DRIVER');
    if (TempWord and IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE) <> 0 then
      SL.Add('IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE');

    if SL.Count = 0 then SL.Add('Unknown');
    Memo1.Lines.Add(Format(StrFmt, [StrText]) + SL[0]);
    StrText  := '';
    for i := 1 to SL.Count - 1 do begin
      Memo1.Lines.Add(Format(StrFmt, [StrText]) + SL[i]);
    end;
  finally
    FreeAndNil(SL);
  end;

  StrText := 'SizeOfStackReserve';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfStackReserve);

  StrText := 'SizeOfStackCommit';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfStackCommit);

  StrText := 'SizeOfHeapReserve';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfHeapReserve);

  StrText := 'SizeOfHeapCommit';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfHeapCommit);

  StrText := 'LoaderFlags';
  DisplayValueToMemo(CntChars, 8, StrText, Header.LoaderFlags);

  StrText := 'NumberOfRvaAndSizes';
  DisplayValueToMemo(CntChars, 8, StrText, Header.NumberOfRvaAndSizes);


  //IMAGE_DATA_DIRECTORY構造体の各メンバの値を取得して表示
  //この構造体は配列
  //各種データのPEヘッダ(イメージベース)からの相対的な位置(RVA)と,
  //そのデータのサイズがバイト単位で格納されている
  CntChars := 32;
  for i := 0 to IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1 do begin
    ImageDir := Header.DataDirectory[i];

    StrText := 'DataDirectory[' + IntToStr(i) + '].VirtualAddress';
    DisplayValueToMemo(CntChars, 8, StrText, ImageDir.VirtualAddress);

    StrText := 'DataDirectory[' + IntToStr(i) + '].Size';
    DisplayValueToMemo(CntChars, 8, StrText, ImageDir.Size);
  end;
end;

//-----------------------------------------------------------------------------
//  IMAGE_SECTION_HEADER構造体の各メンバの値を取得して表示
//  セクション数は,アプリケーションによって異なる
//  ネイティブコード,リソース,デバッグ情報等の実際のデータの相対位置(RVA)
//  データサイズ等がある
//-----------------------------------------------------------------------------
procedure TForm1.IMAGE_SECTION_HEADERS_Read(const Header: IMAGE_SECTION_HEADER);
const
  IMAGE_SCN_NO_DEFER_SPEC_EXC = $00004000;
  IMAGE_SCN_GPREL             = $00008000;
  IMAGE_SCN_ALIGN_128BYTES    = $00800000;
  IMAGE_SCN_ALIGN_256BYTES    = $00900000;
  IMAGE_SCN_ALIGN_512BYTES    = $00A00000;
  IMAGE_SCN_ALIGN_1024BYTES   = $00B00000;
  IMAGE_SCN_ALIGN_2048BYTES   = $00C00000;
  IMAGE_SCN_ALIGN_4096BYTES   = $00D00000;
  IMAGE_SCN_ALIGN_8192BYTES   = $00E00000;
var
  StrText   : String;
  CntChars  : Integer;
  StrFmt    : String;
  TempDWORD : DWORD;
  SL        : TStringList;
  i         : Integer;
  Name      : String;
begin
  CntChars := 20;
  StrFmt   := '%-' + IntTostr(CntChars) + 's : ';

  //セクション名のバイト配列(ASCII文字列)
  //バイト配列から文字列に変換
  //PAnsiCharのままだと警告が表示されるので明示的にString型でキャスト
  Name := String(PAnsiChar(@Header.Name));
  Memo1.Lines.Add(Format(StrFmt,  ['Name']) + Name);

  StrText := 'VirtualAddress';
  DisplayValueToMemo(CntChars, 8, StrText, Header.VirtualAddress);

  StrText := 'SizeOfRawData';
  DisplayValueToMemo(CntChars, 8, StrText, Header.SizeOfRawData);

  StrText := 'PointerToRawData';
  DisplayValueToMemo(CntChars, 8, StrText, Header.PointerToRawData);

  StrText := 'PointerToRelocations';
  DisplayValueToMemo(CntChars, 8, StrText, Header.PointerToRelocations);

  StrText := 'PointerToLinenumbers';
  DisplayValueToMemo(CntChars, 8, StrText, Header.PointerToLinenumbers);

  StrText := 'NumberOfRelocations';
  DisplayValueToMemo(CntChars, 4, StrText, Header.NumberOfRelocations);

  StrText := 'NumberOfLinenumbers';
  DisplayValueToMemo(CntChars, 4, StrText, Header.NumberOfLinenumbers);

  //セクションの特性を意味するフラグの組み合わせ
  StrText  := 'Characteristics';
  TempDWord := Header.Characteristics;
  SL := TStringList.Create;
  try
    if (TempDWord and IMAGE_SCN_TYPE_NO_PAD) <> 0 then
        SL.Add('IMAGE_SCN_TYPE_NO_PAD');
    if (TempDWord and IMAGE_SCN_CNT_CODE) <> 0 then
        SL.Add('IMAGE_SCN_CNT_CODE');
    if (TempDWord and IMAGE_SCN_CNT_INITIALIZED_DATA) <> 0 then
        SL.Add('IMAGE_SCN_CNT_INITIALIZED_DATA');
    if (TempDWord and IMAGE_SCN_CNT_UNINITIALIZED_DATA) <> 0 then
        SL.Add('IMAGE_SCN_CNT_UNINITIALIZED_DATA');
    if (TempDWord and IMAGE_SCN_LNK_OTHER) <> 0 then
        SL.Add('IMAGE_SCN_LNK_OTHER');
    if (TempDWord and IMAGE_SCN_LNK_INFO) <> 0 then
        SL.Add('IMAGE_SCN_LNK_INFO');
    if (TempDWord and IMAGE_SCN_LNK_REMOVE) <> 0 then
        SL.Add('IMAGE_SCN_LNK_REMOVE');
    if (TempDWord and IMAGE_SCN_LNK_COMDAT) <> 0 then
        SL.Add('IMAGE_SCN_LNK_COMDAT');
    if (TempDWord and IMAGE_SCN_NO_DEFER_SPEC_EXC) <> 0 then
        SL.Add('IMAGE_SCN_NO_DEFER_SPEC_EXC');
    if (TempDWord and IMAGE_SCN_GPREL) <> 0 then
        SL.Add('IMAGE_SCN_GPREL');
    if (TempDWord and IMAGE_SCN_MEM_PURGEABLE) <> 0 then
        SL.Add('IMAGE_SCN_MEM_PURGEABLE');
    if (TempDWord and IMAGE_SCN_MEM_LOCKED) <> 0 then
        SL.Add('IMAGE_SCN_MEM_LOCKED');
    if (TempDWord and IMAGE_SCN_MEM_PRELOAD) <> 0 then
        SL.Add('IMAGE_SCN_MEM_PRELOAD');
    if (TempDWord and IMAGE_SCN_ALIGN_1BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_1BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_2BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_2BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_4BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_4BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_8BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_8BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_16BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_16BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_32BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_32BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_64BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_64BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_128BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_128BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_256BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_256BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_512BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_512BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_1024BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_1024BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_2048BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_2048BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_4096BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_4096BYTES');
    if (TempDWord and IMAGE_SCN_ALIGN_8192BYTES) <> 0 then
        SL.Add('IMAGE_SCN_ALIGN_8192BYTES');
    if (TempDWord and IMAGE_SCN_LNK_NRELOC_OVFL) <> 0 then
        SL.Add('IMAGE_SCN_LNK_NRELOC_OVFL');
    if (TempDWord and IMAGE_SCN_MEM_DISCARDABLE) <> 0 then
        SL.Add('IMAGE_SCN_MEM_DISCARDABLE');
    if (TempDWord and IMAGE_SCN_MEM_NOT_CACHED) <> 0 then
        SL.Add('IMAGE_SCN_MEM_NOT_CACHED');
    if (TempDWord and IMAGE_SCN_MEM_NOT_PAGED) <> 0 then
        SL.Add('IMAGE_SCN_MEM_NOT_PAGED');
    if (TempDWord and IMAGE_SCN_MEM_SHARED) <> 0 then
        SL.Add('IMAGE_SCN_MEM_SHARED');
    if (TempDWord and IMAGE_SCN_MEM_EXECUTE) <> 0 then
        SL.Add('IMAGE_SCN_MEM_EXECUTE');
    if (TempDWord and IMAGE_SCN_MEM_READ) <> 0 then
        SL.Add('IMAGE_SCN_MEM_READ');
    if (TempDWord and IMAGE_SCN_MEM_WRITE) <> 0 then
        SL.Add('IMAGE_SCN_MEM_WRITE');

    if SL.Count = 0 then SL.Add('Unknown');
    Memo1.Lines.Add(Format(StrFmt, [StrText]) + SL[0]);
    StrText  := '';
    for i := 1 to SL.Count - 1 do begin
      Memo1.Lines.Add(Format(StrFmt, [StrText]) + SL[i]);
    end;
  finally
    FreeAndNil(SL);
  end;
end;

//-----------------------------------------------------------------------------
//  指定文字数と指定桁数で数値を16進表示するメソッド
//  CntChar    : 表示文字数
//  DigitValue : 16進数の表示桁数
//  DispText   : 表示文字
//  DispValue  : 16進数表示する値
//-----------------------------------------------------------------------------
procedure TForm1.DisplayValueToMemo(CntChar, DigitValue: Integer; DispText: String;
  DispValue: Integer);
var
  StrFmt : String;
begin
  StrFmt := '%-' + IntToStr(CntChar) +'s : $%.' + IntTostr(DigitValue) + 'x';
  Memo1.Lines.Add(Format(StrFmt,
                        [DispText, DispValue]) + ' (' + IntToStr(DispValue) + ')');
end;

end.




05_DLL 内のエクスポート関数名を列挙


PE ヘッダの操作例です.PE ヘッダのセクション構造体は複数あります.この数はアプリケーションによって異なります.下表はセクション名の例です.
古い Delph では,ネイティブコードのセクション名が CODE となっているようです.

本サンプルは,DLL ファイル内のエクスポート関数名を列挙します.エクスポート関数は,外部から使用可能な関数のことです.エクスポート関数の情報は,.edata セクション構造体にあります.その構造体に,関数名が格納されている位置の情報があります.それを元に検索していきます.

ヘッダ内の情報の取得には,前項と同じ方法も使えますが,Windows API には,PE ヘッダを操作するための関数類があります.それらは ImageHlp.pas 内で定義されています.この中の ImageNtHeader と ImageRvaToVa という関数を使用しています.
ImageNtHeader 関数を使用すると,PE ヘッダの先頭ポインタが取得できます.そのためには,ファイルを読み込んだ時の先頭アドレスが必要です.これは,メモリマップドファイルを生成して取得しています.メモリマップドファイルは,ファイルハンドルから生成します.

TMemoryStream や TFileStream は,シーケンシャルなアクセスに向いていますが,メモリマップドファイルは,ランダムにメモリ内をアクセスするのに適しています.

サンプルでは,DLL のファイルバージョンも取得して表示していますが,この値は,エクスプローラでファイルのプロパティダイアログの [詳細] タブの [ファイルバージョン] の値と同じです.

本サンプルは,情報の取得ですが,情報を書き込むこともできます.必要性の有無はともかく,EXE 内を書き換えたり,別の EXE を挿入したりすることも可能です.そのためには,PE ヘッダのどこに,どのような情報が格納されているかという知識や,その取得方法が必要になります.

このサンプルは,32 ビット版の EXE, DLL 用のコードとなっています.


セクション名 内容
.text ネイティブコード
.data グローバル変数用の領域
.idata インポート関数情報
.edata エクスポート関数情報
.rsrc リソース情報
.debug デバック情報
.reloc 再配置情報
.tls スレッドごとの情報変数に関する情報



図16
設計時画面
図17
Shell32.dll のエクスポート関数の一覧
  • File Version はファイルのバージョン
  • 外部から使用可能な関数が 333 ある
  • Windows 7 U64(SP1) の場合

リスト5
DLL 内のエクスポート関数を列挙
DLL のファイルバージョンを文字列として取得して表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Memo1: TMemo;
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
    function ListExportDLLfunctionNames(FileHandle: THandle): Boolean;
    function GetFileVersionString(FileName: string): String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ImageHlp;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Name   := 'Courier New';
  Memo1.Font.Size   := 10;
  Memo1.WordWrap    := False;
  Panel1.Font := Memo1.Font;

  Panel1.Caption := '';
end;

//=============================================================================
//  DLL内のエクスポート関数名をリスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath : String;
  FileHandle   : THandle;
begin
  if InitDir = EmptyStr then begin
    InitDir := 'C:\Windows\System32';
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    Memo1.Clear;
    Memo1.Lines.BeginUpdate;

    //対象ファイルを読み出し専用で開き,そのハンドルを取得
    FileHandle := CreateFile(PChar(FileFullPath),
                             GENERIC_READ,
                             FILE_SHARE_READ,
                             nil,
                             OPEN_EXISTING,
                             FILE_ATTRIBUTE_NORMAL,
                             0);
    if FileHandle = INVALID_HANDLE_VALUE then exit;
    try
      Panel1.Caption := ExtractFileName(FileFullPath);

      //ファイルのバージョン文字列を取得
      Memo1.Lines.Add('File Version : ' + GetFileVersionString(FileFullPath));
      //エクスポート関数を列挙
      ListExportDLLfunctionNames(FileHandle);
    finally
      CloseHandle(FileHandle);
    end;
    Memo1.Lines.EndUpdate;
  end;
end;

//-----------------------------------------------------------------------------
//  実際にDLL内のエクスポート関数名をリストする関数
//  引数はファイルのハンドル
//
//  32ビット版専用
//  usesにImageHlpが必要
//-----------------------------------------------------------------------------
function TForm1.ListExportDLLfunctionNames(FileHandle: THandle): Boolean;
var
  HeaderHandle  : THandle;
  HeaderPointer : Pointer;
  pNTHeaders    : PImageNtHeaders;
  pExportTable  : PImageExportDirectory;
  NamesPointer  : PNativeUInt;
  FunctionName  : PAnsiChar;
  i             : Integer;
  StrText       : String;
begin
  Result := False;

  //メモリマップドファイル生成
  //ファイルハンドルからファイルのマッピングオブジェクトを取得
  HeaderHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
  if HeaderHandle = 0 then exit;
  try
    //メモリマップドファイルをアドレス空間にマップ
    //戻り値は開始アドレス
    HeaderPointer := MapViewOfFile(HeaderHandle, FILE_MAP_READ, 0, 0, 0);
    if not Assigned(HeaderPointer) then exit;

    try
      //ヘッダのポインタ(アドレス)を取得
      pNTHeaders := ImageNtHeader(HeaderPointer);
      if not Assigned(pNTHeaders) then exit;;

      //シグネチャ(PE)の文字を確認
      if pNTHeaders.Signature <> IMAGE_NT_SIGNATURE then exit;

      //エクスポートテーブルのアドレスを相対仮想アドレスから仮想アドレスに変換
      pExportTable := ImageRvaToVa(
                       pNTHeaders,
                       HeaderPointer,
                       pNTHeaders.OptionalHeader.DataDirectory[0].VirtualAddress,
                       nil);
      if not Assigned(pExportTable) then exit;

      //関数名が格納されている相対仮想アドレスを仮想アドレスに変換
      //エクスポートテーブルの仮想アドレスから取得する
      NamesPointer := ImageRvaToVa(pNTHeaders,
                                   HeaderPointer,
                                   Cardinal(pExportTable.AddressOfNames),
                                   nil);
      if not Assigned(NamesPointer) then exit;

      Memo1.Lines.Add('Number of Functions : ' + IntToStr(pExportTable.NumberOfNames));;
      Memo1.Lines.Add('--------------------------------------');

      //関数名を順番に取得して表示
      //FunctionNameの型はPAnsiString型にしておくこと
      for i := 0 to pExportTable.NumberOfNames - 1 do begin
        FunctionName := ImageRvaToVa(pNTHeaders, HeaderPointer, NamesPointer^, nil);
        StrText := String(FunctionName);
        Memo1.Lines.Add(FormatFloat('000#', i + 1) + '  ' + StrText);
        //次の関数名が格納されているポインタ(アドレス)に移動
        inc(NamesPointer);
      end;
    finally
      UnmapViewOfFile(HeaderPointer);
    end;
  finally
    CloseHandle(HeaderHandle);
  end;
  Result := True;
end;

//-----------------------------------------------------------------------------
//  [ファイルのバージョン]の文字列を取得
//  [ファイルのバージョン]は,ファイルのプロパティダイアログで表示される値
//  引数はファイルのフルパス名
//-----------------------------------------------------------------------------
function TForm1.GetFileVersionString(FileName: string): String;
var
  dwHandle  : Cardinal;
  pInfo     : Pointer;
  InfoSize  : DWORD;
  pFileInfo : PVSFixedFileInfo;
  iVer      : array[0..3] of Cardinal;
begin
  Result := '';

  InfoSize := GetFileVersionInfoSize(PChar(FileName), dwHandle);
  if InfoSize = 0 then exit;

  GetMem(pInfo, InfoSize);
  try
    GetFileVersionInfo(PChar(FileName), 0, InfoSize, pInfo);
    VerQueryValue(pInfo, PathDelim, Pointer(pFileInfo), InfoSize);

    iVer[0] := pFileInfo.dwFileVersionMS shr 16;
    iVer[1] := pFileInfo.dwFileVersionMS and $FFFF;
    iVer[2] := pFileInfo.dwFileVersionLS shr 16;
    iVer[3] := pFileInfo.dwFileVersionLS and $FFFF;
    Result := Format('%d.%d.%d.%d', [iVer[0], iVer[1], iVer[2], iVer[3]]);
  finally
    FreeMem(pInfo, InfoSize);
  end;
end;

end.




06_EXE ( DLL ) のインポート関数名を列挙


EXE または DLL がインポートする関数名を列挙するサンプルです.
EXE や DLL は,当然ですが,ソースコード内の DLL や,Windows API の関数以外にも多くの関数をインポートして使用しています.それらの DLL 名と関数名を列挙します.

結果の図は,今回作成した Hello2.exe がインポートしている関数です.MessageBoxA はソースコードに書いた関数です.その他にも多くの関数を使用していることが分かります.

このサンプルは,32 ビット版の EXE, DLL 用のコードとなっています.

前項のエクスポート,このサンプルのインポートの他にも,いろいろなセクションテーブルがあり,その各々にセクションデータがあります.セクションデータの最後がファイルの末尾です.Delphi の場合は,最後はアプリケーションマニフェストの XML コードになっています.
このように,EXE や DLL はそれ自体が大きな構造体になっています.
リソースセクションのデータの取得や操作には,専用の Windows API があります.



図18
実行例
  • Hello2.exe のインポート関数類

リスト6
EXE (DLL) がインポートしている関数を列挙
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Memo1: TMemo;
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    InitDir : String;
    function ListImportDLLfunctionNames(FileHandle: THandle): Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ImageHlp;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Name   := 'Courier New';
  Memo1.Font.Size   := 10;
  Memo1.WordWrap    := False;
  Panel1.Font := Memo1.Font;

  Panel1.Caption := '';
end;

//=============================================================================
//  EXEまたはDLLがインポートしているDLLと関数名リスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  FileFullPath : String;
  FileHandle   : THandle;
begin
  if InitDir = EmptyStr then begin
    InitDir := ExpandFileName('../');
  end;

  OpenDialog1.InitialDir := InitDir;
  OpenDialog1.Filter     := 'ファイル|*.exe;*.dll';

  if OpenDialog1.Execute then begin
    FileFullPath := OpenDialog1.FileName;
    InitDir      := ExtractFileDir(FileFullPath);

    Memo1.Clear;
    Memo1.Lines.BeginUpdate;

    //対象ファイルを読み出し専用で開き,そのハンドルを取得
    FileHandle := CreateFile(PChar(FileFullPath),
                             GENERIC_READ,
                             FILE_SHARE_READ,
                             nil,
                             OPEN_EXISTING,
                             FILE_ATTRIBUTE_NORMAL,
                             0);
    if FileHandle = INVALID_HANDLE_VALUE then exit;
    try
      Panel1.Caption := ExtractFileName(FileFullPath);
      ListImportDLLfunctionNames(FileHandle);
    finally
      CloseHandle(FileHandle);
    end;
    Memo1.Lines.EndUpdate;
  end;
end;

//-----------------------------------------------------------------------------
//  実際にインポートしているDLL名と関数名をリストする関数
//  引数はファイルのハンドル
//
//  32ビット版専用
//  usesにImageHlpが必要
//-----------------------------------------------------------------------------
function TForm1.ListImportDLLfunctionNames(FileHandle: THandle): Boolean;
var
  HeaderHandle  : THandle;
  HeaderPointer : Pointer;
  pNTHeaders    : PImageNtHeaders;
  pImportTable  : PImageImportDescriptor;
  TableSize     : Cardinal;
  DLLName       : PAnsiChar;
  pThunkData    : PImageThunkData;
  pImportByName : pImageImportByName;
  StrText       : String;
begin
  Result := False;

  HeaderHandle  := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
  if HeaderHandle = 0 then exit;

  try
    HeaderPointer := MapViewOfFile(HeaderHandle, FILE_MAP_READ, 0, 0, 0);
    if not Assigned(HeaderPointer) then  exit;

    try
      pNTHeaders := ImageNtHeader(HeaderPointer);

      //シグネチャ(PE)の文字を確認
      if pNTHeaders.Signature <> IMAGE_NT_SIGNATURE then exit;

      //DLL名が格納されている構造体の先頭ポインタを取得
      pImportTable := ImageDirectoryEntryToData(HeaderPointer,
                                                     False,
                                                     IMAGE_DIRECTORY_ENTRY_IMPORT,
                                                     TableSize);
      //以下でも同じ
      //DLL名が格納されている構造体の先頭ポインタを取得
      //pImportTable := ImageRvaToVa(
      //                 pNTHeaders,
      //                 HeaderPointer,
      //                 pNTHeaders.OptionalHeader.DataDirectory[1].VirtualAddress,
      //                 nil);

      if not Assigned(pImportTable) then exit;

      //DLL名が検出されなくなるまでループ
      while pImportTable.Name <> 0 Do begin
        DLLName := ImageRVAToVA(pNTHeaders,
                                HeaderPointer,
                                pImportTable.Name,
                                nil);
        StrText := String(PAnsiChar(DLLName));
        Memo1.Lines.Add(StrText);
        Memo1.Lines.Add('---------------------------------');

        //現在のDLLの関数名が格納されている構造体の先頭ポインタを取得
        pThunkData := ImageRVAToVA(pNTHeaders,
                                   HeaderPointer,
                                   pImportTable.FirstThunk,
                                   nil);

        //関数名が検出されなくなるまでループ
        while pThunkData^._Function <> 0 Do begin
          pImportByName := ImageRVAtoVA(pNTHeaders,
                                        HeaderPointer,
                                        Cardinal(pThunkData.AddressOfData),
                                        nil);
          StrText := String(PAnsiChar(@pImportByName.Name));
          Memo1.Lines.Add(StrText);

          //次の関数名のポインタ(アドレス)に移動
          Inc(pThunkData);
        end;
        Memo1.Lines.Add('---------------------------------');
        Memo1.Lines.Add(' ');

        //次のDLL名のポインタ(アドレス)に移動
        Inc(pImportTable);
      end;
      Memo1.Lines.Add('End of List');
    finally
      UnmapViewOfFile(HeaderPointer);
    end;
  finally
    CloseHandle(HeaderHandle);
  end;
  Result := True;
end;

end.