mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-08 16:37:12 +00:00
PE parser: added a feature to parse a PE directly from kernel memory
Could be used in the future to resolve export instead of a
suspicious LoadLibrary("ntoskrnl.exe")
This commit is contained in:
@@ -22,10 +22,17 @@ typedef struct PE_codeview_debug_info_t {
|
|||||||
CHAR pdbName[1];
|
CHAR pdbName[1];
|
||||||
} PE_codeview_debug_info;
|
} PE_codeview_debug_info;
|
||||||
|
|
||||||
|
typedef VOID(*kernel_read_memory_func) (DWORD64 Address, PVOID Buffer, SIZE_T Size);
|
||||||
|
|
||||||
typedef struct PE_pointers {
|
typedef struct PE_pointers {
|
||||||
BOOL isMemoryMapped;
|
BOOL isMemoryMapped;
|
||||||
|
|
||||||
BOOL isInAnotherAddressSpace;
|
BOOL isInAnotherAddressSpace;
|
||||||
HANDLE hProcess;
|
HANDLE hProcess;
|
||||||
|
|
||||||
|
BOOL isInKernelLand;
|
||||||
|
kernel_read_memory_func kernel_read;
|
||||||
|
|
||||||
PVOID baseAddress;
|
PVOID baseAddress;
|
||||||
//headers ptrs
|
//headers ptrs
|
||||||
IMAGE_DOS_HEADER* dosHeader;
|
IMAGE_DOS_HEADER* dosHeader;
|
||||||
@@ -49,6 +56,7 @@ typedef struct PE_pointers {
|
|||||||
|
|
||||||
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped);
|
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped);
|
||||||
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase);
|
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase);
|
||||||
|
PE* PE_create_from_kernel(PVOID imageBase, kernel_read_memory_func ReadPrimitive);
|
||||||
PVOID PE_RVA_to_Addr(PE* pe, DWORD rva);
|
PVOID PE_RVA_to_Addr(PE* pe, DWORD rva);
|
||||||
DWORD PE_Addr_to_RVA(PE* pe, PVOID addr);
|
DWORD PE_Addr_to_RVA(PE* pe, PVOID addr);
|
||||||
IMAGE_SECTION_HEADER* PE_sectionHeader_fromRVA(PE* pe, DWORD rva);
|
IMAGE_SECTION_HEADER* PE_sectionHeader_fromRVA(PE* pe, DWORD rva);
|
||||||
|
|||||||
+115
-82
@@ -177,80 +177,105 @@ VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID PE_read(PE* pe, LPCVOID address, SIZE_T size, PVOID buffer) {
|
||||||
|
if (pe->isInAnotherAddressSpace) {
|
||||||
|
ReadProcessMemory(pe->hProcess, address, buffer, size, NULL);
|
||||||
|
}
|
||||||
|
else if (pe->isInKernelLand) {
|
||||||
|
pe->kernel_read((DWORD64) address, buffer, size);
|
||||||
|
} else {
|
||||||
|
memcpy(buffer, address, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PE_ReadMemoryType(TYPE) \
|
||||||
|
TYPE PE_ ## TYPE ## (PE* pe, LPCVOID address) {\
|
||||||
|
TYPE res;\
|
||||||
|
PE_read(pe, address, sizeof(TYPE), &res);\
|
||||||
|
return res;\
|
||||||
|
}
|
||||||
|
PE_ReadMemoryType(BYTE);
|
||||||
|
PE_ReadMemoryType(WORD);
|
||||||
|
PE_ReadMemoryType(DWORD);
|
||||||
|
PE_ReadMemoryType(DWORD64);
|
||||||
|
|
||||||
|
#define PE_ArrayType(TYPE) \
|
||||||
|
TYPE PE_ ## TYPE ## _Array(PE* pe, PVOID address, SIZE_T index) {\
|
||||||
|
return PE_ ## TYPE ## (pe, (PVOID)(((intptr_t)address)+index*sizeof(TYPE)));\
|
||||||
|
}
|
||||||
|
PE_ArrayType(BYTE);
|
||||||
|
PE_ArrayType(WORD);
|
||||||
|
PE_ArrayType(DWORD);
|
||||||
|
PE_ArrayType(DWORD64);
|
||||||
|
|
||||||
|
LPCSTR PE_STR(PE* pe, LPCSTR address) {
|
||||||
|
if (pe->isInAnotherAddressSpace || pe->isInKernelLand) {
|
||||||
|
SIZE_T slen = 16;
|
||||||
|
LPSTR s = calloc(slen, 1);
|
||||||
|
if (s == NULL) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
SIZE_T i = 0;
|
||||||
|
do {
|
||||||
|
if (slen <= i) {
|
||||||
|
slen *= 2;
|
||||||
|
LPSTR tmp = realloc(s, slen);
|
||||||
|
if (NULL == tmp) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
s = tmp;
|
||||||
|
}
|
||||||
|
s[i] = PE_BYTE(pe, address + i);
|
||||||
|
i++;
|
||||||
|
} while (s[i - 1] != '\0');
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID PE_STR_free(PE* pe, LPCSTR s) {
|
||||||
|
if (pe->isInAnotherAddressSpace || pe->isInKernelLand) {
|
||||||
|
free((PVOID)s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PE* _PE_create_common(PVOID imageBase, BOOL isMemoryMapped, BOOL isInAnotherAddressSpace, HANDLE hProcess, BOOL isInKernelLand, kernel_read_memory_func ReadPrimitive);
|
||||||
|
|
||||||
|
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
|
||||||
|
return _PE_create_common(imageBase, TRUE, TRUE, hProcess, FALSE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped) {
|
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped) {
|
||||||
|
return _PE_create_common(imageBase, isMemoryMapped, FALSE, INVALID_HANDLE_VALUE, FALSE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
PE* PE_create_from_kernel(PVOID imageBase, kernel_read_memory_func ReadPrimitive) {
|
||||||
|
return _PE_create_common(imageBase, TRUE, FALSE, INVALID_HANDLE_VALUE, TRUE, ReadPrimitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PE* _PE_create_common(PVOID imageBase, BOOL isMemoryMapped, BOOL isInAnotherAddressSpace, HANDLE hProcess, BOOL isInKernelLand, kernel_read_memory_func ReadPrimitive) {
|
||||||
PE* pe = calloc(1, sizeof(PE));
|
PE* pe = calloc(1, sizeof(PE));
|
||||||
if (NULL == pe) {
|
if (NULL == pe) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
pe->isMemoryMapped = isMemoryMapped;
|
pe->isMemoryMapped = isMemoryMapped;
|
||||||
pe->isInAnotherAddressSpace = FALSE;
|
|
||||||
pe->hProcess = INVALID_HANDLE_VALUE;
|
|
||||||
pe->dosHeader = imageBase;
|
|
||||||
pe->ntHeader = (IMAGE_NT_HEADERS*)(((PBYTE)imageBase) + pe->dosHeader->e_lfanew);
|
|
||||||
pe->optHeader = &pe->ntHeader->OptionalHeader;
|
|
||||||
if (isMemoryMapped) {
|
|
||||||
pe->baseAddress = imageBase;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pe->baseAddress = (PVOID)pe->optHeader->ImageBase;
|
|
||||||
}
|
|
||||||
pe->dataDir = pe->optHeader->DataDirectory;
|
|
||||||
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)(((PBYTE)pe->optHeader) + pe->ntHeader->FileHeader.SizeOfOptionalHeader);
|
|
||||||
DWORD exportRVA = pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
|
||||||
if (exportRVA == 0) {
|
|
||||||
pe->exportDirectory = NULL;
|
|
||||||
pe->exportedNames = NULL;
|
|
||||||
pe->exportedFunctions = NULL;
|
|
||||||
pe->exportedOrdinals = NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
|
|
||||||
pe->exportedNames = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNames);
|
|
||||||
pe->exportedFunctions = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfFunctions);
|
|
||||||
pe->exportedOrdinals = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNameOrdinals);
|
|
||||||
pe->exportedNamesLength = pe->exportDirectory->NumberOfNames;
|
|
||||||
}
|
|
||||||
pe->relocations = NULL;
|
|
||||||
DWORD debugRVA = pe->dataDir[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
|
||||||
if (debugRVA == 0) {
|
|
||||||
pe->debugDirectory = NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pe->debugDirectory = PE_RVA_to_Addr(pe, debugRVA);
|
|
||||||
if (pe->debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
|
||||||
pe->debugDirectory = NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pe->codeviewDebugInfo = PE_RVA_to_Addr(pe, pe->debugDirectory->AddressOfRawData);
|
|
||||||
if (pe->codeviewDebugInfo->signature != *((DWORD*)"RSDS")) {
|
|
||||||
pe->debugDirectory = NULL;
|
|
||||||
pe->codeviewDebugInfo = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pe;
|
|
||||||
}
|
|
||||||
|
|
||||||
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
|
|
||||||
PE* pe = calloc(1, sizeof(PE));
|
|
||||||
if (NULL == pe) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
pe->isMemoryMapped = TRUE;
|
|
||||||
pe->hProcess = hProcess;
|
pe->hProcess = hProcess;
|
||||||
pe->isInAnotherAddressSpace = TRUE;
|
pe->isInAnotherAddressSpace = isInAnotherAddressSpace;
|
||||||
|
pe->isInKernelLand = isInKernelLand;
|
||||||
|
pe->kernel_read = ReadPrimitive;
|
||||||
pe->baseAddress = imageBase;
|
pe->baseAddress = imageBase;
|
||||||
pe->dosHeader = imageBase;
|
pe->dosHeader = imageBase;
|
||||||
DWORD ntHeaderPtrAddress = 0;
|
DWORD ntHeaderPtrAddress = PE_DWORD(pe, &((IMAGE_DOS_HEADER*)imageBase)->e_lfanew);
|
||||||
ReadProcessMemory(hProcess, (LPCVOID)((intptr_t)imageBase + offsetof(IMAGE_DOS_HEADER, e_lfanew)), &ntHeaderPtrAddress, sizeof(ntHeaderPtrAddress), NULL);
|
|
||||||
pe->ntHeader = (IMAGE_NT_HEADERS*)((intptr_t)pe->baseAddress + ntHeaderPtrAddress);
|
pe->ntHeader = (IMAGE_NT_HEADERS*)((intptr_t)pe->baseAddress + ntHeaderPtrAddress);
|
||||||
pe->optHeader = (IMAGE_OPTIONAL_HEADER*)(&pe->ntHeader->OptionalHeader);
|
pe->optHeader = (IMAGE_OPTIONAL_HEADER*)(&pe->ntHeader->OptionalHeader);
|
||||||
pe->dataDir = pe->optHeader->DataDirectory;
|
pe->dataDir = pe->optHeader->DataDirectory;
|
||||||
WORD sizeOfOptionnalHeader = 0;
|
WORD sizeOfOptionnalHeader = PE_WORD(pe, &pe->ntHeader->FileHeader.SizeOfOptionalHeader);
|
||||||
ReadProcessMemory(hProcess, &pe->ntHeader->FileHeader.SizeOfOptionalHeader, &sizeOfOptionnalHeader, sizeof(sizeOfOptionnalHeader), NULL);
|
|
||||||
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)((intptr_t)pe->optHeader + sizeOfOptionnalHeader);
|
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)((intptr_t)pe->optHeader + sizeOfOptionnalHeader);
|
||||||
DWORD exportRVA = 0;
|
DWORD exportRVA = PE_DWORD(pe, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||||
ReadProcessMemory(hProcess, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, &exportRVA, sizeof(exportRVA), NULL);
|
|
||||||
if (exportRVA == 0) {
|
if (exportRVA == 0) {
|
||||||
pe->exportDirectory = NULL;
|
pe->exportDirectory = NULL;
|
||||||
pe->exportedNames = NULL;
|
pe->exportedNames = NULL;
|
||||||
@@ -260,37 +285,32 @@ PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
|
|||||||
else {
|
else {
|
||||||
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
|
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
|
||||||
|
|
||||||
DWORD AddressOfNames = 0;
|
DWORD AddressOfNames = PE_DWORD(pe, &pe->exportDirectory->AddressOfNames);
|
||||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNames, &AddressOfNames, sizeof(AddressOfNames), NULL);
|
|
||||||
pe->exportedNames = PE_RVA_to_Addr(pe, AddressOfNames);
|
pe->exportedNames = PE_RVA_to_Addr(pe, AddressOfNames);
|
||||||
|
|
||||||
DWORD AddressOfFunctions = 0;
|
DWORD AddressOfFunctions = PE_DWORD(pe, &pe->exportDirectory->AddressOfFunctions);
|
||||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfFunctions, &AddressOfFunctions, sizeof(AddressOfFunctions), NULL);
|
|
||||||
pe->exportedFunctions = PE_RVA_to_Addr(pe, AddressOfFunctions);
|
pe->exportedFunctions = PE_RVA_to_Addr(pe, AddressOfFunctions);
|
||||||
|
|
||||||
DWORD AddressOfNameOrdinals = 0;
|
DWORD AddressOfNameOrdinals = PE_DWORD(pe, &pe->exportDirectory->AddressOfNameOrdinals);
|
||||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNameOrdinals, &AddressOfNameOrdinals, sizeof(AddressOfNameOrdinals), NULL);
|
|
||||||
pe->exportedOrdinals = PE_RVA_to_Addr(pe, AddressOfNameOrdinals);
|
pe->exportedOrdinals = PE_RVA_to_Addr(pe, AddressOfNameOrdinals);
|
||||||
|
|
||||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->NumberOfNames, &pe->exportedNamesLength, sizeof(pe->exportedNamesLength), NULL);
|
pe->exportedNamesLength = PE_DWORD(pe, &pe->exportDirectory->NumberOfNames);
|
||||||
}
|
}
|
||||||
pe->relocations = NULL;
|
pe->relocations = NULL;
|
||||||
DWORD debugRVA = 0;
|
DWORD debugRVA = PE_DWORD(pe, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress);
|
||||||
ReadProcessMemory(hProcess, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress, &debugRVA, sizeof(debugRVA), NULL);
|
|
||||||
if (debugRVA == 0) {
|
if (debugRVA == 0) {
|
||||||
pe->debugDirectory = NULL;
|
pe->debugDirectory = NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pe->debugDirectory = PE_RVA_to_Addr(pe, debugRVA);
|
pe->debugDirectory = PE_RVA_to_Addr(pe, debugRVA);
|
||||||
DWORD debugDirectoryType;
|
DWORD debugDirectoryType = PE_DWORD(pe, &pe->debugDirectory->Type);
|
||||||
ReadProcessMemory(hProcess, &pe->debugDirectory->Type, &debugDirectoryType, sizeof(debugDirectoryType), NULL);
|
|
||||||
if (debugDirectoryType != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
if (debugDirectoryType != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||||
pe->debugDirectory = NULL;
|
pe->debugDirectory = NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pe->codeviewDebugInfo = PE_RVA_to_Addr(pe, pe->debugDirectory->AddressOfRawData);
|
DWORD debugDirectoryAddressOfRawData = PE_DWORD(pe, &pe->debugDirectory->AddressOfRawData);
|
||||||
DWORD codeviewDebugInfoSignature;
|
pe->codeviewDebugInfo = PE_RVA_to_Addr(pe, debugDirectoryAddressOfRawData);
|
||||||
ReadProcessMemory(hProcess, &pe->codeviewDebugInfo->signature, &codeviewDebugInfoSignature, sizeof(pe->codeviewDebugInfo->signature), NULL);
|
DWORD codeviewDebugInfoSignature = PE_DWORD(pe, &pe->codeviewDebugInfo->signature);
|
||||||
if (codeviewDebugInfoSignature != *((DWORD*)"RSDS")) {
|
if (codeviewDebugInfoSignature != *((DWORD*)"RSDS")) {
|
||||||
pe->debugDirectory = NULL;
|
pe->debugDirectory = NULL;
|
||||||
pe->codeviewDebugInfo = NULL;
|
pe->codeviewDebugInfo = NULL;
|
||||||
@@ -300,7 +320,7 @@ PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
|
|||||||
return pe;
|
return pe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO : implement the case where the PE is in another address space
|
||||||
DWORD PE_functionRVA(PE* pe, LPCSTR functionName) {
|
DWORD PE_functionRVA(PE* pe, LPCSTR functionName) {
|
||||||
IMAGE_EXPORT_DIRECTORY* exportDirectory = pe->exportDirectory;
|
IMAGE_EXPORT_DIRECTORY* exportDirectory = pe->exportDirectory;
|
||||||
LPDWORD exportedNames = pe->exportedNames;
|
LPDWORD exportedNames = pe->exportedNames;
|
||||||
@@ -308,24 +328,37 @@ DWORD PE_functionRVA(PE* pe, LPCSTR functionName) {
|
|||||||
LPWORD exportedNameOrdinals = pe->exportedOrdinals;
|
LPWORD exportedNameOrdinals = pe->exportedOrdinals;
|
||||||
|
|
||||||
DWORD nameOrdinal_low = 0;
|
DWORD nameOrdinal_low = 0;
|
||||||
LPCSTR exportName_low = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_low]);
|
LPCSTR exportName_low = PE_RVA_to_Addr(pe, PE_DWORD_Array(pe, exportedNames, nameOrdinal_low));
|
||||||
DWORD nameOrdinal_high = exportDirectory->NumberOfNames;
|
exportName_low = PE_STR(pe, exportName_low);
|
||||||
|
DWORD nameOrdinal_high = PE_DWORD(pe, &exportDirectory->NumberOfNames);
|
||||||
DWORD nameOrdinal_mid;
|
DWORD nameOrdinal_mid;
|
||||||
LPCSTR exportName_mid;
|
LPCSTR exportName_mid = NULL;
|
||||||
|
|
||||||
while (nameOrdinal_high - nameOrdinal_low > 1) {
|
while (nameOrdinal_high - nameOrdinal_low > 1) {
|
||||||
nameOrdinal_mid = (nameOrdinal_high + nameOrdinal_low) / 2;
|
nameOrdinal_mid = (nameOrdinal_high + nameOrdinal_low) / 2;
|
||||||
exportName_mid = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_mid]);
|
if (exportName_mid) {
|
||||||
|
PE_STR_free(pe, exportName_mid);
|
||||||
|
}
|
||||||
|
exportName_mid = PE_RVA_to_Addr(pe, PE_DWORD_Array(pe, exportedNames, nameOrdinal_mid));
|
||||||
|
exportName_mid = PE_STR(pe, exportName_mid);
|
||||||
|
|
||||||
if (strcmp(exportName_mid, functionName) > 0) {
|
if (strcmp(exportName_mid, functionName) > 0) {
|
||||||
nameOrdinal_high = nameOrdinal_mid;
|
nameOrdinal_high = nameOrdinal_mid;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nameOrdinal_low = nameOrdinal_mid;
|
nameOrdinal_low = nameOrdinal_mid;
|
||||||
|
PE_STR_free(pe, exportName_low);
|
||||||
exportName_low = exportName_mid;
|
exportName_low = exportName_mid;
|
||||||
|
exportName_mid = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!strcmp(exportName_low, functionName))
|
if (exportName_mid) {
|
||||||
return exportedFunctions[exportedNameOrdinals[nameOrdinal_low]];
|
PE_STR_free(pe, exportName_mid);
|
||||||
|
}
|
||||||
|
if (!strcmp(exportName_low, functionName)) {
|
||||||
|
PE_STR_free(pe, exportName_low);
|
||||||
|
return PE_DWORD_Array(pe, exportedFunctions, PE_WORD_Array(pe, exportedNameOrdinals, nameOrdinal_low));
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user