用VirtualBox 5.2.20调试macOS内核

这篇文章会手把手教你如何用 VirtualBox 进行 macOS 内核的源码级调试。虽然这些步骤都是在 VirtualBox上进行的,但在 VMWare 上也是通用的,甚至更简单。

安装 VirtualBox 和 Sierra/High Sierra

如果你还没在 VirtualBox 上安装 macOS 镜像,你可以复用 VMWare 的 vmdk,也可以装个新的。重新安装系统需要 ISO 镜像,下面的命令可以将从Mac app store下载的 Sierra 转成 ISO。

macOS Sierra

注意,目前已知macOS Sierra 10.12.4/10.12.5/10.12.6的安装包,无法通过虚拟机安装。请安装之前的版本。

macOS High Sierra

网络设置

如果你用了桥接,可以跳过这部分。

如果用的是 NAT,需要为 KDP 开启端口转发。点击“网络”,选择“高级” -> “端口转发”,将 localhost 41139/UDP 转发到虚拟机的 41139/UDP,就能够访问到虚拟机的 41139 端口了。

显存调整到最大

取消软盘

内存设置

内存至少4GB

CPU设置

CPU至少2颗核心

3D加速

开启3D加速,提高性能

完成这一步,就可以安装系统了。


点击磁盘工具

显示出的界面如下

选择下面的"VBOX HARDDISK Media",然后选择"抹掉",如下图:


之后关闭窗口,返回上层窗口,选择"重新安装 macOS"。进入真正系统安装流程。






安装完成后,从虚拟机中移除安装镜像

重启之后进入如下界面

我们需要安装启动引导


上述执行的命令如下:

稍后会出现如下界面:

等待完成操作,就完成了引导部分的安装。重启之后,就是常规操作了。


安装 XCode

在你的主机上安装 XCode,通过 App store 安装最简单。 可以直接打开 XCode,也可以执行 sudo xcodebuild -license accept 接受 XCode license。

安装内核调试组件(KDK)

根据我们要调试的 macOS 版本从 Apple 开发者中心安装 KDK ,这里我的是10.12 build 16A323.

KDK 的安装目录是/Library/Developer/KDKs,提供的内核版本、符号、内核扩展都有 RELEASE、DEVELOPMENT、DEBUG 三种版本。不同之处在于 DEVELOPMENT 和 DEBUG 版比 RELEASE 版多了些断言和错误检查,DEBUG 版的最丰富。

注意:被调试的系统不需要安装 KDK。

修改 nvram boot-args

为了能够调试虚拟机,需要设置虚拟机上 nvram 中的 debug 项。除了 debug 的值外其他值也能被我们所用。下面是一些因垂斯挺的选项:

  • -v:以 verbose 模式启动系统。
  • kcsuffix:填写后缀以指定启动的内核。
  • pmuflags:貌似大家都推荐把这项值设为1。然而 Apple’s Kernel Programming Guide 已经明确指出管理电源的看门狗定时器“只在台式机、笔记本的 G4 版本和台式机的 G5 版本及之前有作用”,其他的看门狗定时器“只在 OS X Server 中启用”。所以,虽然设置了也不影响,但这个选项真没什么卵用。
  • debug:允许远程内核调试。 Apple docs 里有列出可用的标志。我常用的是DB_LOG_PI_SCRN | DB_ARP | DB_NMI。另外,control + option + command + shift + escape 可以触发不可屏蔽中断(NMI),继而引发调试器中断,超级方便。这对组合键跟 host key 的组合键冲突的时候用起来很难受,所以我把 host key 重新绑定成command + right了。

修改 nvram

在 VMware 里,可用这样修改 nvram:

在 virtualbox 里没那么简单,因为一重启修改的值就没了。还好从 virtualbox 手册的 3.13.2 看到了希望:

不能在运行的虚拟机内部操作 EFI 变量了(如:在Mac OS X 虚拟机里运行 nvram 来设置“boot-args”不管用了)。不过,可以通过给虚拟机发送附加数据”VBoxInternal2/EfiBootArgs” 来设置“boot-args”。…

因此,我们需要关闭虚拟机,在主机上运行命令:

如果想个性化定制部分信息,可以执行如下命令修改参数,主要是屏幕,CPU等的属性信息,不是必须

交换内核

前面我提到可以指定kcsuffix选项以调试不同版本的内核。内核文件在虚拟机的/System/Library/Kernels目录,然而这个目录受“系统完整性保护”机制保护(SIP)。所以要想使用 KDK 或者自己编译的内核,必须启动进入 recovery,把目标内核拷进/System/Library/Kernels目录,使 kextcache 失效,然后重新启动。

启动进入 recovery

在 VMware 里,cmd + R就能进入 recovery 模式。virtualbox 要多几步。

启动虚拟机时,按住F12,然后选择Boot Manager -> EFI Internal Shell,就会看到 EFI Shell 的欢迎界面。输入命令进入 recovery:

进入 recovery 的界面,打开终端,操作目标内核,并将 kextcache 设置为无效。

如果需要禁用 SIP,在重启之前执行:

源码调试

下载要调试的版本的 XNU 源码。调试时,LLDB 会去 /Library/Caches/com.apple.xbs/Sources/xnu/xnu-...目录寻找内核源码,所以可以把下载的源码放这个目录,也可以建一个符号链接指向源码目录。还有个方法是 设置 LLDB 的target.source-map变量:

早一些的 macOS 版本比如 Yosemite 就只能把源码放在 /SourceCache/xnu/里。

安装 LLDB

终于到调试器了。下面会举例用的是 RELEASE 内核。

为了在 Sierra KDK 里用 XNU LLDB 宏,执行pip install macholib安装macholib模块。粘贴执行加载内核文件时提示我们要执行的命令。(如下)

触发 NMI 后(或者等待设置了DB_HALT的调试器暂停启动进程时),执行kdp-remote <ip>连接到调试进程(若使用的是 NAT 端口转发,ip 是 localhost)。(如下)

瞧,macOS 内核源码调试这就开始了!

参考链接


发布者

发表评论

电子邮件地址不会被公开。 必填项已用*标注