PE文件详解 - DOS头
1、PE文件详解
- PE文件格式是微软Windows NT内核系列系统和Win32子集中可执行的二进制文件格式,是基于COFF文件格式设计出来的
1.1、名词解析
名词 | 解析 |
---|---|
头 | 泛指一个数据结构的起始位置,这个头可能是指一个文件的起始结构,也可能是指结构中的起始字段 |
偏移 | 多用于文件中,用于指定文件中的某个位置,例如一个文件的大小为0x10字节,那么偏移0XB就是指这个文件的第11个字节 |
页 | x86平台内存中管理数据的最小单位,大小一般为4kb |
区段 | 区段是指PE文件结构中的一个数据段,在PE文件结构中,不同类型的数据往往会被保存在不同的区段中 |
映像 | 泛指映射在内存中的PE结构的可执行文件(如exe,dll,sys,ocx) |
字段 | 指的是数据结构中的最小单位 |
1.2、MS-DOS头
-
MS-DOS头存在于每个PE文件中,它的存在完全是出于兼容性考虑
-
重要的字段是第一个e_magic与最后一个e_lfanew
- e_magic:表示这是一个MS-DOS系统中的可执行文件
- e_lfanew:可以引导找到PE头的开始
-
使用代码实现 : 打开一个文件显示出它的MS-DOS头信息 (c版本)
#include<stdio.h>
// 设置结构体对其方式
#pragma pack(push,1)
typedef struct {
unsigned short e_magic; // 魔数
unsigned short e_cblp; // 文件最后一个页的字节数
unsigned short e_cp; // 文件页数
unsigned short e_crlc; // 重定位项数
unsigned short e_cparhdr; // 头部尺寸(段表项数量)
unsigned short e_minalloc; // 所需的最小附加段字节数
unsigned short e_maxalloc; // 所需的最大附加段字节数
unsigned short e_ss; // 初始的SS值
unsigned short e_sp; // 初始的SP值
unsigned short e_csum; // 校验和
unsigned short e_ip; // 初始的IP值
unsigned short e_cs; // 初始的CS值
unsigned short e_lfarlc; // 重定位表的文件地址
unsigned short e_ovno; // 覆盖号
unsigned short e_res[4]; // 保留字段
unsigned short e_oemid; // OEM标识符
unsigned short e_oeminfo; // OEM信息
unsigned short e_res2[10]; // 保留字段
unsigned int e_lfanew; // 新的PE头的文件地址
}IMAGE_DOS_HEADER;
#pragma pack(pop)
int main() {
IMAGE_DOS_HEADER doHeader;
// 打开PE文件,读取DOS头
FILE* file = fopen("1.exe","rb");
if (file == NULL)
{
printf("无法打开文件");
return 1;
}
fread(&doHeader, sizeof(IMAGE_DOS_HEADER), 1, file);
printf("e_magic: 0x%X\n", doHeader.e_magic);
printf("e_cblp: %u\n", doHeader.e_cblp);
printf("e_cp: %u\n", doHeader.e_cp);
printf("e_crlc: %u\n", doHeader.e_crlc);
printf("e_lfanew: %lx\n", doHeader.e_lfanew);
return 0;
}
1.2.1、通过代码读取DOS头
- c++
#include<stdio.h>
#include<Windows.h>
int main()
{
FILE* pFile = NULL;
char* buffer;
int nFileLength = 0;
errno_t err = fopen_s(&pFile, "E:\\C_code\\MyDemo\\Debug\\MyDemo.exe", "rb");
if (err != 0)
{
// 处理文件打开错误
printf("Failed to open file.\n");
return 1;
}
// fseek()设置文件指针的位置。这里将文件指针移动到文件末尾
// SEEK_END 指定偏移量为0
fseek(pFile, 0, SEEK_END);
// ftell() 获取文件指针的当前位置,也就是文件的长度
nFileLength = ftell(pFile);
// rewind() 将文件指针重置到文件开头
rewind(pFile);
// 存储缓冲区大小
// 计算方式:文件长度乘以sizeof(char)(sizeof(char)表示char类型的大小,通常是1字节),再加1字节用于存储字符串结束符\0
int imageLength = nFileLength * sizeof(char) + 1;
// 动态分配内存 : malloc函数动态分配了一块内存,大小为imageLength字节
buffer = (char*)malloc(imageLength);
// memset() :将分配的内存初始化为0。
memset(buffer, 0, nFileLength * sizeof(char) + 1);
// fread函数用于从文件中读取数据。这里将文件中的内容读取到之前分配的缓冲区buffer中,每次读取1字节,总共读取imageLength个字节。
fread(buffer, 1, imageLength, pFile);
// 定义指向 PIMAGE_DOS_HEADER的指针 ReadDosHeader
PIMAGE_DOS_HEADER ReadDosHeader;
// 因为buffer是char类型,所以要转成指针类型
// 目的是将缓冲区中的数据解释为IMAGE_DOS_HEADER结构
ReadDosHeader = (PIMAGE_DOS_HEADER)buffer;
// 打印指针中的数据
printf("MS-DOS Info:\n");
printf("MZ标志位:%x\n", ReadDosHeader->e_magic);
printf("PE头位置:%x\n", ReadDosHeader->e_lfanew);
// 释放动态内存
free(buffer);
// 关闭文件
fclose(pFile);
return 0;
}
- 通过断点分析运行结果
- 直接打印输出
推荐阅读: