12#define min(a,b) (((a) < (b)) ? (a) : (b))
17 #define RTL_VERIFY_FLAGS_MAJOR_VERSION 0
18 #define RTL_VERIFY_FLAGS_MINOR_VERSION 1
19 #define RTL_VERIFY_FLAGS_BUILD_NUMBERS 2
20 #define RTL_VERIFY_FLAGS_DEFAULT RTL_VERIFY_FLAGS_MAJOR_VERSION|RTL_VERIFY_FLAGS_MINOR_VERSION|RTL_VERIFY_FLAGS_BUILD_NUMBERS
161 static void NTAPI RtlCurrentVersion(_Out_
PNtVersion pVersion) {
162 RtlGetNtVersionNumbers(
163 &pVersion->MajorVersion,
164 &pVersion->MinorVersion,
165 &pVersion->BuildNumber
169 static BOOL NTAPI RtlIsWindowsVersionOrGreater(
170 _In_ ULONG MajorVersion,
171 _In_ ULONG MinorVersion,
172 _In_ ULONG BuildNumber
175 RtlCurrentVersion(&version);
176 if (version.MajorVersion == MajorVersion) {
177 if (version.MinorVersion == MinorVersion)
return version.BuildNumber >= BuildNumber;
178 else return (version.MinorVersion > MinorVersion);
180 else return version.MajorVersion > MajorVersion;
183 static BOOL NTAPI RtlVerifyVersion(
184 _In_ ULONG MajorVersion,
185 _In_ ULONG MinorVersion,
186 _In_ ULONG BuildNumber,
190 RtlCurrentVersion(&version);
191 if (version.MajorVersion == MajorVersion &&
197 static int NTAPI RtlCaptureImageExceptionValues(PVOID BaseAddress, PDWORD SEHandlerTable, PDWORD SEHandlerCount) {
198 PIMAGE_LOAD_CONFIG_DIRECTORY pLoadConfigDirectory =
nullptr;
199 PIMAGE_COR20_HEADER pCor20 =
nullptr;
202 auto hdrs =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(BaseAddress));
204 if (hdrs->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) {
205 *SEHandlerTable = *SEHandlerCount = -1;
210 pLoadConfigDirectory = (
decltype(pLoadConfigDirectory))RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &Size);
211 if (pLoadConfigDirectory) {
212 if (Size == 0x40 && pLoadConfigDirectory->Size >= 0x48u) {
213 if (pLoadConfigDirectory->SEHandlerTable && pLoadConfigDirectory->SEHandlerCount) {
214 *SEHandlerTable = pLoadConfigDirectory->SEHandlerTable;
215 return *SEHandlerCount = pLoadConfigDirectory->SEHandlerCount;
221 pCor20 = (
decltype(pCor20))RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &Size);
222 *SEHandlerTable = *SEHandlerCount = ((pCor20 && pCor20->Flags & 1) ? -1 : 0);
228 if (RtlIsWindowsVersionOrGreater(6, 2, 0)) {
232 return entry->DllBase ==
nullptr;
236 static NTSTATUS NTAPI RtlFindMemoryBlockFromModuleSection(
237 _In_ HMODULE ModuleHandle,
238 _In_ LPCSTR SectionName,
241 NTSTATUS status = STATUS_SUCCESS;
244#define RtlFindMemoryBlockFromModuleSection__leave __leave
246#define RtlFindMemoryBlockFromModuleSection__leave return status
249 LOG_DEBUG(
"Searching in section %s in module %p.", SectionName, ModuleHandle);
257 if (!SearchContext->SearchPattern || !SearchContext->PatternSize) {
258 SearchContext->Result =
nullptr;
259 SearchContext->MemoryBlockSize = 0;
260 status = STATUS_INVALID_PARAMETER;
264 if (SearchContext->Result) {
265 ++SearchContext->Result;
266 --SearchContext->MemoryBlockSize;
274 auto headers =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(ModuleHandle));
275 PIMAGE_SECTION_HEADER section =
nullptr;
278 section = IMAGE_FIRST_SECTION(headers);
279 for (WORD i = 0; i < headers->FileHeader.NumberOfSections; ++i) {
280 if (!_strnicmp(SectionName,
reinterpret_cast<LPCSTR
>(section->Name), 8)) {
281 SearchContext->Result =
reinterpret_cast<LPBYTE
>(ModuleHandle) + section->VirtualAddress;
282 SearchContext->MemoryBlockSize = section->Misc.VirtualSize;
289 if (!SearchContext->Result || !SearchContext->MemoryBlockSize || SearchContext->MemoryBlockSize < SearchContext->PatternSize) {
290 SearchContext->Result =
nullptr;
291 SearchContext->MemoryBlockSize = 0;
292 status = STATUS_NOT_FOUND;
297 status = STATUS_INVALID_PARAMETER_1;
306 LPBYTE end = SearchContext->Result + SearchContext->MemoryBlockSize - SearchContext->PatternSize;
307 while (SearchContext->Result <= end) {
308 if (RtlCompareMemory(SearchContext->SearchPattern, SearchContext->Result, SearchContext->PatternSize) == SearchContext->PatternSize) {
312 ++SearchContext->Result;
313 --SearchContext->MemoryBlockSize;
320 SearchContext->Result =
nullptr;
321 SearchContext->MemoryBlockSize = 0;
322 status = STATUS_NOT_FOUND;
325 status = GetExceptionCode();
332 static PVOID MrdataBase =
nullptr;
333 static SIZE_T size = 0;
340 MEMORY_BASIC_INFORMATION mbi= { 0 };
341 status = NtQueryVirtualMemory(NtCurrentProcess(), mrdata, MemoryBasicInformation, &mbi,
sizeof(mbi),
nullptr);
342 if (!NT_SUCCESS(status))
return status;
343 MrdataBase = mbi.BaseAddress;
344 size = mbi.RegionSize;
349 return NtProtectVirtualMemory(NtCurrentProcess(), &tmp, &tmp_len, Protect, &old);
352 static PVOID RtlFindInvertedFunctionTable() {
366 HMODULE hModule =
nullptr, hNtdll = GetModuleHandleW(L
"ntdll.dll");
367 LPCSTR lpSectionName =
".data";
368 if (!hNtdll)
return nullptr;
369 auto NtdllHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hNtdll));
370 PIMAGE_NT_HEADERS ModuleHeaders =
nullptr;
371 _RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 entry = { 0 };
372 PIMAGE_DATA_DIRECTORY dir =
nullptr;
374 SearchContext.
SearchPattern =
reinterpret_cast<LPBYTE
>(&entry);
375 SearchContext.PatternSize =
sizeof(entry);
376 RtlSecureZeroMemory(&entry,
sizeof(entry));
381 ModuleHeaders = NtdllHeaders;
385 else if (RtlIsWindowsVersionOrGreater(6, 3, 0)) {
387 ModuleHeaders = NtdllHeaders;
388 lpSectionName =
".mrdata";
391 PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList,
392 ListEntry = ListHead->Flink;
393 PLDR_DATA_TABLE_ENTRY CurEntry =
nullptr;
394 while (ListEntry != ListHead) {
395 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
396 ListEntry = ListEntry->Flink;
397 hModule =
reinterpret_cast<HMODULE
>(
398 hModule ?
reinterpret_cast<HMODULE
>(
min(
399 reinterpret_cast<uintptr_t
>(hModule),
400 reinterpret_cast<uintptr_t
>(CurEntry->DllBase)
401 )) : CurEntry->DllBase
404 if (hModule) ModuleHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hModule));
406 if (!hModule || !ModuleHeaders || !hNtdll || !NtdllHeaders)
return nullptr;
407 dir = &ModuleHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
409 entry.ExceptionDirectory = dir->Size ?
410 reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY
>(
411 reinterpret_cast<size_t>(hModule) + dir->VirtualAddress
413 entry.ImageBase =
reinterpret_cast<PVOID
>(hModule);
414 entry.ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
415 entry.ExceptionDirectorySize = dir->Size;
417 while (NT_SUCCESS(RtlFindMemoryBlockFromModuleSection(hNtdll, lpSectionName, &SearchContext))) {
419 if (RtlIsWindowsVersionOrGreater(6, 2, 0) && tab->MaxCount == 0x200 && !tab->Overflow)
return tab;
420 else if (tab->MaxCount == 0x200 && !tab->Epoch)
return tab;
437 HMODULE hModule =
nullptr, hNtdll = GetModuleHandleW(L
"ntdll.dll");
438 auto NtdllHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hNtdll));
439 PIMAGE_NT_HEADERS ModuleHeaders =
nullptr;
440 _RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 entry = { 0 };
441 RtlSecureZeroMemory(&entry,
sizeof(entry));
442 LPCSTR lpSectionName =
".data";
444 SearchContext.
SearchPattern =
reinterpret_cast<LPBYTE
>(&entry);
445 SearchContext.PatternSize =
sizeof(entry);
446 PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InMemoryOrderModuleList,
447 ListEntry = ListHead->Flink;
448 PLDR_DATA_TABLE_ENTRY CurEntry =
nullptr;
449 DWORD SEHTable, SEHCount;
452 if (RtlIsWindowsVersionOrGreater(6, 3, 0)) lpSectionName =
".mrdata";
453 else if (!RtlIsWindowsVersionOrGreater(6, 2, 0)) Offset = 0xC;
455 while (ListEntry != ListHead) {
456 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
457 ListEntry = ListEntry->Flink;
458 if (IsModuleUnloaded(CurEntry))
460 if (CurEntry->DllBase == hNtdll && Offset == 0x20)
463 hModule =
reinterpret_cast<HMODULE
>(
464 hModule ?
reinterpret_cast<HMODULE
>(
min(
465 reinterpret_cast<uintptr_t
>(hModule),
466 reinterpret_cast<uintptr_t
>(CurEntry->DllBase)
467 )) : CurEntry->DllBase
470 ModuleHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hModule));
471 if (!hModule || !ModuleHeaders || !hNtdll || !NtdllHeaders)
return nullptr;
473 RtlCaptureImageExceptionValues(hModule, &SEHTable, &SEHCount);
475 if (RtlIsWindowsVersionOrGreater(6, 2, 0)) {
479 entry2->ImageBase =
reinterpret_cast<PVOID
>(hModule);
480 entry2->ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
481 entry2->SEHandlerCount = SEHCount;
484 entry.EntrySEHandlerTableEncoded = RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(SEHTable));
485 entry.ImageBase =
reinterpret_cast<PVOID
>(hModule);
486 entry.ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
487 entry.SEHandlerCount = SEHCount;
490 while (NT_SUCCESS(RtlFindMemoryBlockFromModuleSection(hNtdll, lpSectionName, &SearchContext))) {
494 if (RtlIsWindowsVersionOrGreater(6, 2, 0) && tab->MaxCount == 0x200 && !tab->NextEntrySEHandlerTableEncoded)
return tab;
495 else if (tab->MaxCount == 0x200 && !tab->Overflow)
return tab;
501 static VOID RtlpInsertInvertedFunctionTable(
503 _In_ PVOID ImageBase,
504 _In_ ULONG SizeOfImage) {
506 ULONG CurrentSize = InvertedTable->Count;
507 PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTable =
nullptr;
508 ULONG SizeOfTable = 0;
509 BOOL IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0);
510 ULONG Index =
static_cast<ULONG
>(IsWin8OrGreater);
512 if (CurrentSize != InvertedTable->MaxCount) {
513 if (CurrentSize != 0) {
514 while (Index < CurrentSize) {
515 if (ImageBase < InvertedTable->Entries[Index].ImageBase)
break;
520 if (Index != CurrentSize) {
521 RtlMoveMemory(&InvertedTable->Entries[Index + 1],
522 &InvertedTable->Entries[Index],
527 FunctionTable =
reinterpret_cast<decltype(FunctionTable)
>(RtlImageDirectoryEntryToData(ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &SizeOfTable));
528 InvertedTable->Entries[Index].ExceptionDirectory = FunctionTable;
529 InvertedTable->Entries[Index].ImageBase = ImageBase;
530 InvertedTable->Entries[Index].ImageSize = SizeOfImage;
531 InvertedTable->Entries[Index].ExceptionDirectorySize = SizeOfTable;
532 InvertedTable->Count++;
533 LOG_INFO(
"Exception table was set.");
536 IsWin8OrGreater ? (InvertedTable->Overflow = TRUE) : (InvertedTable->Epoch = TRUE);
540 DWORD ptr = 0, count = 0;
541 BOOL IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0);
542 ULONG Index = IsWin8OrGreater ? 1 : 0;
544 if (InvertedTable->Count == InvertedTable->MaxCount) {
545 if (IsWin8OrGreater)InvertedTable->NextEntrySEHandlerTableEncoded = TRUE;
546 else InvertedTable->Overflow = TRUE;
549 while (Index < InvertedTable->Count) {
550 if (ImageBase < (IsWin8OrGreater ?
552 InvertedTable->Entries[Index].ImageBase))
556 if (Index != InvertedTable->Count) {
557 if (IsWin8OrGreater) {
558 RtlMoveMemory(&InvertedTable->Entries[Index + 1], &InvertedTable->Entries[Index],
562 RtlMoveMemory(&InvertedTable->Entries[Index].EntrySEHandlerTableEncoded,
563 Index ? &InvertedTable->Entries[Index - 1].EntrySEHandlerTableEncoded : (PVOID)&InvertedTable->NextEntrySEHandlerTableEncoded,
568 RtlCaptureImageExceptionValues(ImageBase, &ptr, &count);
569 if (IsWin8OrGreater) {
573 entry->SEHandlerCount = count;
574 entry->ImageBase = ImageBase;
575 entry->ImageSize = SizeOfImage;
578 if (Index) InvertedTable->Entries[Index - 1].EntrySEHandlerTableEncoded = RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(ptr));
579 else InvertedTable->NextEntrySEHandlerTableEncoded =
reinterpret_cast<ULONG
>(RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(ptr)));
580 InvertedTable->Entries[Index].ImageBase = ImageBase;
581 InvertedTable->Entries[Index].ImageSize = SizeOfImage;
582 InvertedTable->Entries[Index].SEHandlerCount = count;
585 ++InvertedTable->Count;
590 static NTSTATUS NTAPI RtlInsertInvertedFunctionTable(
591 _In_ PVOID BaseAddress,
596 LOG_INFO(
"Exception table not found.");
597 return STATUS_NOT_SUPPORTED;
599 LOG_INFO(
"Found exception table: %p.", table);
600 BOOL need_virtual_protect = RtlIsWindowsVersionOrGreater(6, 3, 0);
602 LOG_DEBUG(
"Need virtual protect: %s.", need_virtual_protect ?
"true" :
"false");
605 if (need_virtual_protect) {
606 status = RtlProtectMrdata(PAGE_READWRITE, table);
607 if (!NT_SUCCESS(status))
return status;
609 RtlpInsertInvertedFunctionTable(table, BaseAddress, ImageSize);
610 if (need_virtual_protect) {
611 status = RtlProtectMrdata(PAGE_READONLY, table);
612 if (!NT_SUCCESS(status))
return status;
616 STATUS_NO_MEMORY : STATUS_SUCCESS;
622 if (moduleSize == 0) {
623 const DWORD img_size =
get_image_size(
reinterpret_cast<BYTE*
>(modulePtr));
627 moduleSize = img_size;
629 return NT_SUCCESS(peconv::RtlInsertInvertedFunctionTable(modulePtr, (ULONG)moduleSize)) ? true :
false;
#define RTL_VERIFY_FLAGS_MAJOR_VERSION
#define RTL_VERIFY_FLAGS_BUILD_NUMBERS
#define RTL_VERIFY_FLAGS_MINOR_VERSION
#define RtlFindMemoryBlockFromModuleSection__leave
Functions related to Exceptions Table.
#define PECONV_FORCEINLINE
#define PECONV_TRY_EXCEPT_BLOCK_END
#define PECONV_TRY_EXCEPT_BLOCK_START
Compile-time configurable logging macros for peconv.
#define LOG_DEBUG(fmt,...)
#define LOG_INFO(fmt,...)
struct peconv::_LDR_DATA_TABLE_ENTRY_WIN8 * PLDR_DATA_TABLE_ENTRY_WIN8
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_WIN7_32 RTL_INVERTED_FUNCTION_TABLE_WIN7_32
struct peconv::_SEARCH_CONTEXT SEARCH_CONTEXT
struct peconv::_SEARCH_CONTEXT * PSEARCH_CONTEXT
struct peconv::_NtVersion * PNtVersion
_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 RTL_INVERTED_FUNCTION_TABLE_ENTRY
struct peconv::_NtVersion NtVersion
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32
RTL_INVERTED_FUNCTION_TABLE_WIN7_32 _RTL_INVERTED_FUNCTION_TABLE
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE_WIN7_32
struct peconv::_LDR_DDAG_NODE_WIN8 * PLDR_DDAG_NODE_WIN8
_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32 RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32
DWORD get_image_size(IN const BYTE *payload)
bool setup_exceptions(IN BYTE *modulePtr, IN size_t moduleSize)
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32
RTL_INVERTED_FUNCTION_TABLE_WIN7_32 RTL_INVERTED_FUNCTION_TABLE
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_64 RTL_INVERTED_FUNCTION_TABLE_64
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 RTL_INVERTED_FUNCTION_TABLE_ENTRY_64
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_64 * PRTL_INVERTED_FUNCTION_TABLE_64
struct peconv::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY_64
struct peconv::_LDR_DATA_TABLE_ENTRY_WIN8 LDR_DATA_TABLE_ENTRY_WIN8
_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 _RTL_INVERTED_FUNCTION_TABLE_ENTRY
struct peconv::_LDR_DDAG_NODE_WIN8 LDR_DDAG_NODE_WIN8
RTL_INVERTED_FUNCTION_TABLE_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE
Wrappers over various fields in the PE header. Read, write, parse PE headers.
ULONG ProcessAttachFailed
UNICODE_STRING BaseDllName
PACTIVATION_CONTEXT EntryPointActivationContext
UNICODE_STRING FullDllName
RTL_BALANCED_NODE MappingInfoIndexNode
ULONG TelemetryEntryProcessed
ULONG LoadNotificationsSent
LIST_ENTRY NodeModuleLink
LIST_ENTRY InInitializationOrderLinks
ULONG ProcessStaticImport
RTL_BALANCED_NODE BaseAddressIndexNode
LDR_DLL_LOAD_REASON LoadReason
PLDR_DDAG_NODE_WIN8 DdagNode
ULONG CorDeferredValidate
ULONG CompatDatabaseProcessed
ULONG ProcessAttachCalled
LIST_ENTRY InMemoryOrderLinks
LIST_ENTRY InProgressLinks
LIST_ENTRY InLoadOrderLinks
SINGLE_LIST_ENTRY CondenseLink
PLDRP_CSLIST_DEPENDENT Dependencies
PLDRP_CSLIST_INCOMMING IncomingDependencies
PLDR_SERVICE_TAG_RECORD ServiceTagList
RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 Entries[0x200]
ULONG ExceptionDirectorySize
PIMAGE_RUNTIME_FUNCTION_ENTRY ExceptionDirectory
PVOID EntrySEHandlerTableEncoded
PVOID EntrySEHandlerTableEncoded
ULONG NextEntrySEHandlerTableEncoded
RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 Entries[0x200]
Miscellaneous utility functions.