深入理解Pwn_Qemu逃逸基础篇

韩乔落

QEMU 架构

QEMU-架构图 deepwiki

image-20260403011232817

QEMU 与 KVM 的完整架构整体上分为三大部分:

  • VMX root 模式的用户空间应用层(QEMU 进程)
  • VMX root 模式的内核空间(Linux KVM 驱动模块)
  • VMX non-root 模式的虚拟机运行环境(Guest 虚拟机)

其中,VMX root 和 VMX non-root 是 CPU 支持硬件虚拟化指令集(Intel 的 VT-x 技术)之后引入的两个模式:

  • VMX root 模式用于宿主机系统(即运行虚拟化软件的 Host),在该模式下可执行特权虚拟化指令,完整控制 CPU 虚拟化行为。
  • VMX non-root 模式用于运行客户机(即 Guest OS),Guest 在 non-root 模式下正常运行,绝大部分指令可直接由物理 CPU 执行。特殊敏感操作会导致 CPU 从 non-root 模式退出到 root 模式(VM-Exit),交由 KVM/QEMU 处理。

无论 VMX root 还是 VMX non-root 模式,都包含 ring 0 到 ring 3 共 4 个特权级别。

QEMU 进程

在 QEMU 与 KVM 虚拟化架构中,QEMU 进程位于 VMX root 模式的用户空间,承担如下任务:

  • 初始化虚拟机硬件环境

    • 创建虚拟芯片组(如 PCI 主桥、内存控制器)
    • 根据用户启动参数 (-device 等) 创建并初始化各类虚拟设备(如磁盘、网卡、显卡、输入设备)
    • 分配并管理来宾(Guest)物理内存空间,QEMU 将 Guest 的物理内存映射到宿主机进程虚拟地址空间中(使用 mmap 等系统调用)。
  • 设备模拟与 IO 请求处理
    在虚拟机运行期间,QEMU 主线程会使用事件循环机制(main loop)监听并处理多种事件:

    • 设备 IO 请求事件:当虚拟机对虚拟设备发起 IO 请求(PIO/MMIO)并触发 VM-Exit 后,KVM 会通过 ioctl 接口通知 QEMU 处理这些事件。
    • 管理命令事件:如用户通过 QEMU 的管理界面或 QMP(QEMU Machine Protocol)发送的命令。
    • 宿主机设备事件:如网络数据接收或宿主设备状态变化(例如 tap 网络设备的数据包到达),QEMU 会做出响应并模拟设备行为。

    对于虚拟机设备 IO 访问事件,QEMU 用户空间通过预先注册的 MemoryRegionOps 等设备模型回调函数完成 IO 请求处理,模拟真实硬件的行为(例如返回设备寄存器值、进行 DMA 操作、发起中断请求)。

  • CPU 线程管理
    QEMU 为每个虚拟 CPU (vCPU) 创建单独的宿主机线程,用于代表并调度虚拟机 CPU 的执行流。QEMU 借助 KVM 驱动控制 CPU 的虚拟化行为,使 vCPU 线程能够在宿主机的 CPU 上直接执行 Guest 代码。

虚拟机 (Guest) 环境

Guest OS 在 VMX non-root 模式下运行,有自己的应用层和内核层:

  • 对 Guest OS 而言,QEMU 和 KVM 完全透明,不需要对 Guest OS 做任何修改,就可以在虚拟机中正常运行。
  • Guest 虚拟机的每个 vCPU 对应宿主机中 QEMU 进程的一个线程。通过 KVM 和宿主 OS 调度,这些线程能直接在物理 CPU 上执行 Guest 代码。
  • Guest 虚拟机内存通过两层映射实现地址转换:
    • GVA→GPA(Guest 虚拟地址 → Guest 物理地址):由虚拟机自身 OS 页表管理。
    • GPA→HPA(Guest 物理地址 → Host 物理地址):由 KVM 驱动维护的 Extended Page Tables (EPT) 或 Shadow 页表完成。
  • Guest OS 中的设备通过 QEMU 呈现,Guest OS 在启动时进行设备枚举并加载相应的设备驱动程序。
  • Guest OS 运行中,通过 IO 端口 (PIO) 或内存映射 IO (MMIO) 与设备进行交互时,KVM 会截获这些敏感操作(VM-Exit)并将请求分发至 QEMU 用户空间,由 QEMU 负责处理这些设备请求。

KVM 内核驱动

KVM 驱动位于 VMX root 模式的 Linux 内核空间,以 misc 设备驱动形式 (/dev/kvm) 存在,提供如下功能:

  • 为用户空间提供虚拟化控制接口(通过 ioctl 接口):
    • QEMU 等用户程序通过 /dev/kvm 接口创建并控制虚拟机实例,包括:
      • 创建虚拟机 (KVM_CREATE_VM)
      • 创建 vCPU (KVM_CREATE_VCPU)
      • 设置 Guest 内存布局 (KVM_SET_USER_MEMORY_REGION)
      • 启动和调度 vCPU 执行 (KVM_RUN ioctl)
  • 处理虚拟机 VM-Exit 事件
    当 Guest 在 VMX non-root 模式下执行某些特殊指令或敏感操作时,会触发 VM-Exit,CPU 从 VMX non-root 模式退出到 VMX root 模式,KVM 接管控制权。KVM 在内核态解析退出原因 (vmexit_reason):
    • 对于常见的 MMIO/PIO 操作,KVM 通过 KVM_EXIT_MMIOKVM_EXIT_IO 等事件类型将请求发送到用户空间 QEMU 进程。
    • 对于部分性能敏感事件(如某些定时器、中断控制器或 virtio IO 请求),KVM 通过 ioeventfd 或 irqfd 机制高效通知用户空间或直接内核态处理,以减少 VM-Exit 次数。

QEMU 虚拟化

CPU 虚拟化

vCPU 创建与初始化

QEMU 为每个 vCPU 启动一个线程,使用 /dev/kvm 的 ioctl 建立虚拟机/虚拟 CPU:
KVM_CREATE_VM → KVM_CREATE_VCPU → mmap(KVM_RUN) → KVM_SET_REGS/SET_SREGS/SET_MSRS …
初始化完寄存器/CPUID/特性后进入主循环。

执行循环与 VM-Exit/Entry

vCPU 线程反复 ioctl(KVM_RUN) 进入来宾(VM-Entry)。当发生敏感事件/条件时硬件触发 VM-Exit 返回宿主:
典型原因:PIO、MMIO、CPUID/MSR 访问、HLT、外部中断窗口、EPT 缺页/权限、I/O 指令等。
KVM 将退出原因写入 struct kvm_run,QEMU读出后分发处理(设备回调、注入中断、继续运行等),随后再次 KVM_RUN(VM-Entry)

简化伪码:

1
2
3
4
5
6
7
8
for (;;) {
ioctl(vcpu_fd, KVM_RUN);
switch (run->exit_reason) {
case KVM_EXIT_MMIO: qemu_mmio_dispatch(run->mmio); break;
case KVM_EXIT_IO: qemu_pio_dispatch(run->io); break;
case KVM_EXIT_HLT: /* idle / wait */ break;
/* … CPUID/MSR/INT_WINDOW/EPT_VIOLATION 等 … */
}

VMCS/VMCB

Intel VT-x 使用 VMCS 保存每个 vCPU 的来宾/宿主状态(AMD SVM 对应 VMCB)。这不是“像系统调用那样的内核栈切换”,而是硬件虚拟化态切换:VM-Entry/Exit 时由 CPU 在 VMCS/VMCB 与宿主状态之间来回装载。

内存虚拟化

1
2
3
4
5
6
Guest process         Guest kernel                      QEMU (userspace)               Host kernel (KVM)              DRAM
GVA ──►(guest PT)──► GPA ──►(EPT/NPT)──► HPA (HVA 仅用于用户态管理/拷贝)
│ │ ▲
│ └─MMIO(未映射/设备型)───┘ ← VM-Exit → QEMU MemoryRegionOps 回调

└─IOVA(可选, 有vIOMMU时)──►(vIOMMU映射)──►GPA───►(EPT/NPT)──►HPA

内存地址类别

  • GVA(Guest Virtual Address):来宾进程看到的虚拟地址,受来宾内核维护的页表(CR3 指向的页表根)管理。
  • GPA(Guest Physical Address):来宾眼中的“物理地址”,由来宾内核分配/管理,实际上只是一个客户机物理地址空间
  • HVA(Host Virtual Address):宿主机用户态(QEMU 进程)的虚拟地址,QEMU 用 mmap() 得到,用来承载来宾的“物理内存”数据
  • HPA(Host Physical Address):宿主机真实物理地址。

真正跑指令的“硬件级地址翻译”,根本不认识 HVA

CPU 在来宾里做访存时,只走这条链路:

GVA(来宾虚拟) →【来宾页表】→ GPA(来宾物理) →【EPT/NPT(二级页表)】→ HPA(宿主物理)

HVA(Host Virtual Address)只是QEMU 这个用户态进程里的指针,给 QEMU/KVM 在“管理阶段/缺页处理/用户态拷贝”用的,不在硬件翻译链路里

  • Title: 深入理解Pwn_Qemu逃逸基础篇
  • Author: 韩乔落
  • Created at : 2026-04-03 00:28:32
  • Updated at : 2026-04-21 14:31:44
  • Link: https://jelasin.github.io/2026/04/03/深入理解Pwn_Qemu逃逸基础篇/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments