A practitioner's perspective on why free, purpose-built tooling matters for iOS application security.
The Problem Nobody Talks About
If you've spent time doing iOS application security assessments, you've likely noticed an inconvenient truth: the iOS ecosystem is severely underserved by free and open-source static application security testing (SAST) tooling.
On the web application side, analysts have CodeQL, Semgrep, Bandit, and an ecosystem of open-source scanners. For Android, MobSF provides reasonably good static coverage out of the box and Mariana Trench performs SAST with source and sink traces. But for iOS, where applications handle some of the most sensitive data in the world, the free tooling landscape is remarkably thin.
Commercial tools exist, but they're expensive, often black-box in their methodology, and rarely tuned for the nuances of iOS security analysis. What's left for the independent researcher or the consultant who needs to go deeper than surface-level scanning? Usually Ghidra or Binary Ninja, a Frida session, and a lot of manual work.
Trellis was built to address this gap.
What Trellis Is (and Isn't)
Trellis is not a "push button, get report" scanner. Its own documentation is explicit about this:
"Trellis is not designed to provide findings ready to be copied and pasted into an iOS app pentest report. It's designed to perform triage and provide the iOS security analyst with a map of what to investigate and where to find it."
This framing is important. Trellis is an analyst multiplier. It takes the tedious, repetitive work of manually combing through disassembly for security-relevant function usage and automates it, leaving the analyst free to focus on understanding, verifying, and exploiting what they find.
It runs as a Ghidra script (or headlessly via PyGhidra), performs static analysis of iOS Mach-O binaries, and produces two artifacts:
- Markdown security reports - structured findings organized by severity and category, each with a memory address, calling function, evidence, and recommendation.
- Frida instrumentation scripts - ready-to-deploy hooks targeting the exact call sites flagged by static analysis, for runtime verification.
The combination of these two outputs is what makes Trellis genuinely useful. Static analysis surfaces what to look at. Frida confirms whether it's actually exploitable at runtime.
The SAST Gap in iOS Analysis
To understand why Trellis fills a real need, it helps to be specific about what "free iOS SAST tooling" does and doesn't give you.
String greps and otool commands are fast but shallow. You can grep for CCCrypt to see if an app uses CommonCrypto, but you won't know whether it's using DES in ECB mode with a hardcoded key, or AES-256-GCM with keys derived properly from the Keychain.
MobSF is excellent for automated scoring and basic coverage, but its iOS static analysis is surface-level for native code. It handles plist parsing, basic permission analysis, and Info.plist security settings well, but doesn't perform deep binary analysis of crypto parameter usage, WebView security configurations, or runtime manipulation vectors in compiled Swift/Objective-C code.
Ghidra and Binary Ninja are powerful reverse engineering platforms, but they require the analyst to know what to look for, where to look, and how to interpret what they see. This is valuable expertise, but it doesn't scale well for the typical timeboxed iOS app assessment faced by security consultants and engineers.
Trellis sits between these extremes. It uses Ghidra's analysis engine to go deep, leveraging the decompiler, cross-reference database, function analysis, and string tables. It encodes security expertise into the checks themselves, so you get actionable, address-specific findings without needing to know in advance exactly what you're hunting for.
What Trellis Actually Checks
The coverage is broad and deliberately prioritized around what matters in real iOS assessments. A few highlights:
Cryptographic Analysis
Trellis finds calls to CCCrypt, CCHmac, CCKeyDerivationPBKDF, and Swift CryptoKit operations, then checks for:
- Weak algorithms: DES, 3DES, RC4, MD5, SHA-1 — flagged as CRITICAL or HIGH
- ECB mode: Block cipher mode that leaks structure about plaintext
- Hardcoded keys and IVs: Keys loaded from the binary's constant data sections
- Dangerously low PBKDF2 iteration counts: Values below 10,000 are flagged as CRITICAL; below 100,000 as HIGH
Each finding includes the memory address of the call site and the name of the calling function — ready to load directly in Ghidra or use as a Frida hook target.
Keychain Security
Findings here cover insecure accessibility levels (kSecAttrAccessibleAlways), Keychain items synchronized to iCloud when they shouldn't be, and missing biometric protection for sensitive items.
WebView Security
Exposed WebViews are an iOS security concern. Trellis detects JavaScript bridge exposure via WKScriptMessageHandler, loadHTMLString:baseURL: calls with a nil base URL (a common XSS vector), UIWebView usage (deprecated and insecure by design), and deep-link handlers that may funnel untrusted URL data into WebView loading.
Jailbreak and Anti-Debug Detection
Rather than just noting whether jailbreak detection exists, Trellis maps out the specific techniques in use: file path checks for Cydia, Substrate, and common jailbreak tool locations; fork() sandbox escape detection; dynamic library enumeration; ptrace(PT_DENY_ATTACH) and sysctl-based debugger checks. This is exactly the information needed to write targeted Frida bypasses.
Biometric Authentication
A particularly useful check: Trellis differentiates between client-side LAContext.evaluatePolicy calls (a biometric check that can be trivially bypassed with Frida) and SecAccessControl-bound biometric checks that are properly tied to Keychain item access. The former is flagged HIGH — because it often is an exploitable auth bypass.
Runtime Manipulation Vectors
The runtime category finds hardcoded credential comparisons using isEqualToString: in authentication contexts, which are trivially bypassable by hooking the comparison and forcing a true return. These are flagged CRITICAL.
The Obfuscated Secrets Problem
One of the areas where Trellis has delivered real-world findings is in detecting hardcoded secrets that developers have attempted to conceal using encoding or encryption. This is more common than you might expect.
Developers sometimes recognize that a plaintext password in a string table will be caught by basic scanning, so they encode it: Base64, hex encoding, or XOR with a static key. The secret is still hardcoded, it's just one decode operation away from being useful to an attacker.
Trellis addresses this through several complementary mechanisms:
Obfuscation detection tracks calls to dataFromHexString:, base64EncodedStringWithOptions:, and similar decode/encode functions, flagging them for investigation. When hex decoding occurs inside a function named installationIDAsBytes, that's worth a closer look. There may be a static identifier or key embedded in the binary as a hex string.
Interprocedural call-chain analysis takes this further. When the string-table scanner finds a high-entropy string that looks like a password or key, Trellis uses Ghidra's cross-reference database to trace which functions reference that string, then performs a BFS traversal of the call chain (up to 3 hops) to determine whether the secret eventually flows into a crypto function, Keychain operation, or network call.
For Objective-C code, where method dispatch goes through objc_msgSend (making direct call-graph traversal impossible), Trellis uses a forward selector search: if a function references both a known secret string and a crypto-related ObjC selector string (like encryptData:withSettings:password:error:), the secret is confirmed to flow to a crypto sink. This has caught real hardcoded encryption keys in production apps during authorized security assessments... keys that would have been invisible to any tool that only checked direct import usage.
A representative finding from an DVIA-v2 analysis run demonstrates the capability:
Hardcoded Secret Flows to Crypto Sink — Function
textFieldShouldReturnreferences both hardcoded secret@daloq3as$qweasdlasasjdnjand crypto selectordecryptData:withPassword:error:. Detection method: forward_selector_search.
The address (0x1001978e8) and secret address (0x100389440) are both provided and immediately navigable in Ghidra.
The Bridge: From Static Findings to Frida Hooks
This is where Trellis becomes more than a report generator.
After running the static analysis, the findings are serialized to JSON alongside the Markdown reports. The Frida script generator reads these JSON findings and produces address-specific hooks, not generic API hooks that intercept every call to CCCrypt in the process, but targeted hooks that only fire at the exact call sites flagged by the static analysis.
The output from a targeted Frida hook looks like this:
🟡 [FINDING #3 TRIGGERED] 🟡
Severity: MEDIUM
Issue: Missing Keychain Accessibility Attribute
Function: SecItemAdd
Ghidra: 0x1001141bc
Description: Keychain item stored without explicit accessibility level
[✓] FINDING CONFIRMED: kSecAttrAccessible NOT set at runtime
Runtime arguments:
[0] = 0x102345678 (query dict)
Backtrace:
0x102345678 (Ghidra: 0x1001141bc) storeString:forKey:Every alert includes the Ghidra address. When the hook fires during dynamic analysis, you can immediately jump to the corresponding location in Ghidra to understand the surrounding context. The loop from static finding → runtime confirmation → deeper static investigation closes in seconds rather than minutes.
This bidirectional workflow between static and dynamic analysis is the core value proposition. Neither tool is as effective alone.
ARM64 Backward Slicing: Getting Parameters When the Decompiler Can't
One subtle but important challenge in iOS binary analysis is that Swift's interoperability with C libraries (like CommonCrypto) often defeats Ghidra's decompiler. The Swift-to-C bridge introduces thunks that the decompiler cannot trace through, resulting in parameter values being reported as "unknown."
Trellis solves this with an ARM64 backward-slice parameter extractor. When the decompiler fails to resolve parameters at a call site, Trellis falls back to scanning raw ARM64 instructions backward from the call, looking for MOV/MOVZ immediate loads (which produce constant parameter values) and ADRP+ADD address construction pairs (which produce pointers to strings or data in the binary). This technique often recovers algorithm constants, key sizes, and iteration counts that the decompiler reports as variables.
Without this fallback, a CCCrypt call in a Swift app might produce: "algorithm: unknown, mode: unknown." With it: "algorithm: DES (0x1) [CRITICAL], mode: ECB (0x2) [HIGH]."
The difference is whether the finding is actionable or noise.
Practical Workflow
For an iOS app security assessment, the typical workflow looks like this:
- Decrypt the IPA - Production iOS apps are encrypted. Trellis requires a decrypted binary. (frida-decrypt handles this step.)
- Run Trellis - Either interactively via Ghidra's Script Manager or headlessly via
trellis_headless.py. Output goes to a directory of your choice. - Triage findings by severity — CRITICAL and HIGH findings are the starting point. Addresses and caller names in each finding guide where to look in Ghidra.
- Generate Frida scripts — The findings-driven mode generates targeted hooks for the call sites that matter.
- Deploy and test — Run the generated scripts against the live app on device to confirm which static findings are reachable and exploitable at runtime.
- Iterate — Frida output points back to Ghidra addresses; Ghidra decompilation surfaces more context for the next Frida investigation.
For teams using CI/CD pipelines, the headless mode (trellis_headless.py) makes this straightforward to automate. Ghidra analysis, Markdown reports, and Frida scripts from a single command, no GUI required.
Conclusion
The iOS security tooling ecosystem has a gap, and it's not going to close on its own. Trellis doesn't pretend to be a fully automated vulnerability scanner. It's a triage and investigation tool designed for analysts who know what they're doing and need to work faster and more systematically.
What makes it practically useful is the combination of things it does well: broad coverage across the iOS security domains that matter in real assessments, address-specific findings that map directly to Ghidra and Frida, intelligent parameter extraction that works around the limitations of Ghidra's decompiler on Swift code, and a forward selector search that has surfaced hardcoded secrets flowing into crypto functions in real-world applications to uncover secrets that simpler tools would have missed entirely.
If you're doing iOS security work and haven't integrated static analysis into your workflow beyond dumping symbols and grepping strings, Trellis is worth a look.
Trellis is licensed under the Business Source License 1.1. Commercial use in security assessments is permitted. Source: https://github.com/cylentsec/Trellis