6

I'm implementing a software for performing some PE classification. Among the features values I'm gathering from each PE are, the amount of sections, the name of sections, image section headers.

I have been reading about ImageHlp Structures. But there is no example on how to get those struct initialized from a file/path_to_a_file you pass. I'm specially interested in the IMAGE_SECTION_HEADER structure.

How can I get the IMAGE_SECTION_HEADERs form an executable file programatically?

1
  • 1
    The PE format is well documented. Parse the file manually. This page can serve as a reference although the examples are in python. Other than this you can use a prebuilt parser library such as pe-parse
    – 0xec
    Aug 15, 2014 at 15:25

1 Answer 1

9

I have extensive experience with parsing the PE on Windows, mainly for use in function interception. Here are the steps you should follow to achieve your goal.

The first step is to find the base address of the image loaded into memory. This step will be different depending on if the executable has or hasn't been mapped into memory, but the basic idea will be the same. Assuming that you are interested in doing this for a file on disk, then you may do this with the file mapping API, if you would prefer to implement this on an executable loaded into memory as a running process, you can achieve the equivalent by using the the tool help snapshot API. The base address will be the same as the field hModule in the MODULEENTRY32 data retrieved from the snapshot. For more information about what a module handle is, see here.

Once you have completed the first step, the structure at the base address is the IMAGE_DOS_HEADER, while this is not documented on MSDN, it has two very important but cryptic fields. The two fields you will need to know are the e_magic field and the e_lfanew field. The e_magic field contains a double word for 32-bit or quadruple for 64-bit that allows you to test if the file being read or your implementation is correctly formatted with the correct value being defined as IMAGE_DOS_SIGNATURE, which is the ASCII-Z string of "MZ\0". The e_lfanew field contains a relative virtual address which you need to add to the image base you found in step one to calculate the virtual address of the IMAGE_NT_HEADERS structure.

The third step will be to check the first member of the IMAGE_NT_HEADERS to see if it is an actual PE file, this will be defined by the Signature field of the structure, and the defined constant to test for will be IMAGE_NT_SIGNATURE. This is not typically nessecary, since most Windows versions will be using the PE format of executable file, but it's good practice in order to ensure your code is a bit more robust.

Once everything checks out and you have performed steps 1-3, step four will be when you can finally calculate the address of the IMAGE_SECTION_HEADER structures. The IMAGE_SECTION_HEADER structures are stored as an array in the file, so to obtain the size of the array, you will need to use the NumberOfSections member in the IMAGE_FILE_HEADER structure which is nested in the IMAGE_NT_HEADERS structure mentioned above.Once you have that value, you may find the virtual address of the first IMAGE_SECTION_HEADER by adding the size of the Signature member of the IMAGE_NT_HEADERS structure, the size of the FileHeader member in the IMAGE_NT_HEADERS structure, and finally the SizeOfOptionalHeader value in the IMAGE_FILE_HEADER structure. The reason you can't simply do a sizeof( IMAGE_OPTIONAL_HEADER ) for the last value in the formula listed above is because a file on disk will not have the IMAGE_OPTIONAL_HEADER, so to obtain the proper size dynamically, you should do so through the structure member I mentioned earlier.

Now you may simply copy the array of IMAGE_SECTION_HEADER structures from memory any way you please. Just remember, that these structures are contigous in memory, so all you need to do is multiply the size of each structure by the number of sections to find the total size of the entire array in memory. After you have calculate that value, it will be trivial to collect the data.

For more resources on the PE executable format, see this wonderful article written by Matt Pietrek, Peering Inside the PE. You may also take a look at the official specification here.

5
  • 5
    Nice effort but many small issues. 1) DOS header's e_magic is always a WORD (16-bit), never 32 or 64 bits; 2) IMAGE_DOS_SIGNATURE matches the two-character "MZ" sequence - zero terminator is not required; 3) strictly speaking, e_lfanew is a file offset and not RVA (though they happen to match in this case); 4) IMAGE_OPTIONAL_HEADER definitely is present in the file on disk, it does not appear magically just because you use file mapping; 5) the reason you need to use SizeOfOptionalHeader is because some malware/packers use a value different from sizeof(IMAGE_OPTIONAL_HEADER).
    – Igor Skochinsky
    Aug 15, 2014 at 23:05
  • @Igor Oops, I was thinking of an object file. My mistake. Aug 15, 2014 at 23:12
  • The "file mapping API" mentioned is for Windows only. The PE format is described in detail in various sources, and there is no actual need to use Windows API's - for example, I wrote my latest PE disassembler from scratch on my Mac.
    – Jongware
    Aug 15, 2014 at 23:13
  • 3
    @Jongware Well, if he wants, he can always use some third party library to parse the PE in a more portable fashion. Windows is the only operating system I use, so if someone wants to give an answer for other systems, feel free to. The references he posted were for Windows implementations, so I answered the question as such. Aug 15, 2014 at 23:18
  • Yes, you're correct, the OP indeed appears to be using Windows. My load_exec source code is (.. checking) close to 300 lines of code, and it makes use of another 250-or-so lines of structures and #define's. All in all, it takes about 2,000 lines of C code to only load an executable (mind, it displays generous amounts of debugging data as well). I would hazard that's way too much for an RE answer.
    – Jongware
    Aug 15, 2014 at 23:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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