Dynamic Instrumentation
What is Dynamic Instrumentation ??
In this session we will:
Analyze behavior of applications
Modify & intercept execution flow
For this, we will mainly use Frida, the dynamic instrumentation toolkit written by Ole André Rætta. Frida is fully open source and supports iOS, Android, Windows, Macs, and so on.

The way this works is that we have to inject the Frida agent into our Android application. There are different ways of doing so. For example, we can patch the APK, or if we have root on our device, we can run something called the Frida server, and once we have the agent injected into our application, we can start to trace and analyze the execution flow, similar to what you potentially could do in a debugger.

But Frida also allows us to very easily replace entire functions with our own implementations or even just modify arguments and return values, and this makes Frida really powerful. We mainly control Frida using scripts and tools that we run on our computer, and then we connect to the Frida agent via USB or over the network. These scripts are mainly written in JavaScript and Python.
We will also use Frida to understand what happens when we, for example, press a button in an application, which functions are called, and so on.

There's also a tool called Objection that tries to package a lot of the functionality that you commonly need, such as SSL bypassing, into a single program, and so we will also look at Objection.

In most cases, you don't really decide between dynamic analysis and static analysis, but instead, you try to combine them both.

Setup
Installing Frida & Objection
To install frida server, run:
If you don't have pip, you can find installation instructions here.
To test whether the install was successful, run:
We also want to install Objection, which we can do by just running:
Patching APKs with Frida
Download File FridaTarget.apk
Now, as mentioned before, we need to somehow inject the Frida agent into our application. And the easiest way to do so is using Objection. In this course we will mainly work with a special application I built called FridaTarget.

To inject Frida into an APK we can use objection:
Objection will extract, patch, re-pack, align and sign the application, and so it's a very fast and easy way to get Frida running.

When using an older version of frida-server, run a command like this:
And then this generates a new APK for us. The FridaTarget.objection.apk

So I'm going to type adb install FridaTarget.objection.apk. And now if we check in our emulator we can see that indeed our Frida target application is there.

Note that the application will wait on launch for Frida to connect to it, so to start the application we have to run:
The -U here specifies that we want to connect by USB.

Running the Frida Server
If you have a rooted device, you can also run frida-server instead of patching the APK. You can download frida-server on the Github Releases Page of Frida. Note that it comes xz compressed, so you have to extract it (xz -d on unixoid systems, 7zip on for example Windows).
Note: Make sure to use the same version of Frida Server and Frida Client.
To install it in an emulator we can adb push the server over:
We chose this path because other parts, such as /sdcard, are commonly mounted no-exec.
Afterwards we want to run adb as root, and also make the server executable:
And then we are ready to go: We can launch the server by running
Now we can connect to the application by running:
The Frida REPL & Frida scripts
The Frida REPL

Let’s start by starting the patched app, and as before, it will just go into a white screen because it waits for us to attach with Frida. So let’s connect by just saying Frida, you and Frida target as the application name. And now we are dropped into the Frida REPL, the read eval print loop. This CLI is really just a JavaScript interpreter.
And so for example we can say console.log("0x1ceKing Test") and it will just log 0x1ceKing Test.

The Frida REPL (Read-Eval-Print-Loop) is a JavaScript interpreter, and so we can directly run JavaScript statements:

To create multi-line statements, suffix each line with a \ backslash:

Frida also has a very powerful tab completion. And so if we just type con, you can see that it searches through all available objects that contain con, so SocketConnection and Console.


You can search your command history by pressing
Ctrl R.You can clear the screen by pressing
Ctrl L.

If you want to get an overview over the available functionality in classes, you can just hit Tab. This will give you a big list of all the classes from Frida and also all the JavaScript integrated classes.

There are also a couple of built-in Non-JavaScript commands and those can be accessed by typing percentage (%).

You can, for example, say percentage help (%help) to get a list of all available commands. Note that the number in parentheses after each command indicates the number of expected arguments.

The full JavaScript API is documented on Frida. And it’s really useful to take a scroll through here to see all the functionality that is there.
For example, on the Java documentation, we can see that Java Android version should give us the version of Android we are running.

