更换Mac OSX启动内核(XNU)

前面的文章讲述了编译Mac OSX内核的基本步骤,下面的步骤就是更换内核重启了。对于Macbook的同学来说,除了如何把内核启动起来,最关心的恐怕就是如果内核crash的话,怎么样换回来原来的内核文件,至少别把自己的Mac给玩挂了。
网上搜了很多资料,最靠谱的就是这封邮件列表里的邮件了。邮件的标题是“choosing kernel on Intel”,我想很大一部分原因是之前众多的资料是关于PowerPC平台的Open Firmware(连苹果官网里的文章还停留在那个年代)。而现在鸟枪换炮用上了EFI这个新鲜玩意儿,方法自然是大不同了。(而苹果却又没把EFI shell这些东西给提供出来,也给开发者造成了一些麻烦。不过从苹果角度出发的确也没必要给普通用户提供SHELL)。
下面是这些方法:

EFI Shell

装个EFI Shell,比如rEFIt。rEFIt提供了在Macbook上一些方便的安装方法,比如光盘启动、U盘启动或者直接放硬盘上。其中硬盘上的安装一步到位,兼容BootCamp,开机以后看到的就是rEFIt的界面,几个菜单项,如下图,另外每个菜单项还可以有简单的定制,以带入不同的启动选项。左下角就是EFI Shell。

相比于古老的BOIS,EFIShell提供了强大的功能和运行环境,具体的用法可以看Intel的这个手册,或者中文版。常用的命令有:
(EFI Shell进入后分辨率偏低,可以通过mode命令调整。打一个命令往往会超出屏幕的显示范围,这个时候需要在每个命令后加一个“-b“的选项,达到more命令那样的效果。)

  • help/?: 用法很简单,help <command>,类似man
  • fs1:     切换文件系统,类似于DOS下的盘符切换。具体有什么fs可以切换参见map命令
  • map:显示当前映射表,也可以把当前的块设备加上盘符;
  • 简单的文件系统命令,比如cd/rm/mkdir等等,还可以通过edit, hexedit来编辑文件(这个很有用,可以用来改下面步骤里的东西)

而如果需要启动内核的话(就像grub里敲initrd,boot),只需要找到你Mac OSX的挂载盘(我的是fs1)
fs1:/> \System\Library\CoreServices\boot.efi Kernel=mach_kernel.test
我相信应该还可以带一些别的参数,比如调试位,不过目前还没有尝试过。

编辑启动文件

这应该是最简单的办法,也是邮件里最推荐的办法。
打开/Library/Preferences/SystemConfiguration/com.apple.Boot.plist,往里面增加一项

<key>Kernel</key>
<string>mach_kernel.test</string>

如果你自己编译的内核挂了,建议的办法是用rEFIt引导,然后修改配置文件。建议无论如何都要在文件系统里保留一份mach_kernel的备份。

NVRAM变量

修改NVRAM里的boot-args变量,使用以下命令:

$sudo nvram boot-args=”debug=0x144 -v Kernel=mach_kernel.test”

如果玩坏了,办法是在启动时按住“Cmd-Opt-P-R”把NVRAM清空(并载入默认选项)。当然如果你把mach_kernel搞坏了这招估计行不通。

EFI启动选项

$sudo bless –mount / -setBoot –options “Kernel=mach_kernel.test”

这是传给EFI启动的引导选项(显然可以用rEFIt引导来跳过),如果你想暂时用原来的kernel启动,则在启动时按住Opt,进入启动菜单,并选择Mac OSX进行启动。这样会覆盖bless的选项,从而直接启动mach_kernel。同样用上面的办法清空NVRAM也可以达到同样的目的(只不过下次启动就是默认了)。

在Mac OSX 10.7(Lion)上编译XNU内核

Apple虽然备受封闭系统的指责,但事实上只要访问http://www.opensource.apple.com,你就能看到苹果对开源软件的贡献。其中最值得一提的就是OSX内核——XNU。
根据我的理解,XNU是Mac OSX的内核,是一种融合了Mach、BSD和IOKit的混合型内核。其中Mach提供核心的基本操作(IPC、同步、VM、进程管理),BSD负责POSIX API,网络,文件系统(以及很多其他),另外IOKit则包含了驱动模型。
虽然网上有不少关于如何编译XNU的文章(包括项目),但在我的试验中,最后能够成功编译的只有这篇文章(因为正好符合各项条件,另外10.8的文章链接在此,都需翻墙),苹果官网上的文档甚至还停留在PowerPC的时代,告诉你如何通过Open Firmware进入调试模式,而Macbook早已在5年前转换到Intel平台,使用EFI作为bootloader。(另外一个明显问题是gcc4.3可以编译最新XNU,无需倒退到3.3)
具体的步骤我不逐句搬过来,概括来说就是:

  1. 下载dtrace(调试追踪工具)和bootstrap_cmds(用来生成Mach IPC服务端代码,又称Mach Interface Generator,这个是服务器版叫migcom);
  2. 分别编译并安装;
  3. 进入xnu代码目录编译;
  4. 在BUILD/obj/RELEASE_<arch> 目录里的mach_kernel就是最终文件。

