飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 1581|回复: 1

[病毒分析] [翻译]规避技术: CPU

  [复制链接]
  • TA的每日心情
    开心
    2019-3-15 11:00
  • 签到天数: 262 天

    [LV.8]以坛为家I

    发表于 2021-5-28 11:43:57 | 显示全部楼层 |阅读模式
    备注
    原文地址: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返回处理器识别和特征信息的指令。接收到这些寄存器的信息可以用来识别一个供应商。
    代码样本:
    [C++] 纯文本查看 复制代码
    __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):
    [C++] 纯文本查看 复制代码
    __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位于内存中的位置低于在客户机(即虚拟机)上的位置。
    代码样本:
    [C++] 纯文本查看 复制代码
    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指令的这种指令子集,因此会引起异常,而不是进行分析。
    例子:
    1.png
    5.通过执行非法指令检测环境(仅VirtualPC)
    恶意软件执行非法指令,这些指令在真实的CPU上应该产生异常,但在虚拟环境中却正常执行--或以某种不同的方式执行。

    关于CPU异常的信息由这个链接提供。
    代码样本(variant 1, generating #ud exception):
    [C++] 纯文本查看 复制代码
    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种的组合
    [C++] 纯文本查看 复制代码
    0x0F
    0x3F
    0xXX
    0xYY

    恶意软件可能使用的字节,以检测VirtualPC环境。
    代码样本: (variant 2, executing illegal STI instruction)
    [C++] 纯文本查看 复制代码
    // 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):
    [C++] 纯文本查看 复制代码
    // 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):
    [C++] 纯文本查看 复制代码
    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):
    [C++] 纯文本查看 复制代码
    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已经实现了所有这些功能,但由于代码的模块化结构,需要更多的空间来展示这个工具的代码样本,以达到同样的目的。这就是为什么我们决定在整个百科全书中使用其他伟大的开源项目作为例子。

    评分

    参与人数 1威望 +1 飘云币 +1 收起 理由
    zhczf + 1 + 1 PYG有你更精彩!

    查看全部评分

    PYG19周年生日快乐!
  • TA的每日心情
    奋斗
    2023-5-13 23:22
  • 签到天数: 853 天

    [LV.10]以坛为家III

    发表于 2021-5-28 23:29:59 | 显示全部楼层
    感谢楼主分享
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

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