火绒安全软件

安全技术探讨
发新帖
打印 上一主题 下一主题

[安全技术] [翻译]规避技术: CPU

[复制链接]
2463 0
楼主
发表于 2021-5-28 11:39:16 | 显示全部楼层 |只看大图 |倒序浏览 |阅读模式
跳转到指定楼层
备注
原文地址:https://evasions.checkpoint.com/techniques/cpu.html
原文标题:Evasions: CPU
更新日期: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返回处理器识别和特征信息的指令。接收到这些寄存器的信息可以用来识别一个供应商。
代码样本:
  1. __declspec(naked) void get_cpuid_vendor(char *vendor_id) {
  2.   __asm {        
  3.     ; save non-volatile register
  4.     push ebx
  5.    
  6.     ; nullify output registers
  7.     xor ebx, ebx
  8.     xor ecx, ecx
  9.     xor edx, edx
  10.    
  11.     ; call cpuid with argument in EAX
  12.     mov eax, 0x40000000
  13.     cpuid
  14.    
  15.     ; store vendor_id ptr to destination
  16.     mov edi, vendor_id
  17.    
  18.     ; move string parts to destination
  19.     mov eax, ebx  ; part 1 of 3 from EBX
  20.     stosd
  21.     mov eax, ecx  ; part 2 of 3 from ECX
  22.     stosd
  23.     mov eax, edx  ; part 3 of 3 from EDX
  24.     stosd
  25.    
  26.     ; restore saved non-volatile register
  27.     pop ebx
  28.    
  29.     ; return from function
  30.     retn
  31.   }
  32. }
复制代码

检测表:
通过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. __declspec(naked) bool is_run_in_hypervisor() {
  2.   __asm {
  3.     ; nullify output register
  4.     xor ecx, ecx
  5.    
  6.     ; call cpuid with argument in EAX
  7.     mov eax, 1
  8.     cpuid
  9.    
  10.     ; set CF equal to 31st bit in ECX
  11.     bt ecx, 31
  12.    
  13.     ; set AL to the value of CF
  14.     setc al
  15.    
  16.     ; return from function
  17.     retn
  18.   }
  19. }
复制代码

检测表:
检查是否正在虚拟机管理程序中运行(通过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. idt_vm_detect = ((get_idt_base() >> 24) == 0xff);
  2. ldt_vm_detect = (get_ldt_base() == 0xdead0000);
  3. gdt_vm_detect = ((get_gdt_base >> 24) == 0xff);

  4. // sidt instruction stores the contents of the IDT Register
  5. // (the IDTR which points to the IDT) in a processor register.
  6. ULONG get_idt_base() {   
  7.     UCHAR idtr[6];
  8. #if defined (ENV32BIT)
  9.     _asm sidt idtr
  10. #endif
  11.     return *((unsigned long *)&idtr[2]);
  12. }

  13. // sldt instruction stores the contents of the LDT Register
  14. // (the LDTR which points to the LDT) in a processor register.
  15. ULONG get_ldt_base() {
  16.     UCHAR ldtr[5] = "\xef\xbe\xad\xde";
  17. #if defined (ENV32BIT)
  18.     _asm sldt ldtr
  19. #endif
  20.     return *((unsigned long *)&ldtr[0]);
  21. }

  22. // sgdt instruction stores the contents of the GDT Register
  23. // (the GDTR which points to the GDT) in a processor register.
  24. ULONG get_gdt_base() {
  25.     UCHAR gdtr[6];
  26. #if defined (ENV32BIT)
  27.     _asm sgdt gdtr
  28. #endif
  29.     return gdt = *((unsigned long *)&gdtr[2]);
  30. }
复制代码

该代码样本的作者:al-khaser项目
4.使用异域指令愚弄虚拟模拟器
这个链接对这一技术进行了描述(slide #37)。
MMX指令可能被恶意软件用作随机指令。有时虚拟机不支持CPU指令的这种指令子集,因此会引起异常,而不是进行分析。
例子:

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

关于CPU异常的信息由这个链接提供。
代码样本(variant 1, generating #ud exception):
  1. push ebx
  2. xor ebx, ebx
  3. mov eax, 1
  4. ; the following 4 bytes below generate #ud exception
  5. db 0x0F
  6. db 0x3F
  7. db 0x0D
  8. db 0x00
  9. test ebx, ebx
  10. setz al
  11. pop ebx
复制代码

应该强调的是,有超过1,000种的组合
  1. 0x0F
  2. 0x3F
  3. 0xXX
  4. 0xYY
复制代码

恶意软件可能使用的字节,以检测VirtualPC环境。
代码样本: (variant 2, executing illegal STI instruction)
  1. // Taken here: https://pastebin.com/Nsv5B1yk
  2. // http://waleedassar.blogspot.com
  3. // http://www.twitter.com/waleedassar
  4. // Use this code to detect if Windows XP is running inside Virtual PC 2007
  5. #include "stdafx.h"
  6. #include "windows.h"
  7. #include "stdio.h"

  8. #define CONTEXT_ALL 0x1003F

  9. int dummy(int);
  10. unsigned long gf=0;

  11. int __cdecl Handler(EXCEPTION_RECORD* pRec,void* est,unsigned char* pContext,void* disp)
  12. {
  13.     if(pRec->ExceptionCode==0xC0000096)  //Privileged instruction
  14.     {
  15.         //---------------------Installing the trick--------------------------------------
  16.         *(unsigned long*)(pContext)=CONTEXT_ALL;/*CONTEXT_DEBUG_REGISTERS|CONTEXT_FULL*/
  17.         *(unsigned long*)(pContext+0x4)=(unsigned long)(&dummy);
  18.         *(unsigned long*)(pContext+0x8)=(unsigned long)(&dummy);
  19.         *(unsigned long*)(pContext+0xC)=(unsigned long)(&dummy);
  20.         *(unsigned long*)(pContext+0x10)=(unsigned long)(&dummy);
  21.         *(unsigned long*)(pContext+0x14)=0;
  22.         *(unsigned long*)(pContext+0x18)=0x155; //Enable the four DRx On-Execute
  23.         //---------------------------------------------------------------------------------
  24.         (*(unsigned long*)(pContext+0xB8))++;
  25.         return ExceptionContinueExecution;
  26.     }
  27.     else if(pRec->ExceptionCode==EXCEPTION_SINGLE_STEP)
  28.     {
  29.         if(gf==1)
  30.         {
  31.             MessageBox(0,"Expected behavior (XP)","waliedassar",0);
  32.             ExitProcess(0);
  33.         }
  34.         gf++;
  35.         (*(unsigned long*)(pContext+0xC0))|=0x00010000; //Set the RF (Resume Flag)
  36.         return ExceptionContinueExecution;
  37.     }
  38.     return ExceptionContinueSearch;
  39. }

  40. int dummy(int x)
  41. {
  42.     x+=0x100;
  43.     return x;
  44. }

  45. int main(int shitArg)
  46. {
  47.     unsigned long ver_=GetVersion();
  48.     unsigned long major=ver_&0xFF;
  49.     unsigned long minor=(ver_>>0x8)&0xFF;
  50.     if(major==0x05 & minor==0x01) //Windows XP
  51.     {
  52.         unsigned long x=0;
  53.         __asm
  54.         {
  55.             push offset Handler
  56.             push dword ptr fs:[0x0]
  57.             mov dword ptr fs:[0x0],esp
  58.             STI; Triggers an exception(privileged instruction)
  59.         }
  60.         dummy(0xFF);
  61.         __asm
  62.         {
  63.             pop dword ptr fs:[0x0]
  64.             pop ebx
  65.         }
  66.         MessageBox(0,"Virtual PC 2007 detected (XP)","waliedassar",0);
  67.     }
  68.     return 0;
  69. }
复制代码

代码样本(variant 3, resetting VirtualPC):
  1. // Taken here: https://pastebin.com/exAK5XQx
  2. // http://waleedassar.blogspot.com (@waleedassar)
  3. // Executing "\x0F\xC7\xC8\x05\x00" in VirtualPC 2007 triggers a reset error.
  4. #include "stdafx.h"
  5. #include "windows.h"
  6. #include "stdio.h"

  7. bool flag=false;

  8. int __cdecl Handler(EXCEPTION_RECORD* pRec,void* est,unsigned char* pContext,void* disp)
  9. {
  10.     if(pRec->ExceptionCode==0xC000001D  || pRec->ExceptionCode==0xC000001E || pRec->ExceptionCode==0xC0000005)
  11.     {
  12.         flag=true;
  13.         (*(unsigned long*)(pContext+0xB8))+=5;
  14.         return ExceptionContinueExecution;
  15.     }
  16.     return ExceptionContinueSearch;
  17. }

  18. int main(int argc, char* argv[])
  19. {
  20.     __asm
  21.     {
  22.         push offset Handler
  23.         push dword ptr fs:[0x0]
  24.         mov dword ptr fs:[0x0],esp
  25.     }
  26.     flag=false;
  27.     __asm
  28.     {
  29.         __emit 0x0F
  30.         __emit 0xC7
  31.         __emit 0xC8
  32.         __emit 0x05
  33.         __emit 0x00
  34.     }
  35.     if(flag==false)
  36.     {
  37.         MessageBox(0,"VirtualPC detected","waliedassar",0);
  38.     }
  39.     __asm
  40.     {
  41.         pop dword ptr fs:[0x0]
  42.         pop eax
  43.     }
  44.     return 0;
  45. }
复制代码

6.通过指令内-后门端口(仅适用于VMware)检测环境
这篇文章首先解释了为什么在VMware中使用后门端口通信。
代码样本(variant 1):
  1. bool VMWare::CheckHypervisorPort() const {
  2.     bool is_vm = false;
  3.     __try {
  4.         __asm {
  5.             push edx
  6.             push ecx
  7.             push ebx
  8.             mov eax, 'VMXh'
  9.             mov ebx, 0
  10.             mov ecx, 10
  11.             mov edx, 'VX'
  12.             in eax, dx      // <- key point is here
  13.             cmp ebx, 'VMXh'
  14.             setz[is_vm]
  15.             pop ebx
  16.             pop ecx
  17.             pop edx
  18.         }
  19.     }
  20.     __except (EXCEPTION_EXECUTE_HANDLER) {
  21.         is_vm = false;
  22.     }
  23.     return is_vm;
  24. }
复制代码

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

识别标志
对于这个规避技术,没有提供识别标志,因为很难跟踪正在执行的代码。
反制措施
修补虚拟机监控程序(hypervisor)。如果证明不可能--由于许可证问题或其他原因--修补虚拟机配置。通常没有记录的选项会有帮助。
  • vs CPUID指令:请参考这篇文章,了解这种补丁的例子。
  • vs IN指令(VMware后门):看看这些配置变化

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

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

使用道具 举报

您需要登录后才可以回帖 登录 | [立即注册]

本版积分规则

快速回复 返回顶部 返回列表