Check-out the full Frida JavaScript API documentation here!
Writing Frida Scripts
Now let's run Frida -help to see how we can actually load a script.
To load scripts with Frida, we can just start Frida with the -l option:
We can enable and disable auto-reload by doing:
To manually reload all scripts, we can just run %reload in the REPL.
Frida basics
Instantiating Objects and Calling Methods
Note: For more details and practical examples, check out the https://frida.re/docs/javascript-api/#java
We can get JavaScript wrappers for Java classes by using Java.use:
We can then instantiate those classes by calling $new:
We can dispose of instances (for example to free up memory) using $dispose(), however this is almost never required, as the Garbage Collector should collect unused instances.
We can also replace the implementation of a method by overwriting it on the class:
Mixing Static and Dynamic Analysis & Java.perform
t's start by exploring the AndroidManifest. If we take a look at the exposed activities, we can see that the only one exposed is io.hextree.fridatarget.MainActivity.

So let's take a look at what we have under io.hextree.fridatarget.MainActivity. Let's check out the example class here. There are two functions here return returnDecryptedString and return returnDecryptedStringIfPasswordCorrect.
Now it will probably not be too difficult to reverse engineer the flag crypto, but instead, let's try to use Frida to just call these functions and get the decrypted value directly. Now we could just try to do Java.use("io.hextree.fridatarget.MainActivity.ExampleClass"). But Jadx is one step ahead of us.

If we right-click the class, we can say copy as Frida snippet, and if we copy that into our Frida, we can see that it automatically created.

let ExampleClass = Java.use("io.hextree.fridatarget.ExampleClass");. Super useful. However, if we hit return, we get a large error saying that it couldn't find the class.
The reason for this error is the way that our Frida-to-Java bridge works under the hood. Essentially, the code executed in the normal Frida REPL does not run directly within the main thread of our application.

Therefore, we have to use something called Java.perform to ensure that Frida executes the code in the VM's thread, where all classes and so on have already been loaded and are easily accessible. Java.perform takes in a function as its first argument, and in that function we can actually just paste in our code and we will be able to run it without any error.

After that, we can create a JavaScript file using Frida to solve the challenge in ExampleClass.
This Frida script:
Java.perform– runs the code in the app’s Java environment once the VM is ready.Java.use("io.hextree.fridatarget.ExampleClass")– retrieves theExampleClassfrom the app.$new()– creates a new instance (object) of that class.returnDecryptedString()– calls a method on the instance to get the decrypted string.The result "I am very securely encrypted!" is printed in the terminal when running
frida -U FridaTarget -l .\FridaScript.js
Similarly to the returnDecryptedString() method, you can write console.log(ExampleInstance.returnDecryptedStringIfPasswordCorrect("VerySecret")) to execute and solve the returnDecryptedStringIfPasswordCorrect() method.

Code solved challenge here:
Code solved flag here:

Run Frida and got flags:

Tracing Activities
When reverse engineering an Android application, we often want to know which activity is in the foreground and what screen we are currently looking at. And so let’s write ourselves a useful Frida script that will always tell us which activity is currently in the foreground. To do that, let’s start by looking at the documentation for Activity.
A detailed Android Activity guide:
You can also read this:
If we scroll down on this page we can find the lifecycle documentation for Activities. And here we can see which functions are called when an Activity is created and shown. First onCreate is called. Then onStart and then onResume. Now onResume is called when the app is, for example, brought forward from the background or when activity is switched.

So let’s write a script to do that
This script uses Frida to hook into the
onResume()method ofandroid.app.Activity.Java.perform()ensures that the code runs only when the application's Java environment is ready.Java.use("android.app.Activity")obtains the original Android Activity class for manipulation.It overrides (
implementation) theonResume()method to insert custom behavior before calling the original logic.Whenever an Activity is brought to the foreground, the script logs the message
"Activity resumed:"along with the Activity’s full class name (this.getClass().getName()).Finally, it calls
this.onResume()to execute the original method, ensuring the application continues to function normally.
As a result, every time an Activity appears or returns from the background, its name is logged for monitoring. And so we can just say frida -U FridaTarget -l .\FridaScript.js and FridaTarget hit return and we see that our main activity gets logged away.

Tracing Fragments
However, if we switch to the different screens of the application, we don’t get any new activity logs.
This is because these different screens are based on fragments. But if we check the documentation for Fragment, we can see that they have similar lifecycle functionalities.
A detailed Android Fragment guide:
And so it should be easy to write a similar script. But for Fragments. And to do that we first need to find the full class name for the Fragment. And Fragments have been deprecated on API level 28.
But there is a new replacement that has the same API, and if we click View Source here.
Then scroll up in the resulting source code file, we can see that the full path of the class is Android.app.Fragment.

