# How ipsw Parses Kernelcaches, Extracts Syscalls, and Analyzes Kexts: A Deep Dive into the blacktop/ipsw Source Code

> Explore blacktop ipsw source code to parse iOS kernelcaches, extract BSD syscall tables and Mach traps, and analyze kext metadata. Understand the tool's deep dive.

- Repository: [blacktop/ipsw](https://github.com/blacktop/ipsw)
- Tags: deep-dive
- Published: 2026-02-26

---

**The `ipsw` tool treats iOS kernelcaches as Mach-O images, decompressing Img4 containers to extract version strings from `__TEXT.__const`, BSD syscall tables from `__DATA_CONST.__const` via pattern matching, Mach traps using a tiny disassembler, and kext metadata from `__PRELINK_INFO` segments.**

The `blacktop/ipsw` repository provides a comprehensive Go-based toolkit for analyzing Apple firmware images. At its core, the `pkg/kernelcache` package implements a sophisticated pipeline to parse kernelcaches, extract syscalls, and analyze kexts, enabling security researchers to automate kernel introspection and signature generation.

## Understanding the Kernelcache Parsing Pipeline

The parsing pipeline begins with Img4 container handling and progresses through Mach-O fileset resolution to extract the actual kernel slice.

### Decompressing Img4 Containers

The entry point `kernelcache.Open` (via `img4.Open` in [`pkg/img4/img4.go`](https://github.com/blacktop/ipsw/blob/main/pkg/img4/img4.go)) reads the Img4 wrapper, validates the magic, and extracts the compressed payload. The `Decompress` and `DecompressData` functions handle LZFSE or LZSS compression to obtain a raw Mach-O file represented as `*macho.File`.

```go
km, err := img4.Open(kcachePath)          // read Img4 wrapper
kc, err := kernelcache.Decompress(km)     // raw Mach-O (may be LZFSE/LZSS)

```

### Handling Fileset Mach-Os

Modern kernelcaches use the `MH_FILESET` format containing multiple Mach-O slices. The code in [`pkg/kernelcache/kernelcache.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kernelcache.go) detects this header type and extracts the actual kernel slice using `GetFileSetFileByName("kernel")` or `"com.apple.kernel"` when analyzing kexts.

```go
if kc.FileHeader.Type == macho.MH_FILESET {
    kernel, err := kc.GetFileSetFileByName("kernel")
    // proceed with kernel analysis
}

```

## Extracting Kernel Version Information

The `GetVersion` function in [`pkg/kernelcache/kernelcache.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kernelcache.go) extracts build metadata by walking the `__TEXT.__const` section. It reads C-strings and applies two compiled regex patterns: one for the *Darwin Kernel Version* string and one for the *Apple LLVM* compiler string.

```go
kv, err := kernelcache.GetVersion(kc)
fmt.Printf("Darwin %s (%s) – XNU %s\n",
    kv.KernelVersion.Darwin,
    kv.KernelVersion.Date,
    kv.KernelVersion.XNU)

```

The function populates a `Version` struct containing the Darwin version, XNU version, build date, and LLVM compiler information.

## How ipsw Extracts BSD Syscalls

The `GetSyscallTable` function in [`pkg/kernelcache/syscall.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/syscall.go) reconstructs the BSD syscall table by combining binary pattern matching with embedded metadata.

### Pattern Matching the Sysent Table

The function scans the `__DATA_CONST.__const` section for the binary `syscall2Pattern` to locate the `sysent` array. It reads the array entries, fixes up slide-relative addresses using `m.SlidePointer`, and extracts the syscall numbers and function pointers.

### Augmenting with Embedded Metadata

The function loads embedded syscall metadata from `data/syscall.gz` via `getSyscallData`. It decorates each raw syscall entry with human-readable names and argument lists from the JSON master data, setting a `New` flag when a syscall appears in the current kernel but not in the embedded reference data.

```go
syscalls, err := kernelcache.GetSyscallTable(kc)
for _, s := range syscalls {
    newTag := ""
    if s.New {
        newTag = " ← NEW"
    }
    fmt.Printf("%3d %-20s %s%s\n", s.Number, s.Name, strings.Join(s.Args, ", "), newTag)
}

```

## Parsing Mach Trap Tables

The `GetMachTrapTable` function in [`pkg/kernelcache/mach_trap.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/mach_trap.go) extracts Mach trap entries using similar pattern-matching techniques but requires additional disassembly to locate sentinel values.

The routine uses the same embedded `syscalls.gz` file for Mach-trap metadata. It locates the trap table in `__DATA_CONST.__const` via `patternMatch`, then resolves the `kern_invalid` address using a tiny disassembler (`disass.NewMachoDisass`) to identify the end of the valid trap range.

For each `machTrapT` structure found, the function rebases function pointers using `DyldChainedPtr64KernelCacheRebase` and decorates entries with argument strings from the JSON metadata.

```go
traps, err := kernelcache.GetMachTrapTable(kc)
for _, t := range traps {
    fmt.Printf("%3d %s(%s) // %s\n", t.Number, t.Name, strings.Join(t.Args, ", "), t.Function)
}

```

## Analyzing Kernel Extensions (Kexts)

Kext analysis involves parsing both the plist metadata and the raw kmod information tables embedded in the kernelcache.

### Reading __PRELINK_INFO Segments

The `GetKexts` function in [`pkg/kernelcache/kext.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kext.go) decodes the binary plist stored in the `__PRELINK_INFO.__info` section into a slice of `CFBundle` structures. This provides metadata such as bundle identifiers, versions, and dependencies.

### Rebasing Kmod Addresses

The `GetKextInfos` function reads the `__kmod_info` table from the `__PRELINK_INFO` segment, dereferences each `KmodInfoT` structure, and rebases addresses using `DyldChainedPtr64KernelCacheRebase`. The `KextList` function merges both data sources, optionally printing VM start addresses or generating diff-friendly `id (version)` lists.

```go
bundles, err := kernelcache.GetKexts(kc)      // plist → []CFBundle
addrs,   err := kernelcache.GetKextStartVMAddrs(kc) // __kmod_start table
list,    err := kernelcache.KextList(kc, false) // human-readable

```

Typical output shows the virtual memory start address followed by the bundle identifier and version:

```

0xFFFFFFF0077A0000: com.apple.iokit.IOUSBHostFamily (1.0.0)
0xFFFFFFF0075A4000: com.apple.driver.AppleHDA (2.2.3)

```

## Building Kernel Signatures

The `signature` package in [`pkg/signature/signature.go`](https://github.com/blacktop/ipsw/blob/main/pkg/signature/signature.go) consumes the kernelcache APIs to build deterministic fingerprints. When a user runs `ipsw kernel analyze`, the engine executes:

```go
kv,  _ := kernelcache.GetVersion(kc)
sys, _ := kernelcache.GetSyscallTable(kc)
trap, _ := kernelcache.GetMachTrapTable(kc)
kext, _ := kernelcache.KextList(kc, true)
sig := signature.NewFromKernel(kv, sys, trap, kext)

```

The resulting `Signature` struct contains hashed representations of the version string, syscall table, Mach traps, and kext list. The `sig.Hash()` method produces a deterministic SHA-256 value suitable for storage in vulnerability databases or automated matching systems.

## Practical Code Examples

### Example 1: Extracting Version and LLVM Info

```go
package main

import (
    "fmt"
    "github.com/blacktop/ipsw/pkg/img4"
    "github.com/blacktop/ipsw/pkg/kernelcache"
)

func main() {
    km, _ := img4.Open("MacOS14.0_20A5343a_Release.ipsw")
    kc, _ := kernelcache.Decompress(km)

    kv, _ := kernelcache.GetVersion(kc)
    fmt.Printf("Darwin %s – XNU %s – LLVM %s (clang %s)\n",
        kv.KernelVersion.Darwin,
        kv.KernelVersion.XNU,
        kv.LLVMVersion.Version,
        kv.LLVMVersion.Clang)
}

```

### Example 2: Listing BSD Syscalls with New Detection

```go
kc, _ := kernelcache.Decompress(km)
sys, _ := kernelcache.GetSyscallTable(kc)

for _, s := range sys {
    newTag := ""
    if s.New {
        newTag = " ← NEW"
    }
    fmt.Printf("%3d %-20s %s%s\n", s.Number, s.Name, strings.Join(s.Args, ", "), newTag)
}

```

### Example 3: Enumerating Kexts with VM Addresses

```go
kc, _ := kernelcache.Decompress(km)
kexts, _ := kernelcache.KextList(kc, false)

for _, line := range kexts {
    fmt.Println(line)
}

```

### Example 4: Generating Kernel Signatures

```go
kv, _   := kernelcache.GetVersion(kc)
sys, _  := kernelcache.GetSyscallTable(kc)
traps, _:= kernelcache.GetMachTrapTable(kc)
kexts, _:= kernelcache.KextList(kc, true)

sig := signature.NewSignature(kv, sys, traps, kexts)
fmt.Println(sig.Hash())

```

## Key Source Files Reference

| File | Primary Responsibility | Direct Link |
|------|------------------------|-------------|
| [`pkg/kernelcache/kernelcache.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kernelcache.go) | Open, decompress, version extraction (`GetVersion`) | [kernelcache.go](https://github.com/blacktop/ipsw/blob/master/pkg/kernelcache/kernelcache.go) |
| [`pkg/kernelcache/syscall.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/syscall.go) | Parse BSD syscall table, embed data, JSON master handling (`GetSyscallTable`) | [syscall.go](https://github.com/blacktop/ipsw/blob/master/pkg/kernelcache/syscall.go) |
| [`pkg/kernelcache/mach_trap.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/mach_trap.go) | Parse Mach-trap table, pattern-matching, disassembly (`GetMachTrapTable`) | [mach_trap.go](https://github.com/blacktop/ipsw/blob/master/pkg/kernelcache/mach_trap.go) |
| [`pkg/kernelcache/kext.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kext.go) | Read `__PRELINK_INFO` plist, decode kmod structures, list kexts (`KextList`) | [kext.go](https://github.com/blacktop/ipsw/blob/master/pkg/kernelcache/kext.go) |
| [`pkg/signature/signature.go`](https://github.com/blacktop/ipsw/blob/main/pkg/signature/signature.go) | Consumes the above APIs to create a kernel **Signature** object | [signature.go](https://github.com/blacktop/ipsw/blob/master/pkg/signature/signature.go) |
| [`pkg/img4/img4.go`](https://github.com/blacktop/ipsw/blob/main/pkg/img4/img4.go) | Handles Img4 container parsing used by `kernelcache` | [img4.go](https://github.com/blacktop/ipsw/blob/master/pkg/img4/img4.go) |
| `cmd/ipsw/cmd/kernel/*.go` | User-facing CLI wrappers that invoke the core functions | [kernel command files](https://github.com/blacktop/ipsw/tree/master/cmd/ipsw/cmd/kernel) |

## Summary

- **ipsw** treats kernelcaches as Mach-O images (or filesets) wrapped in Img4 containers, using `img4.Open` and `kernelcache.Decompress` to obtain raw binaries.
- **Version extraction** occurs via `GetVersion` in [`pkg/kernelcache/kernelcache.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kernelcache.go), which scans `__TEXT.__const` with regex patterns to parse Darwin and XNU version strings.
- **Syscall analysis** uses `GetSyscallTable` in [`pkg/kernelcache/syscall.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/syscall.go) to pattern-match the `sysent` array in `__DATA_CONST.__const`, rebase slide-relative pointers, and decorate entries with metadata from embedded `data/syscall.gz`.
- **Mach-trap extraction** via `GetMachTrapTable` in [`pkg/kernelcache/mach_trap.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/mach_trap.go) employs similar pattern matching plus a tiny disassembler to locate the `kern_invalid` sentinel, reading `machTrapT` structures and rebasing with `DyldChainedPtr64KernelCacheRebase`.
- **Kext enumeration** through `KextList` and `GetKextInfos` in [`pkg/kernelcache/kext.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/kext.go) parses the `__PRELINK_INFO.__info` plist and `__kmod_info` table, merging bundle metadata with rebased VM start addresses.
- **Signature generation** in [`pkg/signature/signature.go`](https://github.com/blacktop/ipsw/blob/main/pkg/signature/signature.go) consumes all these APIs to create deterministic SHA-256 fingerprints for automatic kernel identification.

## Frequently Asked Questions

### How does ipsw handle compressed kernelcaches?

The `kernelcache.Open` function (via `img4.Open` in [`pkg/img4/img4.go`](https://github.com/blacktop/ipsw/blob/main/pkg/img4/img4.go)) reads the Img4 wrapper, extracts the compressed payload, and calls `Decompress` or `DecompressData` to handle LZFSE or LZSS compression. The result is a raw `*macho.File` ready for analysis.

### What is the difference between BSD syscalls and Mach traps in ipsw's analysis?

BSD syscalls are extracted via `GetSyscallTable` in [`pkg/kernelcache/syscall.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/syscall.go) by pattern-matching the `sysent` array in `__DATA_CONST.__const`. Mach traps are extracted via `GetMachTrapTable` in [`pkg/kernelcache/mach_trap.go`](https://github.com/blacktop/ipsw/blob/main/pkg/kernelcache/mach_trap.go) using a similar pattern match but requiring a tiny disassembler to locate the `kern_invalid` sentinel address that terminates the trap table.

### How does ipsw rebase pointers in modern kernelcaches?

Modern kernelcaches use chained fixups. The code utilizes `DyldChainedPtr64KernelCacheRebase` (from the underlying `go-macho` library) to resolve slide-relative addresses when reading `sysent` entries, `machTrapT` structures, and `KmodInfoT` records in `__kmod_info` tables.