Skip to content

Instantly share code, notes, and snippets.

@jhalon
Created September 9, 2023 23:40
Show Gist options
  • Save jhalon/96ec0642014ef8bb75d37ea4cf022aca to your computer and use it in GitHub Desktop.
Save jhalon/96ec0642014ef8bb75d37ea4cf022aca to your computer and use it in GitHub Desktop.
Simple C Code Example of Parsing x64 PE Files
#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