So let’s write a script to trace Fragments just like we did with Activities:
So I will trace Fragments using the command frida -U FridaTarget -l .\FridaScript.js, and as I navigate through the app, Frida logs the different fragments we are accessing.
Tracing with Frida
frida-trace + tracing into JNI
Frida Trace to work, it needs to instrument each function that we want to trace. And because of that, we have to tell Frida Trace which functions we actually want to look at and which classes we are interested in, because otherwise the instrumentation would take a long time. And while you absolutely could instrument every single function, you will see that it just doesn’t make sense in most cases to tell Frida Trace what we’re interested in.
We use a special filter syntax. We use the fully qualified class name, then exclamation mark, and then the method name.
So if we, for example, want to trace the button click handler on the class io.Hextree.Class, we have to write:
Now what’s really nice is that this also supports wildcards. And so we can also just say:
And this will match all methods on all classes under io.Hextree. We can also match on all functions that include password by just doing this:
To trace all calls on io.hextree.*, we can do:
To trace all calls on io.hextree.* and exclude AnnoyingClass we can do:
Just remember: The part before ! is the filter for the class-name, while the part after the ! is the filter for the methods!
We can also trace into native objects, by specifing the -I option:
So I can really recommend that you check out the Frida trace documentation that I've linked here: https://frida.re/docs/frida-trace/
Intercepting arguments & return values
Frida Interception Basics
Accessing the Fragment Interception screen, you will see a button labeled "Example Function". When you click it, it will return the value "EXAMPLE FUNCTION" displayed below.

We can use Frida to intercept function calls and return values. For example:
To modify the argument value of InterceptionFragment.function_to_intercept, we can just write a simple script:
Frida hook changing a function’s agrument:

To replace the return value of InterceptionFragment.function_to_intercept, we can just write a simple script:
Frida hook changing a function’s return value:

Example: License Check 1 - Solving Method
First, I run frida-trace -U -j 'io.hextree.*!*' FridaTarget and click the CHECK LICENSE button.
In the terminal, we can see that the license_check() function in the InterceptionFragment class calls the isLicenseValid() function in the LicenseManager class, which returns false.
Therefore, simply changing the return value of the isLicenseValid() function to true will solve the challenge.

Run this script for bypass check license: frida -U -l .\FridaScript.js FridaTarget
Example: License Check 2 - Solving Method
Since I already knew the license-checking class from the previous lab, I reviewed its code and saw that the function checks the license based on the unixTimestamp parameter. By modifying this parameter, the challenge can be solved.

Run this script for bypass check license: frida -U -l .\FridaScript.js FridaTarget
The Dice Game - Solving Method
When accessing the Dice Game Fragment screen, you can see that this challenge requires rolling five sixes on the dice.

Using frida-trace -U -j 'io.hextree.*!*' FridaTarget, we see DiceGameFragment.rollDice() calling DiceGameFragment.randomDice(). So, by modifying the return value of randomDice(), we can solve the challenge.

Run this script for solve this game: frida -U -l .\FridaScript.js FridaTarget
You won this game.

Note
When Frida encounters ExampleClass.ExampleFunction.implementation = function() { ... } the original ExampleFunction() is replaced with whatever code you put inside the function body.
If you don’t call the original method (this.ExampleFunction()), then the original logic inside that method won’t execute ⇒ Frida will only run your replacement code (e.g., return 5;).
Since your app only needs the return value from ExampleFunction(), writing the hook like this will not cause an error:
You should keep this.ExampleFunction() in cases where the original method:
Has side effects (updates state, saves data, sends events).
Performs initialization of resources or connections.
Updates the UI or triggers callbacks.
Synchronizes with native code.
This helps prevent runtime errors when running Frida, so you can always add this.ExampleFunction() when using Frida.
SSL Validation Bypasses
SSLContext & Network-Security-Config Bypass
If you have looked at our Android Network analysis course, you encountered a lot of SSL validation and different ways to bypass or modify that validation. Another way to disable SSL validation and also to bypass certificate pinning is to use Frida to just disable it.
Now, the way we bypass SSL pinning on Android depends on how it's implemented. One common way to implement SSL pinning and validation is to explicitly set up a Keystore with a single trusted certificate.

