12#define min(a,b) (((a) < (b)) ? (a) : (b))
16#define RTL_VERIFY_FLAGS_MAJOR_VERSION 0
17#define RTL_VERIFY_FLAGS_MINOR_VERSION 1
18#define RTL_VERIFY_FLAGS_BUILD_NUMBERS 2
19#define RTL_VERIFY_FLAGS_DEFAULT RTL_VERIFY_FLAGS_MAJOR_VERSION|RTL_VERIFY_FLAGS_MINOR_VERSION|RTL_VERIFY_FLAGS_BUILD_NUMBERS
159 static void NTAPI RtlCurrentVersion(_Out_
PNtVersion pVersion) {
160 RtlGetNtVersionNumbers(
161 &pVersion->MajorVersion,
162 &pVersion->MinorVersion,
163 &pVersion->BuildNumber
167 static BOOL NTAPI RtlIsWindowsVersionOrGreater(
168 _In_ ULONG MajorVersion,
169 _In_ ULONG MinorVersion,
170 _In_ ULONG BuildNumber
173 RtlCurrentVersion(&version);
174 if (version.MajorVersion == MajorVersion) {
175 if (version.MinorVersion == MinorVersion)
return version.BuildNumber >= BuildNumber;
176 else return (version.MinorVersion > MinorVersion);
178 else return version.MajorVersion > MajorVersion;
181 static BOOL NTAPI RtlVerifyVersion(
182 _In_ ULONG MajorVersion,
183 _In_ ULONG MinorVersion,
184 _In_ ULONG BuildNumber,
188 RtlCurrentVersion(&version);
189 if (version.MajorVersion == MajorVersion &&
195 static int NTAPI RtlCaptureImageExceptionValues(PVOID BaseAddress, PDWORD SEHandlerTable, PDWORD SEHandlerCount) {
196 PIMAGE_LOAD_CONFIG_DIRECTORY pLoadConfigDirectory =
nullptr;
197 PIMAGE_COR20_HEADER pCor20 =
nullptr;
200 auto hdrs =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(BaseAddress));
202 if (hdrs->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) {
203 *SEHandlerTable = *SEHandlerCount = -1;
208 pLoadConfigDirectory = (
decltype(pLoadConfigDirectory))RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &Size);
209 if (pLoadConfigDirectory) {
210 if (Size == 0x40 && pLoadConfigDirectory->Size >= 0x48u) {
211 if (pLoadConfigDirectory->SEHandlerTable && pLoadConfigDirectory->SEHandlerCount) {
212 *SEHandlerTable = pLoadConfigDirectory->SEHandlerTable;
213 return *SEHandlerCount = pLoadConfigDirectory->SEHandlerCount;
219 pCor20 = (
decltype(pCor20))RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &Size);
220 *SEHandlerTable = *SEHandlerCount = ((pCor20 && pCor20->Flags & 1) ? -1 : 0);
226 if (RtlIsWindowsVersionOrGreater(6, 2, 0)) {
230 return entry->DllBase ==
nullptr;
234 static NTSTATUS NTAPI RtlFindMemoryBlockFromModuleSection(
235 _In_ HMODULE ModuleHandle,
236 _In_ LPCSTR SectionName,
239 NTSTATUS
status = STATUS_SUCCESS;
242#define RtlFindMemoryBlockFromModuleSection__leave __leave
244#define RtlFindMemoryBlockFromModuleSection__leave return status
247 LOG_DEBUG(
"Searching in section %s in module %p.", SectionName, ModuleHandle);
255 if (!SearchContext->SearchPattern || !SearchContext->PatternSize) {
256 SearchContext->Result =
nullptr;
257 SearchContext->MemoryBlockSize = 0;
258 status = STATUS_INVALID_PARAMETER;
262 if (SearchContext->Result) {
263 ++SearchContext->Result;
264 --SearchContext->MemoryBlockSize;
272 auto headers =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(ModuleHandle));
273 PIMAGE_SECTION_HEADER section =
nullptr;
276 section = IMAGE_FIRST_SECTION(headers);
277 for (WORD i = 0; i < headers->FileHeader.NumberOfSections; ++i) {
278 if (!_strnicmp(SectionName,
reinterpret_cast<LPCSTR
>(section->Name), 8)) {
279 SearchContext->Result =
reinterpret_cast<LPBYTE
>(ModuleHandle) + section->VirtualAddress;
280 SearchContext->MemoryBlockSize = section->Misc.VirtualSize;
287 if (!SearchContext->Result || !SearchContext->MemoryBlockSize || SearchContext->MemoryBlockSize < SearchContext->PatternSize) {
288 SearchContext->Result =
nullptr;
289 SearchContext->MemoryBlockSize = 0;
290 status = STATUS_NOT_FOUND;
295 status = STATUS_INVALID_PARAMETER_1;
304 LPBYTE end = SearchContext->Result + SearchContext->MemoryBlockSize - SearchContext->PatternSize;
305 while (SearchContext->Result <= end) {
306 if (RtlCompareMemory(SearchContext->SearchPattern, SearchContext->Result, SearchContext->PatternSize) == SearchContext->PatternSize) {
310 ++SearchContext->Result;
311 --SearchContext->MemoryBlockSize;
318 SearchContext->Result =
nullptr;
319 SearchContext->MemoryBlockSize = 0;
320 status = STATUS_NOT_FOUND;
329 static NTSTATUS RtlProtectMrdata(_In_ ULONG Protect, PRTL_INVERTED_FUNCTION_TABLE mrdata) {
330 static PVOID MrdataBase =
nullptr;
331 static SIZE_T size = 0;
338 MEMORY_BASIC_INFORMATION mbi= { 0 };
339 status = NtQueryVirtualMemory(NtCurrentProcess(), mrdata, MemoryBasicInformation, &mbi,
sizeof(mbi),
nullptr);
341 MrdataBase = mbi.BaseAddress;
342 size = mbi.RegionSize;
347 return NtProtectVirtualMemory(NtCurrentProcess(), &tmp, &tmp_len, Protect, &old);
350 static PVOID RtlFindInvertedFunctionTable() {
364 HMODULE hModule =
nullptr, hNtdll = GetModuleHandleW(L
"ntdll.dll");
365 LPCSTR lpSectionName =
".data";
366 if (!hNtdll)
return nullptr;
367 auto NtdllHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hNtdll));
368 PIMAGE_NT_HEADERS ModuleHeaders =
nullptr;
369 _RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 entry = { 0 };
370 PIMAGE_DATA_DIRECTORY dir =
nullptr;
372 SearchContext.
SearchPattern =
reinterpret_cast<LPBYTE
>(&entry);
373 SearchContext.PatternSize =
sizeof(entry);
374 RtlSecureZeroMemory(&entry,
sizeof(entry));
379 ModuleHeaders = NtdllHeaders;
383 else if (RtlIsWindowsVersionOrGreater(6, 3, 0)) {
385 ModuleHeaders = NtdllHeaders;
386 lpSectionName =
".mrdata";
389 PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList,
390 ListEntry = ListHead->Flink;
391 PLDR_DATA_TABLE_ENTRY CurEntry =
nullptr;
392 while (ListEntry != ListHead) {
393 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
394 ListEntry = ListEntry->Flink;
395 hModule =
reinterpret_cast<HMODULE
>(
396 hModule ?
reinterpret_cast<HMODULE
>(
min(
397 reinterpret_cast<uintptr_t
>(hModule),
398 reinterpret_cast<uintptr_t
>(CurEntry->DllBase)
399 )) : CurEntry->DllBase
402 if (hModule) ModuleHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hModule));
404 if (!hModule || !ModuleHeaders || !hNtdll || !NtdllHeaders)
return nullptr;
405 dir = &ModuleHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
407 entry.ExceptionDirectory = dir->Size ?
408 reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY
>(
409 reinterpret_cast<size_t>(hModule) + dir->VirtualAddress
411 entry.ImageBase =
reinterpret_cast<PVOID
>(hModule);
412 entry.ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
413 entry.ExceptionDirectorySize = dir->Size;
415 while (NT_SUCCESS(RtlFindMemoryBlockFromModuleSection(hNtdll, lpSectionName, &SearchContext))) {
417 if (RtlIsWindowsVersionOrGreater(6, 2, 0) && tab->MaxCount == 0x200 && !tab->Overflow)
return tab;
418 else if (tab->MaxCount == 0x200 && !tab->Epoch)
return tab;
435 HMODULE hModule =
nullptr, hNtdll = GetModuleHandleW(L
"ntdll.dll");
436 auto NtdllHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hNtdll));
437 PIMAGE_NT_HEADERS ModuleHeaders =
nullptr;
438 _RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 entry = { 0 };
439 RtlSecureZeroMemory(&entry,
sizeof(entry));
440 LPCSTR lpSectionName =
".data";
442 SearchContext.
SearchPattern =
reinterpret_cast<LPBYTE
>(&entry);
443 SearchContext.PatternSize =
sizeof(entry);
444 PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InMemoryOrderModuleList,
445 ListEntry = ListHead->Flink;
446 PLDR_DATA_TABLE_ENTRY CurEntry =
nullptr;
447 DWORD SEHTable, SEHCount;
450 if (RtlIsWindowsVersionOrGreater(6, 3, 0)) lpSectionName =
".mrdata";
451 else if (!RtlIsWindowsVersionOrGreater(6, 2, 0)) Offset = 0xC;
453 while (ListEntry != ListHead) {
454 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
455 ListEntry = ListEntry->Flink;
456 if (IsModuleUnloaded(CurEntry))
458 if (CurEntry->DllBase == hNtdll && Offset == 0x20)
461 hModule =
reinterpret_cast<HMODULE
>(
462 hModule ?
reinterpret_cast<HMODULE
>(
min(
463 reinterpret_cast<uintptr_t
>(hModule),
464 reinterpret_cast<uintptr_t
>(CurEntry->DllBase)
465 )) : CurEntry->DllBase
468 ModuleHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hModule));
469 if (!hModule || !ModuleHeaders || !hNtdll || !NtdllHeaders)
return nullptr;
471 RtlCaptureImageExceptionValues(hModule, &SEHTable, &SEHCount);
473 if (RtlIsWindowsVersionOrGreater(6, 2, 0)) {
477 entry2->ImageBase =
reinterpret_cast<PVOID
>(hModule);
478 entry2->ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
479 entry2->SEHandlerCount = SEHCount;
482 entry.EntrySEHandlerTableEncoded = RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(SEHTable));
483 entry.ImageBase =
reinterpret_cast<PVOID
>(hModule);
484 entry.ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
485 entry.SEHandlerCount = SEHCount;
488 while (NT_SUCCESS(RtlFindMemoryBlockFromModuleSection(hNtdll, lpSectionName, &SearchContext))) {
492 if (RtlIsWindowsVersionOrGreater(6, 2, 0) && tab->MaxCount == 0x200 && !tab->NextEntrySEHandlerTableEncoded)
return tab;
493 else if (tab->MaxCount == 0x200 && !tab->Overflow)
return tab;
499 static VOID RtlpInsertInvertedFunctionTable(
500 _In_ PRTL_INVERTED_FUNCTION_TABLE InvertedTable,
501 _In_ PVOID ImageBase,
502 _In_ ULONG SizeOfImage) {
504 ULONG CurrentSize = InvertedTable->Count;
505 PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTable =
nullptr;
506 ULONG SizeOfTable = 0;
507 BOOL IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0);
508 ULONG Index =
static_cast<ULONG
>(IsWin8OrGreater);
510 if (CurrentSize != InvertedTable->MaxCount) {
511 if (CurrentSize != 0) {
512 while (Index < CurrentSize) {
513 if (ImageBase < InvertedTable->Entries[Index].ImageBase)
break;
518 if (Index != CurrentSize) {
519 RtlMoveMemory(&InvertedTable->Entries[Index + 1],
520 &InvertedTable->Entries[Index],
521 (CurrentSize - Index) *
sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY));
525 FunctionTable =
reinterpret_cast<decltype(FunctionTable)
>(RtlImageDirectoryEntryToData(ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &SizeOfTable));
526 InvertedTable->Entries[Index].ExceptionDirectory = FunctionTable;
527 InvertedTable->Entries[Index].ImageBase = ImageBase;
528 InvertedTable->Entries[Index].ImageSize = SizeOfImage;
529 InvertedTable->Entries[Index].ExceptionDirectorySize = SizeOfTable;
530 InvertedTable->Count++;
531 LOG_INFO(
"Exception table was set.");
534 IsWin8OrGreater ? (InvertedTable->Overflow = TRUE) : (InvertedTable->Epoch = TRUE);
538 DWORD ptr = 0, count = 0;
539 BOOL IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0);
540 ULONG Index = IsWin8OrGreater ? 1 : 0;
542 if (InvertedTable->Count == InvertedTable->MaxCount) {
543 if (IsWin8OrGreater)InvertedTable->NextEntrySEHandlerTableEncoded = TRUE;
544 else InvertedTable->Overflow = TRUE;
547 while (Index < InvertedTable->Count) {
548 if (ImageBase < (IsWin8OrGreater ?
550 InvertedTable->Entries[Index].ImageBase))
554 if (Index != InvertedTable->Count) {
555 if (IsWin8OrGreater) {
556 RtlMoveMemory(&InvertedTable->Entries[Index + 1], &InvertedTable->Entries[Index],
557 (InvertedTable->Count - Index) *
sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY));
560 RtlMoveMemory(&InvertedTable->Entries[Index].EntrySEHandlerTableEncoded,
561 Index ? &InvertedTable->Entries[Index - 1].EntrySEHandlerTableEncoded : (PVOID)&InvertedTable->NextEntrySEHandlerTableEncoded,
562 (InvertedTable->Count - Index) *
sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY));
566 RtlCaptureImageExceptionValues(ImageBase, &ptr, &count);
567 if (IsWin8OrGreater) {
571 entry->SEHandlerCount = count;
572 entry->ImageBase = ImageBase;
573 entry->ImageSize = SizeOfImage;
576 if (Index) InvertedTable->Entries[Index - 1].EntrySEHandlerTableEncoded = RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(ptr));
577 else InvertedTable->NextEntrySEHandlerTableEncoded =
reinterpret_cast<ULONG
>(RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(ptr)));
578 InvertedTable->Entries[Index].ImageBase = ImageBase;
579 InvertedTable->Entries[Index].ImageSize = SizeOfImage;
580 InvertedTable->Entries[Index].SEHandlerCount = count;
583 ++InvertedTable->Count;
588 static NTSTATUS NTAPI RtlInsertInvertedFunctionTable(
589 _In_ PVOID BaseAddress,
594 LOG_INFO(
"Exception table not found.");
595 return STATUS_NOT_SUPPORTED;
597 LOG_INFO(
"Found exception table: %p.", table);
598 BOOL need_virtual_protect = RtlIsWindowsVersionOrGreater(6, 3, 0);
600 LOG_DEBUG(
"Need virtual protect: %s.", need_virtual_protect ?
"true" :
"false");
603 if (need_virtual_protect) {
604 status = RtlProtectMrdata(PAGE_READWRITE, table);
607 RtlpInsertInvertedFunctionTable(table, BaseAddress, ImageSize);
608 if (need_virtual_protect) {
609 status = RtlProtectMrdata(PAGE_READONLY, table);
614 STATUS_NO_MEMORY : STATUS_SUCCESS;
620 if (moduleSize == 0) {
621 const DWORD img_size =
get_image_size(
reinterpret_cast<BYTE*
>(modulePtr));
625 moduleSize = img_size;
627 return NT_SUCCESS(details::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 LOG_DEBUG(fmt,...)
#define LOG_INFO(fmt,...)
struct details::_NtVersion * PNtVersion
RTL_INVERTED_FUNCTION_TABLE_WIN7_32 RTL_INVERTED_FUNCTION_TABLE
struct details::_LDR_DATA_TABLE_ENTRY_WIN8 LDR_DATA_TABLE_ENTRY_WIN8
struct details::_LDR_DDAG_NODE_WIN8 LDR_DDAG_NODE_WIN8
_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 RTL_INVERTED_FUNCTION_TABLE_ENTRY
struct details::_RTL_INVERTED_FUNCTION_TABLE_WIN7_32 RTL_INVERTED_FUNCTION_TABLE_WIN7_32
RTL_INVERTED_FUNCTION_TABLE_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE
struct details::_SEARCH_CONTEXT * PSEARCH_CONTEXT
struct details::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32
PECONV_TRY_EXCEPT_BLOCK_END status
struct details::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32 RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32
struct details::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32
struct details::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN8_PLUS_32
struct details::_RTL_INVERTED_FUNCTION_TABLE_64 RTL_INVERTED_FUNCTION_TABLE_64
RTL_INVERTED_FUNCTION_TABLE_WIN7_32 _RTL_INVERTED_FUNCTION_TABLE
struct details::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY_64
struct details::_RTL_INVERTED_FUNCTION_TABLE_64 * PRTL_INVERTED_FUNCTION_TABLE_64
struct details::_LDR_DATA_TABLE_ENTRY_WIN8 * PLDR_DATA_TABLE_ENTRY_WIN8
struct details::_RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 RTL_INVERTED_FUNCTION_TABLE_ENTRY_64
_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 _RTL_INVERTED_FUNCTION_TABLE_ENTRY
struct details::_RTL_INVERTED_FUNCTION_TABLE_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE_WIN7_32
struct details::_LDR_DDAG_NODE_WIN8 * PLDR_DDAG_NODE_WIN8
struct details::_NtVersion NtVersion
struct details::_SEARCH_CONTEXT SEARCH_CONTEXT
_RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 * PRTL_INVERTED_FUNCTION_TABLE_ENTRY
DWORD get_image_size(IN const BYTE *payload)
bool setup_exceptions(IN BYTE *modulePtr, IN size_t moduleSize)
Wrappers over various fields in the PE header. Read, write, parse PE headers.
LIST_ENTRY InInitializationOrderLinks
ULONG ProcessAttachFailed
RTL_BALANCED_NODE MappingInfoIndexNode
UNICODE_STRING BaseDllName
ULONG CompatDatabaseProcessed
ULONG ProcessAttachCalled
PLDR_DDAG_NODE_WIN8 DdagNode
ULONG TelemetryEntryProcessed
PACTIVATION_CONTEXT EntryPointActivationContext
ULONG ProcessStaticImport
LIST_ENTRY InLoadOrderLinks
RTL_BALANCED_NODE BaseAddressIndexNode
LIST_ENTRY NodeModuleLink
ULONG LoadNotificationsSent
ULONG CorDeferredValidate
LDR_DLL_LOAD_REASON LoadReason
LIST_ENTRY InProgressLinks
UNICODE_STRING FullDllName
LIST_ENTRY InMemoryOrderLinks
PLDRP_CSLIST_DEPENDENT Dependencies
PLDRP_CSLIST_INCOMMING IncomingDependencies
PLDR_SERVICE_TAG_RECORD ServiceTagList
SINGLE_LIST_ENTRY CondenseLink
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.
#define PECONV_FORCEINLINE
#define PECONV_TRY_EXCEPT_BLOCK_END
#define PECONV_TRY_EXCEPT_BLOCK_START