Frida iOS Code Snippets
Common Frida scripts for iOS penetration testing.
Basic Setup
Check if Objective-C is available
if (ObjC.available) {
console.log("Objective-C runtime available");
} else {
console.log("Objective-C runtime NOT available");
}List all classes
for (var className in ObjC.classes) {
console.log(className);
}Search classes by name
for (var className in ObjC.classes) {
if (className.toLowerCase().includes("auth")) {
console.log(className);
}
}List methods of a class
var methods = ObjC.classes.ClassName.$ownMethods;
methods.forEach(function(method) {
console.log(method);
});Method Hooking
Hook instance method
var ClassName = ObjC.classes.ClassName;
Interceptor.attach(ClassName['- methodName:'].implementation, {
onEnter: function(args) {
console.log("Method called");
// args[0] = self, args[1] = _cmd, args[2+] = actual arguments
var arg1 = new ObjC.Object(args[2]);
console.log("Arg1: " + arg1);
},
onLeave: function(retval) {
console.log("Return: " + retval);
}
});Hook class method
var ClassName = ObjC.classes.ClassName;
Interceptor.attach(ClassName['+ classMethod'].implementation, {
onEnter: function(args) {
console.log("Class method called");
}
});Modify return value
Interceptor.attach(ClassName['- method'].implementation, {
onLeave: function(retval) {
retval.replace(1); // Replace with YES/true
}
});Replace method implementation
ClassName['- method'].implementation = function() {
console.log("Method replaced");
return 1; // Return YES
};Working with ObjC Objects
Create NSString
var str = ObjC.classes.NSString.stringWithString_("Hello");Create NSDictionary
var keys = ObjC.classes.NSArray.arrayWithObjects_("key1", "key2", null);
var values = ObjC.classes.NSArray.arrayWithObjects_("value1", "value2", null);
var dict = ObjC.classes.NSDictionary.dictionaryWithObjects_forKeys_(values, keys);Read NSData as string
var nsdata = new ObjC.Object(ptr);
var str = nsdata.bytes().readUtf8String(nsdata.length());Convert ObjC object to string
var obj = new ObjC.Object(args[2]);
console.log(obj.toString());Keychain Operations
Hook all keychain operations
["SecItemAdd", "SecItemCopyMatching", "SecItemUpdate", "SecItemDelete"].forEach(function(func) {
var f = Module.findExportByName("Security", func);
Interceptor.attach(f, {
onEnter: function(args) {
console.log("[" + func + "]");
console.log("Query: " + new ObjC.Object(args[0]));
},
onLeave: function(retval) {
console.log("Result: " + retval);
}
});
});Network Hooks
Hook NSURLSession requests
var NSURLSession = ObjC.classes.NSURLSession;
Interceptor.attach(NSURLSession['- dataTaskWithRequest:completionHandler:'].implementation, {
onEnter: function(args) {
var request = new ObjC.Object(args[2]);
console.log("URL: " + request.URL());
console.log("Method: " + request.HTTPMethod());
console.log("Headers: " + request.allHTTPHeaderFields());
}
});Hook URLSession delegate
var resolver = new ApiResolver('objc');
resolver.enumerateMatches('-[* URLSession:dataTask:didReceiveData:]').forEach(function(match) {
Interceptor.attach(match.address, {
onEnter: function(args) {
var data = new ObjC.Object(args[4]);
console.log("Received data length: " + data.length());
}
});
});File System
Hook file operations
var fopen = Module.findExportByName(null, "fopen");
Interceptor.attach(fopen, {
onEnter: function(args) {
console.log("fopen: " + args[0].readUtf8String());
}
});Hook NSFileManager
var NSFileManager = ObjC.classes.NSFileManager;
Interceptor.attach(NSFileManager['- contentsAtPath:'].implementation, {
onEnter: function(args) {
var path = new ObjC.Object(args[2]);
console.log("Reading file: " + path);
}
});Biometric / LAContext
Hook biometric authentication
var LAContext = ObjC.classes.LAContext;
Interceptor.attach(LAContext['- evaluatePolicy:localizedReason:reply:'].implementation, {
onEnter: function(args) {
console.log("Biometric auth requested");
var reply = new ObjC.Block(args[4]);
reply.implementation(true, null); // Simulate success
}
});Memory Search
Search for string in memory
Process.enumerateRanges('r--').forEach(function(range) {
Memory.scanSync(range.base, range.size, "pattern").forEach(function(match) {
console.log("Found at: " + match.address);
});
});Search for ASCII string
var pattern = "password";
var results = Memory.scanSync(ptr(0), Process.pageSize * 1000, pattern);Useful Patterns
Log all method calls to a class
function traceClass(className) {
var clazz = ObjC.classes[className];
clazz.$ownMethods.forEach(function(method) {
Interceptor.attach(clazz[method].implementation, {
onEnter: function(args) {
console.log(className + " " + method);
}
});
});
}
traceClass("AuthManager");Find and hook by method name pattern
for (var className in ObjC.classes) {
var methods = ObjC.classes[className].$ownMethods;
methods.forEach(function(method) {
if (method.includes("password")) {
console.log(className + " " + method);
}
});
}Get instance of singleton
var instance = ObjC.classes.SingletonClass.sharedInstance();
console.log(instance.someProperty());ObjC Blocks
Call an ObjC block
var block = new ObjC.Block(args[3]);
block.implementation(true, null); // Call with argumentsCreate custom block
var block = new ObjC.Block({
retType: 'void',
argTypes: ['bool', 'object'],
implementation: function(success, error) {
console.log("Block called: " + success);
}
});Debugging Tips
Print backtrace
console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n'));Get module list
Process.enumerateModules().forEach(function(mod) {
console.log(mod.name + " @ " + mod.base);
});Find exported function
var func = Module.findExportByName("libSystem.B.dylib", "open");
console.log("open @ " + func);