俺的回收站

架构分析 解释编译原理
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

CLI编译的执行文件结构(1)

Posted on 2006-12-17 21:06  Riceball LEE  阅读(801)  评论(0编辑  收藏  举报
=== MS-DOS Stub ===
首先是 MS-DOS Stub: 用于在Dos环境下显示该程序不能运行在DOS下的提示。
在文件偏移量0x3C的位置是指向PE 签名(signature) 的指针: 80 00 00 00,就是 0x80.

00000000h: 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00 ; MZ?..........
00000010h: B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ; ?......@.......
00000020h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
00000030h: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ; ............€...
...
00000080h: 50 45 00 00 4C 01 03 00 92 38 84 45 00 00 00 00 ; PE..L...?凟....
00000090h: 00 00 00 00 E0 00 0E 01 0B 01 06 00 00 04 00 00 ; ....?..........

  _IMAGE_DOS_HEADER = record
    e_magic: Word;     // Magic number: "MZ"
    e_cblp: Word;      // Bytes on last page of file
    e_cp: Word;        // Pages in file
    e_crlc: Word;      // Relocations
    e_cparhdr: Word;   // Size of header in paragraphs
    e_minalloc: Word;  // Minimum extra paragraphs needed
    e_maxalloc: Word;  // Maximum extra paragraphs needed
    e_ss: Word;        // Initial (relative) SS value
    e_sp: Word;        // Initial SP value
    e_csum: Word;      // Checksum
    e_ip: Word;        // Initial IP value
    e_cs: Word;        // Initial (relative) CS value
    e_lfarlc: Word;    // File address of relocation table
    e_ovno: Word;      // Overlay number
    e_res: array [0..3] of Word;    // Reserved words
    e_oemid: Word;     // OEM identifier (for e_oeminfo)
    e_oeminfo: Word;   // OEM information; e_oemid specific
    e_res2: array [0..9] of Word;  // Reserved words
    e_lfanew: Longint; // File address of new exe header
  end;


PE 签名(signature) 紧接着MS-DOS Stub,是4个字节,内容为 “PE”然后是两个字节0: 0x00004550
  _IMAGE_NT_HEADERS = record
    Signature: DWORD;                      //PE signature: 0x00004550
    FileHeader: IMAGE_FILE_HEADER;         //The COFF header
    OptionalHeader: IMAGE_OPTIONAL_HEADER; //The PE header. 32位头和64位头略有差异,CLR目前版本统一使用32位结构。
  end;


=== COFF 头 ===
在 PE 签名(signature) 之后是 COFF 头,内容如下:
  _IMAGE_FILE_HEADER = record //JwaWinNT.pas
    Machine: WORD;               //目标机器类型字段,托管的IL代码设置该字段为 IMAGE_FILE_MACHINE_I386 (0x014C)
    NumberOfSections: WORD;      //Section节的个数,Section节紧接着PE头
    TimeDateStamp: DWORD;        //文件的创建时间
    PointerToSymbolTable: DWORD; //符号表的文件位置指针,在托管的IL中未用,总是0。
    NumberOfSymbols: DWORD;      //符号表的的个数,在托管的IL中未用,总是0。 (这里的位置在:0x90)
    SizeOfOptionalHeader: WORD;  //PE头的大小。
    Characteristics: WORD;       //文件属性标示。对于一般的托管代码可执行文件的属性为0x010E, DLL是0x210E.
  end;

目标机器类型字段的可能的值以及含义如下:
  IMAGE_FILE_MACHINE_UNKNOWN   = 0;
  IMAGE_FILE_MACHINE_I386      = $014c; // Intel 386. For managed PE files, contents are applicable to any machine type.
  IMAGE_FILE_MACHINE_R3000     = $0162; // MIPS little-endian, 0x160 big-endian
  IMAGE_FILE_MACHINE_R4000     = $0166; // MIPS little-endian
  IMAGE_FILE_MACHINE_R10000    = $0168; // MIPS little-endian
  IMAGE_FILE_MACHINE_WCEMIPSV2 = $0169; // MIPS little-endian WinCE v2
  IMAGE_FILE_MACHINE_ALPHA     = $0184; // Alpha_AXP
  IMAGE_FILE_MACHINE_SH3       = $01a2; // SH3 little-endian
  IMAGE_FILE_MACHINE_SH3DSP    = $01a3;
  IMAGE_FILE_MACHINE_SH3E      = $01a4; // SH3E little-endian
  IMAGE_FILE_MACHINE_SH4       = $01a6; // SH4 little-endian
  IMAGE_FILE_MACHINE_SH5       = $01a8; // SH5
  IMAGE_FILE_MACHINE_ARM       = $01c0; // ARM Little-Endian
  IMAGE_FILE_MACHINE_THUMB     = $01c2; // ARM processor with Thumb decompressor
  IMAGE_FILE_MACHINE_AM33      = $01d3;
  IMAGE_FILE_MACHINE_POWERPC   = $01F0; // IBM PowerPC Little-Endian
  IMAGE_FILE_MACHINE_POWERPCFP = $01f1;
  IMAGE_FILE_MACHINE_IA64      = $0200; // Intel IA64
  IMAGE_FILE_MACHINE_MIPS16    = $0266; // MIPS
  IMAGE_FILE_MACHINE_ALPHA64   = $0284; // ALPHA64
  IMAGE_FILE_MACHINE_MIPSFPU   = $0366; // MIPS
  IMAGE_FILE_MACHINE_MIPSFPU16 = $0466; // MIPS
  IMAGE_FILE_MACHINE_AXP64     = IMAGE_FILE_MACHINE_ALPHA64;
  IMAGE_FILE_MACHINE_TRICORE   = $0520; // Infineon
  IMAGE_FILE_MACHINE_CEF       = $0CEF;
  IMAGE_FILE_MACHINE_EBC       = $0EBC; // EFI Byte Code
  IMAGE_FILE_MACHINE_AMD64     = $8664; // AMD64 (K8)
  IMAGE_FILE_MACHINE_M32R      = $9041; // M32R little-endian
  IMAGE_FILE_MACHINE_CEE       = $C0EE;

文件属性标识的值以及含义:
  IMAGE_FILE_RELOCS_STRIPPED         = $0001; // Relocation info stripped from file. 托管代码不能设置该标志为真。
  IMAGE_FILE_EXECUTABLE_IMAGE        = $0002; // File is executable  (i.e. no unresolved externel references). 托管代码必须设置该标志为真。
  IMAGE_FILE_LINE_NUMS_STRIPPED      = $0004; // Line nunbers stripped from file. 托管代码必须设置该标志为真。因为不能嵌入调试信息。
  IMAGE_FILE_LOCAL_SYMS_STRIPPED     = $0008; // Local symbols stripped from file. 托管代码必须设置该标志为真。
  IMAGE_FILE_AGGRESIVE_WS_TRIM       = $0010; // Agressively trim working set 托管代码不能设置该标志为真。
  IMAGE_FILE_LARGE_ADDRESS_AWARE     = $0020; // App can handle >2gb addresses 托管代码不能设置该标志为真。
  IMAGE_FILE_BYTES_REVERSED_LO       = $0080; // Little endian. Bytes of machine word are reversed. 托管代码不能设置该标志为真。
  IMAGE_FILE_32BIT_MACHINE           = $0100; // 32 bit word machine.  当前版本的托管代码设置该标志为真。
  IMAGE_FILE_DEBUG_STRIPPED          = $0200; // Debugging info stripped from file in .DBG file
  IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = $0400; // If Image is on removable media, copy and run from the swap file.  托管代码不能设置该标志为真。
  IMAGE_FILE_NET_RUN_FROM_SWAP       = $0800; // If Image is on Net, copy and run from the swap file.   托管代码不能设置该标志为真。
  IMAGE_FILE_SYSTEM                  = $1000; // System File.   托管代码不能设置该标志为真。
  IMAGE_FILE_DLL                     = $2000; // File is a DLL.
  IMAGE_FILE_UP_SYSTEM_ONLY          = $4000; // File should only be run on a UP machine   托管代码不能设置该标志为真。
  IMAGE_FILE_BYTES_REVERSED_HI       = $8000; // Big endian. Bytes of machine word are reversed.  托管代码不能设置该标志为真。
 
EXE 托管文件的文件属性是 0x010E (IMAGE_FILE_EXECUTABLE_IMAGE │ IMAGE_FILE_LINE_NUMS_ STRIPPED │ IMAGE_FILE_LOCAL_SYMS_STRIPPED │ IMAGE_FILE_32BIT_ MACHINE).
DLL 托管文件的文件属性是 0x210E (IMAGE_FILE_ EXECUTABLE_IMAGE │ IMAGE_FILE_LINE_NUMS_STRIPPED │ IMAGE_FILE_ LOCAL_SYMS_STRIPPED │ IMAGE_FILE_32BIT_MACHINE │ IMAGE_FILE_DLL).

=== PE 头 ===
紧接着COFF头的就是PE头了,尽管它被称作可选头,但是实际上对于Exe和DLL文件来说,却总是存在的。PE头的大小不是固定的,它由数据目录的大小决定。
它会告诉我们该二进制文件怎样被载入的更多信息:开始的地址、保留的堆栈数、数据段的大小等等。

00000098h: 0B 01 06 00 00 04 00 00 00 04 00 00 00 00 00 00 ; ................
000000a8h: FE 22 00 00 00 20 00 00 00 40 00 00 00 00 40 00 ; ?... ...@....@.
000000b8h: 00 20 00 00 00 02 00 00 04 00 00 00 00 00 00 00 ; . ..............
000000c8h: 04 00 00 00 00 00 00 00 00 80 00 00 00 02 00 00 ; .........€......

  _IMAGE_OPTIONAL_HEADER = record
    //
    // Standard fields.
    //
    Magic: Word;               //魔数标识该PE镜像文件的状态,0x010B表示32位PE文件,托管代码总是0x010B.
    MajorLinkerVersion: Byte;  //链接器的主版本号,MC++设置该字段为7,其它公司的纯IL代码链接器设置为6。
    MinorLinkerVersion: Byte;  //链接器的次版本号
    SizeOfCode: DWORD;         //总代码节区的大小(.text),托管代码只有一个代码区。
    SizeOfInitializedData: DWORD; //总初始化数据节区的大小(统计所有的数据节区的SizeOfRawData字段),需要初始化的数据。
    SizeOfUninitializedData: DWORD; //总不需要初始化的数据节区大小(.bss),该数据区不占用磁盘空间,只是装载后由OS加载器分配内存空间。
    AddressOfEntryPoint: DWORD;     //代码入口地址RVA ,对于托管代码(DLL或Exe)总是指向CLR调用桩。(在文件偏移量:0x0a8)
    BaseOfCode: DWORD;              //代码基址RVA
    BaseOfData: DWORD;              //数据基址RVA
    //
    // NT additional fields.
    //
    ImageBase: DWORD;              //映象文件基址,提供整个二进制文件的优先(线性)载入地址,在ILAsm中可以使用 .imagebase <integer value> 指令指定。
    SectionAlignment: DWORD;       //节对齐值,该值必须大于等于FileAlignment,默认是内存页大小。
    FileAlignment: DWORD;          //文件对齐值,如果SectionAlignment的值小于内存页的大小,该值则是2的n次方,从512~64K。 FileAlignment 必须匹配 SectionAlignment!
                                   // .file alignment <integer value>
    MajorOperatingSystemVersion: Word;
    MinorOperatingSystemVersion: Word;
    MajorImageVersion: Word;
    MinorImageVersion: Word;
    MajorSubsystemVersion: Word;
    MinorSubsystemVersion: Word;
    Win32VersionValue: DWORD;
    SizeOfImage: DWORD;            //映象文件将要使用的内存数量
    SizeOfHeaders: DWORD;          //给出所有头的总长度,包括数据目录和节头。同时,它也是从文件的开头到第一节的原始数据的偏移量。
    CheckSum: DWORD;               //磁盘文件的校验和
    Subsystem: Word;               //子系统: 驱动程序,控制台程序(Console), GUI,etc
    DllCharacteristics: Word;      //废弃,总是0
    SizeOfStackReserve: DWORD;     //保留的栈的大小,默认是1M,并没有真正分配空间
    SizeOfStackCommit: DWORD;      //初始时指定栈大小,默认是1页,实际分配空间的大小,同时也是指定的增加的数量。
    SizeOfHeapReserve: DWORD;      //保留的堆的大小,默认是1M,并没有真正分配空间
    SizeOfHeapCommit: DWORD;       //初始时指定堆大小,默认是1页,实际分配空间的大小,同时也是指定的增加的数量。
    LoaderFlags: DWORD;            //废弃,总是0
    NumberOfRvaAndSize: DWORD;     //数据目录数,至少是16。
    DataDirectory: array [0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES - 1] of IMAGE_DATA_DIRECTORY;
  end;

魔数标识该PE镜像文件的状态:
  IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;
  IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;
  IMAGE_ROM_OPTIONAL_HDR_MAGIC  = $107;

子系统:
  IMAGE_SUBSYSTEM_UNKNOWN                 = 0; // Unknown subsystem.
  IMAGE_SUBSYSTEM_NATIVE                  = 1; // Image doesn't require a subsystem.
  IMAGE_SUBSYSTEM_WINDOWS_GUI             = 2; // Image runs in the Windows GUI subsystem.
  IMAGE_SUBSYSTEM_WINDOWS_CUI             = 3; // Image runs in the Windows character subsystem.
  IMAGE_SUBSYSTEM_OS2_CUI                 = 5; // image runs in the OS/2 character subsystem.
  IMAGE_SUBSYSTEM_POSIX_CUI               = 7; // image runs in the Posix character subsystem.
  IMAGE_SUBSYSTEM_NATIVE_WINDOWS          = 8; // image is a native Win9x driver.
  IMAGE_SUBSYSTEM_WINDOWS_CE_GUI          = 9; // Image runs in the Windows CE subsystem.
  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;