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_MAGIC | 0x10b | PE32 |
IMAGE_NT_OPTIONAL_HDR64_MAGIC | 0x20b | PE64 |
IMAGE_ROM_OPTIONAL_HDR_MAGIC | 0x107 | ROM |
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 value | Constant meaning |
---|---|---|
IMAGE_SUBSYSTEM_UNKNOWN | 0 | Unknown subsystem |
IMAGE_SUBSYSTEM_NATIVE | 1 | Device drivers and Native Windows processes |
IMAGE_SUBSYSTEM_WINDOWS_GUI | 2 | Windows graphical user interface |
IMAGE_SUBSYSTEM_WINDOWS_CUI | 3 | Windows console |
IMAGE_SUBSYSTEM_OS2_CUI | 5 | OS/2 console |
IMAGE_SUBSYSTEM_POSIX_CUI | 7 | Posix console |
IMAGE_SUBSYSTEM_NATIVE_WINDOWS | 8 | Windows 9x driver |
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI | 9 | Windows CE graphical interface |
IMAGE_SUBSYSTEM_EFI_APPLICATION | 10 | Extensible firmware interface EFI application |
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER | 11 | EFI driver with boot service |
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER | 12 | EFI driver with runtime service |
IMAGE_SUBSYSTEM_EFI_ROM | 13 | EFI ROM image |
IMAGE_SUBSYSTEM_XBOX | 14 | X- Box |
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION | 16 | |
IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG | 17 |
Subsystem = 0x0002(Windows graphical user interface)
DllCharacteristics: File attributes, each meaning is as follows:
Data bit | constant symbols | constant value | Meaning when is 1 |
---|---|---|---|
0 | IMAGE_LIBRARY_PROCESS_INIT | 0x0001 | Reservation must be 0 |
1 | IMAGE_LIBRARY_PROCESS_TERM | 0x0002 | Reservation must be 0 |
2 | IMAGE_LIBRARY_THREAD_INIT | 0x0004 | Reservation must be 0 |
3 | IMAGE_LIBRARY_THREAD_TERM | 0x0008 | Reservation must be 0 |
5 | IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA | 0x0020 | image can handle a high entropy 64-bit virtual address space. |
6 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE | 0x0040 | DLL s can be relocated when loaded |
7 | IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY | 0x0080 | Force code integrity verification |
8 | IMAGE_DLLCHARACTERISTICS_NX_COMPAT | 0x0100 | The image is DEP compatible |
9 | IMAGE_DLLCHARACTERISTICS_NO_ISOLATION | 0x0200 | You can isolate, but not the secondary image |
10 | IMAGE_DLLCHARACTERISTICS_NO_SEH | 0x0400 | Image does not use SEH |
11 | IMAGE_DLLCHARACTERISTICS_NO_BIND | 0x0800 | Do not bind image |
12 | IMAGE_DLLCHARACTERISTICS_APPCONTAINER | 0x1000 | Reservation must be 0 |
13 | IMAGE_DLLCHARACTERISTICS_WDM_DRIVER | 0x2000 | The image is a WDM Driver |
14 | IMAGE_DLLCHARACTERISTICS_GUARD_CF | 0x4000 | Reservation must be 0 |
15 | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE | 0x8000 | Available 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 definition | constant value | meaning |
---|---|---|
IMAGE_DIRECTORY_ENTRY_EXPORT | 0 | Export table |
IMAGE_DIRECTORY_ENTRY_IMPORT | 1 | Import table |
IMAGE_DIRECTORY_ENTRY_RESOURCE | 2 | resources |
IMAGE_DIRECTORY_ENTRY_EXCEPTION | 3 | abnormal |
IMAGE_DIRECTORY_ENTRY_SECURITY | 4 | Security certificate |
IMAGE_DIRECTORY_ENTRY_BASERELOC | 5 | Relocation table |
IMAGE_DIRECTORY_ENTRY_DEBUG | 6 | debug information |
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE | 7 | copyright |
IMAGE_DIRECTORY_ENTRY_GLOBALPTR | 8 | Global pointer |
IMAGE_DIRECTORY_ENTRY_TLS | 9 | TLS table |
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG | 10 | load configuration |
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT | 11 | Binding import |
IMAGE_DIRECTORY_ENTRY_IAT | 12 | IAT table |
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT | 13 | Delay import |
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR | 14 | COM |
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); } }