📄
字号:
还要说明,IMAGE_OS2_HEADER并不如其名称所示那样仅仅是用于OS/2软件映像的,实际上也用于一些16位Windows软件的映像和DOS软件的映像。
IMAGE_DOS_HEADER结构中的最后一个字段e_lfanew说明了扩充头部在文件中的位移,所以这一次把读/写指针移到这个位置上。
读入扩充头部以后,首先就检查是否有“PE”格式的签名。如果是,并且头部是完整的,就根据头部中FileHeader.Characteristics字段的IMAGE_FILE_DLL标志位判定其为.exe映像还是DLL映像。此外,可能还要根据所读入头部提供的信息修正两个全局量res_start和res_end的数值,不过那与映像类型的识别是无关的。
如果头部签名不是“PE”,那就可能是OS/2或其它Windows/DOS的可执行映像了,此类映像在扩充头部中的签名是“NE”。我们再往下看。
[code] if (!memcmp( &ext_header.os2.ne_magic, "NE", 2 ))
{
/* This is a Windows executable (NE) header. This can
* mean either a 16-bit OS/2 or a 16-bit Windows or even a
* DOS program (running under a DOS extender). To decide
* which, we'll have to read the NE header.
*/
if (len >= sizeof(ext_header.os2))
{
switch ( ext_header.os2.ne_exetyp )
{
case 1: return BINARY_OS216; /* OS/2 */
case 2: return BINARY_WIN16; /* Windows */
case 3: return BINARY_DOS; /* European MS-DOS 4.x */
case 4: return BINARY_WIN16; /* Windows 386; FIXME: is this 32bit??? */
case 5: return BINARY_DOS;
/* BOSS, Borland Operating System Services */
/* other types, e.g. 0 is: "unknown" */
default:
return MODULE_Decide_OS2_OldWin(hfile, &header.mz, &ext_header.os2);
}
}
/* Couldn't read header, so abort. */
return BINARY_DOS;
}
/* Unknown extended header, but this file is nonetheless DOS-executable. */
return BINARY_DOS;
}
return BINARY_UNKNOWN;
}[/code]
显然,IMAGE_OS2_HEADER头部中的字段ne_exetyp进一步说明了具体的映像类型。从这里也可以看出,DOS/Windows与OS/2真的是你中有我、我中有你。除1-5以外,还有些类型码和头部特征是用于某些特别老的OS/2和Windows(版本3.0以前)目标映像的,那要进一步通过MODULE_Decide_OS2_OldWin()加以识别,这里就不赘述了,有兴趣的读者可以自己阅读和研究。
最后,MODULE_GetBinaryType()的返回值就是目标映像的类型代码。
有了这个函数,加上有关数据结构的定义,读者完全可以自己写个程序,打印出给定二进制映像文件的映像类型,并进一步打印出该映像的许多特性和参数。在Wine代码的tools/winedump目录下那些文件中有一些函数,包括dump_pe_header()、dump_le_header()、dump_ne_header()等等,就是用来打印出各种格式的映像头部。下面不加说明地列出其中dump_pe_header()的代码,供读者自己阅读,好处是可以从这些代码中看出各个头部(例如IMAGE_FILE_HEADER和IMAGE_OPTIONAL_HEADER)中许多字段的作用和意义:
[code]static void dump_pe_header(void)
{
const char *str;
IMAGE_FILE_HEADER *fileHeader;
IMAGE_OPTIONAL_HEADER *optionalHeader;
unsigned i;
printf("File Header\n");
fileHeader = &PE_nt_headers->FileHeader;
printf(" Machine: %04X (%s)\n",
fileHeader->Machine, get_machine_str(fileHeader->Machine));
printf(" Number of Sections: %d\n", fileHeader->NumberOfSections);
printf(" TimeDateStamp: %08lX (%s) offset %lu\n",
fileHeader->TimeDateStamp, get_time_str(fileHeader->TimeDateStamp),
Offset(&(fileHeader->TimeDateStamp)));
printf(" PointerToSymbolTable: %08lX\n", fileHeader->PointerToSymbolTable);
printf(" NumberOfSymbols: %08lX\n", fileHeader->NumberOfSymbols);
printf(" SizeOfOptionalHeader: %04X\n", fileHeader->SizeOfOptionalHeader);
printf(" Characteristics: %04X\n", fileHeader->Characteristics);
#define X(f,s) if (fileHeader->Characteristics & f) printf(" %s\n", s)
X(IMAGE_FILE_RELOCS_STRIPPED, "RELOCS_STRIPPED");
X(IMAGE_FILE_EXECUTABLE_IMAGE, "EXECUTABLE_IMAGE");
X(IMAGE_FILE_LINE_NUMS_STRIPPED, "LINE_NUMS_STRIPPED");
X(IMAGE_FILE_LOCAL_SYMS_STRIPPED, "LOCAL_SYMS_STRIPPED");
X(IMAGE_FILE_16BIT_MACHINE, "16BIT_MACHINE");
X(IMAGE_FILE_BYTES_REVERSED_LO, "BYTES_REVERSED_LO");
X(IMAGE_FILE_32BIT_MACHINE, "32BIT_MACHINE");
X(IMAGE_FILE_DEBUG_STRIPPED, "DEBUG_STRIPPED");
X(IMAGE_FILE_SYSTEM, "SYSTEM");
X(IMAGE_FILE_DLL, "DLL");
X(IMAGE_FILE_BYTES_REVERSED_HI, "BYTES_REVERSED_HI");
#undef X
printf("\n");
/* hope we have the right size */
printf("Optional Header\n");
optionalHeader = &PE_nt_headers->OptionalHeader;
printf(" Magic 0x%-4X %u\n",
optionalHeader->Magic, optionalHeader->Magic);
printf(" linker version %u.%02u\n",
optionalHeader->MajorLinkerVersion, optionalHeader->MinorLinkerVersion);
printf(" size of code 0x%-8lx %lu\n",
optionalHeader->SizeOfCode, optionalHeader->SizeOfCode);
printf(" size of initialized data 0x%-8lx %lu\n",
optionalHeader->SizeOfInitializedData, optionalHeader->SizeOfInitializedData);
printf(" size of uninitialized data 0x%-8lx %lu\n",
optionalHeader->SizeOfUninitializedData, optionalHeader->SizeOfUninitializedData);
printf(" entrypoint RVA 0x%-8lx %lu\n",
optionalHeader->AddressOfEntryPoint, optionalHeader->AddressOfEntryPoint);
printf(" base of code 0x%-8lx %lu\n",
optionalHeader->BaseOfCode, optionalHeader->BaseOfCode);
printf(" base of data 0x%-8lX %lu\n",
optionalHeader->BaseOfData, optionalHeader->BaseOfData);
printf(" image base 0x%-8lX %lu\n",
optionalHeader->ImageBase, optionalHeader->ImageBase);
printf(" section align 0x%-8lx %lu\n",
optionalHeader->SectionAlignment, optionalHeader->SectionAlignment);
printf(" file align 0x%-8lx %lu\n",
optionalHeader->FileAlignment, optionalHeader->FileAlignment);
printf(" required OS version %u.%02u\n",
optionalHeader->MajorOperatingSystemVersion,
optionalHeader->MinorOperatingSystemVersion);
printf(" image version %u.%02u\n",
optionalHeader->MajorImageVersion, optionalHeader->MinorImageVersion);
printf(" subsystem version %u.%02u\n",
optionalHeader->MajorSubsystemVersion, optionalHeader->MinorSubsystemVersion);
printf(" Win32 Version 0x%lX\n", optionalHeader->Win32VersionValue);
printf(" size of image 0x%-8lx %lu\n",
optionalHeader->SizeOfImage, optionalHeader->SizeOfImage);
printf(" size of headers 0x%-8lx %lu\n",
optionalHeader->SizeOfHeaders, optionalHeader->SizeOfHeaders);
printf(" checksum 0x%lX\n", optionalHeader->CheckSum);
switch (optionalHeader->Subsystem)
{
default:
case IMAGE_SUBSYSTEM_UNKNOWN: str = "Unknown"; break;
case IMAGE_SUBSYSTEM_NATIVE: str = "Native"; break;
case IMAGE_SUBSYSTEM_WINDOWS_GUI: str = "Windows GUI"; break;
case IMAGE_SUBSYSTEM_WINDOWS_CUI: str = "Windows CUI"; break;
case IMAGE_SUBSYSTEM_OS2_CUI: str = "OS/2 CUI"; break;
case IMAGE_SUBSYSTEM_POSIX_CUI: str = "Posix CUI"; break;
}
printf(" Subsystem 0x%X (%s)\n", optionalHeader->Subsystem, str);
printf(" DLL flags 0x%X\n", optionalHeader->DllCharacteristics);
printf(" stack reserve size 0x%-8lx %lu\n",
optionalHeader->SizeOfStackReserve, optionalHeader->SizeOfStackReserve);
printf(" stack commit size 0x%-8lx %lu\n",
optionalHeader->SizeOfStackCommit, optionalHeader->SizeOfStackCommit);
printf(" heap reserve size 0x%-8lx %lu\n",
optionalHeader->SizeOfHeapReserve, optionalHeader->SizeOfHeapReserve);
printf(" heap commit size 0x%-8lx %lu\n",
optionalHeader->SizeOfHeapCommit, optionalHeader->SizeOfHeapCommit);
printf(" loader flags 0x%lX\n", optionalHeader->LoaderFlags);
printf(" RVAs & sizes 0x%lX\n", optionalHeader->NumberOfRvaAndSizes);
printf("\n");
printf("Data Directory\n");
printf("%ld\n",
optionalHeader->NumberOfRvaAndSizes* sizeof(IMAGE_DATA_DIRECTORY));
for (i = 0; i < optionalHeader->NumberOfRvaAndSizes && i < 16; i++)
{
printf(" %-12s rva: 0x%-8lX size: %8lu\n",
DirectoryNames[i],
optionalHeader->DataDirectory[i].VirtualAddress,
optionalHeader->DataDirectory[i].Size);
}
printf("\n");
}[/code]
代码中的PE_nt_headers是个PE格式头部数据结构指针,头部的内容在此以前已经读入。Rva是“Relative Virtual Address”的缩写,表示一个符号相对于其所在浮动代码块起点的虚拟地址。
现在,读者应该已经大体上明白了如何识别目标映像,以及如何解释映像头部许多字段和标志位的作用和意义,为进一步考察目标映像的装入和启动打下了基础。在以后的漫谈中,我将讲述Wine怎样装入和启动目标映像,到那时这些字段的作用和意义就更清楚了。
Wine的上述代码都是在用户空间执行的,但是若要将这些代码移到内核中也很容易。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -