Skip to content

Commit ab708ac

Browse files
authored
Merge pull request #369 from NotRequiem/dev
First detection ever for GPU passthrough on QEMU
2 parents 3ed9e37 + 5c0ad70 commit ab708ac

4 files changed

Lines changed: 825 additions & 907 deletions

File tree

docs/documentation.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
441441
| `VM::CPU_BRAND` | Check if CPU brand model contains any VM-specific string snippets | 🐧🪟🍏 | 50% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L2545) |
442442
| `VM::HYPERVISOR_BIT` | Check if hypervisor feature bit in CPUID eax bit 31 is enabled (always false for physical CPUs) | 🐧🪟🍏 | 100% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L2601) |
443443
| `VM::HYPERVISOR_STR` | Check for hypervisor brand string length (would be around 2 characters in a host machine) | 🐧🪟🍏 | 75% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L2622) |
444-
| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 45% | | | | Unsafe to run under binary emulators | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L8169 ) |
444+
| `VM::TIMER` | Check for timing anomalies in the system | 🐧🪟🍏 | 45% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L8169 ) |
445445
| `VM::THREADCOUNT` | Check if there are only 1 or 2 threads, which is a common pattern in VMs with default settings (nowadays physical CPUs should have at least 4 threads for modern CPUs) | 🐧🪟🍏 | 35% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L2649) |
446446
| `VM::MAC` | Check if mac address starts with certain VM designated values | 🐧🪟 | 20% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L2671) |
447447
| `VM::TEMPERATURE` | Check if thermal directory in linux is present, might not be present in VMs | 🐧 | 15% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L2804) |
@@ -544,6 +544,8 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
544544
| `VM::PCI_VM` | Check for PCIe bridge names for known VM keywords and brands | 🐧 | 100% | | | | Disabled by default | [link](https://github.com/kernelwernel/VMAware/blob/8cb2491b1c7d2cb7300d1d698b7c64c953b4ae75/src/vmaware.hpp#L10142) |
545545
| `VM::TPM` | Check if the system has a physical TPM by matching the TPM manufacturer against known physical TPM chip vendors | 🪟 | 50% | | | | | [link](https://github.com/kernelwernel/VMAware/blob/fb66db9fdd7894edebe5eeade4b0148a08bd5514/src/vmaware.hpp#L10011)|
546546
| `VM::PCI_VM_DEVICE_ID` | Check for PCI vendor and device IDs that are VM-specific | 🐧 | 90% | | | | | |
547+
| `VM::QEMU_PASSTHROUGH` | Check for QEMU's hot-plug signature | 🪟 | 100% | | | | | |
548+
547549
<!-- ADD TECHNIQUE DETAILS HERE -->
548550

549551
<br>

src/cli.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ bool is_unsupported(VM::enum_flags flag) {
470470
case VM::FIRMWARE:
471471
case VM::UNKNOWN_MANUFACTURER:
472472
case VM::TPM:
473+
case VM::QEMU_PASSTHROUGH:
473474
// ADD WINDOWS FLAG
474475
return false;
475476
default: return true;
@@ -977,6 +978,8 @@ void general() {
977978
checker(VM::PCI_VM, "PCIe bridge ports");
978979
checker(VM::TPM, "TPM manufacturer");
979980
checker(VM::PCI_VM_DEVICE_ID, "PCI vendor/device ID");
981+
checker(VM::QEMU_PASSTHROUGH, "QEMU passthrough");
982+
980983
// ADD NEW TECHNIQUE CHECKER HERE
981984

982985
std::printf("\n");

src/vmaware.hpp

Lines changed: 114 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
*
2828
*
2929
* ============================== SECTIONS ==================================
30-
* - enums for publicly accessible techniques => line 555
31-
* - struct for internal cpu operations => line 741
32-
* - struct for internal memoization => line 1212
33-
* - struct for internal utility functions => line 1340
34-
* - struct for internal core components => line 10291
35-
* - start of VM detection technique list => line 2427
36-
* - start of public VM detection functions => line 10948
37-
* - start of externally defined variables => line 11892
30+
* - enums for publicly accessible techniques => line 557
31+
* - struct for internal cpu operations => line 743
32+
* - struct for internal memoization => line 1209
33+
* - struct for internal utility functions => line 1337
34+
* - struct for internal core components => line 10102
35+
* - start of VM detection technique list => line 2450
36+
* - start of public VM detection functions => line 10759
37+
* - start of externally defined variables => line 11710
3838
*
3939
*
4040
* ============================== EXAMPLE ===================================
@@ -366,6 +366,9 @@
366366
#include <wrl/client.h>
367367
#include <tbs.h>
368368
#include <mutex>
369+
#include <initguid.h>
370+
#include <devpkey.h>
371+
#include <devguid.h>
369372

370373
#pragma comment(lib, "winmm.lib")
371374
#pragma comment(lib, "setupapi.lib")
@@ -658,6 +661,7 @@ struct VM {
658661
PCI_VM,
659662
TPM,
660663
PCI_VM_DEVICE_ID,
664+
QEMU_PASSTHROUGH,
661665
// ADD NEW TECHNIQUE ENUM NAME HERE
662666

663667
// special flags, different to settings
@@ -1970,6 +1974,36 @@ struct VM {
19701974
return std::string();
19711975
}
19721976

1977+
1978+
[[nodiscard]] static bool is_running_under_translator() {
1979+
#if (WINDOWS)
1980+
u8 ver = get_windows_version();
1981+
if (ver == 10 || ver == 11) {
1982+
USHORT procMachine = 0, nativeMachine = 0;
1983+
if (IsWow64Process2(GetCurrentProcess(), &procMachine, &nativeMachine)) {
1984+
if (nativeMachine == IMAGE_FILE_MACHINE_ARM64 &&
1985+
(procMachine == IMAGE_FILE_MACHINE_AMD64 ||
1986+
procMachine == IMAGE_FILE_MACHINE_I386))
1987+
{
1988+
return true;
1989+
}
1990+
}
1991+
}
1992+
#endif
1993+
1994+
if (cpu::is_leaf_supported(cpu::leaf::hypervisor)) {
1995+
std::string vendor = cpu::cpu_manufacturer(cpu::leaf::hypervisor);
1996+
if (vendor == "VirtualApple" || // Apple Rosetta
1997+
vendor == "PowerVM Lx86") // IBM PowerVM Lx86
1998+
{
1999+
return true;
2000+
}
2001+
}
2002+
2003+
return false;
2004+
}
2005+
2006+
19732007
/**
19742008
* @brief Checks whether the system is running in a Hyper-V virtual machine or if the host system has Hyper-V enabled
19752009
* @note Hyper-V's presence on a host system can set certain hypervisor-related CPU flags that may appear similar to those in a virtualized environment, which can make it challenging to differentiate between an actual Hyper-V virtual machine (VM) and a host system with Hyper-V enabled.
@@ -3652,11 +3686,6 @@ struct VM {
36523686
/* GPL */ SP_DEVINFO_DATA DeviceInfoData{};
36533687
/* GPL */ DWORD i;
36543688
/* GPL */
3655-
/* GPL */ constexpr GUID GUID_DEVCLASS_DISKDRIVE = {
3656-
/* GPL */ 0x4d36e967L, 0xe325, 0x11ce,
3657-
/* GPL */ { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 }
3658-
/* GPL */ };
3659-
/* GPL */
36603689
/* GPL */ hDevInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVCLASS_DISKDRIVE,
36613690
/* GPL */ 0,
36623691
/* GPL */ 0,
@@ -7499,6 +7528,11 @@ struct VM {
74997528
return false;
75007529
}
75017530

7531+
if (util::is_running_under_translator()) {
7532+
debug("Running inside binary translation layer.");
7533+
return false;
7534+
}
7535+
75027536
// to minimize context switching/scheduling
75037537
#if (WINDOWS)
75047538
const HANDLE hThread = GetCurrentThread();
@@ -7539,7 +7573,7 @@ struct VM {
75397573
// 1. TSC Synchronization Check Across Cores
75407574
// Try reading the invariant TSC on two different cores to attempt to detect vCPU timers being shared
75417575
constexpr u8 tscIterations = 10;
7542-
constexpr u16 tscSyncDiffThreshold = 5000;
7576+
constexpr u16 tscSyncDiffThreshold = 500;
75437577

75447578
bool tscSyncDetected = false;
75457579
tscSyncDetected = [&]() noexcept -> bool {
@@ -9984,6 +10018,69 @@ struct VM {
998410018
#endif
998510019
}
998610020

10021+
10022+
/**
10023+
* @brief Check for QEMU's hot-plug signature
10024+
* @category Windows
10025+
* @author Requiem (https://github.com/NotRequiem)
10026+
* @implements VM::QEMU_PASSTHROUGH
10027+
*/
10028+
[[nodiscard]] static bool qemu_passthrough()
10029+
{
10030+
#if (!WINDOWS)
10031+
return false;
10032+
#else
10033+
// QEMU passthrough location paths
10034+
static const std::wregex busRegex(L"PCIROOT\\(0\\)#PCI\\(0202\\)");
10035+
static const std::wregex acpiSlotRegex(L"#ACPI\\(S[0-9]{2}_\\)");
10036+
10037+
HDEVINFO hDevInfo = SetupDiGetClassDevsW(
10038+
&GUID_DEVCLASS_DISPLAY,
10039+
nullptr,
10040+
nullptr,
10041+
DIGCF_PRESENT);
10042+
if (hDevInfo == INVALID_HANDLE_VALUE)
10043+
return false;
10044+
10045+
SP_DEVINFO_DATA devInfo = {};
10046+
devInfo.cbSize = sizeof(devInfo);
10047+
const DEVPROPKEY key = DEVPKEY_Device_LocationPaths;
10048+
10049+
for (DWORD idx = 0; SetupDiEnumDeviceInfo(hDevInfo, idx, &devInfo); ++idx)
10050+
{
10051+
DEVPROPTYPE propType = 0;
10052+
DWORD requiredSize = 0;
10053+
10054+
SetupDiGetDevicePropertyW(
10055+
hDevInfo, &devInfo, &key, &propType,
10056+
nullptr, 0, &requiredSize, 0);
10057+
10058+
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || requiredSize == 0)
10059+
continue;
10060+
10061+
std::vector<BYTE> buffer(requiredSize);
10062+
if (!SetupDiGetDevicePropertyW(
10063+
hDevInfo, &devInfo, &key, &propType,
10064+
buffer.data(), requiredSize, &requiredSize, 0))
10065+
continue;
10066+
10067+
const wchar_t* ptr = reinterpret_cast<const wchar_t*>(buffer.data());
10068+
while (*ptr) {
10069+
std::wstring path(ptr);
10070+
if (std::regex_search(path, busRegex) ||
10071+
std::regex_search(path, acpiSlotRegex))
10072+
{
10073+
SetupDiDestroyDeviceInfoList(hDevInfo);
10074+
return true;
10075+
}
10076+
ptr += path.size() + 1;
10077+
}
10078+
}
10079+
10080+
SetupDiDestroyDeviceInfoList(hDevInfo);
10081+
return false;
10082+
#endif
10083+
}
998710084
// ADD NEW TECHNIQUE FUNCTION HERE
998810085

998910086

@@ -11279,8 +11376,8 @@ struct VM {
1127911376
case NSJAIL_PID: return "NSJAIL_PID";
1128011377
case PCI_VM: return "PCI_VM";
1128111378
case TPM: return "TPM";
11282-
1128311379
case PCI_VM_DEVICE_ID: return "PCI_VM_DEVICE_ID";
11380+
case QEMU_PASSTHROUGH: return "QEMU_PASSTHROUGH";
1128411381
// ADD NEW CASE HERE FOR NEW TECHNIQUE
1128511382
default: return "Unknown flag";
1128611383
}
@@ -11849,6 +11946,8 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1184911946
std::make_pair(VM::PCI_VM, VM::core::technique(100, VM::lspci)),
1185011947
std::make_pair(VM::TPM, VM::core::technique(50, VM::tpm)),
1185111948
std::make_pair(VM::PCI_VM_DEVICE_ID, VM::core::technique(90, VM::pci_vm_device_id)),
11949+
std::make_pair(VM::QEMU_PASSTHROUGH, VM::core::technique(90, VM::qemu_passthrough)),
11950+
1185211951
// ADD NEW TECHNIQUE STRUCTURE HERE
1185311952
};
1185411953

0 commit comments

Comments
 (0)