@@ -570,7 +570,6 @@ struct VM {
570570 EDID,
571571 CPU_HEURISTIC,
572572 CLOCK,
573- LBR,
574573
575574 // Linux and Windows
576575 SIDT,
@@ -10169,205 +10168,6 @@ struct VM {
1016910168 SetupDiDestroyDeviceInfoList (devs);
1017010169 return !found;
1017110170 }
10172-
10173-
10174- /* *
10175- * @brief Check if Last Branch Record MSRs are correctly virtualized
10176- * @category Windows
10177- * @implements VM::LBR
10178- * @note Currently investigating possible false flags with this
10179- */
10180- [[nodiscard]] static bool lbr () {
10181- #if (x86)
10182- const HMODULE ntdll = util::get_ntdll ();
10183- if (!ntdll) return false ;
10184-
10185- const char * names[] = {
10186- " NtAllocateVirtualMemory" ,
10187- " NtFreeVirtualMemory" ,
10188- " NtFlushInstructionCache" ,
10189- " RtlAddVectoredExceptionHandler" ,
10190- " RtlRemoveVectoredExceptionHandler" ,
10191- " NtCreateThreadEx" ,
10192- " NtGetContextThread" ,
10193- " NtSetContextThread" ,
10194- " NtResumeThread" ,
10195- " NtWaitForSingleObject" ,
10196- " NtClose" ,
10197- " NtProtectVirtualMemory"
10198- };
10199- void * funcs[ARRAYSIZE (names)] = {};
10200- util::get_function_address (ntdll, names, funcs, ARRAYSIZE (names));
10201-
10202- using NtAllocateVirtualMemory_t = NTSTATUS (__stdcall*)(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG);
10203- using NtFreeVirtualMemory_t = NTSTATUS (__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG);
10204- using NtFlushInstructionCache_t = NTSTATUS (__stdcall*)(HANDLE, PVOID, SIZE_T);
10205- using RtlAddVectoredExceptionHandler_t = PVOID (__stdcall*)(ULONG, PVECTORED_EXCEPTION_HANDLER);
10206- using RtlRemoveVectoredExceptionHandler_t = ULONG (__stdcall*)(PVOID);
10207- using NtCreateThreadEx_t = NTSTATUS (__stdcall*)(PHANDLE, ACCESS_MASK, PVOID, HANDLE, PVOID, PVOID, BOOLEAN, ULONG_PTR, SIZE_T, SIZE_T, PVOID);
10208- using NtGetContextThread_t = NTSTATUS (__stdcall*)(HANDLE, PCONTEXT);
10209- using NtSetContextThread_t = NTSTATUS (__stdcall*)(HANDLE, PCONTEXT);
10210- using NtResumeThread_t = NTSTATUS (__stdcall*)(HANDLE, PULONG);
10211- using NtWaitForSingleObject_t = NTSTATUS (__stdcall*)(HANDLE, BOOLEAN, PLARGE_INTEGER);
10212- using NtClose_t = NTSTATUS (__stdcall*)(HANDLE);
10213- using NtProtectVirtualMemory_t = NTSTATUS (__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG, PULONG);
10214-
10215- const auto pNtAllocateVirtualMemory = reinterpret_cast <NtAllocateVirtualMemory_t>(funcs[0 ]);
10216- const auto pNtFreeVirtualMemory = reinterpret_cast <NtFreeVirtualMemory_t>(funcs[1 ]);
10217- const auto pNtFlushInstructionCache = reinterpret_cast <NtFlushInstructionCache_t>(funcs[2 ]);
10218- const auto pRtlAddVectoredExceptionHandler = reinterpret_cast <RtlAddVectoredExceptionHandler_t>(funcs[3 ]);
10219- const auto pRtlRemoveVectoredExceptionHandler = reinterpret_cast <RtlRemoveVectoredExceptionHandler_t>(funcs[4 ]);
10220- const auto pNtCreateThreadEx = reinterpret_cast <NtCreateThreadEx_t>(funcs[5 ]);
10221- const auto pNtGetContextThread = reinterpret_cast <NtGetContextThread_t>(funcs[6 ]);
10222- const auto pNtSetContextThread = reinterpret_cast <NtSetContextThread_t>(funcs[7 ]);
10223- const auto pNtResumeThread = reinterpret_cast <NtResumeThread_t>(funcs[8 ]);
10224- const auto pNtWaitForSingleObject = reinterpret_cast <NtWaitForSingleObject_t>(funcs[9 ]);
10225- const auto pNtClose = reinterpret_cast <NtClose_t>(funcs[10 ]);
10226- const auto pNtProtectVirtualMemory = reinterpret_cast <NtProtectVirtualMemory_t>(funcs[11 ]);
10227-
10228- if (!pNtAllocateVirtualMemory || !pNtFreeVirtualMemory || !pNtFlushInstructionCache ||
10229- !pRtlAddVectoredExceptionHandler || !pRtlRemoveVectoredExceptionHandler ||
10230- !pNtCreateThreadEx || !pNtGetContextThread || !pNtSetContextThread ||
10231- !pNtResumeThread || !pNtWaitForSingleObject || !pNtClose || !pNtProtectVirtualMemory) {
10232- return false ;
10233- }
10234-
10235- // ICEBP because the kernel interrupt handler that inserts the LastBranchFromIp into EXCEPTION_RECORD->ExceptionInformation[0] is the INT 01 handler
10236- constexpr unsigned char codeBytes[] = { 0xE8 ,0x00 ,0x00 ,0x00 ,0x00 , 0xF1 , 0xC3 }; // CALL next ; ICEBP ; RET
10237- const SIZE_T codeSize = sizeof (codeBytes);
10238- const HANDLE hCurrentProcess = reinterpret_cast <HANDLE>(-1LL );
10239-
10240- PVOID controlBase = nullptr ;
10241- SIZE_T controlSize = sizeof (PVOID);
10242- NTSTATUS st = pNtAllocateVirtualMemory (hCurrentProcess, &controlBase, 0 , &controlSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
10243- if (st != 0 || !controlBase) return false ;
10244- *reinterpret_cast <PVOID*>(controlBase) = nullptr ;
10245-
10246- PVOID execBase = nullptr ;
10247- SIZE_T allocSize = codeSize;
10248- st = pNtAllocateVirtualMemory (hCurrentProcess, &execBase, 0 , &allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
10249- if (st != 0 || !execBase) {
10250- SIZE_T tmp = controlSize;
10251- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmp, MEM_RELEASE);
10252- return false ;
10253- }
10254-
10255- unsigned char * dst = reinterpret_cast <unsigned char *>(execBase);
10256- for (SIZE_T i = 0 ; i < codeSize; ++i) dst[i] = codeBytes[i];
10257-
10258- ULONG oldProtect = 0 ;
10259- SIZE_T protectSize = allocSize;
10260- PVOID protectBase = execBase;
10261- st = pNtProtectVirtualMemory (hCurrentProcess, &protectBase, &protectSize, PAGE_EXECUTE_READ, &oldProtect);
10262- if (st != 0 ) {
10263- SIZE_T tmpExec = allocSize;
10264- pNtFreeVirtualMemory (hCurrentProcess, &execBase, &tmpExec, MEM_RELEASE);
10265- SIZE_T tmpControl = controlSize;
10266- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmpControl, MEM_RELEASE);
10267- return false ;
10268- }
10269-
10270- pNtFlushInstructionCache (hCurrentProcess, execBase, codeSize);
10271-
10272- // local static pointer to control slot so lambda can access a stable address
10273- static PVOID g_control_slot = nullptr ;
10274- g_control_slot = controlBase;
10275-
10276- auto veh_lambda = [](PEXCEPTION_POINTERS ep) -> LONG {
10277- if (!ep || !ep->ExceptionRecord ) return EXCEPTION_CONTINUE_SEARCH;
10278- if (ep->ExceptionRecord ->ExceptionCode != EXCEPTION_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH;
10279-
10280- ULONG_PTR info0 = 0 ;
10281- if (ep->ExceptionRecord ->NumberParameters > 0 ) info0 = ep->ExceptionRecord ->ExceptionInformation [0 ];
10282- if (info0 && g_control_slot) {
10283- PVOID expected = nullptr ;
10284- _InterlockedCompareExchangePointer (reinterpret_cast <PVOID*>(g_control_slot), reinterpret_cast <PVOID*>(info0), expected);
10285- }
10286- return EXCEPTION_CONTINUE_EXECUTION;
10287- };
10288-
10289- // Register VEH
10290- const PVECTORED_EXCEPTION_HANDLER veh_fn = static_cast <PVECTORED_EXCEPTION_HANDLER>(veh_lambda);
10291- const PVOID vehHandle = pRtlAddVectoredExceptionHandler (1 , veh_fn);
10292- if (!vehHandle) {
10293- SIZE_T tmp = allocSize;
10294- pNtFreeVirtualMemory (hCurrentProcess, &execBase, &tmp, MEM_RELEASE);
10295- tmp = controlSize;
10296- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmp, MEM_RELEASE);
10297- return false ;
10298- }
10299-
10300- // create suspended thread
10301- HANDLE hThread = nullptr ;
10302- NTSTATUS ntres = pNtCreateThreadEx (&hThread, THREAD_ALL_ACCESS, nullptr , hCurrentProcess, execBase, nullptr , TRUE , 0 , 0 , 0 , nullptr );
10303- if (ntres != 0 || !hThread) {
10304- pRtlRemoveVectoredExceptionHandler (vehHandle);
10305- SIZE_T tmp = allocSize;
10306- pNtFreeVirtualMemory (hCurrentProcess, &execBase, &tmp, MEM_RELEASE);
10307- tmp = controlSize;
10308- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmp, MEM_RELEASE);
10309- return false ;
10310- }
10311-
10312- // set debug bits + TF on suspended thread
10313- CONTEXT ctx;
10314- ZeroMemory (&ctx, sizeof (ctx));
10315- ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS;
10316- ntres = pNtGetContextThread (hThread, &ctx);
10317- if (ntres != 0 ) {
10318- pNtClose (hThread);
10319- pRtlRemoveVectoredExceptionHandler (vehHandle);
10320- SIZE_T tmp = allocSize;
10321- pNtFreeVirtualMemory (hCurrentProcess, &execBase, &tmp, MEM_RELEASE);
10322- tmp = controlSize;
10323- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmp, MEM_RELEASE);
10324- return false ;
10325- }
10326- ctx.Dr7 |= (1ull << 8 ) | (1ull << 9 ); // LBR only would be enough
10327- ctx.EFlags |= 0x100 ;
10328- ntres = pNtSetContextThread (hThread, &ctx);
10329- if (ntres != 0 ) {
10330- pNtClose (hThread);
10331- pRtlRemoveVectoredExceptionHandler (vehHandle);
10332- SIZE_T tmp = allocSize;
10333- pNtFreeVirtualMemory (hCurrentProcess, &execBase, &tmp, MEM_RELEASE);
10334- tmp = controlSize;
10335- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmp, MEM_RELEASE);
10336- return false ;
10337- }
10338-
10339- // resume and wait
10340- ULONG suspendCount = 0 ;
10341- ntres = pNtResumeThread (hThread, &suspendCount);
10342- if (ntres != 0 ) {
10343- pNtClose (hThread);
10344- pRtlRemoveVectoredExceptionHandler (vehHandle);
10345- SIZE_T tmp = allocSize;
10346- pNtFreeVirtualMemory (hCurrentProcess, &execBase, &tmp, MEM_RELEASE);
10347- tmp = controlSize;
10348- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmp, MEM_RELEASE);
10349- return false ;
10350- }
10351- ntres = pNtWaitForSingleObject (hThread, FALSE , nullptr );
10352-
10353- // read slot (pointer-sized) so if null then no LBR observed
10354- const PVOID slot_val = *reinterpret_cast <PVOID*>(controlBase);
10355-
10356- // cleanup
10357- pRtlRemoveVectoredExceptionHandler (vehHandle);
10358- pNtClose (hThread);
10359- SIZE_T tmpSize = allocSize;
10360- pNtFreeVirtualMemory (hCurrentProcess, &execBase, &tmpSize, MEM_RELEASE);
10361- tmpSize = controlSize;
10362- pNtFreeVirtualMemory (hCurrentProcess, &controlBase, &tmpSize, MEM_RELEASE);
10363- g_control_slot = nullptr ;
10364-
10365- // a breakpoint set anywhere in this function before slot_val is read will cause the kernel to not deliver any LBR info, thereby returning true
10366- return (slot_val == nullptr );
10367- #else
10368- return false ;
10369- #endif
10370- }
1037110171 // ADD NEW TECHNIQUE FUNCTION HERE
1037210172#endif
1037310173
@@ -11457,7 +11257,6 @@ struct VM {
1145711257 case EDID: return " EDID" ;
1145811258 case CPU_HEURISTIC: return " CPU_HEURISTIC" ;
1145911259 case CLOCK: return " CLOCK" ;
11460- case LBR: return " LBR" ;
1146111260 // END OF TECHNIQUE LIST
1146211261 case DEFAULT: return " setting flag, error" ;
1146311262 case ALL: return " setting flag, error" ;
@@ -11996,7 +11795,6 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1199611795 std::make_pair (VM::CLOCK, VM::core::technique (100 , VM::clock)),
1199711796 std::make_pair (VM::POWER_CAPABILITIES, VM::core::technique (45 , VM::power_capabilities)),
1199811797 std::make_pair (VM::CPU_HEURISTIC, VM::core::technique (90 , VM::cpu_heuristic)),
11999- std::make_pair (VM::LBR, VM::core::technique (95 , VM::lbr)),
1200011798 std::make_pair (VM::EDID, VM::core::technique (100 , VM::edid)),
1200111799 std::make_pair (VM::BOOT_LOGO, VM::core::technique (100 , VM::boot_logo)),
1200211800 std::make_pair (VM::GPU_CAPABILITIES, VM::core::technique (45 , VM::gpu_capabilities)),
0 commit comments