虚拟机自省技术相关。

1. 虚拟化技术

虚拟化是云计算的基础。简单的说,虚拟化使得在一台物理的服务器上可以跑多台虚拟机,虚拟机共享物理机的 CPU、内存、IO 硬件资源,但逻辑上虚拟机之间是相互隔离的。虚拟化技术可以扩大硬件的容量,简化软件的重新配置过程。CPU 的虚拟化技术可以单 CPU 模拟多 CPU 并行,允许一个平台同时运行多个操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。物理机我们一般称为宿主机(Host),宿主机上面的虚拟机称为客户机(Guest)。

1.1. 虚拟化技术的分类

  • 模拟(Emulation):QEMU、Bochs 等;
  • 完全虚拟化(Full Virtualization):VMWare Workstation、VirtualBox、KVM 等;
  • 半虚拟化(Para-Virutalization):Xen 等;
  • 容器级虚拟化:libcontainer 等;
  • 库级别虚拟化:Wine 等;
  • 程序级虚拟化:JVM 等。

1.2. CPU 虚拟化

  • 模拟类型(Emulation);
    • 通过软件模拟 Ring0/1/2/3 层,虚拟机的架构与物理平台的架构可以不同,但是性能差。
  • 虚拟类型(Virtualization)。
    • 完全虚拟化(full-virt);
      • 只虚拟出 Ring0 层;
      • BT(Binary Translation):将模拟的 CPU 直接翻译成特权指令,限定虚拟结构平台和底层物理架构必须保持一致;
        • 优点:不用修改 GuestOS 内核可以直接使用,应用广泛;
        • 缺点:在 VMM 捕获特权指令和翻译过程会导致性能的下降。
      • HVM(Hardware Assisted Virtualization):采用 5 个指令环,在 Ring0 的底层加了 Ring-1,将 Ring0 的特权指令给了 Ring-1。
    • 半虚拟化(para-virt)。
      • GuestOS 明确知道自己运行于虚拟技术中,在执行特权指令时直接向 Hypercall 调用。
        • 优点:性能相对来说更高,省去了特权指令的翻译过程;
        • 缺点:需要对 GuestOS 内核的修改,应用有限制。

硬件级物理 CPU 虚拟化技术:

  • 英特尔硬件虚拟化技术:Intel-VT;
  • AMD 硬件虚拟化技术:AMD-V。

1.3. 内存虚拟化

内存虚拟化是虚拟机实现中的重要部分。在虚拟机中,虚拟出来的 Guest OS 和 Host OS 用的是相同的物理内存,却不能让它们相互影响到。如果 OS 在物理机上运行,只要 OS 提供页表,MMU 会在访存时自动做虚拟地址(Virtual Address,VA)到物理地址(Physical Address,PA)的转化。而在虚拟机上运行时,Guest OS 经过地址转化得到的“物理地址”并不是真实物理内存上的地址(GVA->GPA),因此还需要使用软件将其转化为真实物理内存地址(HPA)。也就是说 Guest OS 要访问 VA 需要经过 GVA->GPA->HPA 的转化。

  • MMU Virtualization:MMU 是 Memory Management Unit 的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统;
    • Guest OS 完成 GVA->GPA 第一层转化,硬件同时完成 GPA->HPA 第二层转化。第二层转化对于 Guest OS 来说是透明的。Guest OS 访问内存时和在物理机运行时是相同的,所以可以实现全虚拟化。这种特性 Intel 和 AMD 都有支持。Intel 称之为 Extended Page Tables(EPT),AMD 称之为 Nested Page Tables(NPT)。其优点是 Hypervisor 节省了工作,缺点是需要硬件支持。
  • TLB Virtualization:TLB 是 Translation Lookaside Buffer 的缩写,中文名是转换后缓存存储器。
    • 原生只存储 VA->PA 的对应关系。所以在虚拟内存中的两次转换会导致 TLB 的命中率失效。致使性能降低。所以在硬件内存中添加标识虚拟机标签机制(tagged TLB),它缓存了 Guest 对象和 GVA->HPA 的对应关系,需要 CPU 的支持。

