Opened 6 years ago
Closed 20 months ago
#17961 closed defect (fixed)
VMM missing rflags.TF handling when advance rip that abused by virtual machine detection => fixed in SVN
Reported by: | hzqst | Owned by: | |
---|---|---|---|
Component: | VMM | Version: | VirtualBox 5.2.18 |
Keywords: | detection | Cc: | |
Guest type: | all | Host type: | all |
Description
Found a virtual-box detection vector that abused by VMProtect 3.1+ and Safengine, confirmed by using custom hypervisor to monitor CPUID instruction.
see https://github.com/tandasat/HyperPlatform/issues/26 for more discussion about this.
IEM_STATIC void iemRegAddToRipAndClearRF(PVMCPU pVCpu, uint8_t cbInstr) { pVCpu->cpum.GstCtx.eflags.Bits.u1RF = 0; AssertCompile(IEMMODE_16BIT == 0 && IEMMODE_32BIT == 1 && IEMMODE_64BIT == 2); #if ARCH_BITS >= 64 static uint64_t const s_aRipMasks[] = { UINT64_C(0xffffffff), UINT64_C(0xffffffff), UINT64_MAX }; Assert(pVCpu->cpum.GstCtx.rip <= s_aRipMasks[(unsigned)pVCpu->iem.s.enmCpuMode]); pVCpu->cpum.GstCtx.rip = (pVCpu->cpum.GstCtx.rip + cbInstr) & s_aRipMasks[(unsigned)pVCpu->iem.s.enmCpuMode]; #else if (pVCpu->iem.s.enmCpuMode == IEMMODE_64BIT) pVCpu->cpum.GstCtx.rip += cbInstr; else pVCpu->cpum.GstCtx.eip += cbInstr; #endif }
Here are the test code that you can use to repro "cpuid in real" in real machine but "cpuid run in vmm" inside any virtualbox guest. (though the code is for Windows)
int filter(EXCEPTION_POINTERS * pException, BOOL *pFound) { auto code = *(BYTE*)pException->ContextRecord->Eip; printf("cpuid exception eip = %p code = %02x\n", pException->ContextRecord->Eip, code); //code != nop, in vmm if (code != 0x90) *pFound = TRUE; return EXCEPTION_EXECUTE_HANDLER; } BOOL cpuid_test() { BOOL bFound = FALSE; __try { __asm { mov eax, 0 pushfd; or DWORD ptr[esp], 0x100; popfd;//set TF=1 cpuid; nop;//normal TF int 3//in vmm... } } __except (filter(GetExceptionInformation(), &bFound)) { ; } return bFound; }
what cpu do in real os: execute CPUID->set EIP at nop->raise a single step exception(#DB) with error code 0x80000004->set TF=0->the exception would be handled by SEH->you see EIP at nop
what cpu do in vmm: execute cpuid->cpuid is handled by vm-exit handler->you advance the EIP by one instrution in the handler (so EIP would be at nop)->execute nop->set EIP at int3->raise a single step exception->set TF=0->the exception would be handled by SEH->you see EIP at int3
the solution is to inject #DB when Guest's TF is set, and adjusting RIP normally, also writing the instruction length of CPUID in the vm entry instruction len which is 2 in the case of cpuid.
NB: You need to do this generically though.
Attachments (4)
Change History (16)
comment:1 by , 6 years ago
comment:2 by , 6 years ago
Will provide a test build next week to see if the VMX fix I mentioned in #17316 also fixes this problem. Thanks!
comment:3 by , 5 years ago
Resolution: | → obsolete |
---|---|
Status: | new → closed |
Please reopen if still an issue with a recent VirtualBox release.
comment:4 by , 5 years ago
Resolution: | obsolete |
---|---|
Status: | closed → reopened |
The issue is relevant for most recent VirtualBox release (6.1.10 r138449)
comment:5 by , 4 years ago
The issue is still present in VirtualBox 6.1.18 r142142
I attached a Linux sample for easier demonstration of the issue. I attached its source code and compiled as 32 bit and 64 bit ELF file.
On a real machine, the output is
Found opcode 0x90 Trapped on nop, we are running on a real machine
in a virtual machine, the output is
Found opcode 0xCC Trapped on int3, we are running in a VM
In short, the issue is that VirtualBox ignores the trap flag for emulated instructions. I.e.
; enable trap flag pushf or WORD ptr[rsp], 0x100 popf cpuid <-- a real machine stops after this instruction nop <-- a virtual machine stops after this instruction int 3
comment:6 by , 2 years ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
This has been fixed in VirtualBox 7.0.4.
Thank you very much for the report and the handy testcase.
comment:7 by , 2 years ago
Resolution: | fixed |
---|---|
Status: | closed → reopened |
Unfortunately, the bug is not fixed in my setup. My host system is Windows 10, 22H2, the guest OS is Ubuntu 22.04 and I am using VirtualBox 7.0.4 r154605 (Qt5.15.2).
When I run either vmdetect_32 or vmdetect_64, I get the following output:
$./vmdetect_64 Found opcode 0xCC Trapped on int3, we are running in a VM
comment:9 by , 2 years ago
Ah, okay you VM is using the native execution manager (NEM) rather than HM. I haven't yet tested this yet, but looking at the code for NEM, it seems the bug might still be persistent there, as it calls into CPUM for handling CPUID, increments RIP and continues execution. HM on the other hand calls into IEM's IEMExecDecodedCpuid (which now should be doing the right thing with regards to the EFLAGS).
I'll try to see if I can test and fix this with NEM sometime soon (assuming that really is the issue). On the other hand, if you have a way to test with HM (by disabling Hyper-V on your Windows 10 22H2 host) that would be useful some useful feedback as well.
comment:10 by , 2 years ago
Summary: | VMM missing rflags.TF handling when advance rip that abused by virtual machine detection → VMM missing rflags.TF handling when advance rip that abused by virtual machine detection => fixed in SVN |
---|
Should be fixed for good now, even with Hyper-V. Didn't made it in the next 7.0.x maintenance release unfortunately but will be available in the one after that.
comment:11 by , 2 years ago
I did not manage to use the HM on Windows even though I disabled Hyper-V, unfortunately.
However, I can confirm that the bug is fixed on hosts running Linux. Thank you very much.
comment:12 by , 20 months ago
Resolution: | → fixed |
---|---|
Status: | reopened → closed |
Hi guys,
New VirtualBox 7.0.8 was just released and available at https://www.virtualbox.org/wiki/Downloads. This issue should be fixed there. Could you please give it a try?
See also #17316