Extended PE header of PE file (IMAGE_OPTIONAL_HEADER)

Optional / extended PE header IMAGE_OPTIONAL_HEADER, it has more advantages than Standard PE header (IMAGE_FILE_HEADER) More

IMAGE_OPTIONAL_HEADER

Structure and members have the following meanings:

//Size: 32bit(0xE0) 64bit(0xF0)

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

typedef struct _IMAGE_OPTIONAL_HEADER {

    WORD    Magic;                          //File type: 10bh is a 32-bit PE file / 20bh is a 64 bit PE file
    BYTE    MajorLinkerVersion;             //The linker (Major) version number has no effect on execution
    BYTE    MinorLinkerVersion;             //The linker (minor) version number has no effect on execution
    DWORD   SizeOfCode;                     //The total size of the section containing the code. The size of the file after alignment. It is useless for the compiler to fill in
    DWORD   SizeOfInitializedData;          //The total size of sections containing initialized data. The size after file alignment. It is useless for the compiler to fill in
    DWORD   SizeOfUninitializedData;        //The total size of sections containing uninitialized data. The size after file alignment. It is useless for the compiler to fill in. (uninitialized data does not occupy space in the file; however, after being loaded into memory, the PE loader will allocate an appropriate size of virtual address space for these data)
    DWORD   AddressOfEntryPoint;            //Procedure access (RVA)
    DWORD   BaseOfCode;                     //The base address (RVA) of the code section. It is useless for the compiler to fill in (the RVA at the beginning of the code section indicates the offset address of the beginning of the code section relative to ImageBase when the image is loaded into memory, and the section name is usually ". text")
    DWORD   BaseOfData;                     //The base address (RVA) of the data section. It is useless for the compiler to fill in (the RVA at the beginning of the data section indicates the offset address of the beginning of the data section relative to ImageBase when the image is loaded into memory, and the section name is usually ". Data")
    DWORD   ImageBase;                      //Memory mirror base address
    DWORD   SectionAlignment;               //Memory alignment size
    DWORD   FileAlignment;                  //File alignment size
    WORD    MajorOperatingSystemVersion;    //Identifies the major version number of the operating system 
    WORD    MinorOperatingSystemVersion;    //Identifies the minor version number of the operating system 
    WORD    MajorImageVersion;              //The major version number of the PE file itself
    WORD    MinorImageVersion;              //The minor version number of the PE file itself
    WORD    MajorSubsystemVersion;          //Major version number of subsystem required for operation
    WORD    MinorSubsystemVersion;          //Subsystem minor version number required for operation
    DWORD   Win32VersionValue;              //The value of subsystem version must be 0, otherwise the program will fail
    DWORD   SizeOfImage;                    //The mapping size of the entire PE file in memory. It can be larger than the actual value. It must be an integer multiple of SectionAlignment
    DWORD   SizeOfHeaders;                  //All header + section tables are aligned according to the size of the file
    DWORD   CheckSum;                       //Checksum. For most PE files, the value is 0. In kernel mode drivers and system DLLs, the value must exist and be correct. In IMAGEHLP.DLL, the function CheckSumMappedFile is used to calculate the checksum of the file header. For the whole PE file, there is also a verification function MapFileAndCheckSum
    WORD    Subsystem;                      //File subsystem driver (1) graphical interface (2) console / DLL(3)
    WORD    DllCharacteristics;             //File attribute. Is not for DLL files
    DWORD   SizeOfStackReserve;             //Stack size reserved during initialization. The default value of this field is 0x100000(1MB). If the stack parameter size is NULL when calling API function CreateThread, the created stack size will be 1MB
    DWORD   SizeOfStackCommit;              //The stack size actually submitted during initialization. Ensure the actual memory space occupied by the stack of the initial thread, which is submitted by the system. These submitted stacks do not exist in the exchange file, but in memory
    DWORD   SizeOfHeapReserve;              //The size of the heap reserved during initialization. It is used to reserve the virtual memory used by the initial process heap. The handle of this heap can be obtained by calling the function GetProcessHeap. Each process will have at least one default process heap, which is created when the process is started and will not be deleted during the life of the process. The default value is 1MB
    DWORD   SizeOfHeapCommit;               //The heap size submitted during initialization. The memory space occupied by the heap set during process initialization. The default value is PAGE_SIZE. 
    DWORD   LoaderFlags;                    //Commissioning related
    DWORD   NumberOfRvaAndSizes;            //The number of directory entries. The default is 10h
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//The number of array elements of the structure array is determined by IMAGE_NUMBEROF_DIRECTORY_ENTRIES definition
} IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32;

