SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Windows実行ファイル「EXE」の謎に迫る

プログラムからEXEファイルを生成してみよう

Windows実行ファイル「EXE」の謎に迫る 第4回


_tmain関数をコーディング(1/2)

必要なデータの初期化部分
int _tmain(int argc, _TCHAR* argv[])
{
    /////////////////////////////
    // 必要なデータを初期化
    /////////////////////////////

    //コードセッションのサイズ
    int SizeOf_CodeSection;
    if(sizeof(NativeBuffer)%ALIGNMENT)
        SizeOf_CodeSection=sizeof(NativeBuffer)
            +(ALIGNMENT-sizeof(NativeBuffer)%ALIGNMENT);
    else SizeOf_CodeSection=sizeof(NativeBuffer);

    //インポートセクションのサイズ
    int SizeOf_ImportSection;
    SizeOf_ImportSection=ALIGNMENT;

    //データセクションのサイズ
    int SizeOf_DataSection;
    if(sizeof(InitBuffer)%ALIGNMENT)
        SizeOf_DataSection=sizeof(InitBuffer)
            +(ALIGNMENT-sizeof(InitBuffer)%ALIGNMENT);
    else SizeOf_DataSection=sizeof(InitBuffer);

    //コードセクションの開始位置
    int Pos_CodeSection=EXE_HEADER_SIZE;

    //インポートセクションの開始位置
    int Pos_ImportSection;
    Pos_ImportSection= Pos_CodeSection+
                       SizeOf_CodeSection;

    //データセクションの開始位置
    int Pos_DataSection;
    Pos_DataSection=   Pos_CodeSection+
                       SizeOf_CodeSection+
                       SizeOf_ImportSection;

    //インポートディレクトリテーブルを初期化
    ImportDesc[0].OriginalFirstThunk=
        Pos_ImportSection+
        (USE_DLL+1)*sizeof(IMAGE_IMPORT_DESCRIPTOR)+
        sizeof(szDllName);
    ImportDesc[0].TimeDateStamp=0;
    ImportDesc[0].ForwarderChain=0;
    ImportDesc[0].Name=
        Pos_ImportSection+
        (USE_DLL+1)*sizeof(IMAGE_IMPORT_DESCRIPTOR);
    ImportDesc[0].FirstThunk=
        ImportDesc[0].OriginalFirstThunk+
        sizeof(LookupTable)+
        sizeof(HintTable);

    //ルックアップテーブルを初期化
    LookupTable[0]= Pos_ImportSection+
                    (USE_DLL+1)*sizeof(IMAGE_IMPORT_DESCRIPTOR)+
                    sizeof(szDllName)+
                    sizeof(LookupTable);

 EXEファイルのヘッダを生成するためには、あらかじめ各セクションデータの書き込み位置やサイズを知っておく必要があります。_tmain関数の先頭部分ではコードセクション、インポートセクション、データセクションのサイズおよび開始位置を計算します。

 インポートセクションの内容は多少入り組んでいるので、ネイティブセクションやデータセクションなどのようにバイナリとしてデータを用意せず、きちんと構造体に値を書き込んでいきます。

IMAGE_DOS_HEADER構造体に値をセット
////////////////////////////
// EXEファイルのヘッダ情報
////////////////////////////

IMAGE_DOS_HEADER ImageDosHeader;
ImageDosHeader.e_magic=   0x5A4D;
ImageDosHeader.e_cblp=    0x0090;
ImageDosHeader.e_cp=      0x0003;
ImageDosHeader.e_crlc=    0;
ImageDosHeader.e_cparhdr= 4;
ImageDosHeader.e_minalloc=0x0000;
ImageDosHeader.e_maxalloc=0xFFFF;
ImageDosHeader.e_ss=      0x0000;
ImageDosHeader.e_sp=      0x00B8;
ImageDosHeader.e_csum=    0x0000;
ImageDosHeader.e_ip=      0x0000;
ImageDosHeader.e_cs=      0x0000;
ImageDosHeader.e_lfarlc=  0x0040;
ImageDosHeader.e_ovno=    0x0000;
ImageDosHeader.e_res[0]=  0;
ImageDosHeader.e_res[1]=  0;
ImageDosHeader.e_res[2]=  0;
ImageDosHeader.e_res[3]=  0;
ImageDosHeader.e_oemid=   0x0000;
ImageDosHeader.e_oeminfo= 0x0000;
ImageDosHeader.e_res2[0]= 0;
ImageDosHeader.e_res2[1]= 0;
ImageDosHeader.e_res2[2]= 0;
ImageDosHeader.e_res2[3]= 0;
ImageDosHeader.e_res2[4]= 0;
ImageDosHeader.e_res2[5]= 0;
ImageDosHeader.e_res2[6]= 0;
ImageDosHeader.e_res2[7]= 0;
ImageDosHeader.e_res2[8]= 0;
ImageDosHeader.e_res2[9]= 0;
ImageDosHeader.e_lfanew=  0x0100; //PEヘッダの位置

 この構造体は昔からの名残であり、ほとんど意味を持ちません。しかしながら、EXEファイルの先頭に必ず存在することになっているので、きちんと定義しておきましょう。e_magicにEXEファイルのシグネチャである「MZ」が書き込まれる点、e_lfanewメンバにPEヘッダの先頭位置が書き込まれる点に注意してみると良いでしょう。

PEヘッダに値をセット
/////////////////////////////////////////////
// PEヘッダ
/////////////////////////////////////////////

IMAGE_NT_HEADERS ImagePeHdr;
ImagePeHdr.Signature=IMAGE_NT_SIGNATURE;

//マシンタイプ
ImagePeHdr.FileHeader.Machine=              IMAGE_FILE_MACHINE_I386;

ImagePeHdr.FileHeader.NumberOfSections=     3; //セクション数
ImagePeHdr.FileHeader.TimeDateStamp=        (DWORD)time(NULL);
ImagePeHdr.FileHeader.PointerToSymbolTable= 0x00000000;
ImagePeHdr.FileHeader.NumberOfSymbols=      0x00000000;
ImagePeHdr.FileHeader.SizeOfOptionalHeader=
    IMAGE_SIZEOF_NT_OPTIONAL32_HEADER;
ImagePeHdr.FileHeader.Characteristics= IMAGE_FILE_EXECUTABLE_IMAGE|
                                       IMAGE_FILE_32BIT_MACHINE|
                                       IMAGE_FILE_LINE_NUMS_STRIPPED|
                                       IMAGE_FILE_LOCAL_SYMS_STRIPPED;
ImagePeHdr.OptionalHeader.Magic=       0x010B;
ImagePeHdr.OptionalHeader.MajorLinkerVersion= 1;
ImagePeHdr.OptionalHeader.MinorLinkerVersion= 0;
//コードサイズ(.textのセッションサイズ)
ImagePeHdr.OptionalHeader.SizeOfCode=           sizeof(NativeBuffer);
//データサイズ(.dataのセッションサイズ)
ImagePeHdr.OptionalHeader.SizeOfInitializedData=sizeof(InitBuffer);
//未初期化データのサイズ(なし)
ImagePeHdr.OptionalHeader.SizeOfUninitializedData=0;
ImagePeHdr.OptionalHeader.AddressOfEntryPoint=  Pos_CodeSection;
//.textのアドレス
ImagePeHdr.OptionalHeader.BaseOfCode=           Pos_CodeSection;
//.dataのアドレス
ImagePeHdr.OptionalHeader.BaseOfData=           Pos_DataSection;

//イメージベース
ImagePeHdr.OptionalHeader.ImageBase=            0x00400000;
//セクションアラインメント
ImagePeHdr.OptionalHeader.SectionAlignment=     ALIGNMENT;
ImagePeHdr.OptionalHeader.FileAlignment=        ALIGNMENT;
ImagePeHdr.OptionalHeader.MajorOperatingSystemVersion=4;
ImagePeHdr.OptionalHeader.MinorOperatingSystemVersion=0;
ImagePeHdr.OptionalHeader.MajorImageVersion=    0;
ImagePeHdr.OptionalHeader.MinorImageVersion=    0;
ImagePeHdr.OptionalHeader.MajorSubsystemVersion=4;
ImagePeHdr.OptionalHeader.MinorSubsystemVersion=0;
ImagePeHdr.OptionalHeader.Win32VersionValue=    0;
//すべてのイメージサイズ
ImagePeHdr.OptionalHeader.SizeOfImage=        EXE_HEADER_SIZE+
                                              SizeOf_CodeSection+
                                              SizeOf_ImportSection+
                                              SizeOf_DataSection;
//ヘッダサイズ
ImagePeHdr.OptionalHeader.SizeOfHeaders=      EXE_HEADER_SIZE;
ImagePeHdr.OptionalHeader.CheckSum=           0;
ImagePeHdr.OptionalHeader.Subsystem=          IMAGE_SUBSYSTEM_WINDOWS_GUI;
ImagePeHdr.OptionalHeader.DllCharacteristics= 0;
ImagePeHdr.OptionalHeader.SizeOfStackReserve= 0x00100000;
ImagePeHdr.OptionalHeader.SizeOfStackCommit=  0x00001000;
ImagePeHdr.OptionalHeader.SizeOfHeapReserve=  0x00100000;
ImagePeHdr.OptionalHeader.SizeOfHeapCommit=   0x00001000;
ImagePeHdr.OptionalHeader.LoaderFlags=        0;
ImagePeHdr.OptionalHeader.NumberOfRvaAndSizes=
    IMAGE_NUMBEROF_DIRECTORY_ENTRIES;

//データ ディクショナリ
ImagePeHdr.OptionalHeader.DataDirectory[0].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[0].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[1].VirtualAddress=
    Pos_ImportSection; //インポートテーブル
ImagePeHdr.OptionalHeader.DataDirectory[1].Size=SizeOf_ImportSection;
ImagePeHdr.OptionalHeader.DataDirectory[2].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[2].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[3].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[3].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[4].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[4].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[5].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[5].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[6].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[6].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[7].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[7].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[8].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[8].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[9].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[9].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[10].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[10].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[11].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[11].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[12].VirtualAddress=
    Pos_ImportSection+
    (USE_DLL+1)*sizeof(IMAGE_IMPORT_DESCRIPTOR)+
    16*USE_DLL+           //DLL名
    sizeof(LookupTable)+  //ルックアップテーブル
    sizeof(HintTable);    //ヒント名(関数名)テーブル
ImagePeHdr.OptionalHeader.DataDirectory[12].Size=sizeof(LookupTable);
ImagePeHdr.OptionalHeader.DataDirectory[13].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[13].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[14].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[14].Size=0;
ImagePeHdr.OptionalHeader.DataDirectory[15].VirtualAddress=0;
ImagePeHdr.OptionalHeader.DataDirectory[15].Size=0;

 このサンプルプログラムが生成するEXEファイルは、「.text」「.idata」「.sdata」という3つのセクションを保有します。セクション数が変わると、NumberOfSectionsメンバの値が変わります。

次のページ
_tmain関数をコーディング(2/2)

修正履歴

この記事は参考になりましたか?

Windows実行ファイル「EXE」の謎に迫る連載記事一覧

もっと読む

この記事の著者

山本 大祐(ヤマモト ダイスケ)

普段はActiveBasicと周辺ツールの開発を行っています。最近は諸先輩方を見習いながら勉強中の身。AB開発日記

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/419 2006/07/12 15:02

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー