Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
ghidra_kernelcache — 用于iOS内核缓存逆向工程的Ghidra框架 | Kitploit
工具/GitHubGitHub/0x36/ghidra_kernelcache
iOS安全逆向工程二进制分析固件分析
GitHub0x36/ghidra_kernelcache

ghidra_kernelcache

用于iOS内核缓存逆向工程的Ghidra框架

查看仓库
370374年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

ghidra_kernelcache:一个用于逆向工程的 Ghidra iOS kernelcache 框架

本框架是我在 Kernelcache 逆向工程中的最终成果。我通常通过手动审计内核及其扩展来寻找漏洞,并自动化了大多数我真正希望在 Ghidra 中看到的功能,以加速逆向过程。事实证明,这种方法非常有效且节省了大量时间。该框架适用于 iOS 12/13/14/15 和 macOS 11/12(包括 kernelcache 和单个 KEXT),并将其公开,旨在帮助人们无需费力准备自己的环境即可开始研究 iOS 内核。我相信,该框架(包括其提供的工具集以及一些 IOKit 基础知识)足以入门 Kernelcache 破解。

该框架完全使用 Python 编写,并且可以扩展以构建其他工具。它提供了一些基本 API,你几乎可以在任何项目中使用它们,从而节省阅读冗长手册的时间。欢迎阅读 utils/ 目录中的核心功能。

Ghidra 在分析 Kernelcache 时表现出色,但与其他逆向工程工具一样,它需要一些手动工作。ghidra_kernelcache 提供了一个良好的起点,可以在开始时甚至在进行逆向工程时修复问题,从而提供整洁的反编译器输出。

有一个由 @_bazad 在 IDAPro 中创建的类似项目,名为 ida_kernelcache,它为希望使用内核镜像在 IDA 中工作的研究人员提供了一个良好的起点。我的框架看起来与 Brandon 的工作有些相似,但通过提供更多功能来减轻使用 kernelcache 的痛点,从而超越了它。

功能:

  • iOS 内核缓存符号化。
  • C++ 类层次结构重建和虚函数表。
  • 虚方法调用引用。
  • 自动修复 ::externalMethod() 和 ::getTargetAndMethodForIndex() 的外部方法调度表。
  • 为类方法应用命名空间。
  • 函数参数的符号名称和类型传播。
  • 为已知内核函数应用函数签名。
  • 将旧项目中的旧结构和类导入到新项目中。

这些功能被实现为独立的工具,可以通过快捷键或点击工具栏中的图标来执行。

安装

克隆仓库:```sh git clone https://github.com/0x36/ghidra_kernelcache.git

root@kitploit:~
**重要提示**:该项目已在 Ghidra 10.1_PUBLIC 和 10.2_DEV 上测试过,且不向后兼容。

转到 *`Windows → 脚本管理器`*,点击 *`脚本目录`*,然后将 *`ghidra_kernelcache`* 添加到目录路径列表中。
转到 *`Windows → 脚本管理器`*,在 *脚本* 列表中,转到 *`iOS→kernel`* 类别,勾选那里的插件,它们将出现在 GHIDRA 工具栏中。

在 [logos/](https://github.com/0x36/ghidra_kernelcache/tree/master/logos) 目录中,你可以为每个工具放置自己的 logo。

## iOS kernelcache 符号化

`ghidra_kernelcache` 在第一阶段需要 [iometa](https://github.com/Siguza/iometa/)(由 [@s1guza](https://twitter.com/s1guza) 制作),这是一个强大的工具,提供内核二进制中的 C++ 类信息。它的优点是作为一个独立二进制运行,因此只需解析其输出即可导入到你喜欢的逆向工程框架中。我的框架接收 iometa 的输出并进行解析,以进行符号化并修复虚函数表。

### 使用说明

解压内核后,运行以下命令:```sh
$ iometa -n -A /tmp/kernel A10-legacy.txt > /tmp/kernel.txt
# if you want also to symbolicate using jtool2
$ jtool2 --analyze /tmp/kernel

在 Ghidra 中加载内核缓存,不要使用批量导入,作为 Mach-O 镜像加载。 在加载并自动分析内核缓存后,点击工具栏中显示的图标,或直接按 Meta-Shift-K,然后输入 iometa 输出的完整路径,在本例中为 /tmp/kernel.txt。

如果你想使用 jtool2 符号,也可以使用位于 iOS→kernel 类别中的 jsymbol.py。

iOS 内核缓存 API

完整的 API 示例位于 ghidra_kernelcache/kc.py

→ 以下是一些操作类对象的示例:```py from utils.helpers import * from utils.class import * from utils.iometa import ParseIOMeta

ff = "/Users/mg/ghidra_ios/kernel.txt" iom = ParseIOMeta(ff) Obj = iom.getObjects() kc = kernelCache(Obj)

symbolicate the kernel

kc.process_all_classes()

symbolicate the classes under com.apple.iokit.IOSurface bundle

kc.process_classes_for_bundle("com.apple.iokit.IOSurface")

symbolicate the classes under kernel bundle

kc.process_classes_for_bundle("kernel")

Process one class (including its parents)

kc.process_class("IOGraphicsAccelerator2")

Clears the content of the class structures (vtables are excluded)

kc.clear_class_structures()

Overwrite the old vtable structure definition and resymbolicate it again

kc.update_classes_vtable()

Reconstructing function call trees by enumerating all pac references and find their corresponding virtual method call

kc.explore_pac()

root@kitploit:~
如你所见,你可以完全或部分符号化内核缓存(kernelcache),如果选择了部分符号化,`ghidra_kernelcache` 会在继续之前自动构建所有类的依赖关系。
如果你对整个内核缓存运行脚本(完全符号化),`ghidra_kernelcache` 将花费几分钟来分析内核映像。

完成后,Ghidra 将提供以下内容:

→ 在书签过滤器(Bookmark Filter)中新增了一个名为 "iOS" 的类别:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image1.png" alt="image1" width="200"/>
                    
                    
→ IOKit 类的虚函数表被添加到 "iOS" 书签中,以便更快、更好地查找虚函数表,你只需在搜索栏中输入字母、单词或 kext 包名即可查找特定的 kext 或类。

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image2.png" alt="image2"/>


→ 修复虚函数表:反汇编/编译未知代码,修复命名空间,重新符号化类方法,并为每个方法应用函数定义:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image3.png" alt="image3"/>

→ 创建类命名空间,并将每个方法放入其对应的命名空间:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image4.png" alt="image4"/>

→ 根据类层次结构创建类结构:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image5.png" alt="image5"/>

→ 创建类虚函数表,并且每个方法都有其自己的方法定义,以获得更好的反编译输出:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image6.png" alt="image6"/>

完整实现可在 [`utils/class.py.`](https://github.com/0x36/ghidra_kernelcache/blob/master/utils/class.py) 中找到。

以下是使用 `ghidra_kernelcache` 符号化前后的截图对比:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image7.png" alt="image7"/>

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image8.png" alt="image8"/>




## macOS Kext 符号化
---
`ghidra_kernelcache` 对 macOS 的支持包括对整个内核缓存(kernelcache)和单个 KEXT 的符号化,支持 ARM64e 和 x86_64 架构。 
**重要提示:** 在撰写本文时,Ghidra 无法解析整个 macOS 内核缓存,但可以将其加载到 IDA 中进行初步分析,然后将数据库(idb 转 xml)导入 Ghidra,但这超出了本文范围。如果你成功做到了,`ghidra_kernelcache` 将处理其余部分。

在符号化任何 macOS 内核扩展之前,需要完成一些步骤,因为 `ghidra_kernelcache` 的主要目标是重建类层次结构并将所有类结构管理到单个数据库中,而一个内核扩展无法满足这些要求,这意味着对单个 KEXT 的符号化需要对内核(可能还有其他依赖的内核扩展)进行符号化,因此这里需要做一些额外的工作。
`ghidra_kernelcache` 现在提供了一种强大的方法来符号化内核扩展(包括内核本身),通过 Ghidra 强大的 *DataType Project Archive* 来管理和共享类结构及虚方法定义。

### 符号化内核扩展的步骤
- 在 Ghidra 项目中创建一个新文件夹,然后将 `/System/Library/Kernels/kernel.release.XXXXX` 加载到该文件夹中,让 Ghidra 进行分析。
- 创建新的 *Project Archive*:进入 `DataType Provider` → 点击窗口右上角的箭头 → `New Project Archive` → 将其放置在新创建的文件夹中 → 命名(例如 macOS_12.1)。
- 现在使用 `ghidra_kernelcache` 符号化内核,该过程与 iOS *kernelcache* 符号化非常相似。```bash
$ iometa -n -A /System/Library/Kernels/kernel.release.t8101 > /tmp/kernel.txt
  • 在 Python 控制台中,或者你可以在 KM.py 脚本中找到完整的脚本实现:```py

from utils.helpers import * from utils.kext import * iom = ParseIOMeta("/tmp/kernel.txt") Obj = iom.getObjects() kc = Kext(Obj,shared_p="macOS_12.1") kc.process_kernel_kext()

root@kitploit:~
- 完成后,内核归档与 `macOS_12.1` 归档之间已建立数据库关联。现在,右键点击 `kernel.release.t8001` → `Commit DataTypes To` → `macOS_12.1`。
- 然后 `右键点击` → `全选` → `提交`。
- 保存项目归档:`右键点击` → `保存归档`。
我们刚刚创建了一个项目归档,可在所有内核扩展之间共享。
以 Apple Silicon 的 `IOSurface` Kext 为例:```bash
$ lipo /System/Library/Extensions/IOSurface.kext/Contents/MacOS/IOSurface -thin arm64e -output /tmp/iosurface.arm64e
$ iometa -n -A /tmp/iosurface.arm64e > /tmp/iosurface.txt
  • 将 Kext 加载到内核数据库和项目存档所在的同一文件夹路径中,然后让 Ghidra 完成分析。
  • 加载我们之前创建的项目存档 macOS_12.1:转到 Data Type Manager → Open Project Archive,然后选择 macOS_12.1。
  • 运行以下方法,完整脚本可在 KM.py 中找到:```python from utils.helpers import * from utils.kext import *

kc = Kext(Obj,shared_p="macOS_12.1")

This method fixes LC_DYLD_CHAINED_FIXUPS for M1 Kernel extension

kc.depac()

This method reconstructs class hierarchy and builds virtual table for each class

kc.process_kernel_kext()

root@kitploit:~
**重要提示**:有时候 `kc.process_kernel_kext()` 会失败,因为 Ghidra 无法解析一些 C++ 符号。要解决此问题,请前往脚本管理器并运行 `DemangleAllScript.java` 脚本,然后  重新启动  `kc.process_kernel_kext()`  再次。

### 自定义类
某些情况下,有些 C++ 类 `ghidra_kernelcache` 和 `iometa` 无法符号化,因此添加了一个新功能来处理这种情况。
`Custom()` 类重建会遍历所有 `::vtable` 符号,并检查该类是否已定义,如果未定义,则会自动创建类结构、每个识别出的类方法的函数定义、一个命名空间以及每个类的虚函数表。

目前仅在 macOS 上支持自定义类的创建。```bash
$ iometa -n -A /System/Library/Kernels/kernel.release.t8101 > /tmp/kernel.txt
$ iometa -n -A <kext_path> >> /tmp/kernel.txt

