PostHole
Compose Login
You are browsing eu.zone1 in read-only mode. Log in to participate.
rss-bridge 2025-03-19T08:01:41+00:00

Using & improving frida-trace

TL;DR In this blog I want to show you how useful frida-trace can be at hooking thousands of methods at a time. I also wrote some scripts for improving its output a bit.
I often find that half of the problem is finding out what you don’t know. Take a mobile application for instance:

Which class is responsible for the SSL pinning?
Which class does the crypto?
What method is used to retrieve data from the local storage?

Once you have enough information, life becomes a lot easier. Unfortunately, finding this information can be difficult – especially when the mobile application you’ve been given is obfuscated beyond recognition, and the client refuses to provide you the original version, or the source code.


TL;DR In this blog I want to show you how useful frida-trace can be at hooking thousands of methods at a time. I also wrote some scripts for improving its output a bit.

I often find that half of the problem is finding out what you don’t know. Take a mobile application for instance:

  • Which class is responsible for the SSL pinning?
  • Which class does the crypto?
  • What method is used to retrieve data from the local storage?

Once you have enough information, life becomes a lot easier. Unfortunately, finding this information can be difficult – especially when the mobile application you’ve been given is obfuscated beyond recognition, and the client refuses to provide you the original version, or the source code.

I’ve recently found myself in this situation a couple of times and found that frida-trace can be quite useful.

As the name implies, frida-trace allows you to trace the execution flow of an application. It does this by hooking the functions used by the application and outputting the functions’ input parameters and its return value.

The syntax required to hook methods is fairly straight forward – for Java use the lower case -j flag along with the class name and method name. You need to separate the class name and method name with an exclamation mark, much like the Windows syntax of old. You can hook constructors with the $init method.

For instance, if you want to trace the constructor of the javax.crypto.Cipher class you can use the syntax:

frida-trace -U -f com.not.another.mobile.app -j 'javax.crypto.Cipher!$init'

The -f flag will tell frida-trace to spawn the app, and then hook it, which might not always work because the class loader hasn’t gotten around to loading the target class yet. In such a case, you will see the output:

Started tracing 0 functions.

One way around this is to first start the app manually, and then find its pid using

frida-ps -U

You can then attach frida-trace using the -p flag, e.g.

frida-trace -U -p 12345 -j 'javax.crypto.Cipher!$init'

So far, you could also have used plain frida and a typical frida hooking script to obtain the same result as the above frida-trace commands.

Where frida-trace really shines however, is its ability to inject hooks into thousands of functions at the same time, allowing you to search for specific output or behaviour.

To do this, you can make use of the ******* wildcard operator to hook multiple classes and methods.

For instance, if you want to quickly see all the JSON processed by an app, without even turning on Burp, you can use the following frida-trace command.

frida-trace -U -p 12345 -j '*json*!*' -j '*JSON*!*'

In this case I’m specifying a wildcard in the -j flag to match any class whose name contains the word json in it, i.e the ****json* ***part. I then add in an ***!*** which is used to separate the method names from class names, and then finally specify a ******* to match all method names. I also include an uppercase version of the ***-j*** flag, since the matches are case sensitive.

Similarly, if you want to see all SQL statements made by the app, you can use

frida-trace -U -p 12345 -j '*SQL*!*' -j '*sql*!*'

This approach is incredibly useful to quickly find out what an application does in the background.

Undoubtedly, when tracing hundreds of methods, one or two may spam useless output every second, making it hard to spot things you are interested in. Hooking some methods may also slow the application down to a crawl or worse, cause a crash. You can exclude these methods and classes by using the upper case -J flag, which has similar syntax to its lower-case version. Make sure to add the -J flag after the -j flag. For instance

frida-trace -U -p 12345 -j '*SQL*!*' -j '*sql*!*' -J '*ProblemSQLClass*!*'
frida-trace -U -p 12345 -j '*SQL*!*' -j '*sql*!*' -J '*sql*!ProblemMethod'

While very useful, frida-trace is a bit of an unpolished diamond in its original state, since its default output is pretty confusing and not always helpful.

For instance, by default frida-trace will indent its output each time one traced method calls another traced method, so that the user can understand the flow of the application.

Somewhat helpful indenting

While this idea make sense when you are dealing with one or three levels of nested calls, it quickly becomes a problem in Android mobile applications where 20 nested calls isn’t uncommon.

Indentation hell

To solve this problem is easy enough – find the line in frida-trace’s tracer.py containing the word indent and modify it so that it no longer appends a space for each depth.

Change this:

def on_trace_events(self, events) -> None:
no_attributes = Style.RESET_ALL
for timestamp, thread_id, depth, message in events:
if self._output is not None:
self._output.append(message + "\n")
elif self._quiet:
self._print(message)
else:
**indent = depth * "   | "**
attributes = self._get_attributes(thread_id)
if thread_id != self._last_event_tid:

To this:

def on_trace_events(self, events) -> None:
no_attributes = Style.RESET_ALL
for timestamp, thread_id, depth, message in events:
if self._output is not None:
self._output.append(message + "\n")
elif self._quiet:
self._print(message)
else:
**indent = ""**
attributes = self._get_attributes(thread_id)
if thread_id != self._last_event_tid:

The next issue to solve is how frida-trace outputs functions’ input parameters and return values. For basic types, such as strings, integers and so forth, it does a fine enough job. The same cannot be said for objects.

Consider for instance the humble Java byte array object, i.e. a byte[] object. In some cases, frida-trace will make use of the toString method to obtain the byte array’s string representation, only to return an unhelpful string along the lines of [B@d2c77a1.

In other cases, frida-trace outputs the individual bytes of a byte array; however, it displays them as signed bytes, causing several entries to be shown as negative values. It will also show the complete content of a byte array – this can cause a torrent of bytes to be displayed, as buffers are often hundreds to thousands of bytes large. More importantly, it will only display the content as bytes – even if the bytes could be represented in a more meaning full manner, such as legible text.

Furthermore, the content of collection objects like java.util.Vector are not shown – instead frida-trace will only output something like

***<***instance: java.lang.Object, $className: java.util.Vector>

This happens with quite a lot of objects, such as SecretKeySpec which frida-trace represents as

<instance: java.security.Key, $className: javax.crypto.spec.SecretKeySpec>

It would instead be much more useful to print the encryption key’s bytes using SecretKeySpec.getEncoded().

With these issues in mind, I set out to modify the behavior of frida-trace to be a bit more useful.

The first time frida-trace is told to hook a method, it creates a Frida script for it, which it calls a handler script. These handler scripts are stored in the __handlers__ folder of the current directory. If a handler script already exists for a method, frida-trace will use it instead of creating a new one. The idea here is that users are able to modify the handler scripts for each method, so that they can customise what frida-trace does when it hooks it.

This provides users with extreme granular control over what frida-trace does when hooking method calls. Don’t like the way frida-trace outputs the input arguments or return value of a specific method? Simply modify its corresponding handler script.

[...]


Original source

Reply