# How to Extract and Analyze Kernel Extensions (Kexts) from a Kernelcache

> Learn to extract and analyze kernel extensions kexts from a kernelcache using the ipsw toolkit. Get bundle IDs, versions, and load addresses with Go functions or a CLI command.

- Repository: [blacktop/ipsw](https://github.com/blacktop/ipsw)
- Tags: how-to-guide
- Published: 2026-02-26

---

**The `ipsw` toolkit parses the `__PRELINK_INFO` segment of a Mach-O kernelcache to extract kernel extension metadata, providing both Go library functions (`GetKexts`, `KextList`, `KextJSON`) and a CLI command (`ipsw kernel kexts`) to output kext bundle IDs, versions, and load addresses.**

Extracting and analyzing kernel extensions (kexts) from a kernelcache is essential for iOS and macOS security research and firmware analysis. The `blacktop/ipsw` open-source project provides a complete toolkit for parsing kernelcache files, reading the `__PRELINK_INFO` section, and converting binary plist data into structured `CFBundle` objects that represent each kext.

## Understanding the Kernelcache Structure

iOS and macOS kernelcaches are Mach-O files that store prelinked kernel extensions in the `__PRELINK_INFO` segment. This segment contains a plist-encoded dictionary mapping bundle identifiers to kext metadata, including version strings, dependencies, and load addresses. The `ipsw` library reads this section to reconstruct the original `CFBundle` structures without requiring the XNU source code.

## Extracting Kexts with the ipsw Go Library

The core extraction logic resides in [`pkg/kernelcache/kext.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kext.go). This file provides functions to parse the prelink info, resolve virtual memory addresses, and format output for both human reading and automated processing.

### Parsing the Prelink Info Section

The `GetKexts` function reads the `__PRELINK_INFO.__info` section, trims null bytes, and decodes the binary plist into a `PrelinkInfo` struct containing a slice of `CFBundle` objects.

```go
import "github.com/blacktop/ipsw/pkg/kernelcache"

// f is an open *macho.File
kexts, err := kernelcache.GetKexts(f)
if err != nil {
    log.Fatal(err)
}
for _, kext := range kexts {
    fmt.Printf("Found kext: %s (%s)\n", kext.ID, kext.Version)
}

```

### Resolving Virtual Memory Addresses

For detailed analysis, `GetKextStartVMAddrs` reads the `__kmod_start` array from the kernelcache and converts raw pointers into file offsets using Mach-O utilities. This maps each kext to its actual load address in kernel memory.

```go
addrs, err := kernelcache.GetKextStartVMAddrs(f)
if err != nil {
    log.Fatal(err)
}
// addrs slice corresponds to the kexts returned by GetKexts

```

### Generating Lists and JSON Output

The `KextList` function provides a unified interface that returns either a diff-friendly list of bundle IDs and versions, or a detailed list including load addresses when `diffable` is set to `false`.

```go
// Diff-friendly output: "com.apple.driver.X (1.0.0)"
simpleList, err := kernelcache.KextList(f, true)

// Detailed output: "0xfffffe0007004000: com.apple.driver.X (1.0.0)"
detailedList, err := kernelcache.KextList(f, false)

```

For programmatic analysis, `KextJSON` marshals the complete `CFBundle` slice to JSON format, preserving all metadata fields from the original plist.

```go
jsonData, err := kernelcache.KextJSON(f)
fmt.Println(jsonData)

```

## Using the ipsw CLI to Analyze Kexts

The command-line interface in [`cmd/ipsw/cmd/kernel/kernel_kexts.go`](https://github.com/blacktop/ipsw/blob/main/cmd/ipsw/cmd/kernel/kernel_kexts.go) wraps the library functions for quick inspection without writing Go code.

First, extract the kernelcache from an IPSW file:

```bash
ipsw extract kernelcache -d iPhone12,8 MyDevice_13.5.1_20F66_Restore.ipsw

```

Then list all kexts with their bundle identifiers and versions:

```bash
ipsw kernel kexts -i kernelcache.release.iPhone12,8

```

For detailed analysis including load addresses and JSON export:

```bash
ipsw kernel kexts -i kernelcache.release.iPhone12,8 -j

```

## Comparing Kexts Between Kernelcaches

Security researchers often compare kext sets between iOS versions to identify new drivers or removed functionality. The [`internal/diff/diff.go`](https://github.com/blacktop/ipsw/blob/main/internal/diff/diff.go) file demonstrates this pattern by calling `kernelcache.KextList` on two different kernelcaches and computing the difference.

```go
package main

import (
	"fmt"
	"log"

	"github.com/blacktop/go-macho"
	"github.com/blacktop/ipsw/pkg/kernelcache"
)

func main() {
	f1, err := macho.OpenFile("kernelcache1")
	if err != nil {
		log.Fatal(err)
	}
	defer f1.Close()
	
	f2, err := macho.OpenFile("kernelcache2")
	if err != nil {
		log.Fatal(err)
	}
	defer f2.Close()

	k1, _ := kernelcache.KextList(f1, true)
	k2, _ := kernelcache.KextList(f2, true)

	fmt.Println("Only in kernelcache1:")
	for _, k := range diff(k1, k2) {
		fmt.Println("  -", k)
	}
}

func diff(a, b []string) []string {
	bset := make(map[string]struct{}, len(b))
	for _, x := range b {
		bset[x] = struct{}{}
	}
	var out []string
	for _, x := range a {
		if _, ok := bset[x]; !ok {
			out = append(out, x)
		}
	}
	return out
}

```

This approach enables automated detection of kext additions, removals, or version changes between firmware updates.

## Summary

- **The `ipsw` project** provides complete tooling to extract and analyze kernel extensions (kexts) from iOS/macOS kernelcaches via both a Go library and CLI.
- **Core extraction** happens in [`pkg/kernelcache/kext.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kext.go), where `GetKexts` parses the `__PRELINK_INFO` segment into `CFBundle` structs.
- **Address resolution** uses `GetKextStartVMAddrs` to map kexts to their kernel load addresses via the `__kmod_start` array.
- **Output formats** include diff-friendly text lists (`KextList`), detailed address-inclusive lists, and JSON dumps (`KextJSON`).
- **CLI access** is provided by `ipsw kernel kexts` in [`cmd/ipsw/cmd/kernel/kernel_kexts.go`](https://github.com/blacktop/ipsw/blob/main/cmd/ipsw/cmd/kernel/kernel_kexts.go), supporting quick inspection without code.
- **Comparative analysis** is possible by diffing outputs from two kernelcaches, as demonstrated in [`internal/diff/diff.go`](https://github.com/blacktop/ipsw/blob/main/internal/diff/diff.go).

## Frequently Asked Questions

### How does ipsw extract kext metadata without loading the kernel?

The `ipsw` library reads the static `__PRELINK_INFO` segment stored within the Mach-O kernelcache file. This segment contains a serialized plist dictionary that maps bundle identifiers to kext metadata. The `GetKexts` function in [`pkg/kernelcache/kext.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kext.go) decodes this plist into Go structs without executing any kernel code.

### What is the difference between KextList and KextJSON?

**`KextList`** returns a slice of strings representing kext identifiers, optionally prefixed with their load addresses when the `diffable` parameter is set to `false`. This format is optimized for human reading or line-based diffing. **`KextJSON`** returns a complete JSON serialization of the `[]CFBundle` slice, preserving all metadata fields from the original plist for programmatic analysis.

### Can I extract the actual kext binary files from the kernelcache?

The current implementation in [`pkg/kernelcache/kext.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kext.go) focuses on metadata extraction from `__PRELINK_INFO` rather than binary slicing. While the kernelcache contains the linked kext binaries within segments like `__TEXT` and `__DATA`, extracting individual Mach-O files would require additional logic to split the prelinked image using the load addresses and size information from the plist metadata.

### How do I compare kexts between two different iOS versions?

Load both kernelcaches using `macho.OpenFile`, then call `kernelcache.KextList` with `diffable=true` on each file to get comparable string slices. Use a set difference algorithm to identify kexts present in one version but not the other, as demonstrated in [`internal/diff/diff.go`](https://github.com/blacktop/ipsw/blob/main/internal/diff/diff.go). This approach quickly reveals added, removed, or updated drivers between firmware releases.