Using NativeFunction s¶
Sometimes we may want to call a function defined in the program we’re analyzing, or maybe we want to benefit from some of the imported functions avoiding to reimplement everything from scratch. Cause who wants to rewrite code anyways, right?
This has a simple solution in the Frida world, we just need to create a NativeFunction object, to do this we’ll need the following information:
- The address of the function
- The return type as string
- The type of the arguments as an array of string
In addition, depending on the binary we are working on we may need the following optional values:
- abi
- scheduling
cooperative (Default): Allows other JavaScript to keep runningexclusive: No other threads execute JavaScript code. It’s faster but may cause deadlocks
- exception
steal (Default): If an exception is thrown during runtime, Frida will look in the stack and convert the excetion thrown into a JavaScript exception that we can catch as needed.propagate: Let the application handle the exceptions through SEH. We can register an exception handler usingProcess.setExceptionHandler
In our following example we’ll hook to an nc process and spoof a message using the write import
Getting the NativeFunction¶
1 2 3 4 5 6 7 8 9 10 | /* According to the `man 2 write` linux documentation, the signature for the write function is:
*
* ssize_t write(int fd, const void *buf, size_t count);
*
* Which for us means, ['int', 'pointer', 'uint'] -> 'int'
*/
var write_fn = new NativeFunction(Module.findExportByName(null, 'write'), 'int', ['int', 'pointer', 'uint'] );
var my_str = "Hi, I'm spoofing a message";
my_message = Memory.allocUtf8String(my_str);
console.log('Result:', write_fn(3, ptr(my_message), my_str.length));
|
The SystemFunction API¶
Some system calls provide detailed information in case of error that must be retrieved using errno or lastError, depending on platform. For this particular cases an API called SystemFunction exists. The only difference between SystemFunction and NativeFunction is that the former returns an object with the return value in the field value and the last error number of the current thread in the field errno (UNIX) or lastError (Windows)
1 2 3 4 5 6 7 | var write_fn = new SystemFunction(Module.findExportByName(null, 'write'), 'int', ['int', 'pointer', 'uint'] );
var my_str = "Hi, I'm spoofing a message";
my_message = Memory.allocUtf8String(my_str);
var result = write_fn(10, ptr(my_message), my_str.length); //We use an invalid file descriptor here
if(result.value < 0){
console.log('An error ocurred:', result.errno);
}
|
The previous snippet prints:
An error ocurred: 9
Using the errno util (Package moreutils in most linux distros), we can check the error returned:
errno 9
EBADF 9 Bad file descriptor
Creating a NativeCallback¶
At some point we’ll be interested in adding new functionality to the program we are analyzing, to perform this task frida provides a convenient API to generate JavaScript functions that are executed in the context of the process/application we are instrumenting:
1 2 3 4 5 6 7 8 9 | var my_callback = new NativeCallback(
function(arg1, arg2){
console.log('In my new function with arguments:', arg1, arg2);
return arg1==arg2;
},
'bool',
['int', 'int'],
'unix64'
);
|
The NativeCallback API returns a NativePointer object, we’ll see how to inject these objects into the process to use them as hooks in the next part of this guide.
For now we’ll see how to manually call the function we created. First of all, we need to turn the NativePointer object we got to a NativeFunction and then call it:
1 2 3 4 5 6 7 | var my_function = new NativeFunction(
my_callback,
'bool',
['int', 'int'],
'unix64'
);
console.log(my_function(2,3));
|