/analyzing-ios-binaries
Analyze iOS applications at the binary level — decrypting FairPlay-protected IPAs with frida-ios-dump or bagbak, inspecting Mach-O load commands, recovering Objective-C headers with class-dump, and reading Swift metadata. Use when working with an IPA or .app bundle, when a
$ npx -y skills add trilwu/secskills --skill analyzing-ios-binaries --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/analyzing-ios-binaries
Context preview
The summary Claude sees to decide when to auto-load this skill.
Analyze iOS applications at the binary level — decrypting FairPlay-protected IPAs with frida-ios-dump or bagbak, inspecting Mach-O load commands, recovering Objective-C headers with class-dump, and reading Swift metadata. Use when working with an IPA or .app bundle, when a
SKILL.md
analyzing-ios-binaries.SKILL.mdname: analyzing-ios-binaries
description: Analyze iOS applications at the binary level — decrypting FairPlay-protected IPAs with frida-ios-dump or bagbak, inspecting Mach-O load commands, recovering Objective-C headers with class-dump, and reading Swift metadata. Use when working with an IPA or .app bundle, when a downloaded App Store binary shows cryptid=1, when class-dump returns nothing, or when analyzing iOS frameworks and app extensions.
verified: 2026-07-27
Analyzing iOS Binaries
App Store binaries are encrypted at rest and decrypted by the kernel at load time, so every static tool fails on a downloaded IPA until you dump the decrypted image from memory. That single step gates everything else, and it is the reason most iOS analysis stalls before it starts.
When to Use
- You have an IPA or `.app` bundle and need to read the binary
- `class-dump` returns nothing or garbage
- `otool` shows `cryptid 1`
- You need to enumerate Objective-C classes, Swift types, or exported symbols
- Analyzing embedded frameworks, app extensions, or dynamic libraries
When NOT to Use
- **Android targets** — use `testing-mobile-applications` or the relevant
`reversing-*` skill
- **The wider iOS assessment** (storage, keychain, IPC) — use
`testing-mobile-applications`
- **TLS interception** — use `bypassing-mobile-pinning`
- **Jailbreak detection blocking your tooling** — use
`bypassing-root-jailbreak-detection` first
- **Cross-platform frameworks** — Flutter, Unity, and Xamarin have their own
skills; use them for the managed layer and this one for the native shell
Check Encryption First
otool -l TargetApp | grep -A5 LC_ENCRYPTION_INFO
# cryptid 1 → FairPlay-encrypted, decrypt before anything else
# cryptid 0 → already decrypted (dev build, or you already dumped it)
Everything downstream is meaningless on an encrypted binary. `class-dump` returning nothing is almost always this, not obfuscation.
# Decrypt on a jailbroken device by dumping the loaded image
frida-ios-dump -l # list installed apps
frida-ios-dump com.target.app # produces a decrypted IPA
bagbak com.target.app # alternative, handles extensions/frameworks well
# Rootless jailbreaks and TrollStore installs work with the same tools
# Older devices: Clutch, flexdecrypt
Dump **frameworks and extensions too**, not just the main binary. `PlugIns/` (share sheets, widgets, keyboards) and `Frameworks/` carry their own encrypted Mach-Os, and app extensions frequently hold the interesting entitlements and a weaker security posture than the main app.
Mach-O Structure and Protections
file TargetApp # thin or fat/universal
lipo -info TargetApp # architectures present
lipo -thin arm64 TargetApp -o app64 # extract one before analysis
otool -hv app64 # header flags: PIE
otool -l app64 | rg 'LC_LOAD_DYLIB|LC_RPATH|LC_CODE_SIGNATURE|LC_ENCRYPTION'
otool -Iv app64 | head # indirect symbols
nm -m app64 | rg -v ' U ' | head # defined symbols
# Protections at a glance
otool -hv app64 | rg PIE # ASLR
otool -Iv app64 | rg stack_chk # stack canaries
otool -Iv app64 | rg objc_release # ARC in use
Extract the entitlements — they define what the app is allowed to do and often reveal the interesting attack surface:
codesign -d --entitlements :- TargetApp.app
# Look for: keychain-access-groups, App Groups, associated-domains,
# get-task-allow (debuggable!), custom URL scheme claims
`get-task-allow` set to true on a production build means the app is debuggable and is a finding on its own.
Objective-C
Objective-C keeps its full class metadata in the binary, so recovery is excellent — method names, selectors, class hierarchy, ivars.
class-dump -H app64 -o ./headers # classic
class-dump-dyld # for dyld shared cache resident classes
dsdump --objc --color app64 # modern alternative, handles Swift too
# Then read the type map
rg -l 'Manager|Service|API|Auth|Crypto|Keychain|Payment' ./headers | head -20
In a disassembler, Objective-C calls go through `objc_msgSend`, so the callee is a selector string in a register rather than a direct branch. Both IDA and Ghidra have plugins that resolve this; without them the call graph is largely useless.
Swift
Swift is harder. Names are mangled, and method dispatch is often static or through witness tables rather than `objc_msgSend`.
# Demangle what you find
nm app64 | swift demangle
xcrun swift-demangle '$s10TargetApp11AuthManagerC5loginyyF'
# Swift type metadata lives in dedicated sections
otool -l app64 | rg '__swift5_types|__swift5_proto|__swift5_reflstr'
dsdump --swift app64
Practical approach for Swift-heavy apps: `@objc` and `@objcMembers` members still appear in the Objective-C metadata, so class-dump gives you a partial map. For the rest, work from string references and the reflection sections rather than trying to recover a full class list. Runtime enumeration with Frida is usually faster than static recovery:
frida -U -f com.target.app -l enumerate-swift-classes.js
Runtime Analysis
# Enumerate everything the runtime knows
objection -g com.target.app explore
# ios hooking list classes
# ios hooking search methods <keyword>
# ios hooking watch method "-[AuthManager login:]" --dump-args --dump-return
# Direct Frida for anything objection does not cover
frida -U -f com.target.app -l hooks.js
Runtime beats static on iOS more often than on other platforms: the Objective-C runtime is introspectable, so listing classes and watching methods gets you to the logic faster than reading a disassembly of `objc_msgSend` dispatch.
What to Look For
- **Entitlements over-provisioned** — App Groups shared with less-trusted
extensions, keychain groups shared too widely, `get-task-allow` in production
- **H
Read more
name: analyzing-ios-binaries description: Analyze iOS applications at the binary level — decrypting FairPlay-protected IPAs with frida-ios-dump or bagbak, inspecting Mach-O load commands, recovering Objective-C headers with class-dump, and reading Swift metadata. Use when working with an IPA or .app bundle, when a downloaded App Store binary shows cryptid=1, when class-dump returns nothing, or when analyzing iOS frameworks and app extensions. verified: 2026-07-27
Analyzing iOS Binaries
App Store binaries are encrypted at rest and decrypted by the kernel at load time, so every static tool fails on a downloaded IPA until you dump the decrypted image from memory. That single step gates everything else, and it is the reason most iOS analysis stalls before it starts.
When to Use
- You have an IPA or `.app` bundle and need to read the binary
- `class-dump` returns nothing or garbage
- `otool` shows `cryptid 1`
- You need to enumerate Objective-C classes, Swift types, or exported symbols
- Analyzing embedded frameworks, app extensions, or dynamic libraries
When NOT to Use
- **Android targets** — use `testing-mobile-applications` or the relevant
`reversing-*` skill
- **The wider iOS assessment** (storage, keychain, IPC) — use
`testing-mobile-applications`
- **TLS interception** — use `bypassing-mobile-pinning`
- **Jailbreak detection blocking your tooling** — use
`bypassing-root-jailbreak-detection` first
- **Cross-platform frameworks** — Flutter, Unity, and Xamarin have their own
skills; use them for the managed layer and this one for the native shell
Check Encryption First
otool -l TargetApp | grep -A5 LC_ENCRYPTION_INFO # cryptid 1 → FairPlay-encrypted, decrypt before anything else # cryptid 0 → already decrypted (dev build, or you already dumped it)
Everything downstream is meaningless on an encrypted binary. `class-dump` returning nothing is almost always this, not obfuscation.
# Decrypt on a jailbroken device by dumping the loaded image frida-ios-dump -l # list installed apps frida-ios-dump com.target.app # produces a decrypted IPA bagbak com.target.app # alternative, handles extensions/frameworks well # Rootless jailbreaks and TrollStore installs work with the same tools # Older devices: Clutch, flexdecrypt
Dump **frameworks and extensions too**, not just the main binary. `PlugIns/` (share sheets, widgets, keyboards) and `Frameworks/` carry their own encrypted Mach-Os, and app extensions frequently hold the interesting entitlements and a weaker security posture than the main app.
Mach-O Structure and Protections
file TargetApp # thin or fat/universal lipo -info TargetApp # architectures present lipo -thin arm64 TargetApp -o app64 # extract one before analysis otool -hv app64 # header flags: PIE otool -l app64 | rg 'LC_LOAD_DYLIB|LC_RPATH|LC_CODE_SIGNATURE|LC_ENCRYPTION' otool -Iv app64 | head # indirect symbols nm -m app64 | rg -v ' U ' | head # defined symbols # Protections at a glance otool -hv app64 | rg PIE # ASLR otool -Iv app64 | rg stack_chk # stack canaries otool -Iv app64 | rg objc_release # ARC in use
Extract the entitlements — they define what the app is allowed to do and often reveal the interesting attack surface:
codesign -d --entitlements :- TargetApp.app # Look for: keychain-access-groups, App Groups, associated-domains, # get-task-allow (debuggable!), custom URL scheme claims
`get-task-allow` set to true on a production build means the app is debuggable and is a finding on its own.
Objective-C
Objective-C keeps its full class metadata in the binary, so recovery is excellent — method names, selectors, class hierarchy, ivars.
class-dump -H app64 -o ./headers # classic class-dump-dyld # for dyld shared cache resident classes dsdump --objc --color app64 # modern alternative, handles Swift too # Then read the type map rg -l 'Manager|Service|API|Auth|Crypto|Keychain|Payment' ./headers | head -20
In a disassembler, Objective-C calls go through `objc_msgSend`, so the callee is a selector string in a register rather than a direct branch. Both IDA and Ghidra have plugins that resolve this; without them the call graph is largely useless.
Swift
Swift is harder. Names are mangled, and method dispatch is often static or through witness tables rather than `objc_msgSend`.
# Demangle what you find nm app64 | swift demangle xcrun swift-demangle '$s10TargetApp11AuthManagerC5loginyyF' # Swift type metadata lives in dedicated sections otool -l app64 | rg '__swift5_types|__swift5_proto|__swift5_reflstr' dsdump --swift app64
Practical approach for Swift-heavy apps: `@objc` and `@objcMembers` members still appear in the Objective-C metadata, so class-dump gives you a partial map. For the rest, work from string references and the reflection sections rather than trying to recover a full class list. Runtime enumeration with Frida is usually faster than static recovery:
frida -U -f com.target.app -l enumerate-swift-classes.js
Runtime Analysis
# Enumerate everything the runtime knows objection -g com.target.app explore # ios hooking list classes # ios hooking search methods <keyword> # ios hooking watch method "-[AuthManager login:]" --dump-args --dump-return # Direct Frida for anything objection does not cover frida -U -f com.target.app -l hooks.js
Runtime beats static on iOS more often than on other platforms: the Objective-C runtime is introspectable, so listing classes and watching methods gets you to the logic faster than reading a disassembly of `objc_msgSend` dispatch.
What to Look For
- **Entitlements over-provisioned** — App Groups shared with less-trusted
extensions, keychain groups shared too widely, `get-task-allow` in production
- **H
**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

