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
151 static void NTAPI RtlCurrentVersion(_Out_
PNtVersion pVersion) {
152 RtlGetNtVersionNumbers(
153 &pVersion->MajorVersion,
154 &pVersion->MinorVersion,
155 &pVersion->BuildNumber
159 static BOOL NTAPI RtlIsWindowsVersionOrGreater(
160 _In_ ULONG MajorVersion,
161 _In_ ULONG MinorVersion,
162 _In_ ULONG BuildNumber
165 RtlSecureZeroMemory(&version,
sizeof(
NtVersion));
166 RtlCurrentVersion(&version);
167 if (version.MajorVersion == MajorVersion) {
168 if (version.MinorVersion == MinorVersion)
return version.BuildNumber >= BuildNumber;
169 else return (version.MinorVersion > MinorVersion);
171 else return version.MajorVersion > MajorVersion;
174 static BOOL NTAPI RtlVerifyVersion(
175 _In_ ULONG MajorVersion,
176 _In_ ULONG MinorVersion,
177 _In_ ULONG BuildNumber,
181 RtlSecureZeroMemory(&version,
sizeof(
NtVersion));
182 RtlCurrentVersion(&version);
183 if (version.MajorVersion == MajorVersion &&
189 static int NTAPI RtlCaptureImageExceptionValues(PVOID BaseAddress, PDWORD SEHandlerTable, PDWORD SEHandlerCount) {
190 PIMAGE_LOAD_CONFIG_DIRECTORY pLoadConfigDirectory =
nullptr;
191 PIMAGE_COR20_HEADER pCor20 =
nullptr;
194 auto hdrs =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(BaseAddress));
196 if (hdrs->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) {
197 *SEHandlerTable = *SEHandlerCount = -1;
202 pLoadConfigDirectory = (
decltype(pLoadConfigDirectory))RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &Size);
203 if (pLoadConfigDirectory) {
204 if (Size == 0x40 && pLoadConfigDirectory->Size >= 0x48u) {
205 if (pLoadConfigDirectory->SEHandlerTable && pLoadConfigDirectory->SEHandlerCount) {
206 *SEHandlerTable = pLoadConfigDirectory->SEHandlerTable;
207 return *SEHandlerCount = pLoadConfigDirectory->SEHandlerCount;
213 pCor20 = (
decltype(pCor20))RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &Size);
214 *SEHandlerTable = *SEHandlerCount = ((pCor20 && pCor20->Flags & 1) ? -1 : 0);
220 if (RtlIsWindowsVersionOrGreater(6, 2, 0)) {
224 return entry->DllBase ==
nullptr;
228 static NTSTATUS NTAPI RtlFindMemoryBlockFromModuleSection(
229 _In_ HMODULE ModuleHandle,
230 _In_ LPCSTR SectionName,
233 NTSTATUS
status = STATUS_SUCCESS;
236#define RtlFindMemoryBlockFromModuleSection__leave __leave
238#define RtlFindMemoryBlockFromModuleSection__leave return status
242 std::cout <<
"Searching in section " << SectionName <<
" in module " << ModuleHandle << std::endl;
251 if (!SearchContext->SearchPattern || !SearchContext->PatternSize) {
252 SearchContext->Result =
nullptr;
253 SearchContext->MemoryBlockSize = 0;
254 status = STATUS_INVALID_PARAMETER;
258 if (SearchContext->Result) {
259 ++SearchContext->Result;
260 --SearchContext->MemoryBlockSize;
268 auto headers =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(ModuleHandle));
269 PIMAGE_SECTION_HEADER section =
nullptr;
272 section = IMAGE_FIRST_SECTION(headers);
273 for (WORD i = 0; i < headers->FileHeader.NumberOfSections; ++i) {
274 if (!_strnicmp(SectionName,
reinterpret_cast<LPCSTR
>(section->Name), 8)) {
275 SearchContext->Result =
reinterpret_cast<LPBYTE
>(ModuleHandle) + section->VirtualAddress;
276 SearchContext->MemoryBlockSize = section->Misc.VirtualSize;
283 if (!SearchContext->Result || !SearchContext->MemoryBlockSize || SearchContext->MemoryBlockSize < SearchContext->PatternSize) {
284 SearchContext->Result =
nullptr;
285 SearchContext->MemoryBlockSize = 0;
286 status = STATUS_NOT_FOUND;
291 status = STATUS_INVALID_PARAMETER_1;
300 LPBYTE end = SearchContext->Result + SearchContext->MemoryBlockSize - SearchContext->PatternSize;
301 while (SearchContext->Result <= end) {
302 if (RtlCompareMemory(SearchContext->SearchPattern, SearchContext->Result, SearchContext->PatternSize) == SearchContext->PatternSize) {
306 ++SearchContext->Result;
307 --SearchContext->MemoryBlockSize;
314 SearchContext->Result =
nullptr;
315 SearchContext->MemoryBlockSize = 0;
316 status = STATUS_NOT_FOUND;
325 static NTSTATUS RtlProtectMrdata(_In_ ULONG Protect, PRTL_INVERTED_FUNCTION_TABLE mrdata) {
326 static PVOID MrdataBase =
nullptr;
327 static SIZE_T size = 0;
334 MEMORY_BASIC_INFORMATION mbi{};
335 status = NtQueryVirtualMemory(NtCurrentProcess(), mrdata, MemoryBasicInformation, &mbi,
sizeof(mbi),
nullptr);
337 MrdataBase = mbi.BaseAddress;
338 size = mbi.RegionSize;
343 return NtProtectVirtualMemory(NtCurrentProcess(), &tmp, &tmp_len, Protect, &old);
346 static PVOID RtlFindInvertedFunctionTable() {
360 HMODULE hModule =
nullptr, hNtdll = GetModuleHandleW(L
"ntdll.dll");
361 LPCSTR lpSectionName =
".data";
362 if (!hNtdll)
return nullptr;
363 auto NtdllHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hNtdll));
364 PIMAGE_NT_HEADERS ModuleHeaders =
nullptr;
365 _RTL_INVERTED_FUNCTION_TABLE_ENTRY_64 entry{};
366 PIMAGE_DATA_DIRECTORY dir =
nullptr;
368 SearchContext.
SearchPattern =
reinterpret_cast<LPBYTE
>(&entry);
369 SearchContext.PatternSize =
sizeof(entry);
370 RtlSecureZeroMemory(&entry,
sizeof(entry));
375 ModuleHeaders = NtdllHeaders;
379 else if (RtlIsWindowsVersionOrGreater(6, 3, 0)) {
381 ModuleHeaders = NtdllHeaders;
382 lpSectionName =
".mrdata";
385 PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList,
386 ListEntry = ListHead->Flink;
387 PLDR_DATA_TABLE_ENTRY CurEntry =
nullptr;
388 while (ListEntry != ListHead) {
389 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
390 ListEntry = ListEntry->Flink;
391 hModule =
reinterpret_cast<HMODULE
>(
392 hModule ?
reinterpret_cast<HMODULE
>(
min(
393 reinterpret_cast<uintptr_t
>(hModule),
394 reinterpret_cast<uintptr_t
>(CurEntry->DllBase)
395 )) : CurEntry->DllBase
398 if (hModule) ModuleHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hModule));
400 if (!hModule || !ModuleHeaders || !hNtdll || !NtdllHeaders)
return nullptr;
401 dir = &ModuleHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
403 entry.ExceptionDirectory = dir->Size ?
404 reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY
>(
405 reinterpret_cast<size_t>(hModule) + dir->VirtualAddress
407 entry.ImageBase =
reinterpret_cast<PVOID
>(hModule);
408 entry.ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
409 entry.ExceptionDirectorySize = dir->Size;
411 while (NT_SUCCESS(RtlFindMemoryBlockFromModuleSection(hNtdll, lpSectionName, &SearchContext))) {
413 if (RtlIsWindowsVersionOrGreater(6, 2, 0) && tab->MaxCount == 0x200 && !tab->Overflow)
return tab;
414 else if (tab->MaxCount == 0x200 && !tab->Epoch)
return tab;
431 HMODULE hModule =
nullptr, hNtdll = GetModuleHandleW(L
"ntdll.dll");
432 auto NtdllHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hNtdll));
433 PIMAGE_NT_HEADERS ModuleHeaders =
nullptr;
434 _RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 entry{};
435 RtlSecureZeroMemory(&entry,
sizeof(entry));
436 LPCSTR lpSectionName =
".data";
439 SearchContext.
SearchPattern =
reinterpret_cast<LPBYTE
>(&entry);
440 SearchContext.PatternSize =
sizeof(entry);
441 PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InMemoryOrderModuleList,
442 ListEntry = ListHead->Flink;
443 PLDR_DATA_TABLE_ENTRY CurEntry =
nullptr;
444 DWORD SEHTable, SEHCount;
447 if (RtlIsWindowsVersionOrGreater(6, 3, 0)) lpSectionName =
".mrdata";
448 else if (!RtlIsWindowsVersionOrGreater(6, 2, 0)) Offset = 0xC;
450 while (ListEntry != ListHead) {
451 CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
452 ListEntry = ListEntry->Flink;
453 if (IsModuleUnloaded(CurEntry))
455 if (CurEntry->DllBase == hNtdll && Offset == 0x20)
458 hModule =
reinterpret_cast<HMODULE
>(
459 hModule ?
reinterpret_cast<HMODULE
>(
min(
460 reinterpret_cast<uintptr_t
>(hModule),
461 reinterpret_cast<uintptr_t
>(CurEntry->DllBase)
462 )) : CurEntry->DllBase
465 ModuleHeaders =
reinterpret_cast<PIMAGE_NT_HEADERS
>(RtlImageNtHeader(hModule));
466 if (!hModule || !ModuleHeaders || !hNtdll || !NtdllHeaders)
return nullptr;
468 RtlCaptureImageExceptionValues(hModule, &SEHTable, &SEHCount);
470 entry.EntrySEHandlerTableEncoded = RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(SEHTable));
471 entry.ImageBase =
reinterpret_cast<PVOID
>(hModule);
472 entry.ImageSize = ModuleHeaders->OptionalHeader.SizeOfImage;
473 entry.SEHandlerCount = SEHCount;
475 while (NT_SUCCESS(RtlFindMemoryBlockFromModuleSection(hNtdll, lpSectionName, &SearchContext))) {
479 if (RtlIsWindowsVersionOrGreater(6, 2, 0) && tab->MaxCount == 0x200 && !tab->NextEntrySEHandlerTableEncoded)
return tab;
480 else if (tab->MaxCount == 0x200 && !tab->Overflow)
return tab;
486 static VOID RtlpInsertInvertedFunctionTable(
487 _In_ PRTL_INVERTED_FUNCTION_TABLE InvertedTable,
488 _In_ PVOID ImageBase,
489 _In_ ULONG SizeOfImage) {
491 ULONG CurrentSize = InvertedTable->Count;
492 PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTable =
nullptr;
493 ULONG SizeOfTable = 0;
494 bool IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0);
495 ULONG Index =
static_cast<ULONG
>(IsWin8OrGreater);
497 if (CurrentSize != InvertedTable->MaxCount) {
498 if (CurrentSize != 0) {
499 while (Index < CurrentSize) {
500 if (ImageBase < InvertedTable->Entries[Index].ImageBase)
break;
505 if (Index != CurrentSize) {
506 RtlMoveMemory(&InvertedTable->Entries[Index + 1],
507 &InvertedTable->Entries[Index],
508 (CurrentSize - Index) *
sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY));
512 FunctionTable =
reinterpret_cast<decltype(FunctionTable)
>(RtlImageDirectoryEntryToData(ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &SizeOfTable));
513 InvertedTable->Entries[Index].ExceptionDirectory = FunctionTable;
514 InvertedTable->Entries[Index].ImageBase = ImageBase;
515 InvertedTable->Entries[Index].ImageSize = SizeOfImage;
516 InvertedTable->Entries[Index].ExceptionDirectorySize = SizeOfTable;
517 InvertedTable->Count++;
519 std::cout <<
"Exception table was set! " << std::endl;
523 IsWin8OrGreater ? (InvertedTable->Overflow = TRUE) : (InvertedTable->Epoch = TRUE);
527 DWORD ptr = 0, count = 0;
528 bool IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0);
529 ULONG Index = IsWin8OrGreater ? 1 : 0;
531 if (InvertedTable->Count == InvertedTable->MaxCount) {
532 if (IsWin8OrGreater)InvertedTable->NextEntrySEHandlerTableEncoded = TRUE;
533 else InvertedTable->Overflow = TRUE;
536 while (Index < InvertedTable->Count) {
537 if (ImageBase < (IsWin8OrGreater ?
539 InvertedTable->Entries[Index].ImageBase))
543 if (Index != InvertedTable->Count) {
544 if (IsWin8OrGreater) {
545 RtlMoveMemory(&InvertedTable->Entries[Index + 1], &InvertedTable->Entries[Index],
546 (InvertedTable->Count - Index) *
sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY));
549 RtlMoveMemory(&InvertedTable->Entries[Index].EntrySEHandlerTableEncoded,
550 Index ? &InvertedTable->Entries[Index - 1].EntrySEHandlerTableEncoded : (PVOID)&InvertedTable->NextEntrySEHandlerTableEncoded,
551 (InvertedTable->Count - Index) *
sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY));
555 RtlCaptureImageExceptionValues(ImageBase, &ptr, &count);
556 if (IsWin8OrGreater) {
559 entry->
ExceptionDirectory =
reinterpret_cast<PIMAGE_RUNTIME_FUNCTION_ENTRY
>(RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(ptr)));
560 entry->ExceptionDirectorySize = count;
561 entry->ImageBase = ImageBase;
562 entry->ImageSize = SizeOfImage;
565 if (Index) InvertedTable->Entries[Index - 1].EntrySEHandlerTableEncoded = RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(ptr));
566 else InvertedTable->NextEntrySEHandlerTableEncoded =
reinterpret_cast<ULONG
>(RtlEncodeSystemPointer(
reinterpret_cast<PVOID
>(ptr)));
567 InvertedTable->Entries[Index].ImageBase = ImageBase;
568 InvertedTable->Entries[Index].ImageSize = SizeOfImage;
569 InvertedTable->Entries[Index].SEHandlerCount = count;
572 std::cout <<
"Exception table was set! " << std::endl;
574 ++InvertedTable->Count;
579 static NTSTATUS NTAPI RtlInsertInvertedFunctionTable(
580 _In_ PVOID BaseAddress,
586 std::cout <<
"Exception table not found! " << std::endl;
588 return STATUS_NOT_SUPPORTED;
591 std::cout <<
"Found exception table: " << std::hex << table << std::endl;
593 bool need_virtual_protect = RtlIsWindowsVersionOrGreater(6, 3, 0);
596 std::cout <<
"Need virtual protect: " << std::boolalpha << need_virtual_protect << std::endl;
600 if (need_virtual_protect) {
601 status = RtlProtectMrdata(PAGE_READWRITE, table);
604 RtlpInsertInvertedFunctionTable(table, BaseAddress, ImageSize);
605 if (need_virtual_protect) {
606 status = RtlProtectMrdata(PAGE_READONLY, table);
611 STATUS_NO_MEMORY : STATUS_SUCCESS;
617 if (moduleSize == 0) {
618 const DWORD img_size =
get_image_size(
reinterpret_cast<BYTE*
>(modulePtr));
622 moduleSize = img_size;
624 return NT_SUCCESS(details::RtlInsertInvertedFunctionTable(modulePtr, moduleSize));
#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.
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_WIN7_32 RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_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
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