Reverse Engineering

forge analysis: class-dump, strings, symbols, security posture, IL2CPP references, and version diffing for decrypted IPAs.

General-purpose, read-only static analysis of any .ipa — not tied to a specific patch set or app. Built on the same Mach-O/ObjC analysis engine (ipa_forge/machO/objc.py) that forge hooks uses to verify hook targets; see Architecture for how the two relate. Every command accepts --app-dir <Payload/App.app> in place of --ipa to skip re-extraction while iterating, same as forge hooks.

Scope boundary

Two things are deliberately not implemented, by design rather than oversight:

  • FairPlay/App Store DRM decryption. Every command here assumes an already-decrypted .ipa, exactly like the rest of ipa-forge.
  • General instruction-level disassembly/decompilation. The IL2CPP command recognizes a few ARM64 instruction forms to find references; it does not reconstruct source or replace a disassembler.

forge analysis il2cpp

For Unity IL2CPP apps, builds a read-only index from UnityFramework (or the main executable) and global-metadata.dat. Find compiled method names, methods loading a string literal, or direct callers of a method:

forge analysis il2cpp --ipa Game.ipa --methods 'Enchant'
forge analysis il2cpp --app-dir Payload/Game.app --literal 'network|raid' --limit 30
forge analysis il2cpp --app-dir Payload/Game.app --callers 'ShopScene\\.Open' --json

Choose exactly one of --methods, --literal, or --callers. Method results include their metadata references; --json emits the same data as structured records. --limit defaults to 100. The content-addressed index is cached in the ipa-forge cache; FORGE_NO_CACHE=1 rebuilds it.

This parser currently supports metadata v31 (Unity 2022.3), arm64. Other versions report a clear error; use Cpp2IL for them. Names reflect the app's metadata, so obfuscated games still show obfuscated identifiers. Reference tracking is approximate and can miss loads; inspect a candidate function in a disassembler before changing bytes. Direct callers cover b and bl branches to known function starts, not indirect calls.

forge analysis classdump

Dumps the app's Objective-C runtime metadata as .h-style class-dump text: every class (superclass, protocol conformance, ivars, properties, full method signatures reconstructed from type encodings), protocol declarations, and categories.

Prop

Type

forge analysis classdump --ipa App.ipa
forge analysis classdump --ipa App.ipa --class MainPlayerViewController
forge analysis classdump --ipa App.ipa --search '^App' --output dump.h
forge analysis classdump --ipa App.ipa --class AppConfig --methods-matching '^(try|can|should)'
forge analysis classdump --ipa App.ipa --search '^App' --names-only --json

--class/--search restrict output to matching classes only (protocols and categories are omitted in that case). A class/superclass defined in another image (system frameworks, or a framework you didn't point the tool at) is reported as «external» — the same honest-reporting convention forge hooks uses, rather than guessing.

--json contains classes and protocols objects keyed by name plus a categories array, including raw method type encodings, ivars, properties, and protocol conformance. Filters apply in either format and leave cached analyses unchanged. Empty selections exit 1 (JSON still emits an empty result). forge hooks extract has been removed; use this command.

The type-encoding decoder (ipa_forge/analysis/type_encoding.py) is best-effort: structs/unions render as just their tag name (no field expansion), and truly exotic encodings fall back to the raw string. Good enough to read; not good enough to regenerate a compilable header.

forge analysis strings

Printable-ASCII string extraction across every executable in the bundle (main + frameworks + dylibs + extensions), tagged with which binary each string came from.

Prop

Type

forge analysis strings --ipa App.ipa --min-len 6
forge analysis strings --ipa App.ipa --search 'https?://' --binary MainExecutable

Pipe to grep for anything --search doesn't cover directly — no output cap (unlike the GUI's /analysis page, which caps at 2000 matches for browser sanity).

forge analysis symbols

Linked libraries and imported/exported symbols for one executable in the bundle (otool -L + nm, backed by LIEF's structured symbol table instead of parsing text).

Prop

Type

forge analysis symbols --ipa App.ipa
forge analysis symbols --ipa App.ipa --binary SomeFramework

forge analysis security

Read-only build/security posture for one executable: PIE, an encryption-flag check (LC_ENCRYPTION_INFO's cryptid — detection only, never decryption), stack protector, an ARC heuristic (_objc_storeStrong/_objc_release imported — not definitive), min-OS, and platform.

Prop

Type

forge analysis security --ipa App.ipa

forge analysis diff

Survey of what changed between two builds of the same app: classes and protocols added/removed, per-class method churn, and Info.plist key changes. Purely informational — exit code is always 0 regardless of findings.

Prop

Type

forge analysis diff --old App_1.0.ipa --new App_2.0.ipa

forge analysis diff vs. forge hooks diff

This is broader but shallower than forge hooks diff (see Usage): forge hooks diff only re-checks one patch definition's declared hook targets and is a pass/fail gate (exit 1 on a required-hook regression); forge analysis diff surveys everything that changed, independent of any patch set, and never fails the exit code. Use forge hooks diff to gate a patch set's CI; use forge analysis diff to understand what changed before writing the patch set in the first place.

Entitlements are intentionally not diffed here: reading them requires shelling out to codesign/security, and signing/backend.py is the only module allowed to do that (Architecture's hard constraint) — a real entitlements diff belongs in that subsystem, not in this read-only, signing-independent package.

The web GUI (forge gui → /analysis)

The same four views (class-dump, strings, security, diff), browsable without installing the CLI: forge gui → open the link in the main patcher page's subtitle, or navigate directly to http://127.0.0.1:8765/analysis. Unlike the main patcher page, /analysis needs no patch-set setup and works on any IPA — upload it and pick an action. Strings output is capped at 2000 matches in the browser (the CLI has no such cap). Single-user, local-only, same as the patch-flow page — see Usage.

On this page