VirtualBox

Ticket #17961 (reopened defect)

Opened 3 years ago

Last modified 10 months ago

VMM missing rflags.TF handling when advance rip that abused by virtual machine detection

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 10 months ago.
demo sample source code
vmdetect_32 Download (7.4 KB) - added by georgschoelly_joe 10 months ago.
demo sample 32 bit
vmdetect_64 Download (8.5 KB) - added by georgschoelly_joe 10 months ago.
demo sample 64 bit (ELF, Linux)

Change History

comment:1 Changed 3 years ago by janitor

See also #17316

comment:2 Changed 3 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 21 months 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 18 months 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 10 months ago by georgschoelly_joe

demo sample source code

Changed 10 months ago by georgschoelly_joe

demo sample 32 bit

Changed 10 months ago by georgschoelly_joe

demo sample 64 bit (ELF, Linux)

comment:5 Changed 10 months 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 10 months ago by georgschoelly_joe (previous) (diff)
Note: See TracTickets for help on using tickets.

www.oracle.com
ContactPrivacy policyTerms of Use