1.PE文件之DOS头(IMAGE_DOS_HEADER)

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数据同样可以随意更改.

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了实现.exe文件的节表免疫,可以通过以下步骤: 1. 使用Windows API函数打开可执行文件,获取可执行文件文件和节表信息。 2. 计算新的NT偏移量,将NT偏移至节表之后。 3. 将修改后的NT信息写回可执行文件中。 以下是相应的C++代码实现: ```c++ #include <windows.h> #include <iostream> using namespace std; int main() { // 打开可执行文件 HANDLE hFile = CreateFileA("test.exe", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { cout << "Open file failed!" << endl; return 0; } // 获取可执行文件文件 IMAGE_DOS_HEADER dosHeader; DWORD dwBytesRead = 0; ReadFile(hFile, &dosHeader, sizeof(dosHeader), &dwBytesRead, NULL); if (dwBytesRead != sizeof(dosHeader)) { cout << "Read file header failed!" << endl; CloseHandle(hFile); return 0; } IMAGE_NT_HEADERS ntHeader; ReadFile(hFile, &ntHeader, sizeof(ntHeader), &dwBytesRead, NULL); if (dwBytesRead != sizeof(ntHeader)) { cout << "Read file header failed!" << endl; CloseHandle(hFile); return 0; } // 获取节表信息 DWORD dwSectionOffset = dosHeader.e_lfanew + sizeof(ntHeader.Signature) + sizeof(ntHeader.FileHeader) + ntHeader.FileHeader.SizeOfOptionalHeader; IMAGE_SECTION_HEADER sectionHeader; SetFilePointer(hFile, dwSectionOffset, NULL, FILE_BEGIN); for (int i = 0; i < ntHeader.FileHeader.NumberOfSections; i++) { ReadFile(hFile, &sectionHeader, sizeof(sectionHeader), &dwBytesRead, NULL); if (dwBytesRead != sizeof(sectionHeader)) { cout << "Read section header failed!" << endl; CloseHandle(hFile); return 0; } } // 计算新的NT偏移量 DWORD dwNewNtHeaderOffset = dwSectionOffset + ntHeader.FileHeader.NumberOfSections * sizeof(sectionHeader); // 将NT偏移至节表之后 SetFilePointer(hFile, dwNewNtHeaderOffset, NULL, FILE_BEGIN); WriteFile(hFile, &ntHeader, sizeof(ntHeader), &dwBytesRead, NULL); if (dwBytesRead != sizeof(ntHeader)) { cout << "Write NT header failed!" << endl; CloseHandle(hFile); return 0; } // 关闭文件句柄 CloseHandle(hFile); cout << "File section table immune success!" << endl; return 0; } ``` 需要注意的是,此代码仅供学习和参考使用,具体实现可能需要根据实际情况进行调整和优化。同时,对于修改可执行文件的操作需谨慎,应在测试环境下进行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值