Using Frida To Find Hooks In Android Applications Free Download – Google Drive Links

  • by

image1946×1082 323 KB

One technique that Android applications sometimes use to obfuscate how they work is self-hooking. This, specifically, is utilized by Android packers as a way to protect the contents of the underlying application. Other examples can be seen in the wild in security products, malware, or even games deploying anti-cheat software. As a simplistic example, we will walk through how a security product can provide an “encryption” layer at rest for Android applications, without needing to be invoked directly by the application it’s protecting.

The general idea is that, before an application makes any system calls to bionic-libc, the security instrumentation will place a hook on the functions it wishes to intercept. So for this example we will study a write-to-disk-encryptor. The general flow of any Linux based application from the system level would be as follows when writing to the disk:

  1. Open a file descriptor
  2. Write a buffer to that file descriptor
  3. Close that file descriptor

Easy, right? Now at a very high level, when an Android application calls a higher level system API for writing a file to disk, this is still what happens below the hood. The developer doesn’t need to know this, it just happens. So when writing security tooling around this, we can approach it at the very low level and let the system perform all our work for us. In order to do this, we would want to utilize the following steps;

  1. The application attempts to open a file descriptor
  2. Is this file descriptor inside the directory we want to encrypt?
    e.g. – /data/data/com.our.package.name/?
    If not, don’t bother doing anything. Otherwise continue.
  3. Mark this file descriptor as something we want to intercept on write()
  4. Wait for a write() to occur which contains the above file descriptor as an argument, when this occurs, “encrypt”/xor/scramble the text as needed, before passing it to the real write() function.

So how would this actually be accomplished? While there are a few different ways, one common one is to patch the function you want to intercept, in memory, to point to your own function. You would then remember the original address for the function, and call this at the end. At a high level, this would be replicated in Frida like the following;

var open = new NativeFunction(Module.findExportByName(null, ‘open’), ‘int’, [‘pointer’, ‘int’, ‘int’]); Interceptor.replace(open, new NativeCallback(function(path, flags, mode) { console.log(‘open( path=”’ + Memory.readUtf8String(path) + ‘”, flags=’, flags,’ mode=’, mode, ‘)’); // Check if this is a fd to encrypt contents of, save for later return open(path, flags, mode); }, ‘int’, [‘pointer’, ‘int’, ‘int’]));

Note: To actually do this inline with an Android Application, you would need to write some native code to perform the actual hooking, though hopefully this is illustrative enough for the example. More information on this can be found in a few different places, like the Android GOT Hook article.

This can be pretty useful for folks who want to obscure how things are being done to both the system and the application developer. Though as a reverse engineer, we want to be able to find these things quickly. This is often the case when working on a bug bounty or pentest of an application which has used some type of protection like this. If the protection is bought and not developed in-house, it is merely a hindrance to get past and not often part of the assigned scope of the test. This is when looking for these hooked functions can be a nice way to speed up analysis and avoid spending time unwinding obfuscation or reversing a protector.

Utilizing a Corellium Android Device for this has become my go to for speeding up analysis of such applications. After we had added the Frida tab, it sped up my time even more. Previously when writing unpackers for certain Android targets, I had modified some scripts I found online which did patch checking. After extending this to fit my needs and keeping it up to date with different Frida and Android releases, I’ll be utilizing it as an example to show how to use the embedded Frida on Corellium devices. The basic idea for this script is to read the contents of loaded shared libraries from the disk and compare them to those that are loaded in memory. If we find differences in the code segments, we likely know something has been changed and it would be good to investigate them.

To start we just need to simply create a new Android device on our account. I’ve brought up an Android 11 VM and allowed it to complete booting. After this we would install the target Application following the knowledgebase article for doing so, utilizing the Apps tab. My target for this blog will be the latest DJI Fly application, as I know that they use the SecNeo unpacker which uses this method under the hood. After the application is installed, I launch it from the Apps tab as well by just clicking launch and let it start up. From here we will navigate to the Frida tab, pictured below;Selecting the FRIDA tab after we have launched the target application3410×1374

We can click the + Select a Process button here, and will be presented with a list of running processes.Selecting the process we want to attach to with Frida1528×1240

From here we will choose the second dji.go.v5 process. If we attempted to attach to the first one, we would get an error. This is because SecNeo attaches to itself with a child process so that you cannot attach directly to the main one as a form of anti-debugging. We will tackle a target like this in a later blog, though for now, we can utilize the fact that the memory between the two processes is cloned – which will let us find the patched functions in the main process via the child one. Once the correct process is chosen, click the attach button.The Frida console after we have attached to the process2542×1168

As we see above, we now have a Frida REPL console which is attached to the process in the VM. Here we can copy and paste small scripts or test out functionality. I’ve found this extremely useful for quickly testing out Frida code while developing larger scripts. Next, we will click the scripts tab, so we can upload the provided hook_finder.js script. On this tab, we upload using the upload button and locating the script on your local machine and continuing. From there you should see a screen like the one below.Frida scripts tab after successfully uploading the hook_finder.js script2550×1168

At this point, we can do two different actions to execute the script. We can now go back to the REPL console and manually enter it as %load /data/corellium/frida/scripts/hook_finder.js, or simply click the execute button, which will do this automatically for us. This is a custom command that is not normally found in the Frida REPL environment, so just keep this in mind if you’re attempting this elsewhere. Once we navigate back to the console tab we should see the output of the script as it works. Notably we see hooks being identified in /apex/com.android.runtime/lib64/bionic/libc.so which are similar to the example given in the beginning of this blog. The output when I’ve run it on the DJI target is show below:Hooks found on libc2476×1014

We can also see more hooks which are targeting /apex/com.android.art/lib64/libart.so and /apex/com.android.art/lib64/libdexfile.so. These hooks revolve more around the inner workings of how SecNeo performs “stolen bytecode replacement” as one of it’s primary security mechanisms and is out of scope for this blog.Hooks found on libart and libdexfile2482×1088

So what exactly are we seeing here? Essentially, if we dive into the hook_finder.js code, we have automated the process of reading a subsection of the modules loaded by the application from disk, parsing their headers to identify specific segments of the ELF file, and then comparing the contents of the .text and .rodata sections to those that are found in memory. While this won’t detect all forms of hooking, it does detect a fair amount of those used by Android packers.

At this point, one should be able to take the code as it currently is, and extend it to analyze what has changed and try to understand why. Many of the things hooked are in relation to the above example used in this blog, while others are likely used for other packer features. We could expect to find that the code injected, causes the caller to jump to a memory location within the packers library. What this library might do with each function call from there is an exercise left to the reader.

References:

hook_finder.js – https://github.com/corellium/blog-content/blob/master/frida-android-scripts/hook_finder.js

Happy learning!

Leave a Reply

Your email address will not be published. Required fields are marked *