INPUT:```py

from utils.helpers import * from utils.custom_kc import *

if name == "main": default = "/tmp/kernel.txt" ff = askString("iometa symbol file","Symbol file: ",default) iom = ParseIOMeta(ff) Obj = iom.getObjects()

root@kitploit:~
kc = Custom(Obj)

kc.process_all_classes()
kc.explore_pac()
root@kitploit:~
## 杂项脚本
---
### 导入 KDK 的 Dwarf4 
Ghidra 有时无法加载对应的 `.dsym` 目录,我编写了一个小脚本来解决此问题。该脚本可在[此处](https://github.com/0x36/ghidra_kernelcache/blob/master/dwarf4_fix.py)找到。
**用法**:从 KDK 路径加载内核,让 Ghidra 完成分析,然后运行 `dwarf_fix.py`,它将加载符号,此过程可能需要几分钟。
<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image12.png" alt="image12"/>

### 解析虚方法调用引用 
`ghidra_kernelcache` 提供了两种方式来解决虚方法调用:通过 `kernelCache.explore_pac` 或 `fix_extra_refs` 

**kernelCache.explore_pac()**
如果你在处理 arm64e 二进制文件,`ghidra_kernelcache` 可以通过查找 `Pointer Authentication Code` 值来识别虚方法调用。该过程简单直接,与 `fix_extra_refs()` 不同,`kernelCache.explore_pac` 不依赖于 `Pcode` 或 `varnode` 识别,它只是遍历程序中的所有指令,搜索 `MOVK` 指令,获取第二个操作数,并在数据库中查找其对应的值。
用法:
通过 **kernelCache**、**Kext** 或 **Custom** 创建 *KernelCache* 实例,然后调用 `explore_pac()` 方法。```py
from utils.helpers import *
from utils.kext import *

