{
//
// Standard fields.
//
WORD Magic;
// 总是 0B 01 P: 可选头的标志?意义是?
BYTE MajorLinkerVersion; // 0 链接器的主版本号 // P:不同的链接器设置不同,因此不可靠?
BYTE MinorLinkerVersion; // 0 链接器的小版本号
DWORD SizeOfCode; // 0 // 可执行的代码的大小 P: 这里的可执行代码是指整个PE文件的可执行代码段?
DWORD SizeOfInitializedData; // 0 // 已初始化数据的大小 ,数据段
DWORD SizeOfUninitializedData; // 0 // 未初始化数据的大小, BSS段 P: 已初始化的数据跟未初始化的数据有什么区别?它们都在数据段?
DWORD AddressOfEntryPoint; // // 代码的入口点地址(是一个偏移量,RVA), 如 LibMain, WinMain....
DWORD BaseOfCode; // 0 // 可执行代码的偏移量, 代码基址 P: 不是很懂这里的意思,跟 AddressOfEntryPoint 的区别是?
DWORD BaseOfData; // 0 // 已初始化数据偏称量,数据基址 P: 同问
//
// NT additional fields.
//
DWORD ImageBase; //载入程序的RVA地址,LOADER可以改变 00 00 40 00 == 0x400000
DWORD SectionAlignment; //段加载后在内存的对齐方式 00 10 00 00
DWORD FileAlignment; //段在文件中的对齐方式 00 20 00 00
WORD MajorOperatingSystemVersion; // 0
WORD MinorOperatingSystemVersion; // 0
WORD MajorImageVersion; // 0
WORD MinorImageVersion; // 0
WORD MajorSubsystemVersion; //子系统版本号如果不是4.0 对话框不能显示3D风格 04 00
WORD MinorSubsystemVersion; // 0
DWORD Win32VersionValue; // 0 P:没有作用?通常为0
DWORD SizeOfImage; // 映象文件将要使用的内存数量, 如果是按照“SectionAlignment”对齐的,它就是所有头和节的长度的总和。它提示加载器,为了载入映象文件需要多少页。
DWORD SizeOfHeaders; //给出所有头的总长度,包括数据目录和节头(‘SizeOfHeaders’,“头的大小”)。同时,它也是从文件的开头到第一节的原始数据的偏移量。
DWORD CheckSum; // 校验和,用 0?
WORD Subsystem; // 子系统 02 00 或03 00 ?
WORD DllCharacteristics; // 00
DWORD SizeOfStackReserve; // 00
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags; // 00
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
// 16个结构,第二个结构(导入表)
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
1. CheckSum:
这个校验和,对于当前的NT版本,只在映象文件是NT驱动程序时才校验(如果校验和不正确,驱动就将装载失败)。
对于其他的二进制文件形式,校验和不需提供并且可能为0。
计算校验和的算法是微软的私产,他们不会告诉你的。
但是,Win32 SDK的好几个工具都会计算和/或补正一个有效的校验和,而且imagehelp.dll中的CheckSumMappedFile()函数也会做同样的工作。
使用校验和的目的是为了防止载入无论如何都会冲突的、已损坏的二进制文件----况且一个冲突的驱动程序会导致一个BSOD?错误,因此最好根本就不载入这样的坏文件。
2. Subsystem
IMAGE_SUBSYSTEM_NATIVE (1)
二进制文件不需要子系统。用于驱动程序。
IMAGE_SUBSYSTEM_WINDOWS_GUI (2)
映象文件是一个Win32二进制图象文件。(它还是能用AllocConsole()打开一个控制台界面,但在开始时却不能自动地打开。)
IMAGE_SUBSYSTEM_WINDOWS_CUI (3)
二进制文件是一个Win32控制台界面二进制文件。(它将在开始时按照缺省值打开一个控制台,或者继承其父程序的控制台。)
IMAGE_SUBSYSTEM_OS2_CUI (5)
二进制文件是一个OS/2控制台界面二进制文件。(OS/2控制台界面二进制文件是OS/2格式,因此此值在PE文件中很少使用。)
IMAGE_SUBSYSTEM_POSIX_CUI (7)
二进制文件使用POSIX?控制台子系统。
Windows 95的二进制文件总是使用Win32子系统,因此它的二进制文件的合法值只有2和3;我不知道windows 95的“原”二进制文件是否可能(会有其它值----译者添加,仅供参考)。
3. DllCharacteristics
如果是DLL文件,何时调用DLL文件的入口点(‘DllCharacteristics’,“DLL特性”)。此值似乎不用;很明显地,DLL文件总是被通报所有的情况。
如果位0被置1,DLL文件被通知进程附加(亦即DLL载入)。
如果位1被置1,DLL文件被通知线程附加(亦即线程终止)。
如果位2被置1,DLL文件被通知线程附加(亦即线程创建)。
如果位3被置1,DLL文件被通知进程附加(亦即DLL卸载)。
P: 不是很明白这里的意思。
4. SizeOfStackReserve 保留栈的大小
5. SizeOfStackCommit 初始时指定栈大小
6. SizeOfHeapReserve 保留堆的大小
7. SizeOfHeapCommit 指定堆大小
“保留的”数量是保留给特定目的的地址空间(不是真正的RAM);
在程序开始时,“指定的”数量是指在RAM中实际分配的大小。
如果需要的话,“指定的”值也是指定的堆或栈用来增加的数量。(有资料说,不管“SizeOfStackCommit”的值是多少,栈都是按页增加的。我没有验证过。)
因此,举例来说,如一个程序的保留堆有1 MB,指定堆为64 KB,那么启动时堆的大小为64 KB,并且保证可以扩大到1 MB。
堆将按64 KB一块来增加。“堆”在本文中是指主要(缺省)堆。如果它愿意的话,一个进程可创建很多堆。
栈是指第一个线程的栈(启动main()的那个)。进程可以创建很多线程,每个线程都有自己的栈。
DLL文件没有自己的堆或栈,所以它们的映象文件忽略这些值。我不知道驱动程序是否有它们自己的堆或栈,但我认为它们没有。
8. LoaderFlags 设置为 0 , P: 作用未知
9.
NumberOfRvaAndSizes
它是紧随其后的目录的有效项的数目。我已发现此值不可靠;你也许希望用常量IMAGE_NUMBEROF_DIRECTORY_ENTRIES(映象文件目录项数目)来代替它,或者用它们中的较小者。