Skip to content

Commit 1389d44

Browse files
authored
Merge pull request #327 from NotRequiem/dev
Improvements to VM::GPU_CAPABILITIES
2 parents 55ac6a6 + 95673f0 commit 1389d44

3 files changed

Lines changed: 93 additions & 47 deletions

File tree

docs/documentation.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
510510
| `VM::HDD_SERIAL` | Check for serial numbers of virtual disks | Windows | 100% | | | | |
511511
| `VM::PORT_CONNECTORS` | Check for physical connection ports | Windows | 25% | | | | This technique is known to false flag on devices like Surface Pro |
512512
| `VM::GPU_CAPABILITIES` | Check for GPU capabilities related to VMs | Windows | 100% | Admin | | | Admin only needed for some heuristics |
513-
| `VM::GPU_VM_STRINGS` | Check for specific GPU string signatures related to VMs | Windows | 100% | | | | |
513+
| `VM::GPU_VM_STRINGS` | Check for specific GPU string signatures related to VMs | Windows | 100% | | | | If GPU_CAPABILITIES also flags, the overall score will be 50 instead of 100 |
514514
| `VM::VM_DEVICES` | Check for VM-specific devices | Windows | 50% | | | | |
515515
| `VM::IDT_GDT_SCAN` | Check if the IDT and GDT virtual base addresses are equal across different CPU cores when not running under Hyper-V | Windows | 50% | | | | |
516516
| `VM::PROCESSOR_NUMBER` | Check for number of processors | Windows | 50% | | | | |
@@ -527,9 +527,9 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
527527
| `VM::NATIVE_VHD` | Checks if the OS was booted from a VHD container | | 100% | | | | |
528528
| `VM::NATIVE_VHD` | Check for OS being booted from a VHD container | Windows | 100% | | | | |
529529
| `VM::VIRTUAL_REGISTRY` | Check for particular object directory which is present in Sandboxie virtual environment but not in usual host systems | Windows | 65% | | | | Admin only needed for Linux |
530-
| `VM::FIRMWARE` | Check for VM signatures and patched strings by hardeners in firmware, while ensuring the BIOS serial is valid | Windows and Linux | 75% | | | | |
530+
| `VM::FIRMWARE` | Check for VM signatures and patched strings by hardeners in firmware, while ensuring the BIOS serial is valid | Windows and Linux | 100% | | | | |
531531
| `VM::FILE_ACCESS_HISTORY` | Check if the number of accessed files are too low for a human-managed environment | Linux | 15% | | | | |
532-
| `VM::AUDIO` | Check if audio device is present | Windows | 25% | | | | |
532+
| `VM::AUDIO` | Check if any waveform-audio output devices are present in the system | Windows | 25% | | | | |
533533
| `VM::UNKNOWN_MANUFACTURER` | Check if the CPU manufacturer is not known | | 50% | | | | |
534534
| `VM::OSXSAVE` | Check if running xgetbv in the XCR0 extended feature register triggers an exception | Windows | 50% | | | | |
535535
| `VM::NSJAIL_PID` | Check if process status matches with nsjail patterns with PID anomalies | Linux | 75% | | | | |

