cportable-executable

Getting .text section header of PE file


I am trying to get the header of the .text-section. What seems to work is:

// Get the DOS header.
pDosHeader = (PIMAGE_DOS_HEADER) hMap;

// Get header of first section
pSectionHeader = (PIMAGE_SECTION_HEADER) ((DWORD) hMap + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS));

// Get last section's header.
pLastSectionHeader = pSectionHeader + (pNtHeaders->FileHeader.NumberOfSections - 1);

But I don't exactly get why. I would have thought that the last instruction has to be:

// Get last section's header.
pLastSectionHeader = pSectionHeader + (pNtHeaders->FileHeader.NumberOfSections - 1) * sizeof(IMAGE_SECTION_HEADER);

So that the pointer is moved section-header-wise.

Also, is (pNtHeaders->FileHeader.NumberOfSections - 1) really the .text-section?


Solution

  • The reason that

    pLastSectionHeader = pSectionHeader + (pNtHeaders->FileHeader.NumberOfSections - 1);
    

    is correct, is that pSectionHeader is of type PIMAGE_SECTION_HEADER. When you add an integer value to a pointer in C, the value will first be multiplied by the size of the object pointed to (which is IMAGE_SECTION_HEADER). That's just how pointer arithmetic works!

    You should not make assumptions about the order in which the linker has placed the different sections. It would be better to loop through all the section headers, and e.g. match the name of the section against .text.

    Also, I wouldn't recommend using sizeof(IMAGE_NT_HEADERS) to skip the optional header. This is because the IMAGE_OPTIONAL_HEADER structure (contained in IMAGE_NT_HEADERS) ends with IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];, and you do not actually have any guarantee that all data directories are present in the PE image. A better approach is to read the optional header, and use the NumberOfRvaAndSizes field to determine how many data directories you should skip to find the section header table.

    Finally, I would recommend that you use the IMAGE_XYZ32 and IMAGE_XYZ64 versions of the structures, instead of just IMAGE_XYZ, depending on whether you want to parse PE32 (32-bit) or PE32+ (64-bit). Else, you will default to the architecture size that is default on your system.