VirtualBox

Opened 6 years ago

Closed 12 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;
}

https://user-images.githubusercontent.com/12287588/44799149-d4273580-abe5-11e8-8b06-a2f1230360cc.png https://user-images.githubusercontent.com/12287588/44799388-629bb700-abe6-11e8-8667-fff3f2b1cc8b.png

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)

vmdetect.c (1.7 KB ) - added by georgschoelly_joe 3 years ago.
demo sample source code
vmdetect_32 (7.4 KB ) - added by georgschoelly_joe 3 years ago.
demo sample 32 bit
vmdetect_64 (8.5 KB ) - added by georgschoelly_joe 3 years ago.
demo sample 64 bit (ELF, Linux)
VBox.log (72.5 KB ) - added by georgschoelly_joe 16 months ago.
VBox.log with VirtualBox 7.0.4

Download all attachments as: .zip

Change History (16)

comment:1 by janitor, 6 years ago

See also #17316

comment:2 by Ramshankar Venkataraman, 5 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 aeichner, 4 years ago

Resolution: obsolete
Status: newclosed

Please reopen if still an issue with a recent VirtualBox release.

comment:4 by dmdev, 4 years ago

Resolution: obsolete
Status: closedreopened

The issue is relevant for most recent VirtualBox release (6.1.10 r138449)

by georgschoelly_joe, 3 years ago

Attachment: vmdetect.c added

demo sample source code

by georgschoelly_joe, 3 years ago

Attachment: vmdetect_32 added

demo sample 32 bit

by georgschoelly_joe, 3 years ago

Attachment: vmdetect_64 added

demo sample 64 bit (ELF, Linux)

comment:5 by georgschoelly_joe, 3 years ago

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
Version 0, edited 3 years ago by georgschoelly_joe (next)

comment:6 by Ramshankar Venkataraman, 16 months ago

Resolution: fixed
Status: reopenedclosed

This has been fixed in VirtualBox 7.0.4.

Thank you very much for the report and the handy testcase.

Last edited 16 months ago by Ramshankar Venkataraman (previous) (diff)

comment:7 by georgschoelly_joe, 16 months ago

Resolution: fixed
Status: closedreopened

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:8 by Ramshankar Venkataraman, 16 months ago

Could you please upload VBox.log for the VM you tested this in?

by georgschoelly_joe, 16 months ago

Attachment: VBox.log added

VBox.log with VirtualBox 7.0.4

comment:9 by Ramshankar Venkataraman, 16 months 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 aeichner, 16 months ago

Summary: VMM missing rflags.TF handling when advance rip that abused by virtual machine detectionVMM 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 georgschoelly_joe, 15 months 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 galitsyn, 12 months ago

Resolution: fixed
Status: reopenedclosed

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?

Note: See TracTickets for help on using tickets.

© 2023 Oracle
ContactPrivacy policyTerms of Use