Locate image via WinHex_ OPTIONAL_ HEADER:

Magic: magic word, indicating the PE file type, as shown in the following table:

constant symbols constant value
IMAGE_NT_OPTIONAL_HDR32_MAGIC0x10bPE32
IMAGE_NT_OPTIONAL_HDR64_MAGIC0x20bPE64
IMAGE_ROM_OPTIONAL_HDR_MAGIC0x107ROM

SizeOfCode: represents the sum of all code sections (in bytes). The size is based on the size of the file after alignment. (the method to judge whether a section contains code is based on whether the section attribute contains the IMAGE_SCN_CNT_CODE flag)

AddressOfEntryPoint: indicates the entry address of the program. This value is an RVA, and the Imagebase is the entry address (OEP) of the program in memory. If you attach a piece of your own code to an executable file and want this code to be executed first, you need to modify the value here to point to your own code location. For ordinary programs, it is the start address; For the device driver, it is the address of the initialization function. The entry point is optional for the DLL. If there is no entry point, this field must be set to 0

ImageBase: indicates the starting address of PE file priority loading in memory. If this address has been occupied, the operating system will reallocate it (this happens in DLL). At this time, it needs to relocate the data provided by the table to repair it. This will not happen when EXE is loaded first. Most EXE imagebase s default to 0x400000. This value can be modified freely, It must not exceed the virtual address space and must be an integer multiple of 64KB. It can run normally after repairing the relocation data

Open the process observation entry point using OD / DBG

The entry point is ImageBase(0x00400000) + AddressOfEntryPoint(0x00015757)

SectionAlignment:   Section alignment granularity in memory. This field specifies the alignment unit after the section is loaded into memory. The page size of Win32 is 4KB, so the memory alignment granularity of sections in Win32 PE files is generally 4KB, expressed in hexadecimal as 1000h.SectionAlignment must be greater than or equal to FileAlignment. When it is less than the system page size, it must ensure that sectionalignment and FileAlignment are equal

FileAlignment:   The section alignment granularity in the file and the section alignment granularity in the Win32PE file are generally 200H. Windows will choose to use the size of 512 bytes (the size of a physical sector)

SizeOfImage: represents the mapping size of the entire PE file in memory. It must be an integer multiple of SectionAlignment. (this value can be larger than the actual value, but not smaller than it)

SizeOfHeaders: indicates the size of all header + section tables in the PE file aligned according to the file:

The code is calculated as follows:

int main()
{
  //Read file binary data
  DWORD dwFileSize = 0;
  PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);

  PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
  PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)(pFileBuffer + pDos->e_lfanew);
  PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);


  DWORD SizeOfHeader =  pDos->e_lfanew/*Including IMAGE_DOS_HEADERS + IMAGE_DOS_STUB*/ + \
              4/*IMAGE_NT_HEADER -> Signature*/ + \
              IMAGE_SIZEOF_FILE_HEADER/*IMAGE_FILE_HEADER Structure size*/ + \
              pFil->SizeOfOptionalHeader/*IMAGE_OPTIONAL_HEADER Structure size*/ + \
              pFil->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)/*All images_ SECTION_ Header structure size*/;
  //After that, the size of SizeOfHeader can be calculated according to the file alignment               
  return 0;
}

Subsystem: subsystem ID, defined as follows:

constant symbols constant valueConstant meaning
IMAGE_SUBSYSTEM_UNKNOWN0Unknown subsystem
IMAGE_SUBSYSTEM_NATIVE1Device drivers and Native Windows processes
IMAGE_SUBSYSTEM_WINDOWS_GUI2Windows graphical user interface
IMAGE_SUBSYSTEM_WINDOWS_CUI3Windows console
IMAGE_SUBSYSTEM_OS2_CUI5OS/2 console
IMAGE_SUBSYSTEM_POSIX_CUI7Posix console
IMAGE_SUBSYSTEM_NATIVE_WINDOWS8Windows 9x driver
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI9Windows CE graphical interface
IMAGE_SUBSYSTEM_EFI_APPLICATION10Extensible firmware interface EFI application
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER11EFI driver with boot service
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER12EFI driver with runtime service
IMAGE_SUBSYSTEM_EFI_ROM13EFI ROM image
IMAGE_SUBSYSTEM_XBOX14X- Box
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION16
IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG17