硬件级内存虚拟化(硬件级物理内存映射到虚拟机):

  • 英特尔硬件虚拟化技术 EPT,Extended Page Table;
  • AMD 硬件虚拟化技术 NTP,Nested Page Table。

1.4. I/O 虚拟化

  • I/O 全虚拟化技术
    • 完全使用软件通过 VMM 模拟 I/O 设备(磁盘和网卡等)实现虚拟化,性能很差;
    • Guest OS 所能看到就是一组统一的 I/O 设备。VMM 截获 Guest OS 对 I/O 设备的访问请求,然后通过软件模拟真实的硬件。这种方式对 Guest OS 而言非常透明,无需考虑底层硬件的情况。
  • I/O 半虚拟化技术(virtio)
    • 通过前端(I/O Frontend)和后端(I/O Backend)的模拟实现虚拟化,通常仅适用于硬盘和网卡;
    • Guest OS 中的驱动程序为前端,VMM 提供的与 Guest OS 通信的驱动程序为后端。前端驱动将 Guest OS 的请求通过与 VMM 间的特殊通信机制发送给 VMM 的后端驱动,后端驱动在处理完请求后再发送给物理驱动。
  • I/O 透传技术(I/O Through)
    • 设备透传就是向一个特定客户操作系统提供一种设备隔离,直接给虚拟机分配物理设备,使用设备透传可以获得近乎本机的性能。

VMM 对 I/O 的驱动有三种模式:

  • 自主 VMM:由 VMM 自行提供驱动和控制台;
  • 混合 VMM:借助于 OS 提供驱动;
    • 依赖于外部 OS 实现特权域;
    • 自我提供特权域。
  • 寄宿式 VMM。

1.5. 网络虚拟化

用软件的方式给每个虚拟机虚拟一块网卡和 MAC 地址,当通信时使用同一块物理网卡,网卡通讯有排队方式,在同一台物理网卡上,排队执行任务。(将物理网卡设为混杂模式;无论是不是发向本机物理网卡的内容都给与接收);此时将物理网卡也虚拟化一个 MAC 地址,把物理网卡当作交换机来使用。

  • 桥接(Bridge):把原宿主机上的网卡当交换机;然后虚拟出一个桥来接收发往宿主机的数据包;
  • (Isolation):仅 Guest 之间通信;不与外部网络和宿主机通信;
  • (Routed):与外部主机通信;依赖于静态路由指定到各 Guest 需经过 pnet0。
  • (Host-only):不与外部主机通信。
  • NAT:地址转换;在虚拟网卡和物理网卡之间建立一个 NAT 转发服务器;对数据包进行源地址转换。

1.6. 虚拟化实现的方式(Hypervisor 的类型)

  • Type-I:Hypervisor 直接运行于硬件(如 Xen 等);
  • Type-II:Hypervisor 运行 Host OS 之上(如 VMware Workstation 等)。

1.7. 容器级级虚拟化

  • 缺点:相较于主机级虚拟化隔离的不彻底;
  • 方案:LXC、libcontainer、runC、OpenVZ;
  • Linux 内核运行在物理设备上,在内核上运行多个操作系统(如:CentOS、Ubuntu 等)。因为他们都是基于 Linux 内核来开发的不同操作系统,底层运行的内核是相同的。在每个操作系统上在运行每个应用。在内核上运行一个软件来创建和管理容器,是一个很小的软件,基本不消耗性能;
  • 共享内核:由于多个操作系统使用的是同一个内核,当在操作系统上执行关机命令,就会将内核关闭,所以需要将每个操作系统隔离开来,关掉的只是自身容器本身而已。各个操作系统之间的操作互不干扰。

2. Papers

主要阅读了基于虚拟机自省技术的系统调用捕获相关的论文,也包括对恶意软件和 rootkit 的分析。

2.1. 系统调用捕获

2.1.1. 系统虚拟化环境下客户机系统调用信息捕获与分析(计算机系统应用, 2019)

