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資料同樣可以隨意更改.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/335777.html
標籤:其他
上一篇:面試官問我:從地址欄輸入URL到顯示頁面都發生了什么?(建議收藏)
下一篇:用值初始化泛型類