Subsystem = 0x0002(Windows graphical user interface)

DllCharacteristics:   File attributes, each meaning is as follows:

Data bitconstant symbols constant valueMeaning when is 1
0IMAGE_LIBRARY_PROCESS_INIT0x0001Reservation must be 0
1IMAGE_LIBRARY_PROCESS_TERM0x0002Reservation must be 0
2IMAGE_LIBRARY_THREAD_INIT0x0004Reservation must be 0
3IMAGE_LIBRARY_THREAD_TERM0x0008Reservation must be 0
5IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA0x0020image can handle a high entropy 64-bit virtual address space.
6IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE0x0040DLL s can be relocated when loaded
7IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY0x0080Force code integrity verification
8IMAGE_DLLCHARACTERISTICS_NX_COMPAT0x0100The image is DEP compatible
9IMAGE_DLLCHARACTERISTICS_NO_ISOLATION0x0200You can isolate, but not the secondary image
10IMAGE_DLLCHARACTERISTICS_NO_SEH0x0400Image does not use SEH
11IMAGE_DLLCHARACTERISTICS_NO_BIND0x0800Do not bind image
12IMAGE_DLLCHARACTERISTICS_APPCONTAINER0x1000Reservation must be 0
13IMAGE_DLLCHARACTERISTICS_WDM_DRIVER0x2000The image is a WDM Driver
14IMAGE_DLLCHARACTERISTICS_GUARD_CF0x4000Reservation must be 0
15IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE0x8000Available for terminal server

The DataDirectory: structure represents the information of each directory item in the PE file. It is an array. The structure is defined as follows:

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress; //The directory item corresponds to RVA(VirtualAddress + Imagebase is the starting address of the directory item in memory) PS (the value of this field in the attribute certificate data represents FOA)
    DWORD   Size;           //Directory entries for size
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

Constant definitionconstant valuemeaning
IMAGE_DIRECTORY_ENTRY_EXPORT0Export table
IMAGE_DIRECTORY_ENTRY_IMPORT1Import table
IMAGE_DIRECTORY_ENTRY_RESOURCE2resources
IMAGE_DIRECTORY_ENTRY_EXCEPTION3abnormal
IMAGE_DIRECTORY_ENTRY_SECURITY4Security certificate
IMAGE_DIRECTORY_ENTRY_BASERELOC5Relocation table
IMAGE_DIRECTORY_ENTRY_DEBUG6debug information
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE7copyright
IMAGE_DIRECTORY_ENTRY_GLOBALPTR8Global pointer
IMAGE_DIRECTORY_ENTRY_TLS9TLS table
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG10load configuration
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT11Binding import
IMAGE_DIRECTORY_ENTRY_IAT12IAT table
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT13Delay import
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR14COM

Code location image_ OPTIONAL_ The header is as follows:

Read file code:

