环境准备

一个可以给内核签名的苹果开发者账号。申请网址:https://developer.apple.com/contact/kext/。如果内核扩展只是打算在自己的实验机器上加载,即使不对其签名也是可以的。只需要关闭机器的SIP功能即可,方法是Mac启动的时候, 按住Command+R进入恢复模式。
执行命令csrutil disable后,重启电脑就可以了。

创建工程

  1. 打开Xcode
  2. 选择 File > New > New Project。弹出新建工程面板
  3. 选择 macOS > Generic Kernel Extension。创建内核扩展工程
  4. 输入“HelloKext”作为内核扩展名称,输入Bundle ID,点击继续。
  5. 输入测试代码 ```cpp

    include

    include

kern_return_t hellokext_start(kmod_info_t ki, void d); kern_return_t hellokext_stop(kmod_info_t ki, void d);

kern_return_t hellokext_start(kmod_info_t ki, void d) { printf(“HelloKext has started.\n”); return KERN_SUCCESS; }

kern_return_t hellokext_stop(kmod_info_t ki, void d) { printf(“HelloKext has stopped.\n”); return KERN_SUCCESS; } ``` 以上代码,包含了两个头文件和,这里需要注意,在编写内核扩展时,只能使用Kernel.framework中包含的头文件。应用其它Framework可能会导致扩展无法加载或执行失败。

编译内核扩展

  1. 和编译普通Xcode工程一样,Command+B快捷键编译就完成了。
  2. 打开Terminal,进入到新编译的内核扩展所在目录。
  3. 执行命令kextlibs -xml HelloKext.kext, 把返回的结果填入内核扩展的Info.plist中。

image.png

  1. 执行命令sudo cp -R path/to/HelloKext.kext /tmp/HelloKext.kext,这样HelloKext.kext拥有了正确的权限。


加载内核扩展

  1. 加载前可以用命令kextutil -n -print-diagnostics /tmp/HelloKext.kext检查一遍扩展。测试环境下,即使提示扩展没有签名,也不会影响我们加载测试自己的内核扩展,所以可以忽略。
  2. 执行命令log stream —predicate “processID == 0”关注kernel_task日志打印
  3. 执行命令sudo kextload HelloKext.kext,加载内核,发现日志“HelloKext has started.”打印了出来。
  4. 执行命令kextstat | grep -v com.apple, 可以看到自己的内核模块的确加载成功了。
  5. 执行命令sudo kextunload HelloKext.kext,卸载内核,发现日志“HelloKext has stopped.”打印了出来。

一点感想: 可见内核扩展是作为kernel_task进程运行的,苹果通常不建议开发内核扩展,对内核扩展开发也有很多限制和要求。内核扩展的代码必须稳定高可用,不能有一丝漏洞,否则会造成机器宕机等非常严重等问题。 不过一旦实现内核扩展,可执行代码就能隐藏踪迹,完成一些普通用户态下根本无法完成的事情。

参考