Virtual Method Tables
R5AC is ensuring that VMTP's are pointing within expected bounds, and that read-only VMT related data has not been tampered with.
Here is an example of how this detection might look like.
// Everytime sub_1DCDD1 is called, the detection VTP_GetFilesystemInterface is executed.
char __fastcall sub_1DCDD1()
{
FilesystemInterface = (__int64 *)R5::VTP_GetFilesystemInterface();
*((_QWORD *)v2 + 16) = FilesystemInterface;
gpFileSystemInterface[0] = FilesystemInterface;
return 1;
}void **R5::VTP_GetFilesystemInterface()
{
_LIST_ENTRY* lpK32GetMappedFileName = nullptr;
void* VmtTablePtr = R5::FilesystemInterfaceVMTP;
void* VmtTablePtr2 = R5::FilesystemInterfaceVMTP;
__int64 ImageBaseAddress = (__int64)NtCurrentTeb()->ProcessEnvironmentBlock->ImageBaseAddress;// Plaintext strings (replacing encrypted runtime decoding)
const char ModuleNameStr[] = "KERNEL32.DLL";
const char ExportFuncStr[] = "K32GetMappedFileName";
const char InterfaceStr[] = "VTP_GetFilesystemInterface";if (*(WORD*)ImageBaseAddress == 0x5A4D) // Check for 'MZ' PE signature
{
_IMAGE_NT_HEADERS64* lpNTH = (_IMAGE_NT_HEADERS64*)(ImageBaseAddress + *(int*)(ImageBaseAddress + 0x3C));
if (lpNTH->OptionalHeader.Magic == 0x20B) // PE32+
{
unsigned int NumberOfSections = lpNTH->FileHeader.NumberOfSections;
char* lpFirstSectionContentStart = (char*)&lpNTH->OptionalHeader + lpNTH->FileHeader.SizeOfOptionalHeader;if (NumberOfSections && lpFirstSectionContentStart)
{
_IMAGE_SECTION_HEADER* dwTableRVA = (_IMAGE_SECTION_HEADER*)((char*)R5::FilesystemInterfaceVMTP - ImageBaseAddress);
_DWORD* sectionHeaders = (_DWORD*)(lpFirstSectionContentStart + 8);
unsigned int nSectionIdx = 0;while (true)
{
_IMAGE_SECTION_HEADER* dwSegmentRVA = (_IMAGE_SECTION_HEADER*)(unsigned int)sectionHeaders[1];
if (dwSegmentRVA <= dwTableRVA &&
(unsigned int)((_DWORD)dwSegmentRVA + *sectionHeaders) > (unsigned __int64)dwTableRVA)
{
break; // Found containing section
}++nSectionIdx;
sectionHeaders += 10;if (nSectionIdx >= NumberOfSections)
{
// Outside module section — use default
VmtTablePtr = VmtTablePtr2;
goto ON_DETECTED_OUTSIDE_INGAME_SECTION;
}
}
}
}
}ON_DETECTED_OUTSIDE_INGAME_SECTION:
char lpMappedFileName[272] = {0};
wchar_t NeedleForSomeModule[264] = {0};
mbstowcs_s(nullptr, NeedleForSomeModule, ModuleNameStr, sizeof(ModuleNameStr));// Search loaded modules
_LIST_ENTRY* p_InMemoryOrderModuleList = &NtCurrentTeb()->ProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList;
_LIST_ENTRY* Flink = p_InMemoryOrderModuleList->Flink;if (Flink != p_InMemoryOrderModuleList)
{
while (_wcsicmp(NeedleForSomeModule, (_WCHAR*)Flink[5].Flink) != 0)
{
Flink = Flink->Flink;
if (Flink == p_InMemoryOrderModuleList)
goto ON_DETECTED;
}_LIST_ENTRY* lpModuleBase = Flink[2].Flink;
if (lpModuleBase)
{
LABEL_22:
if (LOWORD(lpModuleBase->Flink) == 0x5A4D) // 'MZ'
{
__int64 lpExportDirectory = (__int64)lpModuleBase + SHIDWORD(lpModuleBase[3].Blink);
if (*(WORD*)(lpExportDirectory + 24) == 0x20B) // PE32+
{
unsigned int* v27 = (unsigned int*)(lpExportDirectory + 0x88);
if (!v27)
v27 = nullptr;if (v27)
{
unsigned int nExportEnumIdx = 0;
_DWORD* v29 = (_DWORD*)((char*)lpModuleBase + *v27);
__int64 AddressOfFunctions = (__int64)lpModuleBase + (unsigned int)v29[7];
__int64 AddressOfNames = (__int64)lpModuleBase + (unsigned int)v29[8];
unsigned int NumberOfExports = v29[6];
__int64 AddressOfNameOrdinals = (__int64)lpModuleBase + (unsigned int)v29[9];while (nExportEnumIdx < NumberOfExports)
{
char* lpExportRecordMaybe = (char*)lpModuleBase + *(unsigned int*)(AddressOfNames + 4i64 * nExportEnumIdx);
if (strcmp(lpExportRecordMaybe, ExportFuncStr) == 0)
{
unsigned int dwFunctionRVA = *(_DWORD*)(AddressOfFunctions
+ 4i64 * *(unsigned __int16*)(AddressOfNameOrdinals + 2i64 * nExportEnumIdx));lpK32GetMappedFileName = (_LIST_ENTRY*)((char*)lpModuleBase + dwFunctionRVA);
break;
}
++nExportEnumIdx;
}
}
}
}
}
}ON_DETECTED:
// Get the module file name of where this VMTP is residing in.
if (lpK32GetMappedFileName)
((void(__fastcall*)(__int64, void*, char*, __int64))lpK32GetMappedFileName)(
-1,
VmtTablePtr2,
lpMappedFileName,
sizeof(lpMappedFileName)
);// Report violation if needed
R5AC::PushViolation(nullptr, 2, (__int64)VmtTablePtr2, (__int64)lpMappedFileName);return &R5::FilesystemInterfaceVMTP;
}