mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-19 22:14:37 +00:00
2252 lines
78 KiB
C++
2252 lines
78 KiB
C++
/**
|
|
* @file pe-parser.cpp
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief Portable Executable parser
|
|
* @details
|
|
* @version 0.1
|
|
* @date 2021-12-26
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
#include "header/pe-image-reader.h"
|
|
|
|
static const char *
|
|
PeGetSubsystemName(WORD Subsystem)
|
|
{
|
|
switch (Subsystem)
|
|
{
|
|
case IMAGE_SUBSYSTEM_NATIVE:
|
|
return "Device Driver(Native windows Process)";
|
|
case IMAGE_SUBSYSTEM_WINDOWS_GUI:
|
|
return "Windows GUI";
|
|
case IMAGE_SUBSYSTEM_WINDOWS_CUI:
|
|
return "Windows CLI";
|
|
case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI:
|
|
return "Windows CE GUI";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
PeShowDllCharacteristics(WORD DllCharacteristics)
|
|
{
|
|
BOOLEAN AnyFlag = FALSE;
|
|
|
|
struct DLL_CHARACTERISTIC_NAME
|
|
{
|
|
WORD Flag;
|
|
const char * Name;
|
|
};
|
|
|
|
static const DLL_CHARACTERISTIC_NAME CommonDllCharacteristics[] = {
|
|
{IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA, "High Entropy VA"},
|
|
{IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, "Dynamic Base"},
|
|
{IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY, "Force Integrity"},
|
|
{IMAGE_DLLCHARACTERISTICS_NX_COMPAT, "NX Compatible"},
|
|
{IMAGE_DLLCHARACTERISTICS_NO_ISOLATION, "No Isolation"},
|
|
{IMAGE_DLLCHARACTERISTICS_NO_SEH, "No SEH"},
|
|
{IMAGE_DLLCHARACTERISTICS_NO_BIND, "No Bind"},
|
|
{IMAGE_DLLCHARACTERISTICS_APPCONTAINER, "AppContainer"},
|
|
{IMAGE_DLLCHARACTERISTICS_WDM_DRIVER, "WDM Driver"},
|
|
{IMAGE_DLLCHARACTERISTICS_GUARD_CF, "Guard CF"},
|
|
{IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, "Terminal Server Aware"},
|
|
};
|
|
|
|
for (UINT32 i = 0; i < RTL_NUMBER_OF(CommonDllCharacteristics); i++)
|
|
{
|
|
if ((DllCharacteristics & CommonDllCharacteristics[i].Flag) == CommonDllCharacteristics[i].Flag)
|
|
{
|
|
ShowMessages("%s%s", AnyFlag ? ", " : "", CommonDllCharacteristics[i].Name);
|
|
AnyFlag = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!AnyFlag)
|
|
{
|
|
ShowMessages("None");
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
PeShowEntrypointFileOffset(PPE_IMAGE_READER Reader, DWORD AddressOfEntryPoint)
|
|
{
|
|
SIZE_T FileOffset = 0;
|
|
|
|
if (PeImageReaderRvaToFileOffset(Reader, AddressOfEntryPoint, 1, &FileOffset))
|
|
{
|
|
ShowMessages("\n%-36s%#llx", "Entrypoint file offset :", (UINT64)FileOffset);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("\n%-36s%s", "Entrypoint file offset :", "not mapped");
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
PeGetDataDirectoryName(UINT32 Index)
|
|
{
|
|
static const char * DirectoryNames[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] = {
|
|
"Export Table",
|
|
"Import Table",
|
|
"Resource Table",
|
|
"Exception Table",
|
|
"Certificate Table",
|
|
"Base Relocation Table",
|
|
"Debug",
|
|
"Architecture",
|
|
"Global Ptr",
|
|
"TLS Table",
|
|
"Load Config Table",
|
|
"Bound Import",
|
|
"Import Address Table",
|
|
"Delay Import Descriptor",
|
|
"CLR Runtime Header",
|
|
"Reserved",
|
|
};
|
|
|
|
if (Index < RTL_NUMBER_OF(DirectoryNames))
|
|
{
|
|
return DirectoryNames[Index];
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeAddDword(DWORD Left, DWORD Right, DWORD * Result)
|
|
{
|
|
if (Result == NULL || Right > MAXDWORD - Left)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*Result = Left + Right;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeRvaContainsRange(DWORD RangeRva, DWORD RangeSize, DWORD Rva, DWORD Size)
|
|
{
|
|
DWORD RangeEnd = 0;
|
|
DWORD RvaEnd = 0;
|
|
|
|
if (!PeAddDword(RangeRva, RangeSize, &RangeEnd) || !PeAddDword(Rva, Size, &RvaEnd))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return Rva >= RangeRva && RvaEnd <= RangeEnd;
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeGetPointerAtRva(PPE_IMAGE_READER Reader, DWORD Rva, DWORD Length, const BYTE ** Pointer)
|
|
{
|
|
SIZE_T FileOffset = 0;
|
|
|
|
if (!PeImageReaderRvaToFileOffset(Reader, Rva, Length, &FileOffset))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return PeImageReaderGetPointerAtOffset(Reader, FileOffset, Length, Pointer);
|
|
}
|
|
|
|
typedef enum _PE_ASCII_STRING_STATUS
|
|
{
|
|
PeAsciiStringInvalid,
|
|
PeAsciiStringOk,
|
|
PeAsciiStringTruncated,
|
|
} PE_ASCII_STRING_STATUS;
|
|
|
|
static PE_ASCII_STRING_STATUS
|
|
PeReadAsciiStringAtRva(PPE_IMAGE_READER Reader, DWORD Rva, DWORD MaxLength, CHAR * Buffer, SIZE_T BufferSize)
|
|
{
|
|
if (Buffer == NULL || BufferSize == 0 || MaxLength == 0)
|
|
{
|
|
return PeAsciiStringInvalid;
|
|
}
|
|
|
|
Buffer[0] = '\0';
|
|
|
|
SIZE_T OutputIndex = 0;
|
|
for (DWORD Index = 0; Index < MaxLength; Index++)
|
|
{
|
|
DWORD CharacterRva = 0;
|
|
const BYTE * Character = NULL;
|
|
|
|
if (!PeAddDword(Rva, Index, &CharacterRva) || !PeGetPointerAtRva(Reader, CharacterRva, 1, &Character))
|
|
{
|
|
return PeAsciiStringInvalid;
|
|
}
|
|
|
|
if (*Character == '\0')
|
|
{
|
|
return PeAsciiStringOk;
|
|
}
|
|
|
|
if (OutputIndex + 1 < BufferSize)
|
|
{
|
|
Buffer[OutputIndex++] = (*Character >= 0x20 && *Character <= 0x7e) ? (CHAR)*Character : '.';
|
|
Buffer[OutputIndex] = '\0';
|
|
}
|
|
}
|
|
|
|
return PeAsciiStringTruncated;
|
|
}
|
|
|
|
static const char *
|
|
PeAsciiStatusName(PE_ASCII_STRING_STATUS Status)
|
|
{
|
|
switch (Status)
|
|
{
|
|
case PeAsciiStringInvalid:
|
|
return "invalid mapping";
|
|
case PeAsciiStringTruncated:
|
|
return "truncated or unterminated";
|
|
default:
|
|
return "readable";
|
|
}
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeVaToRva(ULONGLONG Va, ULONGLONG ImageBase, DWORD * Rva)
|
|
{
|
|
ULONGLONG Difference = 0;
|
|
|
|
if (Rva == NULL || Va < ImageBase)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Difference = Va - ImageBase;
|
|
if (Difference > MAXDWORD)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*Rva = (DWORD)Difference;
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD
|
|
PeReadDwordFromBuffer(const BYTE * Buffer, SIZE_T Offset)
|
|
{
|
|
DWORD Value = 0;
|
|
|
|
CopyMemory(&Value, Buffer + Offset, sizeof(Value));
|
|
return Value;
|
|
}
|
|
|
|
static ULONGLONG
|
|
PeReadQwordFromBuffer(const BYTE * Buffer, SIZE_T Offset)
|
|
{
|
|
ULONGLONG Value = 0;
|
|
|
|
CopyMemory(&Value, Buffer + Offset, sizeof(Value));
|
|
return Value;
|
|
}
|
|
|
|
static ULONGLONG
|
|
PeReadLoadConfigPointer(const BYTE * Buffer, SIZE_T Offset, BOOLEAN Is32Bit)
|
|
{
|
|
return Is32Bit ? PeReadDwordFromBuffer(Buffer, Offset) : PeReadQwordFromBuffer(Buffer, Offset);
|
|
}
|
|
|
|
static PE_ASCII_STRING_STATUS
|
|
PeReadAsciiStringFromBuffer(const BYTE * StringPointer, DWORD MaxLength, CHAR * Buffer, SIZE_T BufferSize)
|
|
{
|
|
if (StringPointer == NULL || Buffer == NULL || BufferSize == 0 || MaxLength == 0)
|
|
{
|
|
return PeAsciiStringInvalid;
|
|
}
|
|
|
|
Buffer[0] = '\0';
|
|
|
|
SIZE_T OutputIndex = 0;
|
|
for (DWORD Index = 0; Index < MaxLength; Index++)
|
|
{
|
|
if (StringPointer[Index] == '\0')
|
|
{
|
|
return PeAsciiStringOk;
|
|
}
|
|
|
|
if (OutputIndex + 1 < BufferSize)
|
|
{
|
|
Buffer[OutputIndex++] = (StringPointer[Index] >= 0x20 && StringPointer[Index] <= 0x7e) ? (CHAR)StringPointer[Index] : '.';
|
|
Buffer[OutputIndex] = '\0';
|
|
}
|
|
}
|
|
|
|
return PeAsciiStringTruncated;
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeReadWordAtRva(PPE_IMAGE_READER Reader, DWORD Rva, WORD * Value)
|
|
{
|
|
const BYTE * Pointer = NULL;
|
|
|
|
if (Value == NULL || !PeGetPointerAtRva(Reader, Rva, sizeof(WORD), &Pointer))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CopyMemory(Value, Pointer, sizeof(*Value));
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeReadThunkAtRva(PPE_IMAGE_READER Reader, DWORD Rva, BOOLEAN Is32Bit, ULONGLONG * Value)
|
|
{
|
|
const BYTE * Pointer = NULL;
|
|
DWORD Size = Is32Bit ? sizeof(DWORD) : sizeof(ULONGLONG);
|
|
|
|
if (Value == NULL || !PeGetPointerAtRva(Reader, Rva, Size, &Pointer))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (Is32Bit)
|
|
{
|
|
DWORD Value32 = 0;
|
|
CopyMemory(&Value32, Pointer, sizeof(Value32));
|
|
*Value = Value32;
|
|
}
|
|
else
|
|
{
|
|
CopyMemory(Value, Pointer, sizeof(*Value));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static VOID
|
|
PeShowTls(PPE_IMAGE_READER Reader, const IMAGE_DATA_DIRECTORY * TlsDirectory, BOOLEAN Is32Bit, ULONGLONG ImageBase)
|
|
{
|
|
const DWORD MaxCallbacks = 0x200;
|
|
DWORD TlsSize = Is32Bit ? sizeof(IMAGE_TLS_DIRECTORY32) : sizeof(IMAGE_TLS_DIRECTORY64);
|
|
|
|
ShowMessages("\n\nTLS\n---");
|
|
|
|
if (TlsDirectory->VirtualAddress == 0 || TlsDirectory->Size == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "TLS directory :", "empty");
|
|
return;
|
|
}
|
|
|
|
if (!PeRvaContainsRange(TlsDirectory->VirtualAddress, TlsDirectory->Size, TlsDirectory->VirtualAddress, TlsSize))
|
|
{
|
|
ShowMessages("\n%-36s%s", "TLS directory :", "invalid bounds");
|
|
return;
|
|
}
|
|
|
|
const BYTE * TlsPointer = NULL;
|
|
if (!PeGetPointerAtRva(Reader, TlsDirectory->VirtualAddress, TlsSize, &TlsPointer))
|
|
{
|
|
ShowMessages("\n%-36s%s", "TLS directory :", "not mapped");
|
|
return;
|
|
}
|
|
|
|
ULONGLONG StartRawDataVa = Is32Bit ? PeReadDwordFromBuffer(TlsPointer, 0) : PeReadQwordFromBuffer(TlsPointer, 0);
|
|
ULONGLONG EndRawDataVa = Is32Bit ? PeReadDwordFromBuffer(TlsPointer, 4) : PeReadQwordFromBuffer(TlsPointer, 8);
|
|
ULONGLONG AddressOfIndexVa = Is32Bit ? PeReadDwordFromBuffer(TlsPointer, 8) : PeReadQwordFromBuffer(TlsPointer, 16);
|
|
ULONGLONG AddressCallbacksVa = Is32Bit ? PeReadDwordFromBuffer(TlsPointer, 12) : PeReadQwordFromBuffer(TlsPointer, 24);
|
|
DWORD SizeOfZeroFill = Is32Bit ? PeReadDwordFromBuffer(TlsPointer, 16) : PeReadDwordFromBuffer(TlsPointer, 32);
|
|
DWORD Characteristics = Is32Bit ? PeReadDwordFromBuffer(TlsPointer, 20) : PeReadDwordFromBuffer(TlsPointer, 36);
|
|
|
|
ShowMessages("\n%-36s%#llx", "Start address of raw data VA :", StartRawDataVa);
|
|
ShowMessages("\n%-36s%#llx", "End address of raw data VA :", EndRawDataVa);
|
|
ShowMessages("\n%-36s%#llx", "Address of index VA :", AddressOfIndexVa);
|
|
ShowMessages("\n%-36s%#llx", "Address of callbacks VA :", AddressCallbacksVa);
|
|
ShowMessages("\n%-36s%#x", "Size of zero fill :", SizeOfZeroFill);
|
|
ShowMessages("\n%-36s%#x", "Characteristics :", Characteristics);
|
|
|
|
DWORD CallbacksRva = 0;
|
|
if (AddressCallbacksVa == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "TLS callbacks :", "empty");
|
|
return;
|
|
}
|
|
|
|
if (!PeVaToRva(AddressCallbacksVa, ImageBase, &CallbacksRva))
|
|
{
|
|
ShowMessages("\n%-36s%#llx", "Warning, invalid callbacks VA :", AddressCallbacksVa);
|
|
return;
|
|
}
|
|
|
|
DWORD EntrySize = Is32Bit ? sizeof(DWORD) : sizeof(ULONGLONG);
|
|
for (DWORD CallbackIndex = 0; CallbackIndex < MaxCallbacks; CallbackIndex++)
|
|
{
|
|
DWORD EntryRva = 0;
|
|
if (!PeAddDword(CallbacksRva, CallbackIndex * EntrySize, &EntryRva))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "TLS callback RVA overflow");
|
|
return;
|
|
}
|
|
|
|
ULONGLONG CallbackVa = 0;
|
|
if (!PeReadThunkAtRva(Reader, EntryRva, Is32Bit, &CallbackVa))
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, invalid callback entry RVA :", EntryRva);
|
|
return;
|
|
}
|
|
|
|
if (CallbackVa == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
DWORD CallbackRva = 0;
|
|
SIZE_T FileOffset = 0;
|
|
if (PeVaToRva(CallbackVa, ImageBase, &CallbackRva))
|
|
{
|
|
if (PeImageReaderRvaToFileOffset(Reader, CallbackRva, 1, &FileOffset))
|
|
{
|
|
ShowMessages("\n [%u] VA %#llx RVA %#x file offset %#llx", CallbackIndex, CallbackVa, CallbackRva, (UINT64)FileOffset);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("\n [%u] VA %#llx RVA %#x file offset %s", CallbackIndex, CallbackVa, CallbackRva, "not mapped");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("\n [%u] VA %#llx RVA %s", CallbackIndex, CallbackVa, "invalid");
|
|
}
|
|
}
|
|
|
|
ShowMessages("\n%-36s%#x", "Warning, TLS callback cap reached :", MaxCallbacks);
|
|
}
|
|
|
|
static VOID
|
|
PeShowDebugCodeView(PPE_IMAGE_READER Reader, const IMAGE_DEBUG_DIRECTORY * Entry)
|
|
{
|
|
const DWORD MaxPdbPathLength = 0x400;
|
|
|
|
if (Entry->Type != IMAGE_DEBUG_TYPE_CODEVIEW || Entry->SizeOfData < sizeof(DWORD))
|
|
{
|
|
return;
|
|
}
|
|
|
|
const BYTE * Payload = NULL;
|
|
if ((Entry->PointerToRawData == 0 || !PeImageReaderGetPointerAtOffset(Reader, Entry->PointerToRawData, Entry->SizeOfData, &Payload)) &&
|
|
(Entry->AddressOfRawData == 0 || !PeGetPointerAtRva(Reader, Entry->AddressOfRawData, Entry->SizeOfData, &Payload)))
|
|
{
|
|
ShowMessages("\n%-36s%s", " CodeView payload :", "invalid bounds");
|
|
return;
|
|
}
|
|
|
|
if (memcmp(Payload, "RSDS", sizeof(DWORD)) == 0)
|
|
{
|
|
if (Entry->SizeOfData < 24)
|
|
{
|
|
ShowMessages("\n%-36s%s", " CodeView RSDS :", "truncated");
|
|
return;
|
|
}
|
|
|
|
DWORD Age = PeReadDwordFromBuffer(Payload, 20);
|
|
DWORD PathOffset = 24;
|
|
DWORD PathSize = Entry->SizeOfData - 24;
|
|
DWORD PathLimit = PathSize < MaxPdbPathLength ? PathSize : MaxPdbPathLength;
|
|
CHAR PdbPath[MaxPdbPathLength + 1] = {0};
|
|
const BYTE * Guid = Payload + 4;
|
|
|
|
PE_ASCII_STRING_STATUS PathStatus = PeReadAsciiStringFromBuffer(Payload + PathOffset, PathLimit, PdbPath, sizeof(PdbPath));
|
|
ShowMessages("\n%-36s%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
" CodeView RSDS GUID :",
|
|
Guid[3],
|
|
Guid[2],
|
|
Guid[1],
|
|
Guid[0],
|
|
Guid[5],
|
|
Guid[4],
|
|
Guid[7],
|
|
Guid[6],
|
|
Guid[8],
|
|
Guid[9],
|
|
Guid[10],
|
|
Guid[11],
|
|
Guid[12],
|
|
Guid[13],
|
|
Guid[14],
|
|
Guid[15]);
|
|
ShowMessages("\n%-36s%u", " CodeView age :", Age);
|
|
ShowMessages("\n%-36s%s", " CodeView PDB path :", PathStatus == PeAsciiStringOk ? PdbPath : PeAsciiStatusName(PathStatus));
|
|
}
|
|
else if (memcmp(Payload, "NB10", sizeof(DWORD)) == 0)
|
|
{
|
|
if (Entry->SizeOfData < 16)
|
|
{
|
|
ShowMessages("\n%-36s%s", " CodeView NB10 :", "truncated");
|
|
return;
|
|
}
|
|
|
|
DWORD Offset = PeReadDwordFromBuffer(Payload, 4);
|
|
DWORD Timestamp = PeReadDwordFromBuffer(Payload, 8);
|
|
DWORD Age = PeReadDwordFromBuffer(Payload, 12);
|
|
DWORD PathOffset = 16;
|
|
DWORD PathSize = Entry->SizeOfData - 16;
|
|
DWORD PathLimit = PathSize < MaxPdbPathLength ? PathSize : MaxPdbPathLength;
|
|
CHAR PdbPath[MaxPdbPathLength + 1] = {0};
|
|
|
|
PE_ASCII_STRING_STATUS PathStatus = PeReadAsciiStringFromBuffer(Payload + PathOffset, PathLimit, PdbPath, sizeof(PdbPath));
|
|
ShowMessages("\n%-36s%#x", " CodeView NB10 offset :", Offset);
|
|
ShowMessages("\n%-36s%#x", " CodeView NB10 signature :", Timestamp);
|
|
ShowMessages("\n%-36s%u", " CodeView age :", Age);
|
|
ShowMessages("\n%-36s%s", " CodeView PDB path :", PathStatus == PeAsciiStringOk ? PdbPath : PeAsciiStatusName(PathStatus));
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
PeShowDebug(PPE_IMAGE_READER Reader, const IMAGE_DATA_DIRECTORY * DebugDirectory)
|
|
{
|
|
const DWORD MaxDebugEntries = 0x1000;
|
|
|
|
ShowMessages("\n\nDebug\n-----");
|
|
|
|
if (DebugDirectory->VirtualAddress == 0 || DebugDirectory->Size == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Debug directory :", "empty");
|
|
return;
|
|
}
|
|
|
|
DWORD EntryCount = DebugDirectory->Size / sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
if (EntryCount == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "debug directory is smaller than one descriptor");
|
|
return;
|
|
}
|
|
|
|
BOOLEAN EntryCapped = FALSE;
|
|
if (EntryCount > MaxDebugEntries)
|
|
{
|
|
EntryCount = MaxDebugEntries;
|
|
EntryCapped = TRUE;
|
|
}
|
|
|
|
for (DWORD EntryIndex = 0; EntryIndex < EntryCount; EntryIndex++)
|
|
{
|
|
DWORD EntryRva = 0;
|
|
if (!PeAddDword(DebugDirectory->VirtualAddress, EntryIndex * (DWORD)sizeof(IMAGE_DEBUG_DIRECTORY), &EntryRva))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "debug directory RVA overflow");
|
|
break;
|
|
}
|
|
|
|
const IMAGE_DEBUG_DIRECTORY * Entry = NULL;
|
|
if (!PeGetPointerAtRva(Reader, EntryRva, sizeof(IMAGE_DEBUG_DIRECTORY), (const BYTE **)&Entry))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "debug directory entry is not mapped");
|
|
break;
|
|
}
|
|
|
|
ShowMessages("\n[%u] characteristics %#x time date stamp %#x major %u minor %u type %u size %#x address %#x raw %#x",
|
|
EntryIndex,
|
|
Entry->Characteristics,
|
|
Entry->TimeDateStamp,
|
|
Entry->MajorVersion,
|
|
Entry->MinorVersion,
|
|
Entry->Type,
|
|
Entry->SizeOfData,
|
|
Entry->AddressOfRawData,
|
|
Entry->PointerToRawData);
|
|
|
|
if (Entry->SizeOfData != 0 && Entry->PointerToRawData != 0)
|
|
{
|
|
const BYTE * Payload = NULL;
|
|
ShowMessages("\n%-36s%s", " Payload bounds :", PeImageReaderGetPointerAtOffset(Reader, Entry->PointerToRawData, Entry->SizeOfData, &Payload) ? "valid" : "invalid");
|
|
}
|
|
|
|
PeShowDebugCodeView(Reader, Entry);
|
|
}
|
|
|
|
if (EntryCapped)
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, debug entry cap reached :", MaxDebugEntries);
|
|
}
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeLoadConfigHasField(DWORD AvailableSize, SIZE_T Offset, SIZE_T FieldSize)
|
|
{
|
|
return Offset <= AvailableSize && FieldSize <= AvailableSize - Offset;
|
|
}
|
|
|
|
static VOID
|
|
PeShowLoadConfigDword(const BYTE * Config, DWORD AvailableSize, SIZE_T Offset, const CHAR * Label)
|
|
{
|
|
if (PeLoadConfigHasField(AvailableSize, Offset, sizeof(DWORD)))
|
|
{
|
|
ShowMessages("\n%-36s%#x", Label, PeReadDwordFromBuffer(Config, Offset));
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
PeShowLoadConfigPointer(const BYTE * Config, DWORD AvailableSize, SIZE_T Offset, const CHAR * Label, BOOLEAN Is32Bit)
|
|
{
|
|
SIZE_T FieldSize = Is32Bit ? sizeof(DWORD) : sizeof(ULONGLONG);
|
|
|
|
if (PeLoadConfigHasField(AvailableSize, Offset, FieldSize))
|
|
{
|
|
ShowMessages("\n%-36s%#llx", Label, PeReadLoadConfigPointer(Config, Offset, Is32Bit));
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
PeShowLoadConfig(PPE_IMAGE_READER Reader, const IMAGE_DATA_DIRECTORY * LoadConfigDirectory, BOOLEAN Is32Bit)
|
|
{
|
|
ShowMessages("\n\nLoad config\n-----------");
|
|
|
|
if (LoadConfigDirectory->VirtualAddress == 0 || LoadConfigDirectory->Size == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Load config directory :", "empty");
|
|
return;
|
|
}
|
|
|
|
if (LoadConfigDirectory->Size < sizeof(DWORD))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "load config size is too small");
|
|
return;
|
|
}
|
|
|
|
const BYTE * Config = NULL;
|
|
if (!PeGetPointerAtRva(Reader, LoadConfigDirectory->VirtualAddress, sizeof(DWORD), &Config))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Load config directory :", "not mapped");
|
|
return;
|
|
}
|
|
|
|
DWORD ConfigSize = PeReadDwordFromBuffer(Config, 0);
|
|
DWORD AvailableSize = LoadConfigDirectory->Size < ConfigSize ? LoadConfigDirectory->Size : ConfigSize;
|
|
|
|
ShowMessages("\n%-36s%#x", "Size :", ConfigSize);
|
|
if (AvailableSize < sizeof(DWORD))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "load config size is too small");
|
|
return;
|
|
}
|
|
|
|
if (!PeGetPointerAtRva(Reader, LoadConfigDirectory->VirtualAddress, AvailableSize, &Config))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Load config directory :", "invalid bounds");
|
|
return;
|
|
}
|
|
|
|
PeShowLoadConfigDword(Config, AvailableSize, 4, "Time date stamp :");
|
|
if (PeLoadConfigHasField(AvailableSize, 8, sizeof(WORD)))
|
|
{
|
|
WORD MajorVersion = 0;
|
|
CopyMemory(&MajorVersion, Config + 8, sizeof(MajorVersion));
|
|
ShowMessages("\n%-36s%u", "Major version :", MajorVersion);
|
|
}
|
|
if (PeLoadConfigHasField(AvailableSize, 10, sizeof(WORD)))
|
|
{
|
|
WORD MinorVersion = 0;
|
|
CopyMemory(&MinorVersion, Config + 10, sizeof(MinorVersion));
|
|
ShowMessages("\n%-36s%u", "Minor version :", MinorVersion);
|
|
}
|
|
PeShowLoadConfigDword(Config, AvailableSize, 12, "Global flags clear :");
|
|
PeShowLoadConfigDword(Config, AvailableSize, 16, "Global flags set :");
|
|
PeShowLoadConfigDword(Config, AvailableSize, 20, "Critical section timeout :");
|
|
PeShowLoadConfigDword(Config, AvailableSize, Is32Bit ? 44 : 72, "Process heap flags :");
|
|
PeShowLoadConfigPointer(Config, AvailableSize, Is32Bit ? 60 : 88, "Security cookie :", Is32Bit);
|
|
PeShowLoadConfigPointer(Config, AvailableSize, Is32Bit ? 64 : 96, "SE handler table :", Is32Bit);
|
|
PeShowLoadConfigPointer(Config, AvailableSize, Is32Bit ? 68 : 104, "SE handler count :", Is32Bit);
|
|
PeShowLoadConfigPointer(Config, AvailableSize, Is32Bit ? 72 : 112, "Guard CF check pointer :", Is32Bit);
|
|
PeShowLoadConfigPointer(Config, AvailableSize, Is32Bit ? 76 : 120, "Guard CF dispatch pointer :", Is32Bit);
|
|
PeShowLoadConfigPointer(Config, AvailableSize, Is32Bit ? 80 : 128, "Guard CF function table :", Is32Bit);
|
|
PeShowLoadConfigPointer(Config, AvailableSize, Is32Bit ? 84 : 136, "Guard CF function count :", Is32Bit);
|
|
PeShowLoadConfigDword(Config, AvailableSize, Is32Bit ? 88 : 144, "Guard flags :");
|
|
}
|
|
|
|
static VOID
|
|
PeShowDataDirectories(PPE_IMAGE_READER Reader,
|
|
const IMAGE_DATA_DIRECTORY * Directories,
|
|
DWORD NumberOfRvaAndSizes)
|
|
{
|
|
ShowMessages("\n\nData directories\n----------------");
|
|
ShowMessages("\n%-36s%u", "Number of RVA and sizes :", NumberOfRvaAndSizes);
|
|
|
|
for (UINT32 Index = 0; Index < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; Index++)
|
|
{
|
|
const char * Name = PeGetDataDirectoryName(Index);
|
|
const char * AddressLabel = Index == IMAGE_DIRECTORY_ENTRY_SECURITY ? "file offset" : "RVA";
|
|
|
|
if (Index >= NumberOfRvaAndSizes)
|
|
{
|
|
ShowMessages("\n[%2u] %-27s %s", Index, Name, "not declared");
|
|
continue;
|
|
}
|
|
|
|
const IMAGE_DATA_DIRECTORY * Directory = &Directories[Index];
|
|
if (Directory->VirtualAddress == 0 || Directory->Size == 0)
|
|
{
|
|
ShowMessages("\n[%2u] %-27s %s %#x, size %#x, %s",
|
|
Index,
|
|
Name,
|
|
AddressLabel,
|
|
Directory->VirtualAddress,
|
|
Directory->Size,
|
|
"empty");
|
|
continue;
|
|
}
|
|
|
|
const BYTE * Pointer = NULL;
|
|
SIZE_T FileOffset = 0;
|
|
|
|
if (Index == IMAGE_DIRECTORY_ENTRY_SECURITY)
|
|
{
|
|
BOOLEAN HasBounds = PeImageReaderGetPointerAtOffset(Reader,
|
|
Directory->VirtualAddress,
|
|
Directory->Size,
|
|
&Pointer);
|
|
|
|
ShowMessages("\n[%2u] %-27s file offset %#x, size %#x, %s",
|
|
Index,
|
|
Name,
|
|
Directory->VirtualAddress,
|
|
Directory->Size,
|
|
HasBounds ? "valid bounds" : "invalid bounds");
|
|
if (HasBounds)
|
|
{
|
|
ShowMessages(", mapped file offset %#llx", (UINT64)Directory->VirtualAddress);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (PeImageReaderRvaToFileOffset(Reader, Directory->VirtualAddress, Directory->Size, &FileOffset))
|
|
{
|
|
ShowMessages("\n[%2u] %-27s RVA %#x, size %#x, mapped, mapped file offset %#llx",
|
|
Index,
|
|
Name,
|
|
Directory->VirtualAddress,
|
|
Directory->Size,
|
|
(UINT64)FileOffset);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("\n[%2u] %-27s RVA %#x, size %#x, %s",
|
|
Index,
|
|
Name,
|
|
Directory->VirtualAddress,
|
|
Directory->Size,
|
|
"not mapped");
|
|
}
|
|
}
|
|
}
|
|
|
|
static VOID
|
|
PeShowImports(PPE_IMAGE_READER Reader, const IMAGE_DATA_DIRECTORY * ImportDirectory, BOOLEAN Is32Bit)
|
|
{
|
|
const DWORD MaxImportDescriptors = 0x1000;
|
|
const DWORD MaxTotalImports = 0x2000;
|
|
const DWORD MaxDllNameLength = 0x200;
|
|
const DWORD MaxImportNameLength = 0x200;
|
|
|
|
ShowMessages("\n\nImports\n-------");
|
|
|
|
if (ImportDirectory->VirtualAddress == 0 || ImportDirectory->Size == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Import directory :", "empty");
|
|
return;
|
|
}
|
|
|
|
const BYTE * DirectoryPointer = NULL;
|
|
if (!PeGetPointerAtRva(Reader, ImportDirectory->VirtualAddress, sizeof(IMAGE_IMPORT_DESCRIPTOR), &DirectoryPointer))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Import directory :", "not mapped");
|
|
return;
|
|
}
|
|
|
|
DWORD DescriptorCount = ImportDirectory->Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
|
|
if (DescriptorCount == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "import directory is smaller than one descriptor");
|
|
return;
|
|
}
|
|
|
|
BOOLEAN DescriptorCapped = FALSE;
|
|
if (DescriptorCount > MaxImportDescriptors)
|
|
{
|
|
DescriptorCount = MaxImportDescriptors;
|
|
DescriptorCapped = TRUE;
|
|
}
|
|
|
|
DWORD TotalImports = 0;
|
|
BOOLEAN FoundTerminator = FALSE;
|
|
BOOLEAN TotalImportsCapped = FALSE;
|
|
|
|
for (DWORD DescriptorIndex = 0; DescriptorIndex < DescriptorCount; DescriptorIndex++)
|
|
{
|
|
DWORD DescriptorRva = 0;
|
|
|
|
if (!PeAddDword(ImportDirectory->VirtualAddress,
|
|
DescriptorIndex * (DWORD)sizeof(IMAGE_IMPORT_DESCRIPTOR),
|
|
&DescriptorRva))
|
|
{
|
|
break;
|
|
}
|
|
|
|
const IMAGE_IMPORT_DESCRIPTOR * Descriptor = NULL;
|
|
if (!PeGetPointerAtRva(Reader,
|
|
DescriptorRva,
|
|
sizeof(IMAGE_IMPORT_DESCRIPTOR),
|
|
(const BYTE **)&Descriptor))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "import descriptor is not mapped");
|
|
break;
|
|
}
|
|
|
|
if (Descriptor->OriginalFirstThunk == 0 && Descriptor->TimeDateStamp == 0 && Descriptor->ForwarderChain == 0 &&
|
|
Descriptor->Name == 0 && Descriptor->FirstThunk == 0)
|
|
{
|
|
FoundTerminator = TRUE;
|
|
break;
|
|
}
|
|
|
|
CHAR DllName[MaxDllNameLength + 1] = {0};
|
|
PE_ASCII_STRING_STATUS DllNameStatus = PeReadAsciiStringAtRva(Reader,
|
|
Descriptor->Name,
|
|
MaxDllNameLength,
|
|
DllName,
|
|
sizeof(DllName));
|
|
if (DllNameStatus != PeAsciiStringOk)
|
|
{
|
|
ShowMessages("\n[%u] DLL name %s", DescriptorIndex, PeAsciiStatusName(DllNameStatus));
|
|
ShowMessages("\n%-36s%s", "Warning :", "skipping imports for descriptor with unreadable DLL name");
|
|
continue;
|
|
}
|
|
|
|
ShowMessages("\n[%u] DLL name %s", DescriptorIndex, DllName);
|
|
|
|
DWORD LookupThunkRva = Descriptor->OriginalFirstThunk != 0 ? Descriptor->OriginalFirstThunk : Descriptor->FirstThunk;
|
|
if (LookupThunkRva == 0 || Descriptor->FirstThunk == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "import thunk array is empty");
|
|
continue;
|
|
}
|
|
|
|
DWORD ThunkSize = Is32Bit ? sizeof(DWORD) : sizeof(ULONGLONG);
|
|
for (DWORD ThunkIndex = 0; TotalImports < MaxTotalImports; ThunkIndex++)
|
|
{
|
|
DWORD LookupEntryRva = 0;
|
|
DWORD IatEntryRva = 0;
|
|
if (!PeAddDword(LookupThunkRva, ThunkIndex * ThunkSize, &LookupEntryRva) ||
|
|
!PeAddDword(Descriptor->FirstThunk, ThunkIndex * ThunkSize, &IatEntryRva))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "import thunk RVA overflow");
|
|
break;
|
|
}
|
|
|
|
ULONGLONG ThunkValue = 0;
|
|
if (!PeReadThunkAtRva(Reader, LookupEntryRva, Is32Bit, &ThunkValue))
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, invalid thunk RVA :", LookupEntryRva);
|
|
break;
|
|
}
|
|
|
|
if (ThunkValue == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ULONGLONG OrdinalFlag = Is32Bit ? IMAGE_ORDINAL_FLAG32 : IMAGE_ORDINAL_FLAG64;
|
|
if ((ThunkValue & OrdinalFlag) != 0)
|
|
{
|
|
ShowMessages("\n [%u] ordinal %u thunk RVA %#x", ThunkIndex, (UINT32)(ThunkValue & 0xffff), IatEntryRva);
|
|
TotalImports++;
|
|
continue;
|
|
}
|
|
|
|
if (ThunkValue > MAXDWORD)
|
|
{
|
|
ShowMessages("\n%-36s%#llx", "Warning, invalid import name RVA :", (UINT64)ThunkValue);
|
|
TotalImports++;
|
|
continue;
|
|
}
|
|
|
|
DWORD NameRva = (DWORD)ThunkValue;
|
|
WORD Hint = 0;
|
|
if (!PeReadWordAtRva(Reader, NameRva, &Hint))
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, invalid import hint RVA :", NameRva);
|
|
TotalImports++;
|
|
continue;
|
|
}
|
|
|
|
DWORD ImportNameRva = 0;
|
|
if (!PeAddDword(NameRva, sizeof(WORD), &ImportNameRva))
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, invalid import name RVA :", NameRva);
|
|
TotalImports++;
|
|
continue;
|
|
}
|
|
|
|
CHAR ImportName[MaxImportNameLength + 1] = {0};
|
|
PE_ASCII_STRING_STATUS ImportNameStatus = PeReadAsciiStringAtRva(Reader,
|
|
ImportNameRva,
|
|
MaxImportNameLength,
|
|
ImportName,
|
|
sizeof(ImportName));
|
|
if (ImportNameStatus != PeAsciiStringOk)
|
|
{
|
|
ShowMessages("\n%-36s%#x, %s", "Warning, invalid import name RVA :", ImportNameRva, PeAsciiStatusName(ImportNameStatus));
|
|
TotalImports++;
|
|
continue;
|
|
}
|
|
|
|
ShowMessages("\n [%u] hint %#x name %s thunk RVA %#x", ThunkIndex, Hint, ImportName, IatEntryRva);
|
|
TotalImports++;
|
|
}
|
|
|
|
if (TotalImports >= MaxTotalImports)
|
|
{
|
|
TotalImportsCapped = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!FoundTerminator)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "import descriptor terminator not found before bound or cap");
|
|
}
|
|
|
|
if (DescriptorCapped)
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, descriptor cap reached :", MaxImportDescriptors);
|
|
}
|
|
|
|
if (TotalImportsCapped)
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, import cap reached :", MaxTotalImports);
|
|
}
|
|
}
|
|
|
|
static BOOLEAN
|
|
PeValidateExportTable(PPE_IMAGE_READER Reader, DWORD TableRva, DWORD Count, DWORD EntrySize, const BYTE ** Pointer)
|
|
{
|
|
DWORD TableSize = 0;
|
|
|
|
if (Count == 0)
|
|
{
|
|
*Pointer = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
if (EntrySize != 0 && Count > MAXDWORD / EntrySize)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
TableSize = Count * EntrySize;
|
|
return PeGetPointerAtRva(Reader, TableRva, TableSize, Pointer);
|
|
}
|
|
|
|
static VOID
|
|
PeShowExportEntry(PPE_IMAGE_READER Reader,
|
|
const IMAGE_DATA_DIRECTORY * ExportDirectory,
|
|
DWORD Index,
|
|
DWORD Ordinal,
|
|
const CHAR * Name,
|
|
DWORD FunctionRva)
|
|
{
|
|
const DWORD MaxForwarderLength = 0x1000;
|
|
|
|
if (PeRvaContainsRange(ExportDirectory->VirtualAddress, ExportDirectory->Size, FunctionRva, 1))
|
|
{
|
|
DWORD ForwarderRemaining = ExportDirectory->Size - (FunctionRva - ExportDirectory->VirtualAddress);
|
|
DWORD ForwarderMaxLength = ForwarderRemaining < MaxForwarderLength ? ForwarderRemaining : MaxForwarderLength;
|
|
|
|
CHAR Forwarder[MaxForwarderLength + 1] = {0};
|
|
PE_ASCII_STRING_STATUS ForwarderStatus = PeReadAsciiStringAtRva(Reader,
|
|
FunctionRva,
|
|
ForwarderMaxLength,
|
|
Forwarder,
|
|
sizeof(Forwarder));
|
|
if (ForwarderStatus == PeAsciiStringOk)
|
|
{
|
|
ShowMessages("\n[%u] ordinal %u name %s forwarder %s", Index, Ordinal, Name, Forwarder);
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("\n[%u] ordinal %u name %s forwarder %s", Index, Ordinal, Name, PeAsciiStatusName(ForwarderStatus));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ShowMessages("\n[%u] ordinal %u name %s RVA %#x", Index, Ordinal, Name, FunctionRva);
|
|
}
|
|
|
|
static VOID
|
|
PeShowExports(PPE_IMAGE_READER Reader, const IMAGE_DATA_DIRECTORY * ExportDirectory)
|
|
{
|
|
const DWORD MaxExports = 0x2000;
|
|
const DWORD MaxDllNameLength = 0x200;
|
|
const DWORD MaxExportNameLength = 0x200;
|
|
|
|
ShowMessages("\n\nExports\n-------");
|
|
|
|
if (ExportDirectory->VirtualAddress == 0 || ExportDirectory->Size == 0)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Export directory :", "empty");
|
|
return;
|
|
}
|
|
|
|
if (!PeRvaContainsRange(ExportDirectory->VirtualAddress,
|
|
ExportDirectory->Size,
|
|
ExportDirectory->VirtualAddress,
|
|
sizeof(IMAGE_EXPORT_DIRECTORY)))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Export directory :", "invalid bounds");
|
|
return;
|
|
}
|
|
|
|
const IMAGE_EXPORT_DIRECTORY * Directory = NULL;
|
|
if (!PeGetPointerAtRva(Reader,
|
|
ExportDirectory->VirtualAddress,
|
|
sizeof(IMAGE_EXPORT_DIRECTORY),
|
|
(const BYTE **)&Directory))
|
|
{
|
|
ShowMessages("\n%-36s%s", "Export directory :", "not mapped");
|
|
return;
|
|
}
|
|
|
|
CHAR DllName[MaxDllNameLength + 1] = {0};
|
|
PE_ASCII_STRING_STATUS DllNameStatus = PeReadAsciiStringAtRva(Reader,
|
|
Directory->Name,
|
|
MaxDllNameLength,
|
|
DllName,
|
|
sizeof(DllName));
|
|
ShowMessages("\n%-36s%s", "DLL name :", DllNameStatus == PeAsciiStringOk ? DllName : PeAsciiStatusName(DllNameStatus));
|
|
ShowMessages("\n%-36s%u", "Ordinal base :", Directory->Base);
|
|
ShowMessages("\n%-36s%u", "Address table count :", Directory->NumberOfFunctions);
|
|
ShowMessages("\n%-36s%u", "Name pointer count :", Directory->NumberOfNames);
|
|
|
|
const BYTE * AddressTablePointer = NULL;
|
|
const BYTE * NamePointerTablePointer = NULL;
|
|
const BYTE * OrdinalTablePointer = NULL;
|
|
|
|
BOOLEAN AddressTableValid = PeValidateExportTable(Reader,
|
|
Directory->AddressOfFunctions,
|
|
Directory->NumberOfFunctions,
|
|
sizeof(DWORD),
|
|
&AddressTablePointer);
|
|
BOOLEAN NamePointerTableValid = PeValidateExportTable(Reader,
|
|
Directory->AddressOfNames,
|
|
Directory->NumberOfNames,
|
|
sizeof(DWORD),
|
|
&NamePointerTablePointer);
|
|
BOOLEAN OrdinalTableValid = PeValidateExportTable(Reader,
|
|
Directory->AddressOfNameOrdinals,
|
|
Directory->NumberOfNames,
|
|
sizeof(WORD),
|
|
&OrdinalTablePointer);
|
|
|
|
if (!AddressTableValid)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "export address table is not mapped");
|
|
return;
|
|
}
|
|
|
|
if (!NamePointerTableValid)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "export name pointer table is not mapped");
|
|
}
|
|
|
|
if (!OrdinalTableValid)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "export ordinal table is not mapped");
|
|
}
|
|
|
|
DWORD NamedCount = Directory->NumberOfNames;
|
|
BOOLEAN NamedCapped = FALSE;
|
|
if (NamedCount > MaxExports)
|
|
{
|
|
NamedCount = MaxExports;
|
|
NamedCapped = TRUE;
|
|
}
|
|
|
|
DWORD FunctionCount = Directory->NumberOfFunctions;
|
|
BOOLEAN FunctionCapped = FALSE;
|
|
if (FunctionCount > MaxExports)
|
|
{
|
|
FunctionCount = MaxExports;
|
|
FunctionCapped = TRUE;
|
|
}
|
|
|
|
BYTE NamedAddressIndexes[MaxExports] = {0};
|
|
|
|
if (NamePointerTableValid && OrdinalTableValid)
|
|
{
|
|
for (DWORD NameIndex = 0; NameIndex < NamedCount; NameIndex++)
|
|
{
|
|
DWORD NameRva = 0;
|
|
WORD AddressIndex = 0;
|
|
|
|
CopyMemory(&NameRva, NamePointerTablePointer + (NameIndex * sizeof(DWORD)), sizeof(NameRva));
|
|
CopyMemory(&AddressIndex, OrdinalTablePointer + (NameIndex * sizeof(WORD)), sizeof(AddressIndex));
|
|
|
|
if (AddressIndex >= Directory->NumberOfFunctions)
|
|
{
|
|
ShowMessages("\n%-36s%u", "Warning, invalid export ordinal index :", AddressIndex);
|
|
continue;
|
|
}
|
|
|
|
DWORD FunctionRva = 0;
|
|
CopyMemory(&FunctionRva, AddressTablePointer + (AddressIndex * sizeof(DWORD)), sizeof(FunctionRva));
|
|
|
|
CHAR ExportName[MaxExportNameLength + 1] = {0};
|
|
PE_ASCII_STRING_STATUS ExportNameStatus = PeReadAsciiStringAtRva(Reader,
|
|
NameRva,
|
|
MaxExportNameLength,
|
|
ExportName,
|
|
sizeof(ExportName));
|
|
if (ExportNameStatus != PeAsciiStringOk)
|
|
{
|
|
ShowMessages("\n%-36s%#x, %s", "Warning, invalid export name RVA :", NameRva, PeAsciiStatusName(ExportNameStatus));
|
|
continue;
|
|
}
|
|
|
|
DWORD DisplayOrdinal = 0;
|
|
if (!PeAddDword(Directory->Base, AddressIndex, &DisplayOrdinal))
|
|
{
|
|
ShowMessages("\n%-36s%u", "Warning, invalid export ordinal index :", AddressIndex);
|
|
continue;
|
|
}
|
|
|
|
if (AddressIndex < FunctionCount)
|
|
{
|
|
NamedAddressIndexes[AddressIndex] = TRUE;
|
|
}
|
|
|
|
PeShowExportEntry(Reader,
|
|
ExportDirectory,
|
|
NameIndex,
|
|
DisplayOrdinal,
|
|
ExportName,
|
|
FunctionRva);
|
|
}
|
|
}
|
|
|
|
for (DWORD AddressIndex = 0; AddressIndex < FunctionCount; AddressIndex++)
|
|
{
|
|
if (NamedAddressIndexes[AddressIndex])
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DWORD FunctionRva = 0;
|
|
CopyMemory(&FunctionRva, AddressTablePointer + (AddressIndex * sizeof(DWORD)), sizeof(FunctionRva));
|
|
|
|
DWORD DisplayOrdinal = 0;
|
|
if (!PeAddDword(Directory->Base, AddressIndex, &DisplayOrdinal))
|
|
{
|
|
ShowMessages("\n%-36s%u", "Warning, invalid export ordinal index :", AddressIndex);
|
|
continue;
|
|
}
|
|
|
|
PeShowExportEntry(Reader,
|
|
ExportDirectory,
|
|
AddressIndex,
|
|
DisplayOrdinal,
|
|
"<ordinal-only>",
|
|
FunctionRva);
|
|
}
|
|
|
|
if (NamedCapped)
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, named export cap reached :", MaxExports);
|
|
}
|
|
|
|
if (FunctionCapped)
|
|
{
|
|
ShowMessages("\n%-36s%#x", "Warning, export cap reached :", MaxExports);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Locates the Rich header signature in a PE file
|
|
*
|
|
* The Rich header is an undocumented Microsoft structure embedded in PE files
|
|
* that contains information about the tools and compilers used during the build process.
|
|
* This function searches for the "Rich" signature string within the DOS stub area.
|
|
*
|
|
* @param DosHeader Pointer to the DOS header structure of the PE file
|
|
* @param Key Output buffer to store the 4-byte XOR key found after "Rich" signature
|
|
*
|
|
* @note The Rich header is located between the DOS header and PE header
|
|
* @note The XOR key is used to decode the actual Rich header entries
|
|
**/
|
|
INT
|
|
FindRichHeader(PIMAGE_DOS_HEADER DosHeader, DWORD FileSize, CHAR Key[])
|
|
{
|
|
//
|
|
// Get base address for offset calculations
|
|
//
|
|
CHAR * BaseAddr = (CHAR *)DosHeader;
|
|
|
|
//
|
|
// Get PE header offset - this defines our search boundary
|
|
//
|
|
DWORD Offset = DosHeader->e_lfanew;
|
|
|
|
if (FileSize < sizeof(IMAGE_DOS_HEADER) ||
|
|
DosHeader->e_magic != IMAGE_DOS_SIGNATURE ||
|
|
Offset < sizeof(IMAGE_DOS_HEADER) ||
|
|
Offset > FileSize ||
|
|
Offset < 8)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Search for "Rich" signature
|
|
// We stop 4 bytes before the PE header to avoid reading beyond bounds
|
|
//
|
|
for (DWORD i = 0; i < Offset - 4; ++i)
|
|
{
|
|
//
|
|
// Check for "Rich" signature (4 ASCII bytes)
|
|
//
|
|
if (BaseAddr[i] == 'R' &&
|
|
BaseAddr[i + 1] == 'i' &&
|
|
BaseAddr[i + 2] == 'c' &&
|
|
BaseAddr[i + 3] == 'h')
|
|
{
|
|
//
|
|
// Extract the 4-byte XOR key that immediately follows "Rich"
|
|
//
|
|
memcpy(Key, BaseAddr + i + 4, 4);
|
|
|
|
//
|
|
// Return the offset where "Rich" signature was found
|
|
//
|
|
return i;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Rich header signature not found
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Decrypts Rich header data using XOR decryption and initializes header info
|
|
*
|
|
* The Rich header is encrypted using a simple XOR cipher with a 4-byte key.
|
|
* This function decrypts the entire header in-place and populates the
|
|
* PEFILE_RICH_HEADER_INFO structure with metadata about the decrypted data.
|
|
*
|
|
* @param RichHeaderPtr Pointer to the raw Rich header data to be decrypted
|
|
* @param RichHeaderSize Size of the Rich header data in bytes
|
|
* @param Key 4-byte XOR key used for decryption
|
|
* @param PeFileRichHeaderInfo structure containing Rich header metadata and buffer information
|
|
*
|
|
* @note The Rich header uses a repeating 4-byte XOR key for encryption
|
|
* @note After decryption, the header contains 16 bytes of metadata followed by 8-byte entries
|
|
* @note Each entry represents one compilation tool (compiler, linker, assembler, etc.)
|
|
*
|
|
*/
|
|
VOID
|
|
FindRichEntries(CHAR * RichHeaderPtr,
|
|
INT RichHeaderSize,
|
|
CHAR Key[],
|
|
PRICH_HEADER_INFO PeFileRichHeaderInfo)
|
|
{
|
|
//
|
|
// Decrypt the entire Rich header using XOR with the 4-byte key
|
|
//
|
|
for (int i = 0; i < RichHeaderSize; i += 4)
|
|
{
|
|
//
|
|
// Apply XOR decryption to each 4-byte block
|
|
//
|
|
for (int x = 0; x < 4; x++)
|
|
{
|
|
RichHeaderPtr[i + x] ^= Key[x];
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the Rich header info structure
|
|
//
|
|
PeFileRichHeaderInfo->Size = RichHeaderSize;
|
|
PeFileRichHeaderInfo->PtrToBuffer = RichHeaderPtr;
|
|
|
|
//
|
|
// Calculate number of entries: subtract 16-byte header, divide by 8 bytes per entry
|
|
//
|
|
PeFileRichHeaderInfo->Entries = (RichHeaderSize - 16) / 8;
|
|
}
|
|
|
|
/**
|
|
* @brief Parses decrypted Rich header data into structured entries
|
|
*
|
|
* After decryption, the Rich header contains a series of 8-byte entries, each describing
|
|
* a compilation tool used during the build process. This function extracts and converts
|
|
* the little-endian binary data into structured RICH_ENTRY objects.
|
|
*
|
|
* Rich header format after decryption:
|
|
* - Bytes 0-15: Header metadata (DanS signature + padding)
|
|
* - Bytes 16+: 8-byte entries (prodID:2, buildID:2, useCount:4)
|
|
*
|
|
* @param RichHeaderSize Size of the entire Rich header in bytes
|
|
* @param RichHeaderPtr Pointer to the decrypted Rich header data
|
|
* @param PeFileRichHeader structure containing the parsed Rich header entries
|
|
*
|
|
* @warning Assumes the Rich header has been properly decrypted first
|
|
*/
|
|
VOID
|
|
SetRichEntries(INT RichHeaderSize, CHAR * RichHeaderPtr, PRICH_HEADER PeFileRichHeader)
|
|
{
|
|
//
|
|
// Start at offset 16 to skip the header metadata, process 8-byte entries
|
|
//
|
|
for (int i = 16; i < RichHeaderSize; i += 8)
|
|
{
|
|
//
|
|
// Extract Product ID (bytes 2-3 of entry, little-endian)
|
|
//
|
|
WORD ProdID = ((UCHAR)RichHeaderPtr[i + 3] << 8) | (UCHAR)RichHeaderPtr[i + 2];
|
|
|
|
//
|
|
// Extract Build ID (bytes 0-1 of entry, little-endian)
|
|
//
|
|
WORD BuildID = ((UCHAR)RichHeaderPtr[i + 1] << 8) | (UCHAR)RichHeaderPtr[i];
|
|
|
|
//
|
|
// Extract Use Count (bytes 4-7 of entry, little-endian 32-bit)
|
|
//
|
|
DWORD UseCount = ((UCHAR)RichHeaderPtr[i + 7] << 24) |
|
|
((UCHAR)RichHeaderPtr[i + 6] << 16) |
|
|
((UCHAR)RichHeaderPtr[i + 5] << 8) |
|
|
(UCHAR)RichHeaderPtr[i + 4];
|
|
|
|
//
|
|
// Store the parsed entry (adjust index: i/8 gives entry number, -2 for header offset)
|
|
//
|
|
PeFileRichHeader->Entries[(i / 8) - 2] = {ProdID, BuildID, UseCount};
|
|
|
|
//
|
|
// Add null terminator entry if this is the last entry
|
|
//
|
|
if (i + 8 >= RichHeaderSize)
|
|
{
|
|
PeFileRichHeader->Entries[(i / 8) - 1] = {0x0000, 0x0000, 0x00000000};
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Determines the size of the Rich header by finding the DanS signature
|
|
*
|
|
* The Rich header begins with the "DanS" signature (after decryption) and ends
|
|
* with the "Rich" signature. This function works backwards from the "Rich" signature
|
|
* to find the beginning and calculate the total size.
|
|
*
|
|
* Rich header structure:
|
|
* [DanS signature] [Padding] [Tool Entries] [Rich signature] [XOR Key]
|
|
*
|
|
* @param Key 4-byte XOR key for decryption (extracted from after "Rich" signature)
|
|
* @param Index Offset where "Rich" signature was found
|
|
* @param DataPtr Pointer to the beginning of the PE file data
|
|
*
|
|
* @return INT Size of the Rich header in bytes, or 0 if DanS signature not found
|
|
*
|
|
*/
|
|
INT
|
|
DecryptRichHeader(CHAR Key[], INT Index, CHAR * DataPtr)
|
|
{
|
|
//
|
|
// Copy the XOR key from the 4 bytes immediately following "Rich"
|
|
//
|
|
memcpy(Key, DataPtr + (Index + 4), 4);
|
|
|
|
//
|
|
// Start searching backwards from just before the "Rich" signature
|
|
//
|
|
INT IndexPointer = Index - 4;
|
|
INT RichHeaderSize = 0;
|
|
|
|
//
|
|
// Search backwards for the DanS signature that marks the beginning
|
|
//
|
|
while (IndexPointer >= 0)
|
|
{
|
|
char TmpChar[4];
|
|
|
|
//
|
|
// Read 4 bytes and decrypt them with the XOR key
|
|
//
|
|
memcpy(TmpChar, DataPtr + IndexPointer, 4);
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
TmpChar[i] ^= Key[i];
|
|
}
|
|
|
|
//
|
|
// Move backwards and increment size counter
|
|
//
|
|
IndexPointer -= 4;
|
|
RichHeaderSize += 4;
|
|
|
|
//
|
|
// Check for DanS signature (0x44='D', 0x61='a' after decryption)
|
|
// Note: Checking bytes 1,0 due to little-endian storage
|
|
//
|
|
if (TmpChar[1] == 0x61 && TmpChar[0] == 0x44)
|
|
{
|
|
return RichHeaderSize;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Show hex dump of sections of PE
|
|
* @param Ptr
|
|
* @param Size
|
|
* @param SecAddress
|
|
*
|
|
* @return VOID
|
|
*/
|
|
|
|
VOID
|
|
PeHexDump(CHAR * Ptr, INT Size, INT SecAddress)
|
|
{
|
|
INT i = 1, Temp = 0;
|
|
|
|
//
|
|
// Buffer to store the character dump displayed at the
|
|
// right side
|
|
//
|
|
WCHAR Buf[18];
|
|
ShowMessages("\n\n%x: |", SecAddress);
|
|
|
|
Buf[Temp] = ' '; // initial space
|
|
Buf[Temp + 16] = ' '; // final space
|
|
Buf[Temp + 17] = 0; // End of Buf
|
|
Temp++; // Temp = 1;
|
|
|
|
for (; i <= Size; i++, Ptr++, Temp++)
|
|
{
|
|
Buf[Temp] = !iswcntrl((*Ptr) & 0xff) ? (*Ptr) & 0xff : '.';
|
|
ShowMessages("%-3.2x", (*Ptr) & 0xff);
|
|
|
|
if (i % 16 == 0)
|
|
{
|
|
//
|
|
// print the character dump to the right
|
|
//
|
|
_putws(Buf);
|
|
if (i + 1 <= Size)
|
|
ShowMessages("%x: ", SecAddress += 16);
|
|
Temp = 0;
|
|
}
|
|
if (i % 4 == 0)
|
|
ShowMessages("| ");
|
|
}
|
|
if (i % 16 != 0)
|
|
{
|
|
Buf[Temp] = 0;
|
|
for (; i % 16 != 0; i++)
|
|
ShowMessages("%-3.2c", ' ');
|
|
_putws(Buf);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Show information about different sections of PE and the dump of sections
|
|
* @param AddressOfFile
|
|
* @param SectionToShow
|
|
* @param Is32Bit
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
PeShowSectionInformationAndDump(const WCHAR * AddressOfFile,
|
|
const CHAR * SectionToShow,
|
|
BOOLEAN Is32Bit)
|
|
{
|
|
RICH_HEADER_INFO PeFileRichHeaderInfo {0};
|
|
RICH_HEADER PeFileRichHeader {0};
|
|
BOOLEAN Result = FALSE, RichFound = FALSE, SectionFound = FALSE;
|
|
HANDLE MapObjectHandle = NULL, FileHandle = INVALID_HANDLE_VALUE; // File Mapping Object
|
|
UINT32 NumberOfSections; // Number of sections
|
|
LPVOID BaseAddr = NULL; // Pointer to the base memory of mapped file
|
|
PIMAGE_DOS_HEADER DosHeader; // Pointer to DOS Header
|
|
PIMAGE_NT_HEADERS32 NtHeader32 = NULL; // Pointer to NT Header 32 bit
|
|
PIMAGE_NT_HEADERS64 NtHeader64 = NULL; // Pointer to NT Header 64 bit
|
|
IMAGE_FILE_HEADER Header; // Pointer to image file header of NT Header
|
|
IMAGE_OPTIONAL_HEADER32 OpHeader32; // Optional Header of PE files present in NT Header structure
|
|
IMAGE_OPTIONAL_HEADER64 OpHeader64; // Optional Header of PE files present in NT Header structure
|
|
const IMAGE_SECTION_HEADER * SecHeader; // Section Header or Section Table Header
|
|
PE_IMAGE_READER Reader;
|
|
IMAGE_DATA_DIRECTORY EmptyDirectory = {0};
|
|
LARGE_INTEGER FileSize;
|
|
CHAR Key[4];
|
|
INT RichHeaderOffset;
|
|
time_t TimeDateStamp;
|
|
|
|
//
|
|
// Open the EXE File
|
|
//
|
|
FileHandle = CreateFileW(AddressOfFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (FileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ShowMessages("err, could not open the file specified\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER))
|
|
{
|
|
CloseHandle(FileHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Mapping Given EXE file to Memory
|
|
//
|
|
MapObjectHandle = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
|
|
if (MapObjectHandle == NULL)
|
|
{
|
|
CloseHandle(FileHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0);
|
|
|
|
if (BaseAddr == NULL)
|
|
{
|
|
CloseHandle(MapObjectHandle);
|
|
CloseHandle(FileHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the DOS Header Base
|
|
//
|
|
DosHeader = (PIMAGE_DOS_HEADER)BaseAddr; // 0x04000000
|
|
|
|
if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE ||
|
|
DosHeader->e_lfanew < sizeof(IMAGE_DOS_HEADER) ||
|
|
(LONGLONG)DosHeader->e_lfanew > FileSize.QuadPart ||
|
|
(LONGLONG)DosHeader->e_lfanew + (LONGLONG)(Is32Bit ? sizeof(IMAGE_NT_HEADERS32) : sizeof(IMAGE_NT_HEADERS64)) > FileSize.QuadPart)
|
|
{
|
|
ShowMessages("\nGiven File is not a valid PE file\n");
|
|
Result = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
if (!PeImageReaderInitialize((const BYTE *)BaseAddr, (SIZE_T)FileSize.QuadPart, &Reader))
|
|
{
|
|
ShowMessages("\nGiven File is not a valid PE file\n");
|
|
Result = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
RichHeaderOffset = FindRichHeader(DosHeader, (DWORD)FileSize.QuadPart, Key);
|
|
|
|
if (RichHeaderOffset != 0)
|
|
{
|
|
char * DataPtr = new char[DosHeader->e_lfanew];
|
|
DWORD BytesRead = 0;
|
|
|
|
BOOL result = ReadFile(FileHandle, DataPtr, DosHeader->e_lfanew, &BytesRead, NULL);
|
|
if (!result || BytesRead != DosHeader->e_lfanew)
|
|
{
|
|
ShowMessages("ReadFile failed or incomplete read");
|
|
}
|
|
int RichHeaderSize = DecryptRichHeader(Key, RichHeaderOffset, DataPtr);
|
|
int IndexPointer = RichHeaderOffset - RichHeaderSize;
|
|
|
|
if (RichHeaderSize < 16 || ((RichHeaderSize - 16) % 8) != 0 || (RichHeaderSize - 16) / 8 == 0 || IndexPointer < 0)
|
|
{
|
|
delete[] DataPtr;
|
|
goto SkipRichHeader;
|
|
}
|
|
|
|
char * richHeaderPtr = new char[RichHeaderSize];
|
|
memcpy(richHeaderPtr, DataPtr + IndexPointer, RichHeaderSize);
|
|
delete[] DataPtr;
|
|
|
|
FindRichEntries(richHeaderPtr, RichHeaderSize, Key, &PeFileRichHeaderInfo);
|
|
PeFileRichHeader.Entries = new RICH_HEADER_ENTRY[PeFileRichHeaderInfo.Entries + 1];
|
|
|
|
SetRichEntries(RichHeaderSize, richHeaderPtr, &PeFileRichHeader);
|
|
RichFound = TRUE;
|
|
}
|
|
|
|
SkipRichHeader:
|
|
|
|
//
|
|
// Check for Valid DOS file
|
|
//
|
|
if (DosHeader->e_magic == IMAGE_DOS_SIGNATURE)
|
|
{
|
|
//
|
|
// Dump the Dos Header info
|
|
//
|
|
ShowMessages("\nValid Dos Exe File\n------------------\n");
|
|
ShowMessages("\nDumping DOS Header Info....\n---------------------------");
|
|
ShowMessages("\n%-36s%s ",
|
|
"Magic number : ",
|
|
DosHeader->e_magic == 0x5a4d ? "MZ" : "-");
|
|
ShowMessages("\n%-36s%#x", "Bytes on last page of file :", DosHeader->e_cblp);
|
|
ShowMessages("\n%-36s%#x", "Pages in file : ", DosHeader->e_cp);
|
|
ShowMessages("\n%-36s%#x", "Relocation : ", DosHeader->e_crlc);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Size of header in paragraphs : ",
|
|
DosHeader->e_cparhdr);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Minimum extra paragraphs needed : ",
|
|
DosHeader->e_minalloc);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Maximum extra paragraphs needed : ",
|
|
DosHeader->e_maxalloc);
|
|
ShowMessages("\n%-36s%#x", "Initial (relative) SS value : ", DosHeader->e_ss);
|
|
ShowMessages("\n%-36s%#x", "Initial SP value : ", DosHeader->e_sp);
|
|
ShowMessages("\n%-36s%#x", "Checksum : ", DosHeader->e_csum);
|
|
ShowMessages("\n%-36s%#x", "Initial IP value : ", DosHeader->e_ip);
|
|
ShowMessages("\n%-36s%#x", "Initial (relative) CS value : ", DosHeader->e_cs);
|
|
ShowMessages("\n%-36s%#x",
|
|
"File address of relocation table : ",
|
|
DosHeader->e_lfarlc);
|
|
ShowMessages("\n%-36s%#x", "Overlay number : ", DosHeader->e_ovno);
|
|
ShowMessages("\n%-36s%#x", "OEM identifier : ", DosHeader->e_oemid);
|
|
ShowMessages("\n%-36s%#x",
|
|
"OEM information(e_oemid specific) :",
|
|
DosHeader->e_oeminfo);
|
|
ShowMessages("\n%-36s%#x", "RVA address of PE header : ", DosHeader->e_lfanew);
|
|
ShowMessages("\n==============================================================="
|
|
"================\n");
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("\nGiven File is not a valid DOS file\n");
|
|
Result = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
if (RichFound)
|
|
{
|
|
ShowMessages("\n===============================================================================\n");
|
|
ShowMessages(" RICH HEADER \n");
|
|
ShowMessages("===============================================================================\n");
|
|
ShowMessages("Entries: %d\n\n", PeFileRichHeaderInfo.Entries);
|
|
ShowMessages("%-10s %-10s %-10s\n", "Build ID", "Prod ID", "Use Count");
|
|
ShowMessages("---------------------------------------\n");
|
|
|
|
for (int i = 0; i < PeFileRichHeaderInfo.Entries; i++)
|
|
{
|
|
ShowMessages("0x%08X 0x%08X %10d\n",
|
|
PeFileRichHeader.Entries[i].BuildID,
|
|
PeFileRichHeader.Entries[i].ProdID,
|
|
PeFileRichHeader.Entries[i].UseCount);
|
|
}
|
|
|
|
ShowMessages("==============Rich Header End ==================\n");
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("=========== Rich Header Not Found ===========\n");
|
|
}
|
|
|
|
//
|
|
// Offset of NT Header is found at 0x3c location in DOS header specified by
|
|
// e_lfanew
|
|
// Get the Base of NT Header(PE Header) = DosHeader + RVA address of PE
|
|
// header
|
|
//
|
|
if (Is32Bit)
|
|
{
|
|
NtHeader32 = (PIMAGE_NT_HEADERS32)((UINT64)(DosHeader) + (DosHeader->e_lfanew));
|
|
}
|
|
else
|
|
{
|
|
NtHeader64 = (PIMAGE_NT_HEADERS64)((UINT64)(DosHeader) + (DosHeader->e_lfanew));
|
|
}
|
|
|
|
//
|
|
// Identify for valid PE file
|
|
//
|
|
if (Is32Bit && NtHeader32->Signature == IMAGE_NT_SIGNATURE)
|
|
{
|
|
ShowMessages("\nValid PE32 file \n-------------\n");
|
|
}
|
|
else if (!Is32Bit && NtHeader64->Signature == IMAGE_NT_SIGNATURE)
|
|
{
|
|
ShowMessages("\nValid PE64 file \n-------------\n");
|
|
}
|
|
else
|
|
{
|
|
ShowMessages("err, the specified file is not a valid PE file");
|
|
Result = FALSE;
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Dump NT Header Info....
|
|
//
|
|
ShowMessages("\nDumping COFF/PE Header "
|
|
"Info....\n--------------------------------");
|
|
ShowMessages("\n%-36s%s", "Signature :", "PE");
|
|
|
|
//
|
|
// Get the IMAGE FILE HEADER Structure
|
|
//
|
|
if (Is32Bit)
|
|
{
|
|
Header = NtHeader32->FileHeader;
|
|
}
|
|
else
|
|
{
|
|
Header = NtHeader64->FileHeader;
|
|
}
|
|
|
|
//
|
|
// Determine Machine Architecture
|
|
//
|
|
ShowMessages("\n%-36s", "Machine Architecture :");
|
|
|
|
//
|
|
// Only few are determined (for remaining refer
|
|
// to the above specification)
|
|
//
|
|
switch (Header.Machine)
|
|
{
|
|
case 0x0:
|
|
ShowMessages("All ");
|
|
break;
|
|
case 0x14d:
|
|
ShowMessages("Intel i860");
|
|
break;
|
|
case 0x14c:
|
|
ShowMessages("Intel i386, i486, i586");
|
|
break;
|
|
case 0x200:
|
|
ShowMessages("Intel Itanium processor");
|
|
break;
|
|
case 0x8664:
|
|
ShowMessages("AMD x64");
|
|
break;
|
|
case 0x162:
|
|
ShowMessages("MIPS R3000");
|
|
break;
|
|
case 0x166:
|
|
ShowMessages("MIPS R4000");
|
|
break;
|
|
case 0x183:
|
|
ShowMessages("DEC Alpha AXP");
|
|
break;
|
|
default:
|
|
ShowMessages("Not Found");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Determine the characteristics of the given file
|
|
//
|
|
ShowMessages("\n%-36s", "Characteristics : ");
|
|
if ((Header.Characteristics & 0x0002) == 0x0002)
|
|
ShowMessages("Executable Image, ");
|
|
if ((Header.Characteristics & 0x0020) == 0x0020)
|
|
ShowMessages("Application can address > 2GB, ");
|
|
if ((Header.Characteristics & 0x1000) == 0x1000)
|
|
ShowMessages("System file (Kernel Mode Driver(I think)), ");
|
|
if ((Header.Characteristics & 0x2000) == 0x2000)
|
|
ShowMessages("Dll file, ");
|
|
if ((Header.Characteristics & 0x4000) == 0x4000)
|
|
ShowMessages("Application runs only in Uniprocessor, ");
|
|
|
|
//
|
|
// Determine Time Stamp
|
|
//
|
|
TimeDateStamp = Header.TimeDateStamp;
|
|
ShowMessages("\n%-36s%s",
|
|
"Time Stamp :",
|
|
ctime(&TimeDateStamp));
|
|
|
|
//
|
|
// Determine number of sections
|
|
//
|
|
ShowMessages("%-36s%d", "No.sections(size) :", Header.NumberOfSections);
|
|
ShowMessages("\n%-36s%d", "No.entries in symbol table :", Header.NumberOfSymbols);
|
|
ShowMessages("\n%-36s%d",
|
|
"Size of optional header :",
|
|
Header.SizeOfOptionalHeader);
|
|
ShowMessages("\n%-36s%#x", "Raw machine value :", Header.Machine);
|
|
ShowMessages("\n%-36s%#x", "Pointer to symbol table :", Header.PointerToSymbolTable);
|
|
ShowMessages("\n%-36s%#x", "Raw characteristics value :", Header.Characteristics);
|
|
|
|
ShowMessages("\n\nDumping PE Optional Header "
|
|
"Info....\n-----------------------------------");
|
|
|
|
if (Is32Bit)
|
|
{
|
|
//
|
|
// Info about Optional Header
|
|
//
|
|
OpHeader32 = NtHeader32->OptionalHeader;
|
|
ShowMessages("\n\nInfo of optional Header\n-----------------------");
|
|
ShowMessages("\n%-36s%#x",
|
|
"Address of Entry Point : ",
|
|
OpHeader32.AddressOfEntryPoint);
|
|
ShowMessages("\n%-36s%#x", "Raw optional header magic :", OpHeader32.Magic);
|
|
PeShowEntrypointFileOffset(&Reader, OpHeader32.AddressOfEntryPoint);
|
|
ShowMessages("\n%-36s%#llx", "Base Address of the Image : ", OpHeader32.ImageBase);
|
|
ShowMessages("\n%-36s%s", "SubSystem type : ", PeGetSubsystemName(OpHeader32.Subsystem));
|
|
ShowMessages("\n%-36s%s", "Given file is a : ", OpHeader32.Magic == 0x20b ? "PE32+(64)" : "PE32");
|
|
ShowMessages("\n%-36s%#x", "File Alignment :", OpHeader32.FileAlignment);
|
|
ShowMessages("\n%-36s%#x", "Size of Image :", OpHeader32.SizeOfImage);
|
|
ShowMessages("\n%-36s%#x", "Size of Headers :", OpHeader32.SizeOfHeaders);
|
|
ShowMessages("\n%-36s%#x", "Raw DLL characteristics value :", OpHeader32.DllCharacteristics);
|
|
ShowMessages("\n%-36s", "DLL characteristics flags :");
|
|
PeShowDllCharacteristics(OpHeader32.DllCharacteristics);
|
|
ShowMessages("\n%-36s%d", "Size of code segment(.text) : ", OpHeader32.SizeOfCode);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Base address of code segment(RVA) :",
|
|
OpHeader32.BaseOfCode);
|
|
ShowMessages("\n%-36s%d",
|
|
"Size of Initialized data : ",
|
|
OpHeader32.SizeOfInitializedData);
|
|
|
|
ShowMessages("\n%-36s%#x",
|
|
"Base address of data segment(RVA) :",
|
|
OpHeader32.BaseOfData);
|
|
|
|
ShowMessages("\n%-36s%#x", "Section Alignment :", OpHeader32.SectionAlignment);
|
|
ShowMessages("\n%-36s%d", "Major Linker Version : ", OpHeader32.MajorLinkerVersion);
|
|
ShowMessages("\n%-36s%d", "Minor Linker Version : ", OpHeader32.MinorLinkerVersion);
|
|
PeShowDataDirectories(&Reader, OpHeader32.DataDirectory, OpHeader32.NumberOfRvaAndSizes);
|
|
PeShowTls(&Reader,
|
|
OpHeader32.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_TLS ? &OpHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS] : &EmptyDirectory,
|
|
TRUE,
|
|
OpHeader32.ImageBase);
|
|
PeShowDebug(&Reader,
|
|
OpHeader32.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_DEBUG ? &OpHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG] : &EmptyDirectory);
|
|
PeShowLoadConfig(&Reader,
|
|
OpHeader32.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG ? &OpHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG] : &EmptyDirectory,
|
|
TRUE);
|
|
PeShowImports(&Reader,
|
|
OpHeader32.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT ? &OpHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] : &EmptyDirectory,
|
|
TRUE);
|
|
PeShowExports(&Reader,
|
|
OpHeader32.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_EXPORT ? &OpHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] : &EmptyDirectory);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Info about Optional Header
|
|
//
|
|
OpHeader64 = NtHeader64->OptionalHeader;
|
|
ShowMessages("\n\nInfo of optional Header\n-----------------------");
|
|
ShowMessages("\n%-36s%#x",
|
|
"Address of Entry Point : ",
|
|
OpHeader64.AddressOfEntryPoint);
|
|
ShowMessages("\n%-36s%#x", "Raw optional header magic :", OpHeader64.Magic);
|
|
PeShowEntrypointFileOffset(&Reader, OpHeader64.AddressOfEntryPoint);
|
|
ShowMessages("\n%-36s%#llx", "Base Address of the Image : ", OpHeader64.ImageBase);
|
|
ShowMessages("\n%-36s%s", "SubSystem type : ", PeGetSubsystemName(OpHeader64.Subsystem));
|
|
ShowMessages("\n%-36s%s", "Given file is a : ", OpHeader64.Magic == 0x20b ? "PE32+(64)" : "PE32");
|
|
ShowMessages("\n%-36s%#x", "File Alignment :", OpHeader64.FileAlignment);
|
|
ShowMessages("\n%-36s%#x", "Size of Image :", OpHeader64.SizeOfImage);
|
|
ShowMessages("\n%-36s%#x", "Size of Headers :", OpHeader64.SizeOfHeaders);
|
|
ShowMessages("\n%-36s%#x", "Raw DLL characteristics value :", OpHeader64.DllCharacteristics);
|
|
ShowMessages("\n%-36s", "DLL characteristics flags :");
|
|
PeShowDllCharacteristics(OpHeader64.DllCharacteristics);
|
|
ShowMessages("\n%-36s%d", "Size of code segment(.text) : ", OpHeader64.SizeOfCode);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Base address of code segment(RVA) :",
|
|
OpHeader64.BaseOfCode);
|
|
ShowMessages("\n%-36s%d",
|
|
"Size of Initialized data : ",
|
|
OpHeader64.SizeOfInitializedData);
|
|
|
|
ShowMessages("\n%-36s%#x", "Section Alignment :", OpHeader64.SectionAlignment);
|
|
ShowMessages("\n%-36s%d", "Major Linker Version : ", OpHeader64.MajorLinkerVersion);
|
|
ShowMessages("\n%-36s%d", "Minor Linker Version : ", OpHeader64.MinorLinkerVersion);
|
|
PeShowDataDirectories(&Reader, OpHeader64.DataDirectory, OpHeader64.NumberOfRvaAndSizes);
|
|
PeShowTls(&Reader,
|
|
OpHeader64.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_TLS ? &OpHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS] : &EmptyDirectory,
|
|
FALSE,
|
|
OpHeader64.ImageBase);
|
|
PeShowDebug(&Reader,
|
|
OpHeader64.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_DEBUG ? &OpHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG] : &EmptyDirectory);
|
|
PeShowLoadConfig(&Reader,
|
|
OpHeader64.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG ? &OpHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG] : &EmptyDirectory,
|
|
FALSE);
|
|
PeShowImports(&Reader,
|
|
OpHeader64.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT ? &OpHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] : &EmptyDirectory,
|
|
FALSE);
|
|
PeShowExports(&Reader,
|
|
OpHeader64.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_EXPORT ? &OpHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] : &EmptyDirectory);
|
|
}
|
|
|
|
ShowMessages("\n\nDumping Sections Header "
|
|
"Info....\n--------------------------------");
|
|
|
|
//
|
|
// Retrieve a pointer to First Section Header(or Section Table Entry)
|
|
//
|
|
SecHeader = Reader.SectionHeaders;
|
|
NumberOfSections = Reader.FileHeader->NumberOfSections;
|
|
|
|
for (UINT32 i = 0; i < NumberOfSections; i++, SecHeader++)
|
|
{
|
|
CHAR SectionName[IMAGE_SIZEOF_SHORT_NAME + 1];
|
|
const BYTE * Pointer = NULL;
|
|
const CHAR * RawDataBounds = "empty";
|
|
|
|
PeImageReaderGetSectionName(SecHeader, SectionName, sizeof(SectionName));
|
|
|
|
if (SecHeader->SizeOfRawData != 0)
|
|
{
|
|
RawDataBounds = PeImageReaderGetPointerAtOffset(&Reader,
|
|
SecHeader->PointerToRawData,
|
|
SecHeader->SizeOfRawData,
|
|
&Pointer)
|
|
? "valid"
|
|
: "invalid";
|
|
}
|
|
|
|
ShowMessages("\n\nSection Info (%d of %d)", i + 1, NumberOfSections);
|
|
|
|
ShowMessages("\n---------------------");
|
|
ShowMessages("\n%-36s%s", "Section Header name : ", SectionName);
|
|
ShowMessages("\n%-36s%#x",
|
|
"ActualSize of code or data : ",
|
|
SecHeader->Misc.VirtualSize);
|
|
ShowMessages("\n%-36s%#x", "Virtual Address(RVA) :", SecHeader->VirtualAddress);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Size of raw data (rounded to FA) : ",
|
|
SecHeader->SizeOfRawData);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Pointer to Raw Data : ",
|
|
SecHeader->PointerToRawData);
|
|
ShowMessages("\n%-36s%s", "Raw data bounds :", RawDataBounds);
|
|
if (SecHeader->SizeOfRawData != 0 && SecHeader->Misc.VirtualSize > SecHeader->SizeOfRawData)
|
|
{
|
|
ShowMessages("\n%-36s%s", "Warning :", "virtual size is larger than raw data");
|
|
}
|
|
ShowMessages("\n%-36s%#x",
|
|
"Pointer to Relocations : ",
|
|
SecHeader->PointerToRelocations);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Pointer to Line numbers : ",
|
|
SecHeader->PointerToLinenumbers);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Number of relocations : ",
|
|
SecHeader->NumberOfRelocations);
|
|
ShowMessages("\n%-36s%#x",
|
|
"Number of line numbers : ",
|
|
SecHeader->NumberOfLinenumbers);
|
|
ShowMessages("\n%-36s%s", "Characteristics : ", "Contains ");
|
|
if ((SecHeader->Characteristics & 0x20) == 0x20)
|
|
ShowMessages("executable code, ");
|
|
if ((SecHeader->Characteristics & 0x40) == 0x40)
|
|
ShowMessages("initialized data, ");
|
|
if ((SecHeader->Characteristics & 0x80) == 0x80)
|
|
ShowMessages("uninitialized data, ");
|
|
if ((SecHeader->Characteristics & 0x200) == 0x200)
|
|
ShowMessages("comments and linker commands, ");
|
|
if ((SecHeader->Characteristics & 0x10000000) == 0x10000000)
|
|
ShowMessages("shareable data(via DLLs), ");
|
|
if ((SecHeader->Characteristics & 0x40000000) == 0x40000000)
|
|
ShowMessages("Readable, ");
|
|
if ((SecHeader->Characteristics & 0x80000000) == 0x80000000)
|
|
ShowMessages("Writable, ");
|
|
|
|
//
|
|
// show the hex dump if the user needs it
|
|
//
|
|
if (SectionToShow != NULL)
|
|
{
|
|
if (!_strcmpi(SectionToShow, SectionName))
|
|
{
|
|
SectionFound = TRUE;
|
|
|
|
if (SecHeader->SizeOfRawData != 0)
|
|
{
|
|
if (Pointer == NULL)
|
|
{
|
|
ShowMessages("\nerr, invalid section raw data\n");
|
|
continue;
|
|
}
|
|
|
|
if (Is32Bit)
|
|
{
|
|
PeHexDump((char *)Pointer,
|
|
SecHeader->SizeOfRawData,
|
|
OpHeader32.ImageBase + SecHeader->VirtualAddress);
|
|
}
|
|
else
|
|
{
|
|
PeHexDump((char *)Pointer,
|
|
SecHeader->SizeOfRawData,
|
|
(int)(OpHeader64.ImageBase + SecHeader->VirtualAddress));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SectionToShow != NULL && !SectionFound)
|
|
{
|
|
ShowMessages("\nerr, section '%s' was not found\n", SectionToShow);
|
|
}
|
|
|
|
ShowMessages("\n==============================================================="
|
|
"================\n");
|
|
|
|
//
|
|
// Set result to true
|
|
//
|
|
Result = TRUE;
|
|
|
|
Finished:
|
|
//
|
|
// Unmap and close the handles
|
|
//
|
|
if (PeFileRichHeaderInfo.PtrToBuffer != NULL)
|
|
{
|
|
delete[] PeFileRichHeaderInfo.PtrToBuffer;
|
|
}
|
|
|
|
if (PeFileRichHeader.Entries != NULL)
|
|
{
|
|
delete[] PeFileRichHeader.Entries;
|
|
}
|
|
|
|
if (BaseAddr != NULL)
|
|
{
|
|
UnmapViewOfFile(BaseAddr);
|
|
}
|
|
|
|
if (MapObjectHandle != NULL)
|
|
{
|
|
CloseHandle(MapObjectHandle);
|
|
}
|
|
|
|
if (FileHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(FileHandle);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief Detect whether PE is a 32-bit PE or 64-bit PE
|
|
* @param AddressOfFile
|
|
* @param Is32Bit
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
PeIsPE32BitOr64Bit(const WCHAR * AddressOfFile, PBOOLEAN Is32Bit)
|
|
{
|
|
BOOLEAN Result = FALSE;
|
|
HANDLE MapObjectHandle, FileHandle; // File Mapping Object
|
|
LPVOID BaseAddr; // Pointer to the base memory of mapped file
|
|
PIMAGE_DOS_HEADER DosHeader; // Pointer to DOS Header
|
|
PIMAGE_NT_HEADERS32 NtHeader32 = NULL; // Pointer to NT Header 32 bit
|
|
PE_IMAGE_READER Reader;
|
|
LARGE_INTEGER FileSize;
|
|
|
|
//
|
|
// Open the EXE File
|
|
//
|
|
FileHandle = CreateFileW(AddressOfFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (FileHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
ShowMessages("err, unable to read the file (%x)\n", GetLastError());
|
|
return FALSE;
|
|
};
|
|
|
|
if (!GetFileSizeEx(FileHandle, &FileSize) || FileSize.QuadPart < (LONGLONG)sizeof(IMAGE_DOS_HEADER))
|
|
{
|
|
CloseHandle(FileHandle);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Mapping Given EXE file to Memory
|
|
//
|
|
MapObjectHandle =
|
|
CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
|
|
if (MapObjectHandle == NULL)
|
|
{
|
|
CloseHandle(FileHandle);
|
|
|
|
ShowMessages("err, unable to create file mappings (%x)\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
BaseAddr = MapViewOfFile(MapObjectHandle, FILE_MAP_READ, 0, 0, 0);
|
|
|
|
if (BaseAddr == NULL)
|
|
{
|
|
CloseHandle(MapObjectHandle);
|
|
CloseHandle(FileHandle);
|
|
|
|
ShowMessages("err, unable to create map view of file (%x)\n", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the DOS Header Base
|
|
//
|
|
DosHeader = (PIMAGE_DOS_HEADER)BaseAddr; // 0x04000000
|
|
|
|
//
|
|
// Check for Valid DOS file
|
|
//
|
|
if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
|
|
{
|
|
Result = FALSE;
|
|
|
|
ShowMessages("err, the selected file is not in a valid PE format\n");
|
|
goto Finished;
|
|
}
|
|
|
|
if (DosHeader->e_lfanew < sizeof(IMAGE_DOS_HEADER) ||
|
|
(LONGLONG)DosHeader->e_lfanew + (LONGLONG)sizeof(IMAGE_NT_HEADERS32) > FileSize.QuadPart)
|
|
{
|
|
Result = FALSE;
|
|
|
|
ShowMessages("err, invalid image NT header offset\n");
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Offset of NT Header is found at 0x3c location in DOS header specified by
|
|
// e_lfanew
|
|
// Get the Base of NT Header(PE Header) = DosHeader + RVA address of PE
|
|
// header
|
|
//
|
|
|
|
NtHeader32 = (PIMAGE_NT_HEADERS32)((UINT64)(DosHeader) + (DosHeader->e_lfanew));
|
|
|
|
//
|
|
// Identify for valid PE file
|
|
//
|
|
if (NtHeader32->Signature != IMAGE_NT_SIGNATURE)
|
|
{
|
|
Result = FALSE;
|
|
|
|
ShowMessages("err, invalid image NT signature\n");
|
|
goto Finished;
|
|
}
|
|
|
|
if (!PeImageReaderInitialize((const BYTE *)BaseAddr, (SIZE_T)FileSize.QuadPart, &Reader))
|
|
{
|
|
Result = FALSE;
|
|
|
|
ShowMessages("err, the selected file is not in a valid PE format\n");
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Only few are determined (for remaining refer
|
|
// to the above specification)
|
|
//
|
|
switch (Reader.FileHeader->Machine)
|
|
{
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
*Is32Bit = TRUE;
|
|
Result = TRUE;
|
|
goto Finished;
|
|
break;
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
*Is32Bit = FALSE;
|
|
Result = TRUE;
|
|
goto Finished;
|
|
break;
|
|
default:
|
|
ShowMessages("err, PE file is not i386 or AMD64; thus, it's not supported "
|
|
"in HyperDbg\n");
|
|
Result = FALSE;
|
|
goto Finished;
|
|
break;
|
|
}
|
|
|
|
Finished:
|
|
//
|
|
// Unmap and close the handles
|
|
//
|
|
UnmapViewOfFile(BaseAddr);
|
|
CloseHandle(MapObjectHandle);
|
|
CloseHandle(FileHandle);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the syscall number of a given Nt function
|
|
* @param NtFunctionName
|
|
*
|
|
* @return UINT32
|
|
*/
|
|
UINT32
|
|
PeGetSyscallNumber(LPCSTR NtFunctionName)
|
|
{
|
|
HMODULE DllHandle = NULL;
|
|
|
|
//
|
|
// Load ntdll.dll to get the address of the Nt function
|
|
//
|
|
DllHandle = LoadLibraryA("ntdll.dll");
|
|
|
|
if (!DllHandle)
|
|
{
|
|
ShowMessages("err, failed to load ntdll.dll\n");
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Choose any Nt function
|
|
//
|
|
VOID * TargetFunc = GetProcAddress(DllHandle, NtFunctionName);
|
|
|
|
if (!TargetFunc)
|
|
{
|
|
//
|
|
// If we failed to get the address of the Nt function,
|
|
// maybe it's from win32u.dll
|
|
//
|
|
DllHandle = LoadLibraryA("win32u.dll");
|
|
|
|
if (!DllHandle)
|
|
{
|
|
ShowMessages("err, failed to load win32u.dll\n");
|
|
return NULL;
|
|
}
|
|
|
|
TargetFunc = GetProcAddress(DllHandle, NtFunctionName);
|
|
|
|
if (!TargetFunc)
|
|
{
|
|
ShowMessages("err, failed to get address of %s\n", NtFunctionName);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// By default, we send 30 bytes to the disassembler,
|
|
// since usually the syscall handler is less than 30 bytes,
|
|
// and we want to avoid disassembling another function
|
|
//
|
|
return HyperDbgGetImmediateValueOnEaxForSyscallNumber((UCHAR *)TargetFunc, 30, TRUE);
|
|
}
|