《PE2》中介绍了一些可选文件头中重要的属性,为了全面起见,本文将会讲解那些不是那么重要的属性。虽然不重要,但是还是可以发现很多好玩的情况。首先看一下32位的可选文件头详细定义。(转载请指明来源于breaksoftware的CSDN博客)
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
64位版本和32位版本很类似:没有BaseOfCode属性;ImageBase、SizeofStackReserve、SizeOfStackCommit、SizeOfHeapReserve和SizeOfHeapCommit等5个属性由32位版的DWORD改成ULONGLONG。看下详细的64位版定义
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
Magic字段是可选文件头幻数,0x10b是32位版,0x20b是64位版。注意该属性不能说明这个文件是64位文件还是32位文件,至于判断是多少位文件的方案我在《PE2》中已经有了说明。
MajorLinkerVersion和MinorLinkerVersion分别对应于链接器的版本号,比如我电脑上VS2005编译的文件的这两个版本号是8.0;VS2008编译的是9.0;VS2010编译的是10.0。
MajorOperatingSystemVersion和MinorOperatingSystemVersion是所需要的最低的系统版本号的主版本号和次版本号。我看了下我电脑上文件,基本上是4.0。
MajorImageVersion和MinorImageVersion是映像文件的主版本号和次版本号。注意:我们在资源中定义的文件版本号不是通过这两个属性来体现的!目前我也没找到在VC工程设置中可以设置这两个属性的地方。
Subsystem是该文件运行于的子系统信息。一般我们在windows平台上遇到的是2,它对应于IMAGE_SUBSYSTEM_WINDOWS_GUI。
MajorSubsystemVersion和MinorSubsystemVersion是子系统的版本号。熟悉windows的朋友应该知道,微软刚开始设计系统时,是设计成一个平台性质——可以运行3个子系统(OS/2、POSIX和Windows)的系统。这个就是这两个属性的由来。
SectionAlignment是当映像文件加载到内存中时节的对齐值,该大小使用字节来衡量的。它必须要大于我们之后介绍的FileAlignment。它的默认值是相应系统的页面大小。
FileAlignment 是映像文件节中的对齐值,它也是用字节来衡量的。英文文档中说该字段的值要在2^9 ~ 2^16之间,我扫描了下我的系统,发现我系统中文件并不是如此,特别是sys文件,它们的FileAlignment小于2^9(512)。
SizeOfCode是文件中代码段的总共大小。要注意一点,这个大小和.text的大小不一定一致,因为有些代码可能还保存在其他节中。如我电脑上AliAppLoader.exe文件,其SizeOfCode大小是0x1D600,而.text节大小只有0x1D400,另外的0x200是在.orpc这个节中。 SizeOfInitializedData是文件中所有已经初始化数据节的大小。和SizeOfCode一样,初始化数据不一定只在一个节中。
SizeOfUninitializedData是文件中所有未初始化数据节的大小。和SizeOfCode一样,未初始化数据不一定只在一个节中。
Win32VersionValue是保留字段,应该为0。那么目前这个字段就是程序不关心的了,我们可以利用这个位置保存一些私密信息。
SizeOfImage的官方说明是该映像文件被加载入内存时的大小,理论上它应该是SectionAlignment的倍数。但是实际并非如此,我发现我电脑上很多文件的该字段不是SectionAlignment的倍数,而有时SizeOfImage是该文件在磁盘上的大小。可以见得这个不是一个关键字段。
if ( m_lpFileEnd - m_lpFileStart != m_dwSizeOfImage ) {
_ASSERT(FALSE);
}
SizeOfHeaders的官方解释是MS-DOS占位程序、PE文件头和节头的总大小,且其应该是FileAlignment的倍数。但是实际上,我发现我电脑上很多文件的该字段并非FileAlignment的倍数。
DWORD dwHeaderSize = (DWORD)(m_lpPEStart - m_lpFileStart) + (DWORD)(sizeof(IMAGE_FILE_HEADER))
+ (DWORD) ( m_FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
DWORD dwTmp = m_dwFileAlignment - 1;
dwHeaderSize = (dwHeaderSize + dwTmp) &~dwTmp;
if ( dwHeaderSize != m_dwSizeOfHeaders ) {
//_ASSERT(FALSE);
}
CheckSum字段是映像文件的校验和。其计算算法保存在imagehlp.dll中,导出函数名为CheckSumMappedFile。我发现我电脑上很多文件的该PE字段和计算出来的不等。官方解释说当驱动程序、在引导时被加载的Dll以及加载到关键windows进程中的DLL都需要校验该字段以确认其合法性。
typedef PIMAGE_NT_HEADERS(WINAPI *PCheckSumMappedFile)( PVOID, DWORD, PDWORD, PDWORD );
HMODULE hModule = LoadLibrary(L"imagehlp.dll");
if ( NULL == hModule ) {
_ASSERT(FALSE);
}
PCheckSumMappedFile pCheckSumMappedFile = (PCheckSumMappedFile) GetProcAddress( hModule, "CheckSumMappedFile" );
if ( NULL == pCheckSumMappedFile ) {
_ASSERT(FALSE);
}
DWORD dwHeaderSum = 0;
DWORD dwCheckSum = 0;
PIMAGE_NT_HEADERS lpImgNtHeader = pCheckSumMappedFile( m_lpFileStart, (DWORD)(m_lpFileEnd - m_lpFileStart), &dwHeaderSum, &dwCheckSum );
if ( m_dwCheckSum != dwCheckSum ) {
// 不等的很多
//_ASSERT(FALSE);
}
if ( NULL != hModule ) {
FreeLibrary(hModule);
hModule = NULL;
}
SizeOfStackReserve、SizeOfStackCommit、SizeOfHeapReserve和SizeOfHeapCommit分别对应于保留的栈大小、提交的栈大小、保留的堆大小和提交的堆大小。
LoaderFlags字段是保留字段,应该为0,当然你可以不把它设为0。
NumberOfRvaAndSizes是用来指明DataDirectory元素的个数。这儿我们要说一下,我们在IMAGE_FILE_HEADER::SizeOfOptionalHeader得到了可选文件头的大小,而影响可选文件头大小的就是DataDirectory元素的个数(NumberOfRvaAndSizes),那么IMAGE_FILE_HEADER::SizeOfOptionalHeader和NumberOfRvaAndSizes之间应该存在着一种换算关系。的确,理论上 换算关系是:
SizeOfOptionalHeader == offsetof(IMAGE_OPTIONAL_HEADER32(64),NumberOfRvaAndSizes) + NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY)
我跑了下我电脑上的所有文件,发现这个关系是一直成立的。这应该是种强关系。 DllCharacteristics是属性字段,我们看个官方说明
Constant | Value | Description |
---|---|---|
0x0001 | Reserved, must be zero. | |
0x0002 | Reserved, must be zero. | |
0x0004 | Reserved, must be zero. | |
0x0008 | Reserved, must be zero. | |
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE | 0x0040 | DLL can be relocated at load time. |
IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY | 0x0080 | Code Integrity checks are enforced. |
IMAGE_DLL_CHARACTERISTICS_NX_COMPAT | 0x0100 | Image is NX compatible. |
IMAGE_DLLCHARACTERISTICS_ NO_ISOLATION | 0x0200 | Isolation aware, but do not isolate the image. |
IMAGE_DLLCHARACTERISTICS_ NO_SEH | 0x0400 | Does not use structured exception (SE) handling. No SE handler may be called in this image. |
IMAGE_DLLCHARACTERISTICS_ NO_BIND | 0x0800 | Do not bind the image. |
0x1000 | Reserved, must be zero. | |
IMAGE_DLLCHARACTERISTICS_ WDM_DRIVER | 0x2000 | A WDM driver. |
IMAGE_DLLCHARACTERISTICS_ TERMINAL_SERVER_AWARE | 0x8000 | Terminal Server aware. |
MAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE是说Dll可以在加载时被重定向,我发现我电脑上文件SDKDBLib.dll是特例,它没有设置这个属性,这个文件也没有设置IMAGE_DLLCHARACTERISTICS_ NO_SEH,即该文件不使用SEH。