if __name__ == "__main__":
    default = "/tmp/kernel.txt"
    ff = askString("iometa symbol file","Symbol file: ",default)
    iom = ParseIOMeta(ff)
    Obj = iom.getObjects()

    kc = Kext(Obj)

   	 kc.explore_pac()

fix_extra_refs()
此函数基于基本的数据流分析,查找所有虚方法调用并自动解析其实现。它适用于所有架构,能够从反编译器输出中识别源数据类型,并解析函数内所有虚调用引用,使用户无需手动查找即可直接跳转到实现或从实现跳回。

fix_extra_refs 提供的最实用功能是每次执行时保持引用同步。例如,当你将变量数据类型更改为类数据类型时,fix_extra_refs 会自动识别该更改,并递归遍历所有调用点以解析其引用,直到调用点队列为空为止。

fix_extra_refs 还提供了一些其他功能,例如:

  • 自动检测 _ptmf2ptf() 调用,并解析其调用方法(包括偏移量和完整函数地址)
  • 识别未解析函数名(以 FUN_ 开头的函数)的命名空间,并通过将目标函数放入其自身命名空间(例如为相应类添加 this 指针)来解析它。

你可以在 utils/references.py 中找到该实现。fix_extra_refs 解析 pcode 操作,查找 CALLIND 和 CALL 操作码,然后获取操作中涉及的所有 varnode。一旦识别出 Varnode 的定义,它会检索对应的 HighVariable 以确定类对象类型。如果类型未知(即看起来不像类结构),则忽略;否则,它会获取类名,查找其虚函数表,利用 Varnode 提供的偏移量,获取正确的虚方法调用,并在调用指令上放置一个引用。```py fix_extra_refs(toAddr(address))

root@kitploit:~
以下是使用 `fix_extra_refs` 的输出示例:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image9.png" alt="image9"/>

注意,它已成功解析了 **IOService::isOpen()**、**OSArray::getNextIndexOfObject()** 和 **IOStream::removeBuffer()** 的虚函数调用,无需任何手动修改。

接着,`fix_extra_refs` 将反编译 **IOStream::removeBuffer()**,获取该方法的所有 HighVariables,然后像前一个方法一样解析它们的引用,以此类推。
<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image10.png" alt="image10"/>

### 自动修复外部方法表

我相信每个研究人员都有一些脚本来处理这一部分,因为这是 IOKit 的主要攻击面,手动处理是一种负担,并且必须以一种允许研究人员深入研究多个外部方法表的方式进行自动化。
`ghidra_kernelcache` 提供了两个脚本:**fix_methodForIndex.py** 和 **fix_extMethod.py**。您可以像启用其他脚本一样启用它们,如上所示。

***用法***:将光标放在外部调度表的开头,运行脚本:提供目标以及选择器数量。
示例:`IOStreamUserClient::getTargetAndMethodForIndex()` 的用法:

<img src="https://raw.githubusercontent.com/0x36/ghidra_kernelcache/master/screenshots/image11.png" alt="image11"/>

### namespace.py:修复方法命名空间…

这是一个有用的脚本,用于将类类型填充到所有遇到的方法中,并且它是 `extra_refs.py` 脚本的依赖项,以便递归探索被调用函数并解析它们的引用。

***用法***:将光标放在所需函数的反编译器输出中,从工具栏运行脚本或按 **Meta-Shift-N**。

### 符号名称和类型传播

`ghidra_kernelcache` 为基本的 Pcode 操作提供了类型传播支持,但对于使用复杂强制转换的某些变量,它可能会失败。
如果有人想要帮忙,或者想要开始处理 Ghidra 中的低级内容,这是一个机会。
实现可以在 [ghidra_kernelcache/propagate.py](https://github.com/0x36/ghidra_kernelcache/blob/master/propagate.py) 中找到。

### 加载函数签名

在 Ghidra 中解析 C++ 头文件是不可能的,而在 kernelcache 中拥有内核函数签名可以改善反编译器输出中的许多方面。
例如,假设我们添加了 `virtual IOMemoryMap * map(IOOptionBits options = 0 );`,Ghidra 会自动将返回值重新类型化为 `IOMemoryMap` 指针,无论是函数定义还是函数签名。
您可以按照语法规则向 **signatures/** 目录中添加任何 C++ 符号,并且您可以在该目录中找到已定义的函数签名。```c++
// Defining an instance class method
IOMemoryDescriptor * withPersistentMemoryDescriptor(IOMemoryDescriptor *originalMD);

// Defining a virtual method, it must start with "virtual" keyword
virtual IOMemoryMap * createMappingInTask(task_t intoTask,  mach_vm_address_t atAddress,  IOOptionBits options,  mach_vm_size_t offset = 0,  mach_vm_size_t length = 0);

// Defining a structure
struct task_t;

// typedef'ing a type
typedef typedef uint IOOptionBits; 

// Lines begining with '//' are ignored 

用法:在对内核进行符号化后,强烈建议运行脚本 load_sigatnures.py 以加载所有可用的函数签名。与大多数之前的工具一样,可以通过在工具栏中添加、从插件管理器中运行或直接按 Meta-Shift-S 来运行此脚本。

加载旧结构:

此脚本简单直接,它会从旧项目中导入所有结构体、类、类型定义以及函数定义,以及所有带有 SourceType.USER_DEFINED 的内容,并将其应用到新项目中。

用法:在同一个工具中打开旧的和新的 Ghidra 项目,转到 load_structs.py 脚本,将旧程序名称填入 src_prog_string 变量,将新程序名称填入 dst_prog_string 变量,然后运行脚本。

贡献

如果你对这个项目感兴趣并想做出贡献,只需提交一个 PR,我会进行审核。同时,我希望在以下方面看到一些贡献:

  • ghidra_kernelcache/signatures/kernel.txt:持续导入 XNU 内核函数,非常简单,只需复制/粘贴函数定义即可。
  • ghidra_kernelcache/propagate.py:支持未处理的操作码,以实现更好的符号传播。

致谢

我要感谢 @s1guza 出色的 iometa 项目,ghidra_kernelcache 依赖于此。

下载工具