typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;结构体成员分析
PUSH CSPOP DSMOV DX,000EMOV AH,09INT 21MOV AX,4C01INT 21DB 54DB 68DB 69DB 00DB 33DB 70……通过16位的反汇编引擎即可得到对应的反汇编代码
这里我们主要关注DB段,也就是汇编中数据段部分有DB 54;DB 68;DB 69 ……
在WINHEX中找到其对应的数据部分,查看其对应的ASCII
数据部分为This program cannot be run in DOS
联合前面的两个INT 21 停止 不难猜测出该段数据对应的16位反汇编为输出数据部分的内容:This program cannot be run in DOS
自写代码解析DOS MZ头
了解了DOS部首的结构以后就可以自己写代码来读取DOS MZ头了
代码
// PE.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include #include int main(int argc, char* argv[]){ //创建DOS对应的结构体指针 _IMAGE_DOS_HEADER* dos; //读取文件,返回文件句柄 HANDLE hFile = CreateFileA("C:\\Documents and Settings\\Administrator\\桌面\\dbghelp.dll",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,0); //根据文件句柄创建映射 HANDLE hMap = CreateFileMappingA(hFile,NULL,PAGE_READONLY,0,0,0); //映射内容 LPVOID pFile = MapViewOfFile(hMap,FILE_MAP_READ,0,0,0); //范例转换,用结构体的方式来读取 dos=(_IMAGE_DOS_HEADER*)pFile; //输出结构体的第一个成员,以十六进制输出 printf("%X\n",dos->e_magic); return 0;}运行结果
可以看到能够正确地得到DOS MZ头对应的第一个成员的值:5A4D(对应ASCII为MZ)
总结
DOS部首分为两部分:DOS 'MZ' HEADER 和 DOS stub
DOS 'MZ' HEADER对应的结构体_IMAGE_DOS_HEADER中仅第一个成员和末了一个成员在32位及以上的WINDOWS系统上有效
DOS Stub对应为一串反汇编代码,其功能和输出This program cannot be run in DOS相关