从Qemu到qemu-sptm:darwin-vm自定义darwin机器的SPTM启动与xnuboot原理

📅 发布时间:2026/10/14 15:47:06
从Qemu到qemu-sptm:darwin-vm自定义darwin机器的SPTM启动与xnuboot原理
【免费下载链接】darwin-vmRun iOS/ macOS in Qemu. Virtual iPhone 17, 16, 15, 14, 13, 12 and M5-M1 Apple Si Macs supported.项目地址https://gitcode.com/gh_mirrors/da/darwin-vm点击查看免费下载darwin-vm是一个让 iOS / macOS 跑在 Qemu 上的开源虚拟机项目它通过 Qemu 的 SPTM 支持分支qemu-sptm和名为darwin的自定义机器类型把虚拟 iPhone12–17或 Apple Silicon MacM1–M5直接启动到 root shell。本文将拆解它背后的 SPTM 安全启动链条与xnuboot引导原理帮你真正看懂苹果系统上 Qemu是怎么实现的。先认识 darwin-vm一个能进 root shell的苹果虚拟机在讲原理之前先明确darwin-vm的定位见 README.md✅ 运行可调试的轻量 iOS / macOSDarwin系统支持 A14–A19iPhone 12–17与 M1–M5 芯片✅ 几秒内直接启动到 root shell可以编译、运行自己的程序无需越狱或内核打补丁✅ 支持基于 SPTM 的内核和带 MIE 特性的 CPU✅ 可调试 / 打补丁内核、SPTM、TXM、dyld、launchd 与用户态程序❌ 它不是完整模拟器没有屏幕、Wi-Fi、蓝牙、图形界面只有串口控制台。作者用一句话概括如果你在 Qemu 里用-kernel-initrd编译并启动过 Linux busybox那这就是 Darwin 版本的同类玩法。启动成功后终端里会直接出现内核日志和 bash 提示符Darwin Kernel Version 27.0.0: ... root:xnu-13432.1.9~3/RELEASE_ARM64_T8142 pmap_startup() init/release time: 893478 microsec ... bash-3.2# whoami root经过官方测试的配置如下摘自 README.md设备devnameCPU芯片代号iPhone 17 (A19)iPhone18,3t8150✅iPhone 16 (A18)iPhone17,3t8140✅iPhone 12 (A14)iPhone13,2t8101✅M4 Mac MiniMac16,10t8132✅M1 Mac MiniMacmini9,1t8103✅为什么标准 Qemu 跑不动SPTM 与 TXM从 A14t8101/ M1t8103开始Apple Silicon 的启动链中多出了两个安全组件SPTMSecure Page Table Manager安全页表管理器运行在 PPL 特权级的微内核负责管理内核页表、中断路由与代码签名相关的内存保护。任何页表修改都必须经过 SPTM 的系统调用TXMTrust Execution Module可信执行模块运行在 TEE 中的可信执行环境负责 Image4 校验、代码签名信任链、AMFI 等安全策略。标准 Qemu 的 aarch64 目标没有 PPL/TEE 这套硬件特权模型也没有 Apple 固件因此无法直接加载新版 iOS / macOS 内核。darwin-vm的解法是fork 一个 Qemu 分支qemu-sptm在软件层面模拟出 SPTM / TXM 的启动环境并提供一个全新的darwin机器类型来承载整条启动链。qemu-sptm以 git submodule 形式内置在仓库中见 qemu-sptm/ 与 .gitmodules核心改动位于qemu-sptm/hw/arm/xnuboot_sptm.c。启动链条全景SPTM → TXM → xnuboot → XNUdarwin机器的启动顺序与真机一致只是每一环都由固件文件或 Qemu 内置代码扮演┌──────────┐ ┌───────┐ ┌────────────┐ ┌──────────────┐ ┌──────────┐ │ SPTM │─▶│ TXM │─▶│ xnuboot │─▶│ XNU kernel │─▶│ launchd │ │ (固件 │ │(固件 │ │(qemu-sptm │ │ (bootkc │ │ 拉起 │ │ 文件) │ │ 文件) │ │ 内置 iBoot)│ │ 内核缓存) │ │ /bin/bash│ └──────────┘ └───────┘ └────────────┘ └──────────────┘ └──────────┘SPTM从内存中指定位置开始执行firmware/sptm固件文件SPTM 初始化完成后加载TXMfirmware/txm由其建立 Image4 信任链随后控制权交给xnuboot——qemu-sptm内置的、模拟 Apple iBoot 的引导程序xnuboot 装载bootkckernelcache内核缓存与设备树完成内存布局后跳转进 XNU 内核内核初始化完成后launchd 读取 ramdisk 中被替换过的 com.jprx.bash.plist把/bin/bash的输入输出接到/dev/console——这就是你看到的 root shell。 没有 SPTM 的老芯片或旧系统会跳过前两环run.sh检测到firmware/sptm不存在时就不传-sptm/-txm参数实现一条命令兼容新老机器。自定义 darwin 机器命令行是怎么组装的一切从 run.sh 看起。它的boot_qemu函数run.sh组装了这样一组参数qemu-system-aarch64 \ -M darwin \ -bootkc firmware/bootkc \ -dtree firmware/dtree \ -tc firmware/ramdisk.tc \ -ramdisk firmware/ramdisk.dmg \ -sptm firmware/sptm \ -txm firmware/txm \ -args rdmd0 serial3 -v -noprogress wdt-1 wlan-olyhal-abort \ -nographic -serial mon:stdio -m 8G这些参数在标准 Qemu 里都不存在它们都是qemu-sptm为darwin机器新造的固件通道参数作用-M darwin选用自定义的 darwin 机器Apple Silicon 内存映射 arm-io设备树骨架-bootkc加载 kernelcacheXNU 内核 kext 集合相当于 Linux 的-kernel-dtree加载设备树DeviceTree来自真实 IPSW经脚本改写-tc加载 trustcache代码签名信任缓存决定哪些二进制可执行-ramdisk加载ramdisk.dmg磁盘镜像作为根文件系统rdmd0指它-sptm/-txm加载 SPTM / TXM 固件仅 SPTM 世代芯片需要-args传给内核的 boot-argsserial3让日志走串口-v打开详细日志这里的设计很值得玩味darwin-vm没有发明一套假固件而是从真实 IPSW 镜像里提取真实的sptm、txm、DeviceTree、kernelcache由 get_files.sh 的get_firmware()完成见 get_files.shQemu 只负责模拟硬件环境与引导时序。xnuboot 引导原理详解xnuboot源码位于qemu-sptm/hw/arm/xnuboot_sptm.cREADME 在 Rebasing 一节专门引用了它在 Qemu 中扮演 iBoot 角色它要完成三件关键的事1. 按真机约定摆放固件并填写 memory-mapXNU 内核不自己找文件它读取设备树chosen/memory-map里记录的一堆地址字段来定位各组件。这些字段名可以直接在 dt_fixup.py 的fixup_sptm()中看到SPTM-ro / SPTM-rx / SPTM-rw / SPTM-entry / SPTM-virt TXM-ro / TXM-rx / TXM-bx / TXM-entry / TXM-virt BootKC-rx / BootKC-bx / BootKC-ro / BootKC-rs / BootKC-virt / BootKC-entry TrustCache / DeviceTree / RAMDisk / BootArgs / slidedt_fixup.py会把它们全部初始化为(-1, -1)占位iBoot 会把未初始化区域设为 -1真正填充地址的是 xnuboot它在 Qemu 内存中依次放下 SPTM、TXM、bootkc、trustcache 与 ramdisk再把各段的起止地址写回设备树最后把 CPU 复位向量指向 SPTM 入口。内核启动后读 memory-map 就能找到所有组件——这与真机 iBoot 的行为完全同构。2. 构造 BootArgs 结构体XNU 通过BootArgs结构体接收引导信息内核调试时p/x *(boot_args*)BootArgs可见见 README.md。xnuboot 按 run.sh 的-args字符串填充CommandLinerdmd0 serial3 -v -noprogress wdt-1 ...并设置内存基址、大小、设备树指针等字段供main()解析。3. 处理 bootkc 的 0x20000000 滑动这是整个项目最有意思的细节之一Rebasing the boot KC默认情况下bootkc 会在虚拟内存中前移0x20000000字节。SPTM 让把 bootkc 精确放到想要的位置变得很麻烦而前移0x20000000恰好是在 SPTM 下装载 kernelcache 的一个非常自然的方式所以成了默认值。因此做内核调试时需要做地址换算mach-o 地址 0x20000000 VM 内实际地址。比如符号表里sleh_synchronous在0xfffffe000bb149c8在 VM 中实际位于0xfffffe002bb149c8。xnuboot_sptm.c里还藏了一个编译期选项LINEUP_BOOTKC_NICELY可以强行把 bootkc 放在其标称虚拟地址上但会让 SPTM / TXM 的虚拟地址变得很奇怪、已知会引起不稳定——只建议在深挖 XNU 早期启动时使用。darwin-vm 侧的胶水设备树改写与信任缓存Qemu 只解决了硬件模拟让真实固件在模拟硬件上活着靠的是仓库根目录几个小脚本dt_fixup.py把真机设备树驯化成虚拟机版get_files.sh 下载好DeviceTree后调用 dt_fixup.py 进行批量改写核心在fixup()dt_fixup.py内存布局根据soc-generation设置dram-base/dram-size8GB低世代基址0x800000000新世代0x10000000000并伪造firmware-version为qemu-sptmNVRAM把仓库自带 nvram.bin提取自真实 iPhone 的chosen/nvram-proxy-data注入设备树删除/替换不支持的节点移除dockchannel-uart、sgxGPU 共享区域只保留 compatible 字段匹配AppleARM/aic/arm-io/uart-1,samsung的设备SUPPORTED_DRIVERSdt_fixup.py加快启动用假rtc节点骗过IOKitInitializeTime省掉 30 秒 RTC 超时设置vmm-present属性避免AMFI: No PMGR?panicqemuport 隧道 UARTcreate_tunnel_devices()dt_fixup.py在uart0之后克隆出 4 个qemuport0–3串口设备把内存中相邻的 16K 页当额外串口用——这是 TCP 隧道化 SSH 方案的硬件基础在 VM 内用socat把dropbear的 22 端口流量塞进 UART宿主机的 socat 再收出来变成 TCP。build_tc.py手工构建 trustcache苹果的代码签名要求可执行文件的 CDHash 必须出现在信任缓存中。build_tc.py 按 XNU 的TrustCacheModule1_t二进制结构把 get_files.sh 收集到的全部 CDHash 打包成ramdisk.tc——以后你往 VM 里塞任何新二进制只需追加它的 CDHash 再重跑一次即可。权限与 shellfix_perms.sh launchd 后门fix_perms.sh 挂载ramdisk.dmg并把bin、System、libexec整体chown root:wheel——不做这步launchd 会因 Path had bad ownership/permissions 拒绝启动服务。而 com.jprx.bash.plist 是一个极简的 launchd 守护进程KeepAlive拉起/bin/bash把 stdin/stdout/stderr 全部接到/dev/console。系统启动完成后串口上就自然出现bash-3.2#。快速上手编译 qemu-sptm 并启动完整步骤见 README.md核心四步# 1. 获取代码准备步骤需要在 Mac 上执行 get_files.sh / fix_perms.sh git clone https://gitcode.com/gh_mirrors/da/darwin-vm cd darwin-vm ./get_files.sh ./fix_perms.sh firmware/ramdisk.dmg # 2. 编译 qemu-sptm在 Linux 构建机上 git submodule update --init cd qemu-sptm mkdir build cd build ../configure --target-listaarch64-softmmu --disable-pvg # macOS 27 上必须加 --disable-pvg make -j # 3. 启动 cd ../.. ./run.sh退出 Qemu终端按CtrlA再按x。⚠️ 两个高频坑FAQ报Failed to bootstrap path ... com.jprx.bash.plist ... bad ownership/permissions→ 忘记执行fix_perms.shmacOS 27 上编译qemu-sptm报PGTask_t is unavailable→ configure 漏了--disable-pvg。深入调试用 GDB 抓住 SPTM 的第一条指令darwin-vm把调试能力做进了启动链Debugging the kernel在 run.sh 的args中加-sQemu 暴露 GDB 远程服务器默认localhost:1234再加-SQemu停在 SPTM 的第一条指令等待调试器——你可以单步观察 SPTM 如何初始化页表、如何加载 TXM用lldb附加gdb-remote localhost:1234随后即可对firmware/bootkc设断点、bt看调用栈、x/10gx DTRootNodedump 设备树、读BootArgs。(lldb) b sleh_synchronous Breakpoint 1: where bootkcsleh_synchronous (lldb) c Process 1 stopped * thread #1, stop reason breakpoint 1.1 frame #0: 0xfffffe002bb149c8 bootkcsleh_synchronous配合 KDK 开发内核kmutil create打包kernel.development.${CHIP}见 README.md还能获得带符号的完整 XNU 调试体验。总结darwin-vm的精髓在于分工qemu-sptm负责硬件与特权自定义darwin机器 软件模拟的 SPTM / TXM 环境 xnuboot引导程序按真机 iBoot 的约定摆放固件、填写memory-map、构造BootArgsdarwin-vm负责内容与胶水从真实 IPSW 提取固件用dt_fixup.py驯化设备树、build_tc.py重建 trustcache、fix_perms.sh修权限再用一个 launchd plist 把系统送进 root shell地址滑动0x20000000是 SPTM 约束下装载 kernelcache 的自然解也是调试时地址换算的钥匙。理解这套链条后你不仅能在 Qemu 里玩 Darwin更获得了观察 SPTM / TXM / XNU 启动全过程的窗口——这正是该项目最独特的价值。赞分享【免费下载链接】darwin-vmRun iOS/ macOS in Qemu. Virtual iPhone 17, 16, 15, 14, 13, 12 and M5-M1 Apple Si Macs supported.项目地址https://gitcode.com/gh_mirrors/da/darwin-vm点击查看免费下载相关推荐EDK II RISC-V QEMU virt 平台固件完全指南从源码构建到 QEMU 启动与自定义 OpenSBIEDK II RISC V QEMU virt 平台固件完全指南从源码构建到 QEMU 启动与自定义 OpenSBI 本篇技术指南以 EDK II 仓库中 O固件操作系统驱动开发嵌入式QEMU 的 NBD 协议支持与元数据上下文详解从 base:allocation 到 qemu 自定义命名空间QEMU 的 NBD 协议支持与元数据上下文详解从 base:allocation 到 qemu 自定义命名空间 导读 QEMU 在块设备生态中深度集成了 N虚拟化硬件仿真syzkaller Darwin/XNU 模糊测试实战VM 镜像制作、KASANKSANCOV 内核构建与 QEMU 全自动引导syzkaller Darwin/XNU 模糊测试实战VM 镜像制作、KASANKSANCOV 内核构建与 QEMU 全自动引导 本文基于 syzkalle测试操作系统网络安全创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考