src/vmaware.hpp

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@
3131
* - struct for internal cpu operations => line 749
3232
* - struct for internal memoization => line 1220
3333
* - struct for internal utility functions => line 1344
34-
* - struct for internal core components => line 100128
34+
* - struct for internal core components => line 10153
3535
* - start of VM detection technique list => line 2345
36-
* - start of public VM detection functions => line 10792
37-
* - start of externally defined variables => line 11742
36+
* - start of public VM detection functions => line 10817
37+
* - start of externally defined variables => line 11767
3838
*
3939
*
4040
* ============================== EXAMPLE ===================================
@@ -7598,7 +7598,7 @@ struct VM {
75987598
/**
75997599
* @brief Check for GPU capabilities related to VMs
76007600
* @category Windows
7601-
* @author Requiem
7601+
* @author Requiem (https://github.com/NotRequiem)
76027602
* @implements VM::GPU_CAPABILITIES
76037603
*/
76047604
[[nodiscard]] static bool gpu_capabilities() {
@@ -7611,25 +7611,52 @@ struct VM {
76117611

76127612
IDirect3D9* pD3D = Direct3DCreate9(D3D_SDK_VERSION);
76137613
if (!pD3D) {
7614-
debug("GPU_CAPABILITIES: Direct3DCreate9");
7614+
debug("GPU_CAPABILITIES: Direct3DCreate9 failed");
76157615
return true;
76167616
}
76177617

76187618
D3DADAPTER_IDENTIFIER9 adapterId;
7619-
D3DCAPS9 caps;
76207619
if (SUCCEEDED(pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &adapterId))) {
7621-
if (adapterId.VendorId == 0x15AD) {
7620+
if (adapterId.VendorId == 0x15AD) {
76227621
pD3D->Release();
76237622
return core::add(brands::VMWARE);
76247623
}
7625-
else if (adapterId.VendorId == 0x80EE) {
7624+
else if (adapterId.VendorId == 0x80EE) {
76267625
pD3D->Release();
76277626
return core::add(brands::VBOX);
76287627
}
76297628
}
76307629

7631-
if (FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))) {
7632-
debug("GPU_CAPABILITIES: GetDeviceCaps");
7630+
D3DCAPS9 caps;
7631+
HRESULT hr = pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
7632+
const int maxRetries = 3;
7633+
for (int attempt = 0; FAILED(hr) && (attempt < maxRetries); ++attempt) {
7634+
SleepEx(500, FALSE);
7635+
hr = pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
7636+
}
7637+
if (FAILED(hr)) {
7638+
debug("GPU_CAPABILITIES: GetDeviceCaps failure");
7639+
pD3D->Release();
7640+
return false;
7641+
}
7642+
7643+
// Pixel Shader version 2.0
7644+
if (caps.PixelShaderVersion < D3DPS_VERSION(2, 0)) {
7645+
debug("GPU_CAPABILITIES: Insufficient Pixel Shader version");
7646+
pD3D->Release();
7647+
return true;
7648+
}
7649+
7650+
// hardware transform and lighting capability
7651+
if (!(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)) {
7652+
debug("GPU_CAPABILITIES: Missing hardware T&L support");
7653+
pD3D->Release();
7654+
return true;
7655+
}
7656+
7657+
// simultaneous render targets
7658+
if (caps.NumSimultaneousRTs < 2) {
7659+
debug("GPU_CAPABILITIES: Insufficient simultaneous render targets");
76337660
pD3D->Release();
76347661
return true;
76357662
}
@@ -7638,31 +7665,26 @@ struct VM {
76387665

76397666
IDXGIFactory* pFactory = nullptr;
76407667
if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&pFactory)))) {
7641-
debug("GPU_CAPABILITIES: DXGIFactory");
7668+
debug("GPU_CAPABILITIES: DXGIFactory creation failed");
76427669
return true;
76437670
}
76447671

