滴水逆向-手动解析PE头&PE头字段说明及课后练习
1.PE文件的两种状态 PE文件与内存镜像,她们以节的形式进行分割,由于历史原因,很早之前的编译器将文件在磁盘和执行之后在内存 中的状态以"节"的形式分离,在内存空间的时候分隔的是1000H,在硬盘的时候是200H(十六进制) 一、PE为什么要分节? (1)节省硬盘空间.(这个不是决定的,由编译器决定) (2)一个应用程序多开 (3)理解FileBuffer和ImageBuffer 二、PE文件分了很多个节,那每个节在文件中从哪里开始?有多大?在内存中从哪里开始,有多大?由谁决定? 一个可执行文件所有的密码都在这个结构中. 分析PE文件 DOS头 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number --> DOS头的标识,为4Dh和5Ah。分别为字母MZ WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words DWORD e_lfanew; // File address of new exe header }IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; NT头 typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; }IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 课后练习 (1)找出所有DOS头数据,并统计DOS头大小. typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number --> DOS头的标识,为4Dh和5Ah。分别为字母MZ(ASCII表) WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words DWORD e_lfanew; // 32位可执行文件扩展域,用来指向DOS头之后的NT头相对文件起始地址的偏移,就是指向IMAGE_NT_HEADERS的所在 }IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 测试练习可执行文件ipmsg.exe文件的DOS头部 typedef struct _IMAGE_DOS_HEADER { WORD e_magic; // 5A4D WORD e_cblp; // 0090 WORD e_cp; // 0003 WORD e_crlc; // 0000 WORD e_cparhdr; // 0004 WORD e_minalloc; // 0000 WORD e_maxalloc; // FFFF WORD e_ss; // 0000 WORD e_sp; // 00B8 WORD e_csum; // 0000 WORD e_ip; // 0000 WORD e_cs; // 0000 WORD e_lfarlc; // 0040 WORD e_ovno; // 0000 WORD e_res[4]; // 0000000000000000 WORD e_oemid; // 0000 WORD e_oeminfo; // 0000 WORD e_res2[10]; // 0000000000000000000000000000000000000000 DWORD e_lfanew; // 000000F8 }IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 统计出来的DOS头大小为:16x2=32 2x4+2x10=8+20=28 4 --> 32+28+4=32+32=64byte 总大小:64字节 (2)找出所有标准PE头数据,并统计标准PE头大小. 上面说的标准PE头就是在NT头里面的Signature和FileHeader,再理解下就是PE文件头 typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; }IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; DWORD Signature; 4个字节 0x00004550 typedef struct _IMAGE_FILE_HEADER { WORD Machine; //0x014C WORD NumberOfSections; //0x0004 DWORD TimeDateStamp; //0x4DD1F580 DWORD PointerToSymbolTable; //0x00000000 DWORD NumberOfSymbols; //0x00000000 WORD SizeOfOptionalHeader; //0x00E0 WORD Characteristics; //0x010F }IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 统计出来的PE头大小为:4 2x4=8 3x4=12 4+8+12=24 --> 24byte 总大小:24字节 (3)找出所有可选PE头数据,并统计可选PE头大小. 上面说的可选PE头就是在NT头里面的OptionalHeader,再理解下就是PE可选头 typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; //0x010B BYTE MajorLinkerVersion; //0x06 BYTE MinorLinkerVersion; //0x00 DWORD SizeOfCode; //0x00045000 DWORD SizeOfInitializedData; //0x00028000 DWORD SizeOfUninitializedData; //0x00000000 DWORD AddressOfEntryPoint; //0x000441EC DWORD BaseOfCode; //0x00001000 DWORD BaseOfData; //0x00046000 DWORD ImageBase; //0x00400000 DWORD SectionAlignment; //0x00001000 DWORD FileAlignment; //0x00001000 WORD MajorOperatingSystemVersion; //0x0004 WORD MinorOperatingSystemVersion; //0x0000 WORD MajorImageVersion; //0x0000 WORD MinorImageVersion; //0x0000 WORD MajorSubsystemVersion; //0x0004 WORD MinorSubsystemVersion; //0x0000 DWORD Win32VersionValue; //0x00000000 DWORD SizeOfImage; //0x0006E000 DWORD SizeOfHeaders; //0x00001000 DWORD CheckSum; //0x00000000 WORD Subsystem; //0x0002 WORD DllCharacteristics; //0x0000 DWORD SizeOfStackReserve; //0x00100000 DWORD SizeOfStackCommit; //0x00001000 DWORD SizeOfHeapReserve; //0x00100000 DWORD SizeOfHeapCommit; //0x00001000 DWORD LoaderFlags; //0x00000000 DWORD NumberOfRvaAndSizes; //0x00000010 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; }IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 统计出来的PE头大小为:1x2=2 2x9=18 19x4=76 2+18+76=96 --> 96byte 总大小:96字节 2021/08/27 01:51:03 PM 1.DOS头 0x1 WORD e_magic * "MZ标记" 用于判断是否为可执行文件. 0x2 DWORD e_lfanew; * PE头相对于文件的偏移,用于定位PE文件 PE标记(或者叫PE签名) DWORD Signature 2.标准PE头(也叫标准PE文件头): 0x1 WORD Machine; * 程序运行的CPU型号:0x0 任何处理器/0x14C 386及后续处理器 0x2 WORD NumberOfSections; * 文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值. 0x3 DWORD TimeDateStamp; * 时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的. DWORD PointerToSymbolTable; DWORD NumberOfSymbols; 0x6 WORD SizeOfOptionalHeader; * 可选PE头的大小,32位PE文件默认E0h 64位PE文件默认为F0h 大小可以自定义. 0x7 WORD Characteristics; * 每个位有不同的含义,可执行文件值为10F 即0 1 2 3 8位置1 3、可选PE头: 0x1 WORD Magic; * 说明文件类型:10B 32位下的PE文件 20B 64位下的PE文件 BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; 0x4 DWORD SizeOfCode;* 所有代码节的和,必须是FileAlignment的整数倍 编译器填的 没用 0x5 DWORD SizeOfInitializedData;* 已初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用 0x6 DWORD SizeOfUninitializedData;* 未初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用 0x7 DWORD AddressOfEntryPoint;* 程序入口 0x8 DWORD BaseOfCode;* 代码开始的基址,编译器填的 没用 0x9 DWORD BaseOfData;* 数据开始的基址,编译器填的 没用 0xA DWORD ImageBase;* 内存镜像基址 0xB DWORD SectionAlignment;* 内存对齐 0xC DWORD FileAlignment;* 文件对齐 WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; 0x14 DWORD SizeOfImage;* 内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍 0x15 DWORD SizeOfHeaders;* 所有头+节表按照文件对齐后的大小,否则加载会出错 0x16 DWORD CheckSum;* 校验和,一些系统文件有要求.用来判断文件是否被修改. WORD Subsystem; WORD DllCharacteristics; 0x19 DWORD SizeOfStackReserve;* 初始化时保留的堆栈大小 0x1A DWORD SizeOfStackCommit;* 初始化时实际提交的大小 0x1B DWORD SizeOfHeapReserve;* 初始化时保留的堆大小 0x1C DWORD SizeOfHeapCommit;* 初始化时实践提交的大小 DWORD LoaderFlags; 0x1E DWORD NumberOfRvaAndSizes;* 目录项数目 课堂海哥给的范例代码 前面是否有宏定义 LPVOID ReadPEFile(LPSTR lpszFile) { FILE *pFile = NULL; DWORD fileSize = 0; LPVOID pFileBuffer = NULL; //打开文件 pFile = fopen(lpszFile, "rb"); //判断文件是否打开 if(!pFile) { printf(" 无法打开 EXE 文件! "); return NULL; } //读取文件大小 fseek(pFile, 0, SEEK_END); fileSize = ftell(pFile); fseek(pFile, 0, SEEK_SET); //分配缓冲区 pFileBuffer = malloc(fileSize); if(!pFileBuffer) { printf(" 分配空间失败! "); fclose(pFile); return NULL; } //将文件数据读取到缓冲区 size_t n = fread(pFileBuffer, fileSize, 1, pFile); if(!n) { printf(" 读取数据失败! "); free(pFileBuffer); fclose(pFile); return NULL; } //关闭文件 fclose(pFile); return pFileBuffer; } VOID PrintNTHeaders() { void* LPVOID pFileBuffer = NULL; 目测是一个结构体 PIMAGE_DOS_HEADER pDosHeader = NULL; 目测是一个结构体 PIMAGE_NT_HEADERS pNTHeader = NULL; 目测是一个结构体 PIMAGE_FILE_HEADER pPEHeader = NULL; 目测是一个结构体 PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; 目测是一个结构体 PIMAGE_SECTION_HEADER pSectionHeader = NULL; 进行赋值 pFileBuffer = ReadPEFile(FILEPATH); if(!pFileBuffer) 观察后是否有结构体的赋值方式 { printf("文件读取失败\n"); return ; } //判断是否是有效的MZ标志 if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) { printf("不是有效的MZ标志\n"); free(pFileBuffer); return ; } pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //打印DOC头 printf("********************DOC头********************\n"); printf("MZ标志:%x\n",pDosHeader->e_magic); printf("PE偏移:%x\n",pDosHeader->e_lfanew); //判断是否是有效的PE标志 if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) { printf("不是有效的PE标志\n"); free(pFileBuffer); return ; } pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew); //打印NT头 printf("********************NT头********************\n"); printf("NT:%x\n",pNTHeader->Signature); pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); printf("********************PE头********************\n"); printf("PE:%x\n",pPEHeader->Machine); printf("节的数量:%x\n",pPEHeader->NumberOfSections); printf("SizeOfOptionalHeader:%x\n",pPEHeader->SizeOfOptionalHeader); //可选PE头 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); printf("********************OPTIOIN_PE头********************\n"); printf("OPTION_PE:%x\n",pOptionHeader->Magic); //释放内存 free(pFileBuffer); } 课后练习 练习: 1.编写程序读取一个.exe文件,输出所有的PE头信息. 先测试读取notepad.exe #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <string.h> int* OpenFile() { FILE* PointToFile = NULL; int FileSize = 0; int* StrBuffer = NULL; int Num = 0; //打开文件 if ((PointToFile = fopen("C:\\WINDOWS\\system32\\notepad.exe","rb")) == NULL) { printf("打开文件失败!\n"); exit(1); } //获取文件大小 fseek(PointToFile,0,2); FileSize = ftell(PointToFile); //重定位指针 fseek(PointToFile,0,0); //buffer指向申请的堆 StrBuffer = (int*)(malloc(FileSize)); if (!StrBuffer) { printf("堆空间分配失败!\n"); free(StrBuffer); return 0; } //读取文件内容 Num = fread(StrBuffer,FileSize,1,PointToFile); if (!Num) { printf("读取文件内容失败!\n"); free(StrBuffer); return 0; } //关闭文件 fclose(PointToFile); //将缓冲区内的文件内容的地址返回到调用函数的地方 return StrBuffer; } int* FileSizes = OpenFile(); int PrintfNtHeaders() { //文件指针 unsigned int* PointBuffer = (unsigned int*)FileSizes; unsigned short* pBuffer = (unsigned short*)PointBuffer; unsigned char* pcBuffer = (unsigned char*)PointBuffer; //判断MZ和PE的标志 unsigned short Cmp1 = 0x5A4D; unsigned int Cmp2 = 0x00004550; //判断文件是否读取成功 if(!PointBuffer) { printf("文件读取失败!\n"); free(PointBuffer); return 0; } //判断是否为MZ标志 if (*pBuffer != Cmp1) { printf("不是有效MZ标志!\n"); printf("%X\n",*pBuffer); free(PointBuffer); return 0; } printf("*********打印DOS头*********\n"); printf("e_magic:\t\t\t%X\n",*(pBuffer)); printf("e_ifanew:\t\t\t%08X\n\n\n",*(PointBuffer+15)); //判断是否为PE标志 if (*(PointBuffer+56) != Cmp2) { printf("不是有效的PE标志!\n"); printf("%X\n",*(PointBuffer+56)); free(PointBuffer); return 0; } printf("*********打印标准PE文件头*********\n"); printf("PE标志:\t\t\t\t%X\n",*(PointBuffer+56)); printf("Machine:\t\t\t%04X\n",*(pBuffer+114)); printf("NumberOfSection:\t\t%04X\n",*(pBuffer+115)); printf("TimeDateStamp:\t\t\t%08X\n",*(PointBuffer+58)); printf("PointerToSymbolTable:\t\t%08X\n",*(PointBuffer+59)); printf("NumberOfSymbols:\t\t%08X\n",*(PointBuffer+60)); printf("SizeOfOptionalHeader:\t\t%04X\n",*(pBuffer+122)); printf("Chrarcteristics:\t\t%04X\n\n\n",*(pBuffer+123)); printf("*********打印标准可选PE头*********\n"); printf("Magic:\t\t\t\t%04X\n", *(pBuffer+124)); printf("MajorLinkerVersion:\t\t%02X\n", *(pcBuffer+250)); printf("MinorLinkerVersion:\t\t%02X\n", *(pcBuffer+251)); printf("SizeOfCode:\t\t\t%08X\n", *(PointBuffer+63)); printf("SizeOfInitializedData:\t\t%08X\n", *(PointBuffer+64)); printf("SizeOfUninitializedData:\t%08X\n", *(PointBuffer+65)); printf("AddressOfEntryPoint:\t\t%08X\n", *(PointBuffer+66)); printf("BaseOfCode:\t\t\t%08X\n", *(PointBuffer+67)); printf("BaseOfData:\t\t\t%08X\n", *(PointBuffer+68)); printf("ImageBase:\t\t\t%08X\n", *(PointBuffer+69)); printf("SectionAlignment:\t\t%08X\n", *(PointBuffer+70)); printf("FileAlignment:\t\t\t%08X\n", *(PointBuffer+71)); printf("MajorOperatingSystemVersion:\t%04X\n", *(pBuffer+144)); printf("MinorOperatingSystemVersion:\t%04X\n", *(pBuffer+145)); printf("MajorImageVersion:\t\t%04X\n", *(pBuffer+146)); printf("MinorImageVersion:\t\t%04X\n", *(pBuffer+147)); printf("MajorSubsystemVersion:\t\t%04X\n", *(pBuffer+148)); printf("MinorSubsystemVersion:\t\t%04X\n", *(pBuffer+149)); printf("Win32VersionValue:\t\t%08X\n", *(PointBuffer+75)); printf("SizeOfImage:\t\t\t%08X\n", *(PointBuffer+76)); printf("SizeOfHeaders:\t\t\t%08X\n", *(PointBuffer+77)); printf("CheckSum:\t\t\t%08X\n", *(PointBuffer+78)); printf("Subsystem:\t\t\t%04X\n", *(pBuffer+158)); printf("DllCharacteristics:\t\t%04X\n", *(pBuffer+159)); printf("SizeOfStackReserve:\t\t%08X\n", *(PointBuffer+80)); printf("SizeOfStackCommit:\t\t%08X\n", *(PointBuffer+81)); printf("SizeOfHeapReserve:\t\t%08X\n", *(PointBuffer+82)); printf("SizeOfHeapCommit:\t\t%08X\n", *(PointBuffer+83)); printf("LoaderFlags:\t\t\t%08X\n", *(PointBuffer+84)); printf("NumberOfRvaAndSizes:\t\t%08X\n", *(PointBuffer+85)); free(PointBuffer); return 0; } int main() { PrintfNtHeaders(); OpenFile(); return 0; } 打印结果: Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. pelx.cpp Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Creating library pelx.lib and object pelx.exp *********打印DOS头********* e_magic: 5A4D e_ifanew: 000000E0 *********打印标准PE文件头********* PE标志: 4550 Machine: 014C NumberOfSection: 0003 TimeDateStamp: 41107CC3 PointerToSymbolTable: 00000000 NumberOfSymbols: 00000000 SizeOfOptionalHeader: 00E0 Chrarcteristics: 010F *********打印标准可选PE头********* Magic: 010B MajorLinkerVersion: 07 MinorLinkerVersion: 0A SizeOfCode: 00007800 SizeOfInitializedData: 00008800 SizeOfUninitializedData: 00000000 AddressOfEntryPoint: 0000739D BaseOfCode: 00001000 BaseOfData: 00009000 ImageBase: 01000000 SectionAlignment: 00001000 FileAlignment: 00000200 MajorOperatingSystemVersion: 0005 MinorOperatingSystemVersion: 0001 MajorImageVersion: 0005 MinorImageVersion: 0001 MajorSubsystemVersion: 0004 MinorSubsystemVersion: 0000 Win32VersionValue: 00000000 SizeOfImage: 00013000 SizeOfHeaders: 00000400 CheckSum: 00017959 Subsystem: 0002 DllCharacteristics: 8000 SizeOfStackReserve: 00040000 SizeOfStackCommit: 00011000 SizeOfHeapReserve: 00100000 SizeOfHeapCommit: 00001000 LoaderFlags: 00000000 NumberOfRvaAndSizes: 00000010 请按任意键继续. . . 上述代码有个极大的缺点,就是只能加载打印特定固定的notepad.exe文件,而且在 不同的操作系统下面会有异常,可移植性差 互联网上有一份代码可以完美打印任何文件的PE相关的信息,我做了一些优化,参考地址:https://www.it610.com/article/1304362536954531840.htm 具体代码如下: #include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <malloc.h> #define F_PATH "C:\\cntflx\\ipmsg.exe" FILE* open_file(char* file_path,char* open_mode); int compute_file_size(FILE* file_address); char* allocate_buffer(int file_size); char* readfile2memory(char* file_buffer,int file_size,FILE* file_address); void analysis_PE_head(char* File_buffer); VOID PrintNTHeaders() { // 初始化 //char file_path[] = "C:\\Windows\\System32\\notepad.exe"; char file_path[] = F_PATH; char open_mode[] = "rb"; // 打开文件,返回文件指针 FILE* file_address = open_file(file_path,open_mode); // 计算文件长度 int file_size = compute_file_size(file_address); // 分配内存 char* File_buffer = allocate_buffer(file_size); // 写入内存,返回内存地址 File_buffer = readfile2memory(File_buffer,file_size,file_address); // 打印PE头部信息 analysis_PE_head(File_buffer); // 释放内存、关闭文件流 free(File_buffer); fclose(file_address); } FILE* open_file(char* file_path,char* open_mode) { FILE* file_address = fopen(file_path,open_mode); // fopen() 参数是字符串也就是常量指针类型 if(!file_address) { printf("打开文件失败!\r\n"); return 0; } return file_address; } int compute_file_size(FILE* file_address) { int size = 0; fseek(file_address,0,SEEK_END); size = ftell(file_address); fseek(file_address,0,SEEK_SET); return size; } char* allocate_buffer(int file_size) { char* file_buffer = (char*)malloc(file_size); if(!file_buffer) { printf("申请内存失败!\r\n"); return 0; } memset(file_buffer,0,file_size); return file_buffer; } char* readfile2memory(char* file_buffer,int file_size,FILE* file_address) { if(!(fread(file_buffer,file_size,1,file_address))) { printf("从文件向内存中读取数据失败!\r\n"); return 0; } return file_buffer; // 如果写入内存成功,则返回内地址 } void analysis_PE_head(char* File_buffer) { // 实例化PE文件头几个结构体 PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_FILE_HEADER pPEHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; PIMAGE_SECTION_HEADER pSectionHeader = NULL; // 强制类型转换 pDosHeader = (PIMAGE_DOS_HEADER)File_buffer; // 判断是不是有效的MZ标志 if(*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE) { printf("不是有效的MZ标志!\r\n"); free(File_buffer); return; } // 强制类型转换 PIMAGE_DOS_HEADER结构体 pDosHeader = (PIMAGE_DOS_HEADER)File_buffer; // 打印DOS头 printf("=============================DOS头信息如下=============================\r\n"); printf("MZ标志:\t\t\t%04X\r\n",pDosHeader->e_magic); printf("PE偏移:\t\t\t%08X\r\n",pDosHeader->e_lfanew); // 判断是不是有效的PE标志 if(*((PDWORD)((DWORD)File_buffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) { printf("不是有效的PE标志!\r\n"); free(File_buffer); return; } // 强制类型转换 PIMAGE_NT_HEADERS结构体 pNTHeader = PIMAGE_NT_HEADERS((DWORD)File_buffer+pDosHeader->e_lfanew); // 打印NT头 printf("=============================NT头信息如下===============================\r\n"); printf("NT:\t\t\t\t%04X\r\n",pNTHeader->Signature); // 强制类型转换 PIMAGE_FILE_HEADER结构体 pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4); // 打印标准PE文件头 printf("=============================标准PE头信息如下============================\r\n"); printf("PE_machine:\t\t\t%04X\r\n",pPEHeader->Machine); printf("NumberOfSections:\t\t%04X\n",pPEHeader->NumberOfSections); printf("SizeOfOptionalHeader:\t\t%04X\r\n",pPEHeader->SizeOfOptionalHeader); // 强制类型转换 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);// // 打印可选PE头 printf("==============================可选PE头信息如下==============================\r\n"); printf("Magic:\t\t\t\t%04X\r\n",pOptionHeader->Magic); printf("AddressOfEntryPoint:\t\t%08X\r\n",pOptionHeader->AddressOfEntryPoint); printf("ImageBase:\t\t\t%08X\r\n",pOptionHeader->ImageBase); printf("SizeOfImage:\t\t\t%08X\r\n",pOptionHeader->SizeOfImage); printf("SizeOfHeaders:\t\t\t%08X\r\n",pOptionHeader->SizeOfHeaders); printf("SectionAlignment:\t\t%08X\r\n",pOptionHeader->SectionAlignment); printf("FileAlignment:\t\t\t%08X\r\n",pOptionHeader->FileAlignment); // 强制类型转换 pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader); printf("==============================节表信息如下===============================\n"); //printf("name:%s\n",pSectionHeader->Misc); DWORD dwNumberOfSection = pPEHeader->NumberOfSections; /* printf("%x\n",pPEHeader->NumberOfSections); printf("IMAGE_SIZEOF_SHORT_NAME:%x\n",IMAGE_SIZEOF_SHORT_NAME); printf("option_add:%x\n",pOptionHeader); printf("psection_add:%x\n",pSectionHeader); printf("==============================================================\n");*/ for(DWORD i = 0;i<dwNumberOfSection;i++,pSectionHeader++) { printf("========================第%d个节信息:===============================\n",i+1); printf("section_name:"); for(DWORD j = 0;j<IMAGE_SIZEOF_SHORT_NAME;j++) { printf("%c",pSectionHeader->Name[j]); } printf("\r\n"); printf("Misc:\t\t\t\t%08X\r\n",pSectionHeader->Misc); printf("VirtualAddress:\t\t\t%08X\r\n",pSectionHeader->VirtualAddress); printf("SizeOfRawData:\t\t\t%08X\r\n",pSectionHeader->SizeOfRawData); printf("PointerToRawData:\t\t%08X\r\n",pSectionHeader->PointerToRawData); printf("Characteristics:\t\t%08X\r\n",pSectionHeader->Characteristics); } } int main(int argc, char* argv[]) { PrintNTHeaders(); //getchar(); return 0; } 打印结果如下: Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright (C) Microsoft Corp 1984-1998. All rights reserved. pelx.cpp Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Creating library pelx.lib and object pelx.exp =============================DOS头信息如下============================= MZ标志: 5A4D PE偏移: 000000F8 =============================NT头信息如下=============================== NT: 4550 =============================标准PE头信息如下============================ PE_machine: 014C NumberOfSections: 0004 SizeOfOptionalHeader: 00E0 ==============================可选PE头信息如下============================== Magic: 010B AddressOfEntryPoint: 000441EC ImageBase: 00400000 SizeOfImage: 0006E000 SizeOfHeaders: 00001000 SectionAlignment: 00001000 FileAlignment: 00001000 ==============================节表信息如下=============================== ========================第1个节信息:=============================== section_name:.text Misc: 000440A2 VirtualAddress: 00001000 SizeOfRawData: 00045000 PointerToRawData: 00001000 Characteristics: 60000020 ========================第2个节信息:=============================== section_name:.rdata Misc: 0000D74D VirtualAddress: 00046000 SizeOfRawData: 0000E000 PointerToRawData: 00046000 Characteristics: 40000040 ========================第3个节信息:=============================== section_name:.data Misc: 0000F5F8 VirtualAddress: 00054000 SizeOfRawData: 00007000 PointerToRawData: 00054000 Characteristics: C0000040 ========================第4个节信息:=============================== section_name:.rsrc Misc: 00009968 VirtualAddress: 00064000 SizeOfRawData: 0000A000 PointerToRawData: 0005B000 Characteristics: 40000040 2.使用第三方的PE工具,对比如下信息,看是否一致: 使用了第三方工具,确定了信息是一致的!
优化的完美代码打印结果:
迷茫的人生,需要不断努力,才能看清远方模糊的志向!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-08-28 Vulnhub-靶机-IMF: 1