This keystore is then used to create a trust manager, and that one will only validate this single certificate. If we check the documentation for X509 TrustManager, we can find this check serverTrusted method right here.
And this method seems to validate the certificate chain and will throw an exception if the certificate is not trusted by the TrustManager. So it sounds like the perfect function to replace to disable SSL pinning and even SSL validation altogether.
Essentially they currently have the wrong certificate pinned, And so the request fails. If we manage to bypass the SSL pinning or SSL validation altogether, the buttons should turn green.

Now let's start by using frida-trace to find invocations to the function check serverTrusted that we saw earlier.
If we click the first request now we can see that platform check server trusted is called and that it has quite a long signature. This would be the implementation that we will try to replace, but unfortunately the package name is missing here. So the stuff that comes before the platform.

Luckily, Frida comes with a nice way to enumerate all classes and methods and so on. And so let's drop into a Frida REPL and then we just do Java.enumrateMethods() .And then we can use the same filter syntax that we also can use for frida-trace.

Next we simply try to replace the checkServerTrusted function with our own implementation. And then we are already ready to test. So let's use Frida to run our script. And we get an error message. There are two different overloads for checkServerTrusted.

And so we have to tell Frida which one we want to use. Now, nicely enough, Frida shows us both available overloads and we can just copy paste this code in. I will just try the first one and hope that it works.
And now there's no error message. And so let's try the SSL context pinning function. And it turns green.

Another way to set up SSL pinning and validation is to use the Network Security Config. And down here you can see how, for example, a certificate might be pinned. Now I thought this might need a different bypass, but if we just try it with our existing script, it just works.
Note About overload()
overload()In Frida, .overload(<signature>) is used to explicitly specify which method you want to hook when a class contains multiple methods with the same name but different parameters (method overloading in Java).
Example:
In Java, a class can have multiple methods with the same name but different data types or number of parameters:
If you only hook:
→ Frida won’t know which specific version of the method you intend to hook.
That’s why .overload(<signature>) is needed → It’s the way Frida selects a specific version of an overloaded method based on the type and number of parameters you provide in <signature>.
Syntax:
OKHTTP3 Bypass
A commonly used HTTP library for Android is OKHTTP3 and OKHTTP3 has its own support for certificate pinning, and in the documentation we can see that there's an OKHTTP client builder and that that one can be provided with a certificate pinner. And so to bypass certificate pinning I think we could just replace the certificate pinner function with our own implementation.

Now first we need to find the full package name and class name for this OKHTTP builder. And so we'll use Java.enumerateMethods() and filter for all HTTP classes that contain builder
There are quite a few of these, but after scrolling up a bit, I found the OKHTTP client builder, which is exactly the one that we saw in the documentation.

As always we start with Java.perform(). And then we get the JavaScript wrapper for the builder class. And now we can replace the implementation for certificate pinner with our own and we will just return this.
And so let's give our new script a try. And it doesn't work.

Well we just disabled the SSL pinner but we didn't outright disable SSL validation. And so it turns out that OKHTTP3 will still use the trust manager. And so we can just run both our trust manager script and our HTTP script to bypass the pinning. And now it works awesome.

There's also codeshare where people share their own free scripts, and so often you will be able to find a good SSL bypass there: https://codeshare.frida.re/
Bypassing SSL Pinning with Objection
Let's run Objection Explorer, which will connect to our FridaTarget and then drop us into an Objection CLI
And now, if we type Android in here, we can see a couple of suggested commands. One of them allows us to disable SSL pinning.

So let's try that. In the log output, we can see that it also works by overriding several certificate pinning and SSL-related functions. Let's check how successful it is with our app. We'll start with the SSLContext pinning and it works. It's also able to bypass the network config pinning, but unfortunately, OkHttp3 still doesn’t work.

2048 Game Challenge: Winning!
After installing the app and running Frida, I checked the app name and package of the 2048 game.
With no clear starting point, I traced all functions.
From the trace I saw that io.hextree.privacyfriendly2048.activities.GameActivity is the main class handling the game startup.

Because the output was too noisy, I limited tracing to methods inside io.hextree.privacyfriendly2048.activities.GameActivity class
With the cleaner output, the app’s flow became clear: there’s a method GameActivity.generateNumber() that returns the value of a newly spawned tile.

So I wrote a hook to force its return value to 1024 to validate the idea and it worked :c

Then I rewrote the hook so the game wins without any clicks.
Yay, we got the flag.

Last updated