基于 KVM

  • 捕获系统调用
    • 基于 SYSCALL 的系统调用
      • 将 EFER 寄存器的 SCE 标志置 0 时, 执行 SYSCALL 会发生 UD 未定义的无效操作码异常陷入
    • 基于 SYSENTER 的系统调用
      • 保存 MSR 寄存器原始值,并设置 MSR 寄存器为 NULL,执行 SYSENTER 由于找不到系统调用的入口会产生 GP 通用保护错异常陷入
  • 模拟指令重写
    • 修改内核处理异常代码,判断发生异常时 VCPU 的状态
    • 模拟指令是处理异常中的关键环节, 对该指令的重写是获取系统调用信息的关键因素
    • 在 KVM 模拟指令的代码 em_sysenter()em_sysexit() 中加入 kvm_arch_vcpu_ioctl_get_regs()kvm_arch_vcpu_ioctl_get_sregs() 来获取通用寄存器和特殊寄存器的信息

判断发生异常时 VCPU 的状态代码:

if (is_invalid_opcode(intr_info)) { // 识别无效操作码异常
    if (is_guest_mode(vcpu)) { // 判断异常是否在客户模式下产生
        kvm_queue_exception(vcpu); // 将异常注入Guest OS进行处理
        return 1;
    } else {
        emulate_instruction(vcpu); // 对引起异常的系统调用指令进行模拟
        return 1;
    }
} else if(is_general_protection(intr_info)) { // 识别通用保护异常
    if(is_sysenter_sysexit(vcpu)) { // 若异常发生时VCPU处于sysenter_sysexit状态
        emulate_instruction(vcpu); // 对引起异常的系统调用指令进行模拟
        return 1;
    } else {
        kvm_queue_exception(vcpu); // 将异常注入Guest OS进行处理
        return 1;
    }
}
  • 语义转换
    • 结合线程池技术不断获取 VCPU 事件信息的请求
    • 获取系统调用名
      • 在内核符号表 System.map 中获取 sys_call_table 的地址, 通过计算和 libvmi 的解析得到系统调用名
    • 系统调用参数分析
      • 根据不同的系统调用设计相应的处理规则
      • SYSCALL: rdi, rsi, rdx, r10, r8, r9
      • SYSENTER: rbx, rcx, rdx, rsi, rdi, rbp

获取系统调用名代码:

#define VOID_P_SIEZ 8 // 定义64位下调用表项的大小
p_addr = sys_call_table+rax*VOID_P_SIZE; // 计算系统调用表项的地址
addr = libvmi.read_addr_va(paddr); // 解析地址获取系统调用地址
sys_call_name = libvmi.translate_v2ksym(addr); // 解析系统调用地址获取系统调用名
return sys_call_name; // 返回系统调用名
  • 进程区分
    • 存在与已退出旧进程同名同号的新进程, 由于监控程序无法判断旧进程的退出, 故会重新扫描旧进程的系统调用序列并加入新进程的系统调用序列, 造成新进程系统调用序列不再准确
    • 对于监控程序捕获到的多个进程的系统调用序列, 每个进程的系统调用序列都不再准确, 监控程序所捕获到的以一个进程系统调用序列为单位的总系统调用序列相应地也不再准确
    • 捕获系统调用时及时判断进程是否退出
      • 识别进程是否调用 exit()exit_group()

2.1.2. Ether: Malware Analysis via Hardware Virtualization Extensions(ACM Conference on Computer and Communications Security, 2008)

基于 Xen

系统结构:

  • 虚拟机管理器模块
    • 监听事件: 系统调用, 指令执行, 内存写操作以及上下文切换
  • Dom0 用户控制模块
    • 进行语义分析: 获取系统调用名以及解析系统调用参数

虚拟机相关行为的监控:

  • 指令执行
    • 每次执行指令前设置一个陷阱标识(trap flag), 执行完后会触发调试异常
  • 内存写操作
    • Xen 使用影子页表机制, 可借此构造产生内存页错误
      • 移除影子页表入口地址的写权限, 产生页错误
      • 监听到页错误后, 设置内存页的写权限, 并重新执行相应指令
  • 系统调用
    • 利用 x86 快速系统调用机制的特性
      • 存储原始的 MSR 值, 设置一个值产生页错误
      • 监听到页错误后, 重新将 MSR 设置为原始值
    • 使用软件中断 0x2E
      • 修改中断 0x2E 的 IDT 入口地址为一个不存在的地址, 产生一个页错误触发 VMExit
  • 上下文切换
    • 监听到上下文切换的操作后, 使用虚拟机自省技术来获取进程名并区分不同的进程

