滴水逆向-手动解析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("文件读取失败
");
            return ;
        }

        //判断是否是有效的MZ标志
        if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
        {
            printf("不是有效的MZ标志
");
            free(pFileBuffer);
            return ;
        }
        pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
        //打印DOC头
        printf("********************DOC头********************
");
        printf("MZ标志:%x
",pDosHeader->e_magic);
        printf("PE偏移:%x
",pDosHeader->e_lfanew);
        //判断是否是有效的PE标志
        if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
        {
            printf("不是有效的PE标志
");
            free(pFileBuffer);
            return ;
        }
        pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
        //打印NT头
        printf("********************NT头********************
");

        printf("NT:%x
",pNTHeader->Signature);

        pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);
        printf("********************PE头********************
");
        printf("PE:%x
",pPEHeader->Machine);
        printf("节的数量:%x
",pPEHeader->NumberOfSections);
        printf("SizeOfOptionalHeader:%x
",pPEHeader->SizeOfOptionalHeader);
        //可选PE头
        pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
        printf("********************OPTIOIN_PE头********************
");
        printf("OPTION_PE:%x
",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("打开文件失败!
");
        exit(1);
    }

    //获取文件大小
    fseek(PointToFile,0,2);
    FileSize = ftell(PointToFile);

    //重定位指针
    fseek(PointToFile,0,0);

    //buffer指向申请的堆
    StrBuffer = (int*)(malloc(FileSize));
    if (!StrBuffer)
    {
        printf("堆空间分配失败!
");
        free(StrBuffer);
        return 0;
    }

    //读取文件内容
    Num = fread(StrBuffer,FileSize,1,PointToFile);
    if (!Num)
    {
        printf("读取文件内容失败!
");
        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("文件读取失败!
");
        free(PointBuffer);
        return 0;
    }

    //判断是否为MZ标志
    if (*pBuffer != Cmp1)
    {
        printf("不是有效MZ标志!
");
        printf("%X
",*pBuffer);
        free(PointBuffer);
        return 0;
    }
    printf("*********打印DOS头*********
");
    printf("e_magic:			%X
",*(pBuffer));
    printf("e_ifanew:			%08X


",*(PointBuffer+15));

    //判断是否为PE标志
    if (*(PointBuffer+56) != Cmp2)
    {
        printf("不是有效的PE标志!
");
        printf("%X
",*(PointBuffer+56));
        free(PointBuffer);
        return 0;
    }

    printf("*********打印标准PE文件头*********
");

    printf("PE标志:				%X
",*(PointBuffer+56));

    printf("Machine:			%04X
",*(pBuffer+114));
    printf("NumberOfSection:		%04X
",*(pBuffer+115));
    printf("TimeDateStamp:			%08X
",*(PointBuffer+58));
    printf("PointerToSymbolTable:		%08X
",*(PointBuffer+59));
    printf("NumberOfSymbols:		%08X
",*(PointBuffer+60));
    printf("SizeOfOptionalHeader:		%04X
",*(pBuffer+122));
    printf("Chrarcteristics:		%04X


",*(pBuffer+123));

    printf("*********打印标准可选PE头*********
");

    printf("Magic:				%04X
", *(pBuffer+124));
    printf("MajorLinkerVersion:		%02X
", *(pcBuffer+250));
    printf("MinorLinkerVersion:		%02X
", *(pcBuffer+251));
    printf("SizeOfCode:			%08X
", *(PointBuffer+63));
    printf("SizeOfInitializedData:		%08X
", *(PointBuffer+64));
    printf("SizeOfUninitializedData:	%08X
", *(PointBuffer+65));
    printf("AddressOfEntryPoint:		%08X
", *(PointBuffer+66));
    printf("BaseOfCode:			%08X
", *(PointBuffer+67));
    printf("BaseOfData:			%08X
", *(PointBuffer+68));
    printf("ImageBase:			%08X
", *(PointBuffer+69));
    printf("SectionAlignment:		%08X
", *(PointBuffer+70));
    printf("FileAlignment:			%08X
", *(PointBuffer+71));
    printf("MajorOperatingSystemVersion:	%04X
", *(pBuffer+144));
    printf("MinorOperatingSystemVersion:	%04X
", *(pBuffer+145));
    printf("MajorImageVersion:		%04X
", *(pBuffer+146));
    printf("MinorImageVersion:		%04X
", *(pBuffer+147));
    printf("MajorSubsystemVersion:		%04X
", *(pBuffer+148));
    printf("MinorSubsystemVersion:		%04X
", *(pBuffer+149));
    printf("Win32VersionValue:		%08X
", *(PointBuffer+75));
    printf("SizeOfImage:			%08X
", *(PointBuffer+76));
    printf("SizeOfHeaders:			%08X
", *(PointBuffer+77));
    printf("CheckSum:			%08X
", *(PointBuffer+78));
    printf("Subsystem:			%04X
", *(pBuffer+158));
    printf("DllCharacteristics:		%04X
", *(pBuffer+159));
    printf("SizeOfStackReserve:		%08X
", *(PointBuffer+80));
    printf("SizeOfStackCommit:		%08X
", *(PointBuffer+81));
    printf("SizeOfHeapReserve:		%08X
", *(PointBuffer+82));
    printf("SizeOfHeapCommit:		%08X
", *(PointBuffer+83));
    printf("LoaderFlags:			%08X
", *(PointBuffer+84));
    printf("NumberOfRvaAndSizes:		%08X
", *(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("打开文件失败!
");
        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("申请内存失败!
");
        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("从文件向内存中读取数据失败!
");
        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标志!
");
        free(File_buffer);
        return;
    }
    // 强制类型转换 PIMAGE_DOS_HEADER结构体
    pDosHeader = (PIMAGE_DOS_HEADER)File_buffer;
    // 打印DOS头
    printf("=============================DOS头信息如下=============================
");
    printf("MZ标志:			%04X
",pDosHeader->e_magic);
    printf("PE偏移:			%08X
",pDosHeader->e_lfanew);

    // 判断是不是有效的PE标志
    if(*((PDWORD)((DWORD)File_buffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
    {
        printf("不是有效的PE标志!
");
        free(File_buffer);
        return;
    }
    // 强制类型转换 PIMAGE_NT_HEADERS结构体
    pNTHeader = PIMAGE_NT_HEADERS((DWORD)File_buffer+pDosHeader->e_lfanew);
    // 打印NT头
    printf("=============================NT头信息如下===============================
");
    printf("NT:				%04X
",pNTHeader->Signature);
    // 强制类型转换 PIMAGE_FILE_HEADER结构体
    pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader+4);
    // 打印标准PE文件头
    printf("=============================标准PE头信息如下============================
");
    printf("PE_machine:			%04X
",pPEHeader->Machine);
    printf("NumberOfSections:		%04X
",pPEHeader->NumberOfSections);
    printf("SizeOfOptionalHeader:		%04X
",pPEHeader->SizeOfOptionalHeader);
    // 强制类型转换
    pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);//
    // 打印可选PE头
    printf("==============================可选PE头信息如下==============================
");
    printf("Magic:				%04X
",pOptionHeader->Magic);
    printf("AddressOfEntryPoint:		%08X
",pOptionHeader->AddressOfEntryPoint);
    printf("ImageBase:			%08X
",pOptionHeader->ImageBase);
    printf("SizeOfImage:			%08X
",pOptionHeader->SizeOfImage);
    printf("SizeOfHeaders:			%08X
",pOptionHeader->SizeOfHeaders);
    printf("SectionAlignment:		%08X
",pOptionHeader->SectionAlignment);
    printf("FileAlignment:			%08X
",pOptionHeader->FileAlignment);
    // 强制类型转换
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
    printf("==============================节表信息如下===============================
");
    //printf("name:%s
",pSectionHeader->Misc);
    DWORD dwNumberOfSection = pPEHeader->NumberOfSections;
    /*
    printf("%x
",pPEHeader->NumberOfSections);
    printf("IMAGE_SIZEOF_SHORT_NAME:%x
",IMAGE_SIZEOF_SHORT_NAME);
    printf("option_add:%x
",pOptionHeader);
    printf("psection_add:%x
",pSectionHeader);
    printf("==============================================================
");*/
    for(DWORD i = 0;i<dwNumberOfSection;i++,pSectionHeader++)
    {
        printf("========================第%d个节信息:===============================
",i+1);
        printf("section_name:");
        for(DWORD j = 0;j<IMAGE_SIZEOF_SHORT_NAME;j++)
        {
            printf("%c",pSectionHeader->Name[j]);
        }
        printf("
");
        printf("Misc:				%08X
",pSectionHeader->Misc);
        printf("VirtualAddress:			%08X
",pSectionHeader->VirtualAddress);
        printf("SizeOfRawData:			%08X
",pSectionHeader->SizeOfRawData);
        printf("PointerToRawData:		%08X
",pSectionHeader->PointerToRawData);
        printf("Characteristics:		%08X
",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工具,对比如下信息,看是否一致:

使用了第三方工具,确定了信息是一致的!

优化的完美代码打印结果:

迷茫的人生,需要不断努力,才能看清远方模糊的志向!
原文地址:https://www.cnblogs.com/autopwn/p/15200145.html