Dos头
关于dos头中没有太多需要讲的东西,这个头部是为了兼容老系统留下来的。在这个头部只存在两个有意义的字段。e_magic 它的值是MZ, 二进制值是5A4D。还有一个字段是e_lfanew 这个字段是指向nt头开始的位置。
获取dos头数据的代码
PIMAGE_DOS_HEADER pDosHead = NULL;
pDosHead = (PIMAGE_DOS_HEADER)imageBase;
printf("DOS头: 0x%.8x\n", pDosHead->e_magic);
printf("文件地址: 0x%.8x\n", pDosHead->e_lfarlc);
printf("PE结构偏移: 0x%.8x\n", pDosHead->e_lfanew);
nt头在nt头中又分为Signature字段,文件头,可选头。
Signature 默认是0x00004550 程序位数不同这个数值其实是不一样的,x86的下是0x10B x64下是 0x20B 。
可以用这里位置判断exe程序是32位的还是64位的。
文件头
在文件头中有以下几个字段
字段名 | 字段值 | 字段代表的意义 |
---|---|---|
Machine | 由编译决定也可指定 | 运行平台 |
NumberOfSections | 编译器填充也可自己填充 | 文件的节数目 |
TimeDateStamp | 编译器填充也可自己填充 | 文件创建日期和时间 |
PointerToSymbolTable | 编译器填充也可自己填充 | 指向符号表(用于调试) |
NumberOfSymbols | 编译器填充也可自己填充 | 符号表中的符号数量 |
SizeOfOptionalHeader | 编译器填充也可自己填充t | 可选头的长度 |
Characteristics | 编译器填充也可自己填充 | 文件的属性 exe=0x010h dll=0x210h |
在该字段中比较重要的字段主要有 NumberOfSections 、Characteristics。前者是用来遍历节表数的依据,后者可以用来判断当前文件是一个exe程序还是一个dll库。
获取文件头数据的代码
PIMAGE_NT_HEADERS pNtHead = NULL;
PIMAGE_FILE_HEADER pFileHead = NULL;
pNtHead = GetNtHead(imageBase);
pFileHead = &pNtHead->FileHeader;
printf("运行平台:\t\t0x%.8x\n", pFileHead->Machine);
printf("节区数目:\t\t0x%.8x\n", pFileHead->NumberOfSections);
printf("时间标记:\t\t%.8d\n", pFileHead->TimeDateStamp);
printf("可选头大小:\t\t0x%.8x\n", pFileHead->SizeOfOptionalHeader);
printf("文件特性:\t\t0x%.8x\n", pFileHead->Characteristics);
可选头在可选头中的数据成员
字段名 | 字段值 | 字段意义 |
---|---|---|
Magic | 0x10B 或0x20B | |
MajorLinkerVersion | 连接器版本 | |
MinorLinkerVersion | ||
SizeOfCode | 所有包含代码节的总大小 | |
SizeOfInitializedData | 所有已初始化数据的节总大小 | |
SizeOfUninitializedData | 所有未初始化数据的节总大小 | |
AddressOfEntryPoint | 程序执行入口RVA | |
BaseOfCode | 代码节的起始RVA | |
BaseOfData | 数据节的起始RVA | |
ImageBase | 程序镜像基地址 | |
SectionAlignment | 内存中节的对其粒度 | |
FileAlignment | 文件中节的对其粒度 | |
MajorOperatingSystemVersion | 操作系统主版本号 | |
MinorOperatingSystemVersion | 操作系统副版本号 | |
MajorImageVersion | 可运行于操作系统的最小版本号 | |
MinorImageVersion | ||
MajorSubsystemVersion | 可运行于操作系统的最小子版本号 | |
MinorSubsystemVersion | ||
Win32VersionValue | ||
SizeOfImage | 内存中整个PE映像尺寸 | |
SizeOfHeaders | 所有头加节表的大小 | |
CheckSum | ||
Subsystem | ||
DllCharacteristics | ||
SizeOfStackReserve | 初始化时堆栈大小 | |
SizeOfStackCommit | ||
SizeOfHeapReserve | ||
SizeOfHeapCommit | ||
LoaderFlags | ||
NumberOfRvaAndSizes | 数据目录的结构数量 | |
DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] |
还有一个更重要的部分是DataDirectory 这个后面再说。
获取可选头数据的代码
PIMAGE_NT_HEADERS pNtHead = NULL;
pNtHead = GetNtHead(ImageBase);
printf("入口点:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.AddressOfEntryPoint);
printf("镜像基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.ImageBase);
printf("镜像大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfImage);
printf("代码基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.BaseOfCode);
printf("区块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.SectionAlignment);
printf("文件块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.FileAlignment);
printf("子系统:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.Subsystem);
printf("区段数目:\t\t0x%.8x\n", pNtHead->FileHeader.NumberOfSections);
printf("时间日期标志:\t\t0x%.8x\n", pNtHead->FileHeader.TimeDateStamp);
printf("首部大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfHeaders);
printf("特征值:\t\t\t0x%.8x\n", pNtHead->FileHeader.Characteristics);
printf("校验和:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.CheckSum);
printf("可选头部大小:\t\t0x%.8x\n", pNtHead->FileHeader.SizeOfOptionalHeader);
printf("RVA 数及大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.NumberOfRvaAndSizes);
printf("是否启用随机地址:\t0x%.8x\n", pNtHead->OptionalHeader.DllCharacteristics);
节头在节表头中的数据字段
字段名 | 字段值 | 字段意义 |
---|---|---|
Name[IMAGE_SIZEOF_SHORT_NAME] | 节区名称 | |
Misc | 节区尺寸 | |
VirtualAddress | 节区RVA | |
SizeOfRawData | 在文件中对齐后的尺寸 | |
PointerToRawData | 在文件中的偏移 | |
PointerToRelocations | ||
PointerToLinenumbers | ||
NumberOfRelocations | ||
NumberOfLinenumbers | ||
Characteristics | 节区属性字段 |
PIMAGE_NT_HEADERS pNtHead = NULL;
PIMAGE_FILE_HEADER pFileHead = NULL;
PIMAGE_SECTION_HEADER pSection = NULL;
DWORD NumberOfSectinsCount = 0;
pNtHead = GetNtHead(ImageBase);
pSection = IMAGE_FIRST_SECTION(pNtHead);
pFileHead = &pNtHead->FileHeader;
NumberOfSectinsCount = pFileHead->NumberOfSections; // 获得区段数量
DWORD* difA = NULL; // 虚拟地址开头
DWORD* difS = NULL; // 相对偏移(用于遍历)
difA = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));
difS = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));
printf("节区名称 相对偏移\t虚拟大小\t实际偏移\t实际大小\t节区属性\n");
for (int temp = 0; temp < NumberOfSectinsCount; temp++, pSection++)
{
printf("%s\t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X\n",
pSection->Name, pSection->VirtualAddress, pSection->Misc.VirtualSize,
pSection->PointerToRawData, pSection->SizeOfRawData, pSection->Characteristics);
difA[temp] = pSection->VirtualAddress;
difS[temp] = pSection->VirtualAddress - pSection->PointerToRawData;
}
free(difA);
free(difS);
difA = NULL;
difS = NULL;
在pe中除了以上这些,还有一些重要的东西,比如导入表,导出表(存在dll文件中,exe中也有不过该位置数据为空),RVA到FOA(FOA到RVA),IAT表(IAThook,以及脱壳中IAT修复)。后面这些下回再发

最终完整代码
#include<stdio.h>
#include<Windows.h>
#include<ImageHlp.h>
#pragma comment(lib,"ImageHlp.lib")
HANDLE OpenPeByName(LPTSTR fileName) {
LPTSTR peFile = fileName;
HANDLE hFile, hMapFile, lpMapAddress = NULL;
DWORD dwFileSize = 0;
hFile = CreateFile(peFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("[-] 文件打开失败\n");
exit(0);
}
dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize != 0) {
printf("[+] 文件已读入!\n");
}
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
if (hMapFile == NULL) {
printf("[-]创建文件映射对象失败!!\n");
exit(0);
}
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, dwFileSize);
if (lpMapAddress != NULL) {
return lpMapAddress;
}
}
void displayDosHeadInfo(HANDLE imageBase)
{
PIMAGE_DOS_HEADER pDosHead = NULL;
pDosHead = (PIMAGE_DOS_HEADER)imageBase;
printf("DOS头: 0x%.8x\n", pDosHead->e_magic);
printf("文件地址: 0x%.8x\n", pDosHead->e_lfarlc);
printf("PE结构偏移: 0x%.8x\n", pDosHead->e_lfanew);
}
PIMAGE_NT_HEADERS GetNtHead(HANDLE imageBase)
{
PIMAGE_DOS_HEADER pDosHead = NULL;
PIMAGE_NT_HEADERS pNtHead = NULL;
pDosHead = (PIMAGE_DOS_HEADER)imageBase;
//如果该程序是64位的 pNtHead = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosHead + pDosHead->e_lfanew);
pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
return pNtHead;
}
void displayFileHeadInfo(HANDLE imageBase) {
PIMAGE_NT_HEADERS pNtHead = NULL;
PIMAGE_FILE_HEADER pFileHead = NULL;
pNtHead = GetNtHead(imageBase);
pFileHead = &pNtHead->FileHeader;
printf("运行平台:\t\t0x%.8x\n", pFileHead->Machine);
printf("节区数目:\t\t0x%.8x\n", pFileHead->NumberOfSections);
printf("时间标记:\t\t%.8d\n", pFileHead->TimeDateStamp);
printf("可选头大小:\t\t0x%.8x\n", pFileHead->SizeOfOptionalHeader);
printf("文件特性:\t\t0x%.8x\n", pFileHead->Characteristics);
}
void DisplayOptionalHeaderInfo(HANDLE ImageBase)
{
PIMAGE_NT_HEADERS pNtHead = NULL;
pNtHead = GetNtHead(ImageBase);
printf("入口点:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.AddressOfEntryPoint);
printf("镜像基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.ImageBase);
printf("镜像大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfImage);
printf("代码基址:\t\t0x%.8x\n", pNtHead->OptionalHeader.BaseOfCode);
printf("区块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.SectionAlignment);
printf("文件块对齐:\t\t0x%.8x\n", pNtHead->OptionalHeader.FileAlignment);
printf("子系统:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.Subsystem);
printf("区段数目:\t\t0x%.8x\n", pNtHead->FileHeader.NumberOfSections);
printf("时间日期标志:\t\t0x%.8x\n", pNtHead->FileHeader.TimeDateStamp);
printf("首部大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.SizeOfHeaders);
printf("特征值:\t\t\t0x%.8x\n", pNtHead->FileHeader.Characteristics);
printf("校验和:\t\t\t0x%.8x\n", pNtHead->OptionalHeader.CheckSum);
printf("可选头部大小:\t\t0x%.8x\n", pNtHead->FileHeader.SizeOfOptionalHeader);
printf("RVA 数及大小:\t\t0x%.8x\n", pNtHead->OptionalHeader.NumberOfRvaAndSizes);
printf("是否启用随机地址:\t0x%.8x\n", pNtHead->OptionalHeader.DllCharacteristics);
}
void DisplaySectionHeaderInfo(HANDLE ImageBase)
{
PIMAGE_NT_HEADERS pNtHead = NULL;
PIMAGE_FILE_HEADER pFileHead = NULL;
PIMAGE_SECTION_HEADER pSection = NULL;
DWORD NumberOfSectinsCount = 0;
pNtHead = GetNtHead(ImageBase);
pSection = IMAGE_FIRST_SECTION(pNtHead);
pFileHead = &pNtHead->FileHeader;
NumberOfSectinsCount = pFileHead->NumberOfSections; // 获得区段数量
DWORD* difA = NULL; // 虚拟地址开头
DWORD* difS = NULL; // 相对偏移(用于遍历)
difA = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));
difS = (DWORD*)malloc(NumberOfSectinsCount * sizeof(DWORD));
printf("节区名称 相对偏移\t虚拟大小\t实际偏移\t实际大小\t节区属性\n");
for (int temp = 0; temp < NumberOfSectinsCount; temp++, pSection++)
{
printf("%s\t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X \t 0x%.8X\n",
pSection->Name, pSection->VirtualAddress, pSection->Misc.VirtualSize,
pSection->PointerToRawData, pSection->SizeOfRawData, pSection->Characteristics);
difA[temp] = pSection->VirtualAddress;
difS[temp] = pSection->VirtualAddress - pSection->PointerToRawData;
}
free(difA);
free(difS);
difA = NULL;
difS = NULL;
}
void showPeInfoFunction() {
HANDLE lpMapAddress = NULL;
wchar_t exePath[] = L"./x32dbg.exe";
lpMapAddress = OpenPeByName(exePath);
printf("================================================\n");
printf("Dos head info:\n");
displayDosHeadInfo(lpMapAddress);
printf("================================================\n");
printf("Nt File head info:\n");
displayFileHeadInfo(lpMapAddress);
printf("================================================\n");
printf("Nt Optional head info:\n");
DisplayOptionalHeaderInfo(lpMapAddress);
printf("================================================================================================\n");
printf("Section head info:\n");
DisplaySectionHeaderInfo(lpMapAddress);
}