Skip to content

Commit 2d20f46

Browse files
authored
Merge pull request #417 from NotRequiem/dev
VM::TRAP fix on AMD CPUs
2 parents f394147 + 6e335cd commit 2d20f46

File tree

2 files changed

+17
-10
lines changed

2 files changed

+17
-10
lines changed

docs/documentation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ VMAware provides a convenient way to not only check for VMs, but also have the f
530530
| `VM::TPM` | Check if the system has a physical TPM by matching the TPM manufacturer against known physical TPM chip vendors | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8075) |
531531
| `VM::PCI_DEVICES` | Check for PCI vendor and device IDs that are VM-specific | 🐧🪟 | 95% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L5979) |
532532
| `VM::QEMU_PASSTHROUGH` | Check for QEMU's hot-plug signature | 🪟 | 90% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8158) |
533-
| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8319) |
533+
| `VM::TRAP` | Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery | 🪟 | 100% | | | On AMD CPUs, this technique will always false flag | [link](https://github.com/kernelwernel/VMAware/tree/main/src/vmaware.hpp#L8320) |
534534

535535
<!-- END OF TECHNIQUE DOCUMENTATION -->
536536

src/vmaware.hpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@
5353
* - struct for internal cpu operations => line 717
5454
* - struct for internal memoization => line 1042
5555
* - struct for internal utility functions => line 1183
56-
* - struct for internal core components => line 8409
56+
* - struct for internal core components => line 8416
5757
* - start of VM detection technique list => line 1993
58-
* - start of public VM detection functions => line 8924
59-
* - start of externally defined variables => line 9853
58+
* - start of public VM detection functions => line 8931
59+
* - start of externally defined variables => line 9860
6060
*
6161
*
6262
* ============================== EXAMPLE ===================================
@@ -4289,7 +4289,7 @@ struct VM {
42894289
const u64 sum = std::accumulate(samples_slat.begin(), samples_slat.end(), u64(0));
42904290
const u64 avg = (sum + samples_slat.size() / 2) / samples_slat.size();
42914291

4292-
debug("Average read latency: ", avg, " cycles\n");
4292+
debug("Average read latency: ", avg, " cycles");
42934293

42944294
VirtualUnlock(mem, pageSize);
42954295
VirtualFree(mem, 0, MEM_RELEASE);
@@ -8304,7 +8304,7 @@ struct VM {
83048304

83058305
if (foundQemuAcpi) {
83068306
SetupDiDestroyDeviceInfoList(hDevInfo);
8307-
return true;
8307+
return core::add(brands::QEMU);
83088308
}
83098309
}
83108310

@@ -8316,12 +8316,19 @@ struct VM {
83168316
/**
83178317
* @brief Check if after raising two traps at the same RIP, a hypervisor interferes with the instruction pointer delivery
83188318
* @category Windows
8319+
* @note On AMD CPUs, this technique will always false flag
83198320
* @implements VM::TRAP
83208321
*/
83218322
[[nodiscard]] static bool trap() {
83228323
if (util::hyper_x() == HYPERV_ARTIFACT_VM) {
83238324
return false;
83248325
}
8326+
// Intel explicitly guarantees (in the SDM) that if TF and DR0 both trigger on the same instruction, DR6.BS and DR6.B0 are both set in the resulting #DB
8327+
// On AMD CPUs they do not set both DR6.BS and DR6.B0 when a single‐step and a DR0 breakpoint coincide on a trappable/serializing instruction
8328+
// whenever a hardware breakpoint and TF collide, only the breakpoint bit shows up in DR6
8329+
if (!cpu::is_intel()) {
8330+
return false;
8331+
}
83258332

83268333
// push flags, set TF-bit, pop flags, execute a dummy instruction, then return
83278334
constexpr unsigned char trampoline[] = {
@@ -8335,17 +8342,17 @@ struct VM {
83358342
};
83368343
SIZE_T trampSize = sizeof(trampoline);
83378344

8338-
// allocate RWX memory for trampoline, simple way to support x86 without recurring to inline assembly
8345+
// simple way to support x86 without recurring to inline assembly
83398346
void* execMem = VirtualAlloc(nullptr, trampSize,
83408347
MEM_COMMIT | MEM_RESERVE,
83418348
PAGE_EXECUTE_READWRITE);
83428349
if (!execMem) {
83438350
return false;
83448351
}
8345-
// Copy payload
8352+
// copy payload
83468353
memcpy(execMem, trampoline, trampSize);
83478354

8348-
// Variables to track detection
8355+
// variables to track detection
83498356
bool hypervisorCaught = false;
83508357
int hitCount = 0;
83518358

@@ -8355,7 +8362,7 @@ struct VM {
83558362
HANDLE thr = GetCurrentThread();
83568363
GetThreadContext(thr, &dbgCtx);
83578364

8358-
// Set Dr0 to trampoline+offset (step triggers here)
8365+
// set Dr0 to trampoline+offset (step triggers here)
83598366
const uintptr_t baseAddr = reinterpret_cast<uintptr_t>(execMem);
83608367
dbgCtx.Dr0 = baseAddr + 11; // single step breakpoint address
83618368
dbgCtx.Dr7 = 1; // enable local breakpoint 0

0 commit comments

Comments
 (0)