虚拟机反检测技术:CPU

  • 使用的CPU检测方法

  • 1.通过CPUID指令检查供应商ID字符串

  • 2.通过CPUID指令检查是否在Hypervisor中运行

  • 3.检查全局表位置:IDT/GDT/LDT

  • 4.使用异域指令愚弄虚拟模拟器

  • 5.通过执行非法指令检测环境(仅VirtualPC)

  • 6.通过指令内-后门端口(仅适用于VMware)检测环境

  • 识别标志

  • 反制措施

  • 归功于

使用的CPU检测方法
这组技术使用特定的处理器指令来获取CPU的特定信息,或者执行预定义的指令序列,这些指令在常规的主机操作系统和虚拟环境中表现不同。
1.通过CPUID指令检查供应商ID字符串
CPUID指令是一条向EBX、ECX、EDX返回处理器识别和特征信息的指令。接收到这些寄存器的信息可以用来识别一个供应商。
代码样本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
__declspec(nakedvoid get_cpuid_vendor(char *vendor_id) {
  __asm {        
    ; save non-volatile register
    push ebx
     
    ; nullify output registers
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx
     
    ; call cpuid with argument in EAX
    mov eax, 0x40000000
    cpuid
     
    ; store vendor_id ptr to destination
    mov edi, vendor_id
     
    ; move string parts to destination
    mov eax, ebx  ; part 1 of 3 from EBX
    stosd
    mov eax, ecx  ; part 2 of 3 from ECX
    stosd
    mov eax, edx  ; part 3 of 3 from EDX
    stosd
     
    ; restore saved non-volatile register
    pop ebx 
     
    return from function
    retn
  }
}

 

检测表:

通过CPUID指令检查供应商ID字符串-分别以EBX、ECX、EDX的形式返回信息:

检测

EAX作为CPUID的参数

字符串

FreeBSD HV

0x40000000

bhyve bhyve

Hyper-V

0x40000000

Microsoft Hv

KVM

0x40000000

KVMKVMKVM

Parallels

0x40000000

prl hyperv

VirtualBox

0x40000000

VBoxVBoxVBox

VirtualPC

0x40000000

Microsoft Hv

VMware

0x40000000

VMwareVMware

Xen

0x40000000

XenVMMXenVMM

2.通过CPUID指令检查是否在Hypervisor中运行
另一种检测程序是否在管理程序中运行的方法是以其他方式使用CPUID指
不把EAX(CPUID的参数)设置为0x40000000,而是将EAX设置为1。
当EAX被设置为1时,ECX(CPUID的返回值)中的第31位被设置,它表明程序正在Hypervisor中运行。
代码样本 (函数GetAdaptersAddresses):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__declspec(nakedbool is_run_in_hypervisor() {
  __asm {
    ; nullify output register
    xor ecx, ecx
     
    ; call cpuid with argument in EAX
    mov eax, 1
    cpuid
     
    ; set CF equal to 31st bit in ECX
    bt ecx, 31
     
    ; set AL to the value of CF
    setc al
     
    return from function
    retn
  }
}

 

检测表:

检查是否正在虚拟机管理程序中运行(通过CPUID)

检测

EAX作为CPUID的参数

检查返回值

Hypervisor

1

31st bit in ECX – set if run in Hypervisor

3.检查全局表位置:IDT/GDT/LDT
此技术不适用于最新的VMware版本(所有受影响的Windows版本)。然而,为了完整起见,这里对其进行了描述。
这个技巧包括查看指向关键操作系统表的指针,这些表通常在虚拟机上重新定位。这就是所谓的“Red Pill”,由Joanna Rutkowska首次提出
每个CPU有一个本地描述符表寄存器(LDTR)、一个全局描述符表寄存器(GDTR)和一个中断描述符表寄存器(IDTR)。当虚拟机操作系统运行时,必须将它们移动到其他位置,以避免与主机发生冲突。
例如,在实际机器上,IDT位于内存中的位置低于在客户机(即虚拟机)上的位置。
代码样本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
idt_vm_detect = ((get_idt_base() >> 24) == 0xff);
ldt_vm_detect = (get_ldt_base() == 0xdead0000);
gdt_vm_detect = ((get_gdt_base >> 24) == 0xff);
 
// sidt instruction stores the contents of the IDT Register 
// (the IDTR which points to the IDT) in a processor register.
ULONG get_idt_base() {    
    UCHAR idtr[6];
#if defined (ENV32BIT)
    _asm sidt idtr
#endif
    return *((unsigned long *)&idtr[2]);
}
 
// sldt instruction stores the contents of the LDT Register 
// (the LDTR which points to the LDT) in a processor register.
ULONG get_ldt_base() {
    UCHAR ldtr[5] = "\xef\xbe\xad\xde";
#if defined (ENV32BIT)
    _asm sldt ldtr
#endif
    return *((unsigned long *)&ldtr[0]);
}
 
// sgdt instruction stores the contents of the GDT Register 
// (the GDTR which points to the GDT) in a processor register.
ULONG get_gdt_base() {
    UCHAR gdtr[6];
#if defined (ENV32BIT)
    _asm sgdt gdtr
#endif
    return gdt = *((unsigned long *)&gdtr[2]);
}

 

4.使用异域指令愚弄虚拟模拟器
MMX指令可能被恶意软件用作随机指令。有时虚拟机不支持CPU指令的这种指令子集,因此会引起异常,而不是进行分析。
例子:
图片[1]-虚拟机反检测技术:CPU-vm去虚拟化
5.通过执行非法指令检测环境(仅VirtualPC)
恶意软件执行非法指令,这些指令在真实的CPU上应该产生异常,但在虚拟环境中却正常执行–或以某种不同的方式执行。

代码样本(variant 1, generating #ud exception):

1
2
3
4
5
6
7
8
9
10
11
push ebx
xor ebx, ebx
mov eax, 1
; the following 4 bytes below generate #ud exception
db 0x0F
db 0x3F
db 0x0D
db 0x00
test ebx, ebx
setz al
pop ebx

 

应该强调的是,有超过1,000种的组合

1
2
3
4
0x0F
0x3F
0xXX
0xYY

 

恶意软件可能使用的字节,以检测VirtualPC环境。
代码样本: (variant 2, executing illegal STI instruction)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Taken here: https://pastebin.com/Nsv5B1yk
// http://waleedassar.blogspot.com
// http://www.twitter.com/waleedassar
// Use this code to detect if Windows XP is running inside Virtual PC 2007
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
  
#define CONTEXT_ALL 0x1003F
  
int dummy(int);
unsigned long gf=0;
 
int __cdecl Handler(EXCEPTION_RECORD* pRec,void* est,unsigned char* pContext,void* disp)
{
    if(pRec->ExceptionCode==0xC0000096)  //Privileged instruction
    {
        //---------------------Installing the trick--------------------------------------
        *(unsigned long*)(pContext)=CONTEXT_ALL;/*CONTEXT_DEBUG_REGISTERS|CONTEXT_FULL*/
        *(unsigned long*)(pContext+0x4)=(unsigned long)(&dummy);
        *(unsigned long*)(pContext+0x8)=(unsigned long)(&dummy);
        *(unsigned long*)(pContext+0xC)=(unsigned long)(&dummy);
        *(unsigned long*)(pContext+0x10)=(unsigned long)(&dummy);
        *(unsigned long*)(pContext+0x14)=0;
        *(unsigned long*)(pContext+0x18)=0x155; //Enable the four DRx On-Execute
        //---------------------------------------------------------------------------------
        (*(unsigned long*)(pContext+0xB8))++;
        return ExceptionContinueExecution;
    }
    else if(pRec->ExceptionCode==EXCEPTION_SINGLE_STEP)
    {
        if(gf==1)
        {
            MessageBox(0,"Expected behavior (XP)","waliedassar",0);
            ExitProcess(0);
        }
        gf++;
        (*(unsigned long*)(pContext+0xC0))|=0x00010000; //Set the RF (Resume Flag)
        return ExceptionContinueExecution;
    }
    return ExceptionContinueSearch;
}
  
int dummy(int x)
{
    x+=0x100;
    return x;
}
  
int main(int shitArg)
{
    unsigned long ver_=GetVersion();
    unsigned long major=ver_&0xFF;
    unsigned long minor=(ver_>>0x8)&0xFF;
    if(major==0x05 & minor==0x01) //Windows XP
    {
        unsigned long x=0;
        __asm
        {
            push offset Handler
            push dword ptr fs:[0x0]
            mov dword ptr fs:[0x0],esp
            STI; Triggers an exception(privileged instruction)
        }
        dummy(0xFF);
        __asm
        {
            pop dword ptr fs:[0x0]
            pop ebx
        }
        MessageBox(0,"Virtual PC 2007 detected (XP)","waliedassar",0);
    }
    return 0;
}

 

代码样本(variant 3, resetting VirtualPC):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Taken here: https://pastebin.com/exAK5XQx
// http://waleedassar.blogspot.com (@waleedassar)
// Executing "\x0F\xC7\xC8\x05\x00" in VirtualPC 2007 triggers a reset error.
#include "stdafx.h"
#include "windows.h"
#include "stdio.h"
  
bool flag=false;
  
int __cdecl Handler(EXCEPTION_RECORD* pRec,void* est,unsigned char* pContext,void* disp)
{
    if(pRec->ExceptionCode==0xC000001D  || pRec->ExceptionCode==0xC000001E || pRec->ExceptionCode==0xC0000005)
    {
        flag=true;
        (*(unsigned long*)(pContext+0xB8))+=5;
        return ExceptionContinueExecution;
    }
    return ExceptionContinueSearch;
}
  
int main(int argc, char* argv[])
{
    __asm
    {
        push offset Handler
        push dword ptr fs:[0x0]
        mov dword ptr fs:[0x0],esp
    }
    flag=false;
    __asm
    {
        __emit 0x0F
        __emit 0xC7
        __emit 0xC8
        __emit 0x05
        __emit 0x00
    }
    if(flag==false)
    {
        MessageBox(0,"VirtualPC detected","waliedassar",0);
    }
    __asm
    {
        pop dword ptr fs:[0x0]
        pop eax
    }
    return 0;
}

 

6.通过指令内-后门端口(仅适用于VMware)检测环境

代码样本(variant 1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool VMWare::CheckHypervisorPort() const {
    bool is_vm = false;
    __try {
        __asm {
            push edx
            push ecx
            push ebx
            mov eax, 'VMXh'
            mov ebx, 0
            mov ecx, 10
            mov edx, 'VX'
            in eax, dx      // <- key point is here
            cmp ebx, 'VMXh'
            setz[is_vm]
            pop ebx
            pop ecx
            pop edx
        }
    
    __except (EXCEPTION_EXECUTE_HANDLER) {
        is_vm = false;
    }
    return is_vm;
}

 

代码样本 (variant 2):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
bool VMWare::CheckHypervisorPortEnum() const {
    bool is_vm = false;
    short ioports[] = { 'VX' 'VY' };
    short ioport;
    for (short i = 0; i < _countof(ioports); ++i) {
        ioport = ioports[i];
        for (unsigned char cmd = 0; cmd < 0x2c; ++cmd) {
            __try {
                __asm {
                    push eax
                    push ebx
                    push ecx
                    push edx
                    mov eax, 'VMXh'
                    movzx ecx, cmd
                    mov dx, ioport
                    in eax, dx      // <- key point is here
                    pop edx
                    pop ecx
                    pop ebx
                    pop eax
                }
                is_vm = true;
                break;
            }
            __except (EXCEPTION_EXECUTE_HANDLER) {}
        }
        if (is_vm)
            break;
    }
    return is_vm;
}

 

识别标志
对于这个规避技术,没有提供识别标志,因为很难跟踪正在执行的代码。
反制措施
修补虚拟机监控程序(hypervisor)。如果证明不可能–由于许可证问题或其他原因–修补虚拟机配置。通常没有记录的选项会有帮助。

 

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容