FRIDA: Objective-C hook n+1 arguments
We will read a method that has two arguments. For this purpose, we will write a sample program to call
[fileManager fileExistsAtPath: isDirectory:]
.We will also use FRIDA's REPL to get the method pointer differently.
Sample code:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString *filepath = @"/Users/azurda/Desktop/test.c";
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL res;
if ([fileManager fileExistsAtPath:filepath isDirectory:&res]) {
NSLog(@"File exists.");
}
else {
NSLog(@"File does not exist.");
}
}
return 0;
}
isDirectory
will be nil
in case the target path is not a folder.
Now we build the file and fire up frida's REPL. From there, we will now see how to get the pointer to a method using the ObjC.classes
object which maps ObjC classes to JavaScript objects. If we write in FRIDA's REPL ObjC.classes we can see that it begins to autocomplete:
For instance if we want to know all methods available in ObjC.classes.NSFileManager
we can do it using $ownMethods
which returns an array containing native method names exposed by the object class:
[Local::objCLI]-> ObjC.classes.NSFileManager.$ownMethods
[
"+ defaultManager",
"- dealloc",
"- delegate",
"- setDelegate:",
"- fileExistsAtPath:",
"- createDirectoryAtPath:withIntermediateDirectories:attributes:error:",
"- createDirectoryAtURL:withIntermediateDirectories:attributes:error:",
"- homeDirectoryForCurrentUser",
"- URLsForDirectory:inDomains:",
"- getRelationship:ofDirectoryAtURL:toItemAtURL:error:",
"- enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:",
"- temporaryDirectory",
"- stringWithFileSystemRepresentation:length:",
"- removeItemAtPath:error:",
"- enumeratorAtPath:",
"- contentsOfDirectoryAtPath:error:",
"- isExecutableFileAtPath:",
"- destinationOfSymbolicLinkAtPath:error:",
...
If we take a look at the list, we can see that the - fileExistsAtPath:isDirectory:
method is available to us. We can access it and then its member .implementation
which returns a pointer to the mapped object:
[Local::objCLI]-> t = ObjC.classes.NSFileManager['- fileExistsAtPath:isDirectory:'].implementation
function
[Local::objCLI]-> ptr(t)
"0x7fff2117d115"
Once we have this information, we can do something similar to what we have seen before except that this time it won't be args[2]
the element storing our interesting parameter but args[3]
instead:
Interceptor.attach(ptr(t), {
onEnter: function(args) {
this.isDir = args[3];
},
onLeave: function(retval) {
let objCIsDir = new ObjC.Object(this.isDir); console.log(objCIsDir);
}
})
And once we %resume
:
[Local::objCLI]-> %resume
2021-02-16 22:48:51.725 objCLI[96805:23540150] File exists.
[Local::objCLI]-> nil
It says our .c
file is not a directory so the parameter is set to nil
after method execution.