Search code examples
c++binaryfilesportable-executable

How to access PE NT headers from DOS headers?


I'm trying to read a .exe PE file into memory and access NT headers. I can already access DOS headers but can't resolve NT headers from it.

Here's what I have so far:

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 should contain address to the start of NT headers. I simply add this value to file start: (struct nt_headers_t *)file + dos_header->e_lfanew;

Am I doing that wrong? Attached picture says that e_lfanew contains the NT headers offset in reverse order. How should I reverse it?


Solution

  • I simply add this value to file start: (struct nt_headers_t *)file + dos_header->e_lfanew; Am I doing that wrong?

    Yes, but for a "boring reason" that has nothing to do with PE headers: since you've done the cast before the addition, the offset is scaled by the size of nt_headers_t. The offset needs to be added unscaled, so add it first, and cast afterwards.

    Attached picture says that e_lfanew contains the NT headers offset in reverse order. How should I reverse it?

    It's in little-endian byte order, you're probably running the code on a little-endian machine (that's most of them nowadays) so you don't need to do anything, just reading the value will interpret it correctly.