-
使用的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(naked) void 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(naked) bool 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指令的这种指令子集,因此会引起异常,而不是进行分析。
例子:
5.通过执行非法指令检测环境(仅VirtualPC)
恶意软件执行非法指令,这些指令在真实的CPU上应该产生异常,但在虚拟环境中却正常执行–或以某种不同的方式执行。
代码样本(variant 1, generating #ud exception):
|
1
2
3
4
5
6
7
8
9
10
11
|
push ebxxor ebx, ebxmov eax, 1; the following 4 bytes below generate #ud exceptiondb 0x0Fdb 0x3Fdb 0x0Ddb 0x00test ebx, ebxsetz alpop ebx |
应该强调的是,有超过1,000种的组合
|
1
2
3
4
|
0x0F0x3F0xXX0xYY |
恶意软件可能使用的字节,以检测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)。如果证明不可能–由于许可证问题或其他原因–修补虚拟机配置。通常没有记录的选项会有帮助。














暂无评论内容