PVOID FileToMem(IN PCHAR szFilePath, OUT LPDWORD dwFileSize)
{
	//Open file
	FILE* pFile = fopen(szFilePath, "rb");
	if (!pFile)
	{
		printf("FileToMem fopen Fail \r\n");
		return NULL;
	}
 
	//Get file length
	fseek(pFile, 0, SEEK_END);			//SEEK_END file end
	DWORD Size = ftell(pFile);
	fseek(pFile, 0, SEEK_SET);			//SEEK_ Start of set file
 
	//Request to store file data buffer
	PCHAR pFileBuffer = (PCHAR)malloc(Size);
	if (!pFileBuffer)
	{
		printf("FileToMem malloc Fail \r\n");
		fclose(pFile);
		return NULL;
	}
 
	//Read file data
	fread(pFileBuffer, Size, 1, pFile);
 
	//Determine whether it is an executable file
	if (*(PSHORT)pFileBuffer != IMAGE_DOS_SIGNATURE)
	{
		printf("Error: MZ \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (*(PDWORD)(pFileBuffer + *(PDWORD)(pFileBuffer + 0x3C)) != IMAGE_NT_SIGNATURE)
	{
		printf("Error: PE \r\n");
		fclose(pFile);
		free(pFileBuffer);
		return NULL;
	}
 
	if (dwFileSize)
	{
		*dwFileSize = Size;
	}
 
	fclose(pFile);
 
	return pFileBuffer;
}

Read extended PE header data code

VOID PrintOpoHeader()
{
	//Read file binary data
	DWORD dwFileSize = 0;
	PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
	if (!pFileBuffer)
	{
		return;
	}

	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)(pFileBuffer + pDos->e_lfanew);
	PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
	PIMAGE_OPTIONAL_HEADER pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);

	printf("IMAGE_OPTIONAL_HEADER->Magic  [0x%04x] \r\n", pOpo->Magic);
	printf("IMAGE_OPTIONAL_HEADER->MajorLinkerVersion  [0x%02x] \r\n", pOpo->MajorLinkerVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorLinkerVersion  [0x%02x] \r\n", pOpo->MinorLinkerVersion);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfCode  [0x%08x] \r\n", pOpo->SizeOfCode);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfInitializedData  [0x%08x] \r\n", pOpo->SizeOfInitializedData);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfUninitializedData  [0x%08x] \r\n", pOpo->SizeOfUninitializedData);
	printf("IMAGE_OPTIONAL_HEADER->AddressOfEntryPoint  [0x%08x] \r\n", pOpo->AddressOfEntryPoint);
	printf("IMAGE_OPTIONAL_HEADER->BaseOfCode  [0x%08x] \r\n", pOpo->BaseOfCode);
	printf("IMAGE_OPTIONAL_HEADER->BaseOfData  [0x%08x] \r\n", pOpo->BaseOfData);
	printf("IMAGE_OPTIONAL_HEADER->ImageBase  [0x%08x] \r\n", pOpo->ImageBase);
	printf("IMAGE_OPTIONAL_HEADER->SectionAlignment  [0x%08x] \r\n", pOpo->SectionAlignment);
	printf("IMAGE_OPTIONAL_HEADER->FileAlignment  [0x%08x] \r\n", pOpo->FileAlignment);
	printf("IMAGE_OPTIONAL_HEADER->MajorOperatingSystemVersion  [0x%04x] \r\n", pOpo->MajorOperatingSystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorOperatingSystemVersion  [0x%04x] \r\n", pOpo->MinorOperatingSystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->MajorImageVersion  [0x%04x] \r\n", pOpo->MajorImageVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorImageVersion  [0x%04x] \r\n", pOpo->MinorImageVersion);
	printf("IMAGE_OPTIONAL_HEADER->MajorSubsystemVersion  [0x%04x] \r\n", pOpo->MajorSubsystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->MinorSubsystemVersion  [0x%04x] \r\n", pOpo->MinorSubsystemVersion);
	printf("IMAGE_OPTIONAL_HEADER->Win32VersionValue  [0x%08x] \r\n", pOpo->Win32VersionValue);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfImage  [0x%08x] \r\n", pOpo->SizeOfImage);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfHeaders  [0x%08x] \r\n", pOpo->SizeOfHeaders);
	printf("IMAGE_OPTIONAL_HEADER->CheckSum  [0x%08x] \r\n", pOpo->CheckSum);
	printf("IMAGE_OPTIONAL_HEADER->Subsystem  [0x%04x] \r\n", pOpo->Subsystem);
	printf("IMAGE_OPTIONAL_HEADER->DllCharacteristics  [0x%04x] \r\n", pOpo->DllCharacteristics);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfStackReserve  [0x%08x] \r\n", pOpo->SizeOfStackReserve);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfStackCommit  [0x%08x] \r\n", pOpo->SizeOfStackCommit);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfHeapReserve  [0x%08x] \r\n", pOpo->SizeOfHeapReserve);
	printf("IMAGE_OPTIONAL_HEADER->SizeOfHeapCommit  [0x%08x] \r\n", pOpo->SizeOfHeapCommit);
	printf("IMAGE_OPTIONAL_HEADER->LoaderFlags  [0x%08x] \r\n", pOpo->LoaderFlags);
	printf("IMAGE_OPTIONAL_HEADER->NumberOfRvaAndSizes  [0x%08x] \r\n", pOpo->NumberOfRvaAndSizes);

	PIMAGE_DATA_DIRECTORY pDir = pOpo->DataDirectory;
	for (size_t i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
	{
		printf("IMAGE_DATA_DIRECTORY[%02d]->VirtualAddress  [0x%08x] ->Size [0x%08x] \r\n", i, pDir[i].VirtualAddress, pDir[i].Size);
	}
}

Tags: C C++ data structure security security hole

Posted on Tue, 26 Oct 2021 04:59:33 -0400 by nitestryker