如何从 DOS 头文件访问 PE NT 头文件?

问题描述

我正在尝试将 .exe PE 文件读入内存并访问 NT 标头。我已经可以访问 DOS 标头,但无法从中解析 NT 标头。

这是我目前所拥有的:

static constexpr uint16_t DOS_HDR_MAGIC =           0x5A4D;            // "MZ"
static constexpr uint32_t NT_HDR_MAGIC =            0x00004550;        // "PE\x0\x0"

struct nt_headers_t
{
    uint32_t                    signature;
    file_header_t               file_header;
    optional_header_x64_t       optional_header;
};

struct dos_header_t
{
    uint16_t                    e_magic;
    uint16_t                    e_cblp;
    uint16_t                    e_cp;
    uint16_t                    e_crlc;
    uint16_t                    e_cparhdr;
    uint16_t                    e_minalloc;
    uint16_t                    e_maxalloc;
    uint16_t                    e_ss;
    uint16_t                    e_sp;
    uint16_t                    e_csum;
    uint16_t                    e_ip;
    uint16_t                    e_cs;
    uint16_t                    e_lfarlc;
    uint16_t                    e_ovno;
    uint16_t                    e_res[ 4 ];
    uint16_t                    e_oemid;
    uint16_t                    e_oeminfo;
    uint16_t                    e_res2[ 10 ];
    uint32_t                    e_lfanew;
};

int main(void) {
    std::ifstream input("./stuff.exe",std::ios::in | std::ios::binary );
    input.seekg(0,std::ios::end);
    int file_size = input.tellg();
    input.seekg(0,std::ios::beg);
    std::byte *file = new std::byte[file_size];
    input.read((char *)file,file_size);

    struct dos_header_t *dos_header = (struct dos_header_t *)file;

    assert(dos_header->e_magic == DOS_HDR_MAGIC);

    struct nt_headers_t *nt_headers = (struct nt_headers_t *)file + dos_header->e_lfanew;

    assert(nt_headers->signature == NT_HDR_MAGIC);
}

structure

e_lfanew 应包含 NT 标头开头的地址。我只是将此值添加文件开始:(struct nt_headers_t *)file + dos_header->e_lfanew;

我做错了吗?附图说明 e_lfanew 包含以相反顺序偏移的 NT 标头。我应该如何扭转它?

解决方法

我只是将此值添加到文件开头:datetime 我做错了吗?

是的,但出于一个与 PE 标头无关的“无聊原因”:由于您在添加之前 完成了转换,因此偏移量按 {{1 }}。偏移量需要不缩放添加,所以先添加,然后投射。

附图表示 e_lfanew 包含以相反顺序偏移的 NT 标头。我应该如何扭转它?

它采用 little-endian 字节顺序,您可能在 little-endian 机器上运行代码(现在大多数都是这样),因此您无需执行任何操作,只需读取该值即可正确解释它。