大神论坛

找回密码
快速注册
查看: 851 | 回复: 0

[分享] 翻译国外技术大牛文章 规避技术: CPU检测方法与规避

主题

帖子

15

积分

初入江湖

UID
18
积分
15
精华
威望
30 点
违规
大神币
68 枚
注册时间
2021-03-07 22:41
发表于 2021-05-28 21:42
本帖最后由 sliceso 于 2021-05-28 21:42 编辑

更新日期:2021年5月28日
此文后期:根据自身所学进行内容扩充
因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。

目录

  • 使用的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返回处理器识别和特征信息的指令。接收到这些寄存器的信息可以用来识别一个供应商。
代码样本:

__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
}
}


检测表:

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

__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
}
}

检测表:

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

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]);
}

该代码样本的作者:al-khaser项目
4.使用异域指令愚弄虚拟模拟器

这个链接对这一技术进行了描述(slide #37)。
MMX指令可能被恶意软件用作随机指令。有时虚拟机不支持CPU指令的这种指令子集,因此会引起异常,而不是进行分析。
例子:

5.通过执行非法指令检测环境(仅VirtualPC)
恶意软件执行非法指令,这些指令在真实的CPU上应该产生异常,但在虚拟环境中却正常执行--或以某种不同的方式执行。

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

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种的组合

0x0F
0x3F
0xXX
0xYY

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

// Taken here: [url]https://pastebin.com/Nsv5B1yk[/url]
// [url]http://waleedassar.blogspot.com[/url]
// [url]http://www.twitter.com/waleedassar[/url]
// 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):

// Taken here: [url]https://pastebin.com/exAK5XQx[/url]
// [url]http://waleedassar.blogspot.com[/url] (@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)检测环境
这篇文章首先解释了为什么在VMware中使用后门端口通信。
代码样本(variant 1):

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):

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)。如果证明不可能--由于许可证问题或其他原因--修补虚拟机配置。通常没有记录的选项会有帮助。

  • vs CPUID指令:请参考这篇文章,了解这种补丁的例子。
  • vs IN指令(VMware后门):看看这些配置变化


归功于
归功于开源项目,代码样本来自该项目,并归功于分享其发现的独立研究人员。:

尽管Check Point工具InviZzzible已经实现了所有这些功能,但由于代码的模块化结构,需要更多的空间来展示这个工具的代码样本,以达到同样的目的。这就是为什么我们决定在整个百科全书中使用其他伟大的开源项目作为例子。


版权声明:本文由 梦幻的彼岸 原创,欢迎分享本文,转载请保留出处

返回顶部