Created
September 9, 2023 23:40
-
-
Save jhalon/96ec0642014ef8bb75d37ea4cf022aca to your computer and use it in GitHub Desktop.
Simple C Code Example of Parsing x64 PE Files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include "Windows.h" | |
#define RVA2VA(TYPE, BASE, RVA) (TYPE)((ULONG_PTR)BASE + RVA) | |
int main(int argc, char* argv[]) | |
{ | |
char filePath[255] = { 0 }; | |
HANDLE hFile; | |
DWORD nNumberOfBytesToRead, lpNumberOfBytesRead, importDirectoryRVA, thunk; | |
LPVOID lpFileBuffer; | |
PIMAGE_DOS_HEADER dosHeader = { 0 }; | |
PIMAGE_NT_HEADERS ntHeader = { 0 }; | |
PIMAGE_SECTION_HEADER sectionHeader, importSectionHeader = { 0 }; | |
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = { 0 }; | |
PIMAGE_THUNK_DATA thunkData = { 0 }; | |
memcpy_s(&filePath, 255, argv[1], 255); | |
// Acquire file handle to target file | |
hFile = CreateFileA(filePath, GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
if (hFile == INVALID_HANDLE_VALUE) | |
{ | |
printf("[!] Failed to open file handle: (0x%X)\n", GetLastError()); | |
CloseHandle(hFile); | |
exit(1); | |
} | |
// Get file size and allocate data in the heap | |
nNumberOfBytesToRead = GetFileSize(hFile, NULL); | |
lpFileBuffer = HeapAlloc(GetProcessHeap(), 0, nNumberOfBytesToRead); | |
// Read file data and write into our heap buffer | |
if (!ReadFile(hFile, lpFileBuffer, nNumberOfBytesToRead, &lpNumberOfBytesRead, NULL)) | |
{ | |
printf("[!] Failed to read file: (0x%X)\n", GetLastError()); | |
CloseHandle(hFile); | |
exit(1); | |
} | |
// Prase DOS Header | |
dosHeader = (PIMAGE_DOS_HEADER)lpFileBuffer; | |
printf("\n========= DOS HEADER =========\n"); | |
printf("%-38s 0x%X\n", "Magic number", dosHeader->e_magic); | |
printf("%-38s 0x%X\n", "Bytes on last page of file", dosHeader->e_cblp); | |
printf("%-38s 0x%X\n", "Pages in file", dosHeader->e_cp); | |
printf("%-38s 0x%X\n", "Relocations", dosHeader->e_crlc); | |
printf("%-38s 0x%X\n", "Size of header in paragraphs", dosHeader->e_cparhdr); | |
printf("%-38s 0x%X\n", "Minimum extra paragraphs needed", dosHeader->e_minalloc); | |
printf("%-38s 0x%X\n", "Maximum extra paragraphs needed", dosHeader->e_maxalloc); | |
printf("%-38s 0x%X\n", "Initial (relative) SS value", dosHeader->e_ss); | |
printf("%-38s 0x%X\n", "Initial SP value", dosHeader->e_sp); | |
printf("%-38s 0x%X\n", "Checksum", dosHeader->e_csum); | |
printf("%-38s 0x%X\n", "Initial IP value", dosHeader->e_ip); | |
printf("%-38s 0x%X\n", "Initial (relative) CS value", dosHeader->e_cs); | |
printf("%-38s 0x%X\n", "File address of relocation tabl", dosHeader->e_lfarlc); | |
printf("%-38s 0x%X\n", "Overlay Number", dosHeader->e_ovno); | |
printf("%-38s 0x%X\n", "OEM identifier (for e_oeminfo)", dosHeader->e_oemid); | |
printf("%-38s 0x%X\n", "OEM information; e_oemid specific", dosHeader->e_oeminfo); | |
printf("%-38s 0x%X\n", "File address of new exe header", dosHeader->e_lfanew); | |
// Parse NT Header | |
ntHeader = RVA2VA(PIMAGE_NT_HEADERS, lpFileBuffer, dosHeader->e_lfanew); | |
printf("\n========= NT HEADER =========\n"); | |
printf("%-38s 0x%X\n", "Signature", ntHeader->Signature); | |
printf("\n========= FILE HEADER =========\n"); | |
printf("%-38s 0x%X\n", "Machine", ntHeader->FileHeader.Machine); | |
printf("%-38s 0x%X\n", "NumberOfSections", ntHeader->FileHeader.NumberOfSections); | |
printf("%-38s 0x%X\n", "TimeDateStamp", ntHeader->FileHeader.TimeDateStamp); | |
printf("%-38s 0x%X\n", "PointerToSymbolTable", ntHeader->FileHeader.PointerToSymbolTable); | |
printf("%-38s 0x%X\n", "NumberOfSymbols", ntHeader->FileHeader.NumberOfSymbols); | |
printf("%-38s 0x%X\n", "SizeOfOptionalHeader", ntHeader->FileHeader.SizeOfOptionalHeader); | |
printf("%-38s 0x%X\n", "Characteristics", ntHeader->FileHeader.Characteristics); | |
// Prase Optional Header | |
printf("\n========= OPTIONAL HEADER =========\n"); | |
printf("%-38s 0x%X\n", "Magic", ntHeader->OptionalHeader.Magic); | |
printf("%-38s 0x%X\n", "MajorLinkerVersion", ntHeader->OptionalHeader.MajorLinkerVersion); | |
printf("%-38s 0x%X\n", "MinorLinkerVersion", ntHeader->OptionalHeader.MinorLinkerVersion); | |
printf("%-38s 0x%X\n", "SizeOfCode", ntHeader->OptionalHeader.SizeOfCode); | |
printf("%-38s 0x%X\n", "SizeOfInitializedData", ntHeader->OptionalHeader.SizeOfInitializedData); | |
printf("%-38s 0x%X\n", "SizeOfUninitializedData", ntHeader->OptionalHeader.SizeOfUninitializedData); | |
printf("%-38s 0x%X\n", "AddressOfEntryPoint", ntHeader->OptionalHeader.AddressOfEntryPoint); | |
printf("%-38s 0x%X\n", "BaseOfCode", ntHeader->OptionalHeader.BaseOfCode); | |
printf("%-38s 0x%X\n", "ImageBase", ntHeader->OptionalHeader.ImageBase); | |
printf("%-38s 0x%X\n", "SectionAlignment", ntHeader->OptionalHeader.SectionAlignment); | |
printf("%-38s 0x%X\n", "FileAlignment", ntHeader->OptionalHeader.FileAlignment); | |
printf("%-38s 0x%X\n", "MajorOperatingSystemVersion", ntHeader->OptionalHeader.MajorOperatingSystemVersion); | |
printf("%-38s 0x%X\n", "MinorOperatingSystemVersion", ntHeader->OptionalHeader.MinorOperatingSystemVersion); | |
printf("%-38s 0x%X\n", "MajorImageVersion", ntHeader->OptionalHeader.MajorImageVersion); | |
printf("%-38s 0x%X\n", "MinorImageVersion", ntHeader->OptionalHeader.MinorImageVersion); | |
printf("%-38s 0x%X\n", "MajorSubsystemVersion", ntHeader->OptionalHeader.MajorSubsystemVersion); | |
printf("%-38s 0x%X\n", "MinorSubsystemVersion", ntHeader->OptionalHeader.MinorSubsystemVersion); | |
printf("%-38s 0x%X\n", "Win32VersionValue", ntHeader->OptionalHeader.Win32VersionValue); | |
printf("%-38s 0x%X\n", "SizeOfImage", ntHeader->OptionalHeader.SizeOfImage); | |
printf("%-38s 0x%X\n", "SizeOfHeaders", ntHeader->OptionalHeader.SizeOfHeaders); | |
printf("%-38s 0x%X\n", "CheckSum", ntHeader->OptionalHeader.CheckSum); | |
printf("%-38s 0x%X\n", "Subsystem", ntHeader->OptionalHeader.Subsystem); | |
printf("%-38s 0x%X\n", "DllCharacteristics", ntHeader->OptionalHeader.DllCharacteristics); | |
printf("%-38s 0x%X\n", "SizeOfStackReserve", ntHeader->OptionalHeader.SizeOfStackReserve); | |
printf("%-38s 0x%X\n", "SizeOfStackCommit", ntHeader->OptionalHeader.SizeOfStackCommit); | |
printf("%-38s 0x%X\n", "SizeOfHeapReserve", ntHeader->OptionalHeader.SizeOfHeapReserve); | |
printf("%-38s 0x%X\n", "SizeOfHeapCommit", ntHeader->OptionalHeader.SizeOfHeapCommit); | |
printf("%-38s 0x%X\n", "LoaderFlags", ntHeader->OptionalHeader.LoaderFlags); | |
printf("%-38s 0x%X\n", "NumberOfRvaAndSizes", ntHeader->OptionalHeader.NumberOfRvaAndSizes); | |
// Prase Section Header | |
printf("\n========= SECTION HEADERS =========\n"); | |
// Calculate offset to the first section header | |
sectionHeader = RVA2VA(PIMAGE_SECTION_HEADER, lpFileBuffer, dosHeader->e_lfanew + sizeof(IMAGE_NT_SIGNATURE) + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)); | |
// Get the Import Directory RVA | |
importDirectoryRVA = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; | |
// Loop through all our section headers | |
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) | |
{ | |
printf("%s\n", sectionHeader->Name); | |
printf("\t%-38s 0x%X\n", "VirtualSize", sectionHeader->Misc.VirtualSize); | |
printf("\t%-38s 0x%X\n", "VirtualAddress", sectionHeader->VirtualAddress); | |
printf("\t%-38s 0x%X\n", "SizeOfRawData", sectionHeader->SizeOfRawData); | |
printf("\t%-38s 0x%X\n", "PointerToRawData", sectionHeader->PointerToRawData); | |
printf("\t%-38s 0x%X\n", "PointerToRelocations", sectionHeader->PointerToRelocations); | |
printf("\t%-38s 0x%X\n", "PointerToLinenumbers", sectionHeader->PointerToLinenumbers); | |
printf("\t%-38s 0x%X\n", "NumberOfRelocations", sectionHeader->NumberOfRelocations); | |
printf("\t%-38s 0x%X\n", "NumberOfLinenumbers", sectionHeader->NumberOfLinenumbers); | |
printf("\t%-38s 0x%X\n", "Characteristics", sectionHeader->Characteristics); | |
// Locate the RVA of the IAT (Import Address Table) | |
if (sectionHeader->VirtualAddress <= importDirectoryRVA && sectionHeader->VirtualAddress + sectionHeader->SizeOfRawData > importDirectoryRVA) | |
{ | |
importSectionHeader = sectionHeader; | |
} | |
sectionHeader++; | |
} | |
// Prase DLL Imports | |
printf("\n========= DLL IMPORTS =========\n"); | |
// Get IAT offset and set to PIMAGE_IMPORT_DESCRIPTOR | |
importDescriptor = RVA2VA(PIMAGE_IMPORT_DESCRIPTOR, lpFileBuffer, importDirectoryRVA - importSectionHeader->VirtualAddress + importSectionHeader->PointerToRawData); | |
// Use IAT offset to get the raw data pointer for DLL and Function names | |
BYTE* rawOffset = (BYTE*)lpFileBuffer + importSectionHeader->PointerToRawData; | |
while (importDescriptor->Name) | |
{ | |
// Since rawOffset is the address offset to raw IAT data, we take the RVA of the DLL name and subtract that from the VA of the Import Section | |
// We then take the results and add that to the raw IAT offset | |
// This gives us the relative offset of the DLL name in memory which we can then print | |
printf("%s\n", rawOffset + (importDescriptor->Name - importSectionHeader->VirtualAddress)); | |
// To get module names from the DLL, we set our thunk to the OriginalFirstThunk address | |
// This contains an RVA that points to an array of IMAGE_THUNK_DATA | |
thunk = importDescriptor->OriginalFirstThunk; | |
// Using the raw data offset to the IAT we set PIMAGE_THUNK_DATA, which is the import directory entry for the first function name | |
// Again, we need to find the RVA, so we take the address of OriginalFirstThunk, subtract the import section VA, and add to the raw IAT offset | |
thunkData = RVA2VA(PIMAGE_THUNK_DATA, rawOffset, (thunk - importSectionHeader->VirtualAddress)); | |
// Print function names | |
while (thunkData->u1.AddressOfData != 0) | |
{ | |
// Here we take the functions AddressOfData pointer, subtract the section VA, add 2 bytes to the offset, then we add that to the raw IAT offset | |
// This function checks if the AddressData is in range, if not then the function was imported via ordinal | |
if (thunkData->u1.AddressOfData < 0x80000000) | |
{ | |
printf("\t%s\n", rawOffset + (thunkData->u1.Function - importSectionHeader->VirtualAddress + 2)); | |
thunkData++; | |
} | |
} | |
importDescriptor++; | |
} | |
// Cleanup | |
CloseHandle(hFile); | |
SecureZeroMemory(lpFileBuffer, nNumberOfBytesToRead); | |
HeapFree(GetProcessHeap(), NULL, lpFileBuffer); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment