VirtualBox

Ticket #17961 (closed defect: fixed)

Opened 5 years ago

Last modified 6 weeks ago

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

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

Change History

comment:1 Changed 5 years ago by janitor

See also #17316

comment:2 Changed 4 years ago by ramshankar

Will provide a test build next week to see if the VMX fix I mentioned in #17316 also fixes this problem. Thanks!

comment:3 Changed 3 years ago by aeichner

  • Status changed from new to closed
  • Resolution set to obsolete

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

comment:4 Changed 3 years ago by dmdev

  • Status changed from closed to reopened
  • Resolution obsolete deleted

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

Changed 2 years ago by georgschoelly_joe

demo sample source code

Changed 2 years ago by georgschoelly_joe

demo sample 32 bit

Changed 2 years ago by georgschoelly_joe

demo sample 64 bit (ELF, Linux)

comment:5 Changed 2 years ago by georgschoelly_joe

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
Last edited 2 years ago by georgschoelly_joe (previous) (diff)

comment:6 Changed 5 months ago by ramshankar

  • Status changed from reopened to closed
  • Resolution set to fixed

This has been fixed in VirtualBox 7.0.4.

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

Last edited 5 months ago by ramshankar (previous) (diff)

comment:7 Changed 5 months ago by georgschoelly_joe

  • Status changed from closed to reopened
  • Resolution fixed deleted

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 Changed 5 months ago by ramshankar

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

Changed 5 months ago by georgschoelly_joe

VBox.log with VirtualBox 7.0.4

comment:9 Changed 5 months ago by ramshankar

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 Changed 5 months ago by aeichner

  • Summary changed from VMM missing rflags.TF handling when advance rip that abused by virtual machine detection to 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 Changed 4 months ago by georgschoelly_joe

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 Changed 6 weeks ago by galitsyn

  • Status changed from reopened to closed
  • Resolution set to fixed

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.

www.oracle.com
ContactPrivacy policyTerms of Use