Im trying make my idb beautier. I want to parse the offset to PE structure. Here is some example: My example code

I want to parse (module + 60) to (module_base->e_lfanew) but when i change the type of module_base variable, i got the result. But the problem is the next line, IDA cant convert it to (module_base->OptionalHeader.DataDirectory[0].VirtualAddress).

But if i change the type of module_base to PIMAGE_NT_HEADERS, it look like this: result

Now the first line is wrong. So what can i do to fix the first line. Thanks

  • 2
    can you add definitions of the involved variables (at the start of the function)? Also please try to use code blocks instead of screenshots.
    – Igor Skochinsky
    Dec 24 '21 at 15:04

Declare a new type in the local types window:

union MZ_or_PE {

Then change the type of what you're currently calling module_base to MZ_or_PE. This will allow you to choose which interpretation you want that variable to have at every place where it's used.


The first line can't be changed how you have it. I think you're misunderstanding the layout of the data structures or what your code is doing.

module_base points to the base of the module and assuming it's of PE format, that's where the PE header should be. For backwards compatibility reasons, Microsoft kept the old DOS header as the root data structure. Up there is maintained the tiniest of DOS programs that just prints something like "This program cannot run in DOS". As an aside, you could technically craft your own PE header and include a fully functioning DOS program along side your modern PE program.

The DOS header is defined as IMAGE_DOS_HEADER in winnt.h as so:

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    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
    LONG   e_lfanew;                    // File address of new exe header

Anyway, since DOS is gone for the most part the only useful field up there is a field called e_lfanew (not elfnew). It's an offset to the new NT PE header, hence the name. The comment 'File address of new exe header' is how we know it's an offset. Without going into too much detail, the header doesn't really know where in memory it's going to get loaded. But it does know it's at the top (aka address 0) of the file. So a 'file address' is the distance from the top of the module to where ever you're attempting to go.

The DOS header on its own does not have a field called OptionalHeader therefor you can't module_base->OptionalHeader. OptionalHeader is a field in the new NT Header that e_lfanew points to.

Again in winnt.h the NT header is defined as

typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;

So to get to the NT header you need to do something like PIMAGE_NT_HEADERS nt_headers = (PIMAGE_NT_HEADERS)(module_base + ((PIMAGE_DOS_HEADER)module_base)->e_lfanew);

Reading between the lines it looks like where you're actually trying to go is the IMAGE_DIRECTORY_ENTRY_EXPORT (defined as the 0 index into the DataDirectory, also in winnt.h). So once you have your nt_headers pointer, you can do PIMAGE_EXPORT_DIRECTORY export_dir = (PIMAGE_EXPORT_DIRECTORY)(module_base + nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

All of these data structures are defined in winnt.h but Microsoft doesn't like to document them much online. They kind of talk about the PE format here but it stays general enough to require a lot of reading between the lines. There are plenty of others who have documented parts of this header, but I hesitate to link any without taking the time to thoroughly read through because they're often just out of date enough to be confusing.

Comment to let me know if you need any more clarification.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.