# How ipsw Disassembles and Analyzes Mach-O Binaries: Patching and Entitlements Explained

> Learn how ipsw disassembles and analyzes Mach-O binaries. Discover its capabilities in patching, entitlement management for dyld caches, and binary re-signing. Explore the go-macho parser and ARM64 decoder.

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

---

**ipsw combines the `go-macho` parser, a low-level ARM64 decoder, and the `pkg/disass` orchestration layer to parse Mach-O headers, decode instructions, resolve symbols, extract Swift strings, handle dyld cache patches, and re-sign binaries with custom entitlements.**

The `blacktop/ipsw` repository provides a self-contained Go toolkit for deep Mach-O binary analysis. Unlike external wrapper tools, ipsw implements its own disassembly stack that handles everything from immediate operand extraction to dyld shared cache patch enumeration and entitlement management.

## The Core Architecture of ipsw's Mach-O Analysis

ipsw's analysis engine rests on three integrated libraries that handle distinct layers of the Mach-O format.

### The Three-Pillar Stack

- **`go-macho`** – Parses Mach-O headers, load commands, symbols, and sections. Provides critical helpers like `GetFunctionForVMAddr` and `GetPointerAtAddress` for virtual memory translation.
- **`arm64-cgo/disassemble`** – Low-level ARM64 decoder that transforms 32-bit instruction words into fully-typed `Instruction` structs containing operands and immediate values.
- **`ipsw disass` package** – Orchestrates the parser and decoder, walking the binary to collect immediates, resolve symbols, demangle Swift/Objective-C names, discover patches, and manage code signing workflows.

## How ipsw Disassembles Mach-O Binaries Step-by-Step

The disassembly process follows a pipeline defined in [`pkg/disass/macho.go`](https://github.com/blacktop/ipsw/blob/main/pkg/disass/macho.go), centered around the `MachoDisass` type.

### Loading and Initialization

Disassembly begins by creating a `MachoDisass` object that wraps a `*macho.File` from `go-macho` alongside a `disass.Config` struct. The configuration specifies start addresses, architecture slices, demangling preferences, and color output options.

```go
cfg := &disass.Config{
    StartAddress: m.Entry,
    Demangle:     true,
    Color:        true,
    Data:         m.Data,
}
eng := disass.NewMachoDisass(m, cfg)

```

### Instruction Triaging and Immediate Extraction

The `MachoDisass.Triage()` method drives the analysis. It reads raw data via `d.Data()`, feeds each 4-byte word to `disassemble.Decompose`, and records every address used as an immediate operand. This includes branch targets, literal references, and ADRP/ADD or ADRP/LDR instruction pairs.

The triage phase produces a map `Addresses[address] → imm` that serves as the foundation for all subsequent analysis.

### Symbol Resolution and Context Enrichment

After triage completes, ipsw resolves raw addresses to human-readable symbols. The `FindSymbol`, `IsFunctionStart`, and `IsBranchLocation` methods query the Mach-O symbol table and dyld cache information. When quiet mode is disabled, the engine populates `Locations[imm]` for addresses inside known functions and builds `AddrDetails` structs for section-specific metadata including segment names, section indices, and flags.

The `symbols` package caches these lookups to accelerate repeated queries during large binary analysis.

### Swift and Objective-C Metadata Extraction

The `FindSwiftStrings` method scans the instruction stream for Swift string object patterns (`swift.StringObject` layout) and Objective-C class/method references. Using the `swift` helper from `go-macho/pkg/swift`, it decodes string structures and maps runtime metadata sections back to their originating high-level constructs.

## Handling Patches in Dyld Shared Caches

ipsw provides deep visibility into Apple's dyld shared cache patch mechanisms, essential for analyzing system binaries or creating delta patches.

### Patch Info Structures

When analyzing a dyld shared cache, ipsw reads patch info structures defined in [`pkg/dyld/types.go`](https://github.com/blacktop/ipsw/blob/main/pkg/dyld/types.go). These include `CachePatchInfoV*`, `CachePatchableExportV*`, and related variants that describe export patches and location patches within cache images.

### Accessing Patch Data via the dyld Package

The `dyld` package exposes patch information through helper methods on cache images. When the `MachoDisass` engine targets a cache image, it invokes `dyld.File.GetPatchInfo` to enumerate image patches, export patches, and location patches. These are surfaced via the `Patch` and `PatchableExport` interfaces.

```go
if img.HasPatches() {
    patches := img.GetPatches()
    // Process patch entries
}

```

## Entitlement Handling and Code Re-Signing

Binary modification invalidates code signatures, so ipsw provides utilities to extract existing entitlements and apply new ones during the analysis workflow.

### Extracting Existing Entitlements

The [`internal/commands/ent/ent.go`](https://github.com/blacktop/ipsw/blob/main/internal/commands/ent/ent.go) package implements high-level commands that parse a binary's code signature and print embedded entitlements. This allows analysts to inspect sandboxing rules and capability declarations before patching.

### Re-signing with Custom Entitlements

For binaries requiring modification, [`internal/utils/macos.go`](https://github.com/blacktop/ipsw/blob/main/internal/utils/macos.go) provides `CodeSignWithEntitlements`. This function uses macOS's native `codesign` tool to apply new entitlements from a plist file, with fallback handling for XML format conversions. The CLI exposes this via the `--entitlements` flag in [`cmd/ipsw/cmd/macho/macho_disass.go`](https://github.com/blacktop/ipsw/blob/main/cmd/ipsw/cmd/macho/macho_disass.go), ensuring patched binaries remain valid for system execution.

```bash
ipsw codesign MyApp --entitlements ./MyApp.entitlements.xml

```

## Practical Implementation Examples

### Disassembling a Mach-O File via Go API

This example demonstrates loading a binary, running the triage phase, extracting Swift strings, and generating assembly output:

```go
package main

import (
	"log"

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

func main() {
	m, err := macho.Open("MyApp")
	if err != nil {
		log.Fatalf("open macho: %v", err)
	}
	defer m.Close()

	cfg := &disass.Config{
		StartAddress: m.Entry,
		Demangle:     true,
		Color:        true,
		Data:         m.Data,
	}

	eng := disass.NewMachoDisass(m, cfg)
	if err := eng.Triage(); err != nil {
		log.Fatalf("triage: %v", err)
	}

	if ss, err := eng.FindSwiftStrings(); err == nil {
		for addr, s := range ss {
			log.Printf("Swift string @0x%x: %s", addr, s)
		}
	}

	log.Println(disass.Disassemble(eng))
}

```

### CLI Disassembly with Symbol Resolution and Patching

Analyze a specific function, apply a binary patch, and re-sign with entitlements:

```bash

# Disassemble the main function from the arm64 slice

ipsw macho disass MyApp \
    --arch arm64 \
    --symbol main \
    --demangle \
    --color

# Apply a binary patch at a specific offset

ipsw patch apply MyApp \
    --offset 0x1234 \
    --bytes DE AD BE EF

# Re-sign with custom entitlements

ipsw codesign MyApp --entitlements ./MyApp.entitlements.xml

```

### Extracting Dyld Cache Patches Programmatically

Enumerate all patched images within a shared cache:

```go
package main

import (
	"log"

	"github.com/blacktop/ipsw/pkg/dyld"
)

func main() {
	cache, err := dyld.Open("/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e")
	if err != nil {
		log.Fatalf("open cache: %v", err)
	}
	defer cache.Close()

	for _, img := range cache.Images {
		if img.HasPatches() {
			patches := img.GetPatches()
			log.Printf("%s has %d patch(es)", img.Name, len(patches))
		}
	}
}

```

## Summary

- **ipsw implements a complete Mach-O analysis stack** in Go using `go-macho`, `arm64-cgo/disassemble`, and the `pkg/disass` orchestration layer.
- **The `MachoDisass.Triage()` method** in [`pkg/disass/macho.go`](https://github.com/blacktop/ipsw/blob/main/pkg/disass/macho.go) drives instruction decoding, immediate extraction, and address mapping.
- **Symbol resolution** leverages the Mach-O symbol table and dyld cache information to map virtual addresses to function names and section metadata.
- **Swift and Objective-C analysis** occurs via `FindSwiftStrings`, which detects string objects and runtime metadata patterns.
- **Patch handling** uses [`pkg/dyld/types.go`](https://github.com/blacktop/ipsw/blob/main/pkg/dyld/types.go) structures and `dyld.File.GetPatchInfo` to expose export and location patches within shared cache images.
- **Entitlement workflows** rely on [`internal/utils/macos.go`](https://github.com/blacktop/ipsw/blob/main/internal/utils/macos.go) to extract existing entitlements and re-sign modified binaries with custom capability lists.

## Frequently Asked Questions

### How does ipsw handle universal binaries with multiple architectures?

ipsw's `go-macho` library automatically parses universal binary headers and exposes individual architecture slices. When using the CLI, the `--arch` flag (e.g., `--arch arm64`) selects the specific slice for disassembly, and the `MachoDisass` constructor receives the appropriate `*macho.File` representing that slice.

### Can ipsw disassemble binaries without symbol tables?

Yes. While symbol tables enhance output via `FindSymbol` and `IsFunctionStart`, the `Triage()` phase operates purely on instruction decoding. The `disassemble.Decompose` function processes raw bytes into `Instruction` structs regardless of debug information, though address-to-name resolution will show raw virtual addresses rather than function names.

### What patch types does ipsw detect in dyld shared caches?

According to [`pkg/dyld/types.go`](https://github.com/blacktop/ipsw/blob/main/pkg/dyld/types.go), ipsw detects **image patches**, **export patches** (`CachePatchableExportV*`), and **location patches** (`CachePatchInfoV*`). These cover pointer rebasing, bind fixes, and rebase/bind optimizations used by the dynamic linker during cache loading.

### How does ipsw preserve binary validity after modification?

ipsw uses `CodeSignWithEntitlements` in [`internal/utils/macos.go`](https://github.com/blacktop/ipsw/blob/main/internal/utils/macos.go) to invoke macOS's native `codesign` utility after any binary modification. This strips the invalid existing signature and applies a new ad-hoc or identity-based signature, optionally embedding a custom entitlements plist to maintain or extend the binary's capability declarations.