2.1.3. Nitro: Hardware-based System Call Tracing for Virtual Machines(International Conference on Advances in Information and Computer Security, 2011)

基于 KVM

  • 系统调用追踪可通过机器学习的方法对进程行为进行分类
  • 利用虚拟机自省捕获系统调用
    • 基于用户中断的系统调用
      • 将 IDTR 中 IDT 长度的值设置为 32, 使得所有用户中断(中断号大于 31 的中断)都会触发一般保护错误
      • 将所有一般保护错误捕获到虚拟机管理程序中, 通过判断是否是 INT 指令以及中断号是否大于 31 来判断是不是构造生成的一般保护错误
    • 基于 SYSCALL 的系统调用
    • 基于 SYSENTER 的系统调用

  • 进程的区分
    • 每个进程在 CR3 寄存器中最高级页目录地址的值不同
  • 获取系统调用信息
    • 由于不同操作系统的传参方式不同, 设计基于规则的请求信息方法
    • add_scmon_rule CONDITION_REG CONDITION_VAL ACTION_REG OFFSET ACTION
    • add_scmon_rule rax 4 rcx 0 derefstr

2.2. 恶意软件分析

2.2.1. 基于虚拟机自省的恶意软件分析技术研究(国防科技大学研究生院硕士学位论文, 2015)

基于 Xen

  • 静态分析法
    • 存在模糊化, 加壳和加密技术, 反调试等方法使得代码难以分析
  • 动态分析法
    • 沙箱, 进程监控器, 进程浏览器
  • 基于 API 的动态分析法
    • 利用 API 和机器学习算法来实现恶意软件的分析

2.2.2. 基于虚拟机自省的病毒行为分析与检测技术(国防科技大学研究生院硕士学位论文, 2016)

基于 Xen

  • 病毒检测
    • 特征码技术
      • 对现有病毒样本进行分析与特征码提取, 往往滞后于病毒的出现
    • 病毒行为分析
      • 静态分析, 动态分析
      • 比较病毒和正常程序的行为区别, 与病毒行为特征库进行比对

2.3. Rootkit 分析

2.3.1. VMM-based Hidden Process Detection and Identification using Lycosid(International Conference on Virtual Execution Environments, 2008)

基于 Xen

  • 常见 rootkit 行为
    • 修改二进制程序(ps, netstat, ls 等)
    • 修改库文件, 动态链接器, 系统调用表或一些和操作系统相关的库函数等操作来 Hook 用户应用和内核间调用链
    • 使用 DKOM(Direct Kernel Object Manipulate)修改内核数据结构

检测 rootkit 的方法:

  • 检测是否存在隐藏进程
    • 交叉验证
      • 可信(宿主机外部获取)
        • 使用 VMI 技术获取进程列表
      • 不可信(宿主机内部获取)
        • unix: ps / windows: pslist.exe, tasklist.exe
    • 如果可信列表的长度大于不可信列表, 则可以判断至少有一个进程被隐藏

  • 判断具体的隐藏进程
    • 计算隐藏进程消耗的 CPU 时间, 化为一个多元线性回归问题

2.3.2. KVM 环境下内核级 Rootkit 检测及防护技术研究(信息安全研究, 2019)

基于 KVM

  • Rootkit 检测
    • 检查系统调用表中某项地址是否为非内核代码段
    • 根据 module 数据结构的特征, 遍历内存得到 module 的地址
  • Rootkit 清除
    • 修复系统调用表或内核关键函数, 卸载模块
  • 内核加固
    • 通过 hook_sys_init_module 系统调用对 rootkit 文件进行禁用

3. References

KVM 虚拟化技术实现原理
Virtualization-CPU/Memory/IO 虚拟化详解
CPU 的三种虚拟化机制
QEMU KVM 学习笔记