76457672
IDXGIAdapter* pAdapter = nullptr;
7646-
// Do not enumerate all adapters, otherwise it would false flag with adapters with no dedicated GPUs
7647-
// like Microsoft Basic Render Driver (vid 0x1414)
7673+
// Enumerate only the primary adapter so as not to mistakenly flag machines without a dedicated GPU, like Microsoft Basic Render Driver (vid 0x1414)
76487674
if (pFactory->EnumAdapters(0, &pAdapter) != DXGI_ERROR_NOT_FOUND) {
76497675
DXGI_ADAPTER_DESC adapterDesc;
76507676
if (SUCCEEDED(pAdapter->GetDesc(&adapterDesc))) {
7651-
char description[128] = { 0 };
7652-
size_t converted = 0;
7653-
wcstombs_s(&converted, description, adapterDesc.Description, sizeof(description));
7654-
7655-
if (adapterDesc.DedicatedVideoMemory < static_cast<unsigned long long>(1024 * 1024) * 1024) {
7656-
debug("GPU_CAPABILITIES: Video memory");
7677+
if (adapterDesc.DedicatedVideoMemory < (1024ULL * 1024ULL * 1024ULL)) {
7678+
debug("GPU_CAPABILITIES: Video memory below threshold");
76577679
pAdapter->Release();
76587680
pFactory->Release();
76597681
return true;
76607682
}
76617683
}
76627684
pAdapter->Release();
76637685
}
7664-
76657686
pFactory->Release();
7687+
76667688
return false;
76677689
#endif
76687690
}
@@ -7730,7 +7752,7 @@ struct VM {
77307752
* However, when Windows is running under Hyper-V (in a root partition), the IDT and GDT base address will always be the same across all CPU cores if called from user-mode.
77317753
* This kernel address leak prevention measure is done by Hyper-V on purpose and can be abused to detect VMs.
77327754
* @category Windows, x64
7733-
* @author Requiem
7755+
* @author Requiem (https://github.com/NotRequiem)
77347756
* @implements VM::IDT_GDT_SCAN
77357757
*/
77367758
[[nodiscard]] static bool idt_gdt_scan() {
@@ -8000,6 +8022,7 @@ struct VM {
80008022
/**
80018023
* @brief Check for timing anomalies in the system
80028024
* @category x86
8025+
* @author Requiem (https://github.com/NotRequiem)
80038026
* @implements VM::TIMER
80048027
*/
80058028
[[nodiscard]]
@@ -9903,7 +9926,7 @@ struct VM {
99039926
}
99049927

99059928

9906-
/* @brief Check if audio device is present
9929+
/* @brief Check if any waveform-audio output devices are present in the system
99079930
* @category Windows
99089931
* @implements VM::AUDIO
99099932
*/
@@ -11976,7 +11999,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1197611999
std::make_pair(VM::AMD_SEV, VM::core::technique(50, VM::amd_sev)),
1197712000
std::make_pair(VM::NATIVE_VHD, VM::core::technique(100, VM::native_vhd)),
1197812001
std::make_pair(VM::VIRTUAL_REGISTRY, VM::core::technique(65, VM::virtual_registry)),
11979-
std::make_pair(VM::FIRMWARE, VM::core::technique(75, VM::firmware_scan)),
12002+
std::make_pair(VM::FIRMWARE, VM::core::technique(100, VM::firmware_scan)),
1198012003
std::make_pair(VM::FILE_ACCESS_HISTORY, VM::core::technique(15, VM::file_access_history)),
1198112004
std::make_pair(VM::AUDIO, VM::core::technique(25, VM::check_audio)),
1198212005
std::make_pair(VM::UNKNOWN_MANUFACTURER, VM::core::technique(50, VM::unknown_manufacturer)),

src/vmaware_MIT.hpp

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@
5353
* - struct for internal cpu operations => line 764
5454
* - struct for internal memoization => line 1236
5555
* - struct for internal utility functions => line 1361
56-
* - struct for internal core components => line 9934
56+
* - struct for internal core components => line 9959
5757
* - start of VM detection technique list => line 2364
58-
* - start of public VM detection functions => line 10609
59-
* - start of externally defined variables => line 11561
58+
* - start of public VM detection functions => line 10634
59+
* - start of externally defined variables => line 11586
6060
*
6161
*
6262
* ============================== EXAMPLE ===================================
@@ -7403,7 +7403,7 @@ struct VM {
74037403
/**
74047404
* @brief Check for GPU capabilities related to VMs
74057405
* @category Windows
7406-
* @author Requiem
7406+
* @author Requiem (https://github.com/NotRequiem)
74077407
* @implements VM::GPU_CAPABILITIES
74087408
*/
74097409
[[nodiscard]] static bool gpu_capabilities() {
@@ -7416,12 +7416,11 @@ struct VM {
74167416

74177417
IDirect3D9* pD3D = Direct3DCreate9(D3D_SDK_VERSION);
74187418
if (!pD3D) {
7419-
debug("GPU_CAPABILITIES: Direct3DCreate9");
7419+
debug("GPU_CAPABILITIES: Direct3DCreate9 failed");
74207420
return true;
74217421
}
74227422

74237423
D3DADAPTER_IDENTIFIER9 adapterId;
7424-
D3DCAPS9 caps;
74257424
if (SUCCEEDED(pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &adapterId))) {
74267425
if (adapterId.VendorId == 0x15AD) {
74277426
pD3D->Release();
@@ -7433,8 +7432,36 @@ struct VM {
74337432
}
74347433
}
74357434

7436-
if (FAILED(pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps))) {
7437-
debug("GPU_CAPABILITIES: GetDeviceCaps");
7435+
D3DCAPS9 caps;
7436+
HRESULT hr = pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
7437+
const int maxRetries = 3;
7438+
for (int attempt = 0; FAILED(hr) && (attempt < maxRetries); ++attempt) {
7439+
SleepEx(500, FALSE);
7440+
hr = pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);
7441+
}
7442+
if (FAILED(hr)) {
7443+
debug("GPU_CAPABILITIES: GetDeviceCaps failure");
7444+
pD3D->Release();
7445+
return false;
7446+
}
7447+
7448+
// Pixel Shader version 2.0
7449+
if (caps.PixelShaderVersion < D3DPS_VERSION(2, 0)) {
7450+
debug("GPU_CAPABILITIES: Insufficient Pixel Shader version");
7451+
pD3D->Release();
7452+
return true;
7453+
}
7454+
7455+
// hardware transform and lighting capability
7456+
if (!(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)) {
7457+
debug("GPU_CAPABILITIES: Missing hardware T&L support");
7458+
pD3D->Release();
7459+
return true;
7460+
}
7461+
7462+
// simultaneous render targets
7463+
if (caps.NumSimultaneousRTs < 2) {
7464+
debug("GPU_CAPABILITIES: Insufficient simultaneous render targets");
74387465
pD3D->Release();
74397466
return true;
74407467
}
@@ -7443,31 +7470,26 @@ struct VM {
74437470

74447471
IDXGIFactory* pFactory = nullptr;
74457472
if (FAILED(CreateDXGIFactory(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&pFactory)))) {
7446-
debug("GPU_CAPABILITIES: DXGIFactory");
7473+
debug("GPU_CAPABILITIES: DXGIFactory creation failed");
74477474
return true;
74487475
}
74497476

74507477
IDXGIAdapter* pAdapter = nullptr;
7451-
// Do not enumerate all adapters, otherwise it would false flag with adapters with no dedicated GPUs
7452-
// like Microsoft Basic Render Driver (vid 0x1414)
7478+
// Enumerate only the primary adapter so as not to mistakenly flag machines without a dedicated GPU, like Microsoft Basic Render Driver (vid 0x1414)
74537479
if (pFactory->EnumAdapters(0, &pAdapter) != DXGI_ERROR_NOT_FOUND) {
74547480
DXGI_ADAPTER_DESC adapterDesc;
74557481
if (SUCCEEDED(pAdapter->GetDesc(&adapterDesc))) {
7456-
char description[128] = { 0 };
7457-
size_t converted = 0;
7458-
wcstombs_s(&converted, description, adapterDesc.Description, sizeof(description));
7459-
7460-
if (adapterDesc.DedicatedVideoMemory < static_cast<unsigned long long>(1024 * 1024) * 1024) {
7461-
debug("GPU_CAPABILITIES: Video memory");
7482+
if (adapterDesc.DedicatedVideoMemory < (1024ULL * 1024ULL * 1024ULL)) {
7483+
debug("GPU_CAPABILITIES: Video memory below threshold");
74627484
pAdapter->Release();
74637485
pFactory->Release();
74647486
return true;
74657487
}
74667488
}
74677489
pAdapter->Release();
74687490
}
7469-
74707491
pFactory->Release();
7492+
74717493
return false;
74727494
#endif
74737495
}
@@ -7535,7 +7557,7 @@ struct VM {
75357557
* However, when Windows is running under Hyper-V (in a root partition), the IDT and GDT base address will always be the same across all CPU cores if called from user-mode.
75367558
* This kernel address leak prevention measure is done by Hyper-V on purpose and can be abused to detect VMs.
75377559
* @category Windows, x64
7538-
* @author Requiem
7560+
* @author Requiem (https://github.com/NotRequiem)
75397561
* @implements VM::IDT_GDT_SCAN
75407562
*/
75417563
[[nodiscard]] static bool idt_gdt_scan() {
@@ -7805,6 +7827,7 @@ struct VM {
78057827
/**
78067828
* @brief Check for timing anomalies in the system
78077829
* @category x86
7830+
* @author Requiem (https://github.com/NotRequiem)
78087831
* @implements VM::TIMER
78097832
*/
78107833
[[nodiscard]]
@@ -9708,7 +9731,7 @@ struct VM {
97089731
}
97099732

97109733

9711-
/* @brief Check if audio device is present
9734+
/* @brief Check if any waveform-audio output devices are present in the system
97129735
* @category Windows
97139736
* @implements VM::AUDIO
97149737
*/
@@ -11788,7 +11811,7 @@ std::pair<VM::enum_flags, VM::core::technique> VM::core::technique_list[] = {
1178811811
std::make_pair(VM::AMD_SEV, VM::core::technique(50, VM::amd_sev)),
1178911812
std::make_pair(VM::NATIVE_VHD, VM::core::technique(100, VM::native_vhd)),
1179011813
std::make_pair(VM::VIRTUAL_REGISTRY, VM::core::technique(65, VM::virtual_registry)),
11791-
std::make_pair(VM::FIRMWARE, VM::core::technique(75, VM::firmware_scan)),
11814+
std::make_pair(VM::FIRMWARE, VM::core::technique(100, VM::firmware_scan)),
1179211815
std::make_pair(VM::FILE_ACCESS_HISTORY, VM::core::technique(15, VM::file_access_history)),
1179311816
std::make_pair(VM::AUDIO, VM::core::technique(25, VM::check_audio)),
1179411817
std::make_pair(VM::UNKNOWN_MANUFACTURER, VM::core::technique(50, VM::unknown_manufacturer)),

0 commit comments

Comments
 (0)