IMAGE_DOS_HEADER结构是面对于16位程序的.现在大部分程序都是32或者64位的. 32或者64位的程序仅使用其中两个成员.
IMAGE_DOS_HEADER
结构及成员含义如下:
//大小为: 0x40(64)字节
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // MZ标记 0x5a4d
WORD e_cblp; // 最后(部分)页中的字节数
WORD e_cp; // 文件中的全部和部分页数
WORD e_crlc; // 重定位表中的指针数
WORD e_cparhdr; // 头部尺寸以段落为单位
WORD e_minalloc; // 所需的最小附加段
WORD e_maxalloc; // 所需的最大附加段
WORD e_ss; // 初始的SS值(相对偏移量)
WORD e_sp; // 初始的SP值
WORD e_csum; // 补码校验值
WORD e_ip; // 初始的IP值
WORD e_cs; // 初始的SS值
WORD e_lfarlc; // 重定位表的字节偏移量
WORD e_ovno; // 覆盖号
WORD e_res[4]; // 保留字
WORD e_oemid; // OEM标识符(相对m_oeminfo)
WORD e_oeminfo; // OEM信息
WORD e_res2[10]; // 保留字
LONG e_lfanew; // NT头(PE标记)相对于文件的偏移地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
e_magic: 该成员为MZ标记(DOS系统开发人员中一个人的名字),用于判断是否为可执行文件,如果此值不是0x5a4d程序将不会正常启动 .
e_lfanew: 该成员中存储的值为IMAGE_NT_HEADERS结构的偏移,加上文件头来定位IMAGE_NT_HEADERS结构.
IMAGE_DOS_HEADER结构之后的数据和IMAGE_NT_HEADERS结构之前的数据为垃圾值编译器填充称为DOS_STUB,可随意修改,不会影响程序正常运行.
通过十六进制编辑器查看可执行文件在硬盘时数据:
通过代码查看IMAGE_DOS_HEADER结构数据:
读取文件代码:
PVOID FileToMem(IN PCHAR szFilePath, OUT LPDWORD dwFileSize)
{
//打开文件
FILE* pFile = fopen(szFilePath, "rb");
if (!pFile)
{
printf("FileToMem fopen Fail \r\n");
return NULL;
}
//获取文件长度
fseek(pFile, 0, SEEK_END); //SEEK_END文件结尾
DWORD Size = ftell(pFile);
fseek(pFile, 0, SEEK_SET); //SEEK_SET文件开头
//申请存储文件数据缓冲区
PCHAR pFileBuffer = (PCHAR)malloc(Size);
if (!pFileBuffer)
{
printf("FileToMem malloc Fail \r\n");
fclose(pFile);
return NULL;
}
//读取文件数据
fread(pFileBuffer, Size, 1, pFile);
//判断是否为可执行文件
if (*(PSHORT)pFileBuffer != IMAGE_DOS_SIGNATURE)
{
printf("Error: MZ \r\n");
fclose(pFile);
free(pFileBuffer);
return NULL;
}
if (*(PDWORD)(pFileBuffer + *(PDWORD)(pFileBuffer + 0x3C)) != IMAGE_NT_SIGNATURE)
{
printf("Error: PE \r\n");
fclose(pFile);
free(pFileBuffer);
return NULL;
}
if (dwFileSize)
{
*dwFileSize = Size;
}
fclose(pFile);
return pFileBuffer;
}
输出文件代码:
VOID MemToFile(IN PCHAR szFilePath, IN PVOID pFileBuffer, IN DWORD dwFileSize)
{
//打开文件
FILE* pFile = fopen(szFilePath, "wb");
if (!pFile)
{
printf("MemToFile fopen Fail \r\n");
return;
}
//输出文件
fwrite(pFileBuffer, dwFileSize, 1, pFile);
fclose(pFile);
}
读取DOS头数据代码:
VOID PrintDosHeader()
{
//读取文件二进制数据
DWORD dwFileSize = 0;
PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
if (!pFileBuffer)
{
return;
}
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
printf("IMAGE_DOS_HEADER.e_magic -> [0x%04x] \r\n", pDos->e_magic);
printf("IMAGE_DOS_HEADER.e_cblp -> [0x%04x] \r\n", pDos->e_cblp);
printf("IMAGE_DOS_HEADER.e_cp -> [0x%04x] \r\n", pDos->e_cp);
printf("IMAGE_DOS_HEADER.e_crlc -> [0x%04x] \r\n", pDos->e_crlc);
printf("IMAGE_DOS_HEADER.e_cparhdr -> [0x%04x] \r\n", pDos->e_cparhdr);
printf("IMAGE_DOS_HEADER.e_minalloc -> [0x%04x] \r\n", pDos->e_minalloc);
printf("IMAGE_DOS_HEADER.e_maxalloc -> [0x%04x] \r\n", pDos->e_maxalloc);
printf("IMAGE_DOS_HEADER.e_ss -> [0x%04x] \r\n", pDos->e_ss);
printf("IMAGE_DOS_HEADER.e_sp -> [0x%04x] \r\n", pDos->e_sp);
printf("IMAGE_DOS_HEADER.e_csum -> [0x%04x] \r\n", pDos->e_csum);
printf("IMAGE_DOS_HEADER.e_ip -> [0x%04x] \r\n", pDos->e_ip);
printf("IMAGE_DOS_HEADER.e_cs -> [0x%04x] \r\n", pDos->e_cs);
printf("IMAGE_DOS_HEADER.e_lfarlc -> [0x%04x] \r\n", pDos->e_lfarlc);
printf("IMAGE_DOS_HEADER.e_ovno -> [0x%04x] \r\n", pDos->e_ovno);
for (size_t i = 0; i < 4; i++)
{
printf("IMAGE_DOS_HEADER.e_res[%d] -> [0x%04x] \r\n", i, pDos->e_res[i]);
}
printf("IMAGE_DOS_HEADER.e_oemid -> [0x%04x] \r\n", pDos->e_oemid);
printf("IMAGE_DOS_HEADER.e_oeminfo -> [0x%04x] \r\n", pDos->e_oeminfo);
for (size_t i = 0; i < 10; i++)
{
printf("IMAGE_DOS_HEADER.e_res2[%d] -> [0x%04x] \r\n", i, pDos->e_res2[i]);
}
printf("IMAGE_DOS_HEADER.e_lfanew -> [0x%08x] \r\n", pDos->e_lfanew);
}
测试抹除除e_magic,e_lfanew 以外的数据观察程序是否可以正常执行:
#include "Tools.h"
int main()
{
//读取文件二进制数据
DWORD dwFileSize = 0;
PVOID pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
//抹除IMAGE_DOS_HEADER垃圾数据
memset((PCHAR)pFileBuffer + 2, 0, 0x3A);
//将二进制数据输出到文件
MemToFile(FILE_PATH_OUT, pFileBuffer, dwFileSize);
return 0;
}
程序正常运行
测试抹除e_magic观察程序是否可以正常执行:
#include "Tools.h"
int main()
{
//读取文件二进制数据
DWORD dwFileSize = 0;
PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
memset(pFileBuffer, 0, 2);
//将二进制数据输出到文件
MemToFile(FILE_PATH_OUT, pFileBuffer, dwFileSize);
return 0;
}
程序运行失败
测试抹除e_lfanew 观察程序是否可以正常执行:
#include "Tools.h"
int main()
{
//读取文件二进制数据
DWORD dwFileSize = 0;
PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
memset(pFileBuffer + 0x3C, 0, 4);
//将二进制数据输出到文件
MemToFile(FILE_PATH_OUT, pFileBuffer, dwFileSize);
return 0;
}
程序运行失败
测试抹除垃圾数据观察程序是否可以正常执行:
#include "Tools.h"
int main()
{
//读取文件二进制数据
DWORD dwFileSize = 0;
PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
memset(pFileBuffer + sizeof(IMAGE_DOS_HEADER), 0, pDos->e_lfanew - sizeof(IMAGE_DOS_HEADER));
//将二进制数据输出到文件
MemToFile(FILE_PATH_OUT, pFileBuffer, dwFileSize);
return 0;
}
程序正常运行
通过代码测试可以得出结论:IMAGE_DOS_HEADER中除e_magic,e_lfanew成员外均可修改,不会影响程序正常运行.DOS_STUB数据同样可以随意更改.