«

PE文件详解 - 区段表

ljierui 发布于 阅读:90 技术杂谈


1、区段表

#include <Windows.h>

// 获取PE文件中第一个节(section)的指针
PIMAGE_SECTION_HEADER GetFirstSection(PIMAGE_NT_HEADERS pNTHeaders) {
    // 使用IMAGE_FIRST_SECTION宏计算第一个节的指针
    PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNTHeaders);

    // 返回第一个节的指针
    return pSectionHeader;
}

1.1、IMAGE_SECTION_HEADER结构

typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]; //区段名
    union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
    } Misc;
    DWORD   VirtualAddress; // 区段大小
    DWORD   SizeOfRawData;  // 区段的RAV地址
    DWORD   PointerToRawData; 
    DWORD   PointerToRelocations; // 区段在文件中的偏移
    DWORD   PointerToLinenumbers;
    WORD    NumberOfRelocations;
    WORD    NumberOfLinenumbers;
    DWORD   Characteristics; // 区段的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

1.2、区段对齐

1.3、通过代码读取区段表

#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);

    // 读取DOS头
    // 定义指向 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);

    // PE头
    // 要注意你读取的EXE是32位还是64位的,64位要使用 PIMAGE_NT_HEADERS64
    printf("PE Header Info:\n");
    PIMAGE_NT_HEADERS ReadNTHeaders;
    // 因为要跳过DOS头,读取到PE开头,所以要加e_lfanew
    ReadNTHeaders = (PIMAGE_NT_HEADERS)(buffer + ReadDosHeader->e_lfanew);
    printf("PE标志位:%x\n", ReadNTHeaders->Signature);
    // 打印标准PE头里的东西
    printf("运行平台:%x\n", ReadNTHeaders->FileHeader.Machine);
    // 打印拓展PE头里的东西
    printf("ImageBase: %x\n",ReadNTHeaders->OptionalHeader.ImageBase);

    // 区段解析遍历
    printf("Section Header Info:\n");
    // Windows定义了宏来解析区段,使用IMAGE_FIRST_SECTION 
    // 定位到区段表
    PIMAGE_SECTION_HEADER ReadSectionHeader = IMAGE_FIRST_SECTION(ReadNTHeaders);
    // 在标准PE头中有个字段可以遍历区段的数量,在 PIMAGE_FILE_HEADER中的NumberOfSections字段
    PIMAGE_FILE_HEADER pFileHeader = &ReadNTHeaders->FileHeader;
    for (int i = 0; i < pFileHeader->NumberOfSections; i++)
    {
        printf("Name(区段名称):%s\n", ReadSectionHeader[i].Name);
        printf("VOffset(起始相对虚拟地址):%08X\n", ReadSectionHeader[i].VirtualAddress);
        printf("VSize(区段大小):%08X\n", ReadSectionHeader[i].SizeOfRawData);
        printf("ROffset(文件偏移):%08X\n", ReadSectionHeader[i].PointerToRawData);
        printf("RSize(文件中区段大小):%08X\n", ReadSectionHeader[i].Misc.VirtualSize);
        printf("标记(区段的属性):%08X\n\n", ReadSectionHeader[i].Characteristics);
    }

    // 释放动态内存
    free(buffer);
    // 关闭文件
    fclose(pFile);
    return 0;

}

PE文件

推荐阅读: