Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Reverse engineers iOS applications using Frida dynamic instrumentation to understand internal logic, extract encryption keys, bypass security controls, and discover hidden functionality without source code access. Use when performing authorized iOS penetration testing, analyzing proprietary protocols, understanding obfuscated logic, or extracting runtime secrets from iOS binaries. Activates for requests involving iOS reverse engineering, Frida iOS hooking, Objective-C/Swift method tracing, or iOS binary analysis.
.claude/skills/reverse-engineering-ios-app-with-frida/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | — | — |
| case-09 | ✓→✓ | = Same ✓ | — | — |
| case-05 | ✓→✓ | = Same ✓ | — | — |
| case-23 | ✗→✗ | = Same ✗ | — | — |
| case-11 | ✗→✗ | = Same ✗ | — | — |
Use this skill when:
Do not use this skill for unauthorized reverse engineering that violates terms of service or intellectual property law.
frida-tools (pip install frida-tools)bash# On jailbroken device, find app binary ssh root@<device_ip> find /var/containers/Bundle/Application/ -name "TargetApp" -type f # Pull decrypted binary (apps from App Store are encrypted with FairPlay) # Use frida-ios-dump or Clutch for decryption pip install frida-ios-dump dump.py com.target.app # Extract Objective-C class headers class-dump -H decrypted_binary -o headers/ ls headers/ # Lists all class header files
javascript// enumerate_classes.js - List all loaded classes Java.perform(function() {}); // N/A for iOS // iOS uses ObjC runtime if (ObjC.available) { var classes = ObjC.classes; for (var className in classes) { if (className.indexOf("Target") !== -1 || className.indexOf("Auth") !== -1 || className.indexOf("Crypto") !== -1) { console.log("[Class] " + className); // List methods var methods = classes[className].$ownMethods; for (var i = 0; i < methods.length; i++) { console.log(" [Method] " + methods[i]); } } } }
bashfrida -U -n TargetApp -l enumerate_classes.js
bash# Trace all methods of a class frida-trace -U -n TargetApp -m "*[TargetAuth *]" # Trace specific patterns frida-trace -U -n TargetApp -m "*[*Crypto* *]" frida-trace -U -n TargetApp -m "*[*KeyChain* *]" frida-trace -U -n TargetApp -m "*[*Token* *]" # Trace Swift methods (mangled names) frida-trace -U -n TargetApp -m "*[*$s*Auth*]"
javascript// hook_auth.js - Intercept authentication logic if (ObjC.available) { // Hook Objective-C method var AuthManager = ObjC.classes.AuthManager; if (AuthManager) { Interceptor.attach(AuthManager["- validateToken:"].implementation, { onEnter: function(args) { // args[0] = self, args[1] = selector, args[2+] = method args var token = new ObjC.Object(args[2]); console.log("[Auth] validateToken called with: " + token.toString()); }, onLeave: function(retval) { console.log("[Auth] validateToken returned: " + retval); // Optionally modify return value // retval.replace(ptr(1)); // Force return true } }); } // Hook CommonCrypto for encryption analysis var CCCrypt = Module.findExportByName("libcommonCrypto.dylib", "CCCrypt"); if (CCCrypt) { Interceptor.attach(CCCrypt, { onEnter: function(args) { this.operation = args[0].toInt32(); // 0=encrypt, 1=decrypt this.algorithm = args[1].toInt32(); // 0=AES128, 1=DES, 2=3DES this.keyLength = args[4].toInt32(); this.key = Memory.readByteArray(args[3], this.keyLength); console.log("[CCCrypt] Op:" + (this.operation === 0 ? "Encrypt" : "Decrypt")); console.log("[CCCrypt] Key: " + hexify(this.key)); }, onLeave: function(retval) { console.log("[CCCrypt] Status: " + retval); } }); } } function hexify(buffer) { var bytes = new Uint8Array(buffer); var hex = []; for (var i = 0; i < bytes.length; i++) { hex.push(("0" + bytes[i].toString(16)).slice(-2)); } return hex.join(""); }
javascript// swift_analysis.js - Hook Swift methods // Swift methods use name mangling: $s<module><class><method> // Use frida-trace to discover actual mangled names first if (ObjC.available) { // Swift classes that inherit from NSObject are accessible via ObjC runtime var swiftClasses = Object.keys(ObjC.classes).filter(function(name) { return name.indexOf("_TtC") === 0 || name.indexOf("TargetApp.") !== -1; }); swiftClasses.forEach(function(className) { console.log("[Swift] " + className); var methods = ObjC.classes[className].$ownMethods; methods.forEach(function(method) { console.log(" " + method); }); }); } // For pure Swift (non-ObjC-bridged), use Module.enumerateExports Module.enumerateExports("TargetApp", { onMatch: function(exp) { if (exp.name.indexOf("Auth") !== -1 || exp.name.indexOf("Crypto") !== -1) { console.log("[Export] " + exp.name + " @ " + exp.address); } }, onComplete: function() {} });
javascript// extract_secrets.js if (ObjC.available) { // Hook NSUserDefaults var NSUserDefaults = ObjC.classes.NSUserDefaults; Interceptor.attach(NSUserDefaults["- objectForKey:"].implementation, { onEnter: function(args) { this.key = new ObjC.Object(args[2]).toString(); }, onLeave: function(retval) { if (retval.isNull()) return; var value = new ObjC.Object(retval); console.log("[NSUserDefaults] " + this.key + " = " + value.toString()); } }); // Hook Keychain access var SecItemCopyMatching = Module.findExportByName("Security", "SecItemCopyMatching"); Interceptor.attach(SecItemCopyMatching, { onEnter: function(args) { var query = new ObjC.Object(args[0]); console.log("[Keychain] Query: " + query.toString()); }, onLeave: function(retval) { console.log("[Keychain] Result: " + retval); } }); }
| Term | Definition | |------|-----------| | Objective-C Runtime | Dynamic runtime enabling method dispatch, class introspection, and method swizzling at runtime | | Swift Name Mangling | Compiler-applied encoding of Swift function signatures into linker-compatible symbol names | | FairPlay DRM | Apple's encryption applied to App Store binaries; must be decrypted before static analysis | | class-dump | Tool extracting Objective-C class declarations from Mach-O binaries for header-level analysis | | CommonCrypto | Apple's C-level cryptographic library; primary target for encryption key extraction via Frida hooks |
@objc annotation are not visible through ObjC.classes. Use Module.enumerateExports() instead.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-09 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.