有几点需要注意:

  • CC和C++一定要换成GCC前端而不是clang,CC是一个符号链接,C++我改了符号链接似乎还不行,最后指定了CXX的环境变量。
  • dtrace和bootstrap_cmds可以在http://www.opensource.apple.com/tarballs/下载打包好的tarball;
  • Xcode的Command Line Tools需要升到最新,这个不会跟着Xcode一起升级,否则在安装bootstrap_cmds时install_name_tool会报告“malformed object (unknown load command XX)”,具体看这个问答

编译完成之后当然就是更换内核重新启动了。具体步骤请看这里
另外编个内核好歹留点纪念吧,如果需要在uname信息留下你的印记的话,编辑config/version.c (我的OS是中文的,居然uname还给出中文了)

[email protected]$ uname -a
Darwin Marshalls-MacBook-Pro.local 11.4.2 DarwinMarshall’s Kernel Version 11.4.2: 2013年 2月24日 星期日 20时20分00秒 CST; wum:xnu-1699.32.7/BUILD/obj//RELEASE_X86_64 x86_64

最后附上原文里的一些步骤,防止原来的链接丢失。

  1. Build dtrace

    $ cd dtrace-90
    $ mkdir -p obj sym dst
    $ xcodebuild install -target ctfconvert -target ctfdump -target ctfmerge ARCHS="i386 x86_64" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
    ...
    $ sudo ditto $PWD/dst/usr/local /usr/local
    Password:
    $ cd ..
  2. Build bootstrap_cmds

    $ cd bootstrap_cmds-79
    $ mkdir -p obj sym dst
    $ make install RC_ARCHS="i386" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
    ...
    $ sudo ditto $PWD/dst/usr/local /usr/local
    Password:
    $ cd ..
  3. Build xnu

    $ cd xnu-1699.22.73
    $ make ARCH_CONFIGS="I386 X86_64" KERNEL_CONFIGS="RELEASE"
    ...
    $ file BUILD/obj/RELEASE_*/mach_kernel
    BUILD/obj/RELEASE_I386/mach_kernel: Mach-O executable i386
    BUILD/obj/RELEASE_X86_64/mach_kernel: Mach-O 64-bit executable x86_64

解密IPSW固件文件

最近看“Mac OSX and iOS Internals”,顺便小动手了一把,把IPSW(iOS固件包)里的文件拆开来看了看。操作平台为Mac OSX Lion。(另外似乎只适用于6.0以前的固件)
工具:
vfdecrypt (http://theiphonewiki.com/wiki/VFDecrypt
xpwntool (http://code.google.com/p/ios-jailbreaking-stuff/source/browse/trunk/tools_bin/xpwntool?r=5)点击下载raw file
lzssdec(Google第一个结果是源代码,下下来直接用g++编译)
固件下载:
http://www.ipswdownloader.com/download-iphone-ipsw-files.php
步骤:
1. ipsw直接当成zip文件解压;
2. 得到一个文件夹和几个文件,其中最大的那个以dmg结尾的就是文件系统ramdisk,另外一个dmg是restore ramdisk(大概将近20M)。不过由于文件被加密,不能直接mount;
3. 在 http://theiphonewiki.com/wiki/VFDecrypt_Keys 找到对应的版本,并得到文件系统的key;
4. 通过“./vfdecrypt -i<dmg location> -k<key> -o<out location>.dmg” 命令解密后的文件系统镜像;
5. Mount 解密后的镜像,可以看到一个类Unix的文件系统结构,其实这就是iOS的文件系统的layout;
6. 在ipsw解压的文件里找到kernelcache文件,基本上这可以算是iOS的内核文件(加上了一些静态链接好的扩展)
7. 通过xpwntool先把kernelcache给解密出来
./xpwntool <infile> <outfile>  [-k <key>] [-iv <key>] [-decrypt]
8. 用lzssdec把kernelcache从解密的文件里提取出来(其实就是找到相应的offset然后用lzss算法解压缩出来),注意“<“和“>”都要打
./lzssdec -o 448 < input_file > out_file
9. 用file确定解压文件的属性“Mach-O executable arm”