You’re almost there — sign up to start building in Notion today.
Sign up or login
Hands On Frida

Hands On Frida

While the CLI tools like frida, frida-trace, etc., are quite useful, there might be times when you’d like to build your own tools harnessing the powerful Frida APIs.

Frida API

Java.available: This API is used to check Frida running on android or not. It is to check if you are actually running on Android. For example, you could create 1 SSL bypassing script that first checks if you’re on Android or iOS, and act accordingly .It specify whether the current process has the a Java VM loaded, i.e. Dalvik or ART return boolean (true or false)
Java.androidVersion: This API return the android version of device that we are using.
Java.enumerateLoadedClasses(callback): This API enumerates classes where object specifying onMatch(name, handle): called for each loaded class with a name that may be passed to use() to get a JavaScript wrapper. onComplete(): called when all classes have been enumerated
console.log("[+] Running Frida Script") console.log("[+] The android version is: " + Java.androidVersion) Java.enumerateLoadedClasses({ "onMatch": function(name){ if(name.includes("com.apphacking.certificatepinning")){ console.log(name) } }, "onComplete": function(){ console.log("[+] Done") }, })
For running the script:
frida -U -f com.apphacking.certificatepinning -l .\enumLoadedClasses.js --no-pause
-f → Specify the target package. it’s run the apk. If you don’t specify this option, you should manually run this apk. In case of you don’t want to target application pause, you can use
--no-pause
switch.
-U → Connect to frida server with USB connection.
-l → Load JavaScript script
Java.enumerateClassLoaders(callbacks) : This API enumerates the class loaders in Java VM, which have a callback function, which are onMatch: function (loader) and
onComplete
: function ()
console.log("[+] Running Frida Script") console.log("[+] The android version is: " + Java.androidVersion) Java.enumerateClassLoaders({ "onMatch": function(loader){ console.log("Loader: " + loader) }, "onComplete": function(){ console.log("[+] Done") }, })
Enumerate Methods
Java.enumerateMethods()
You can use this script too.
Java.perform(function () { var targetClass = "com.apphacking.certificatepinning.MainActivity"; // Replace with the desired class name var targetClassRef = Java.use(targetClass); var methods = targetClassRef.class.getDeclaredMethods(); methods.forEach(function (method) { console.log(method.toString()); }); });
Java.perform(fn): ensure that the current thread is attached to the VM and call fn. and the
fnfunction
is called . This function calls
VM::AttachCurrentThread
internally, then executes the JavaScript in the
fn
callback function to operate the Java runtime, and finally uses
VM::DetachCurrentThread
to release resources
Java.use(className) : It dynamically get a JavaScript wrapper for
className
. Wrapper is basically a function that is intended to call one or more other functions.
Java.perform(function() { var Test = Java.use("com.example.demotest.xyz"); console.log( Test.AClassVariable.value ); });
Java.scheduleOnMainThread(fn) : The callback function is executed on the VM main thread (UI thread). Operating UI elements in Android requires code execution in the main thread, and
scheduleOnMainThread
its role is to execute functions in the main thread
Java.openClassFile(filePath) : This APIused for hook dynamic loaded
dex
.
Java.choose(className, callbacks) : Scan Java heap in memory and enumerate Java object (className) instances. For example, you can use java.lang.String Scan strings in memory. callbacks provide two parameters: onMatch(instance) and onComplete, which are to find the matching object and scan to complete the call.
Java.perform( function(){ Java.choose("java.lang.String", { onMatch: function(instance){ console.log(instance.toString()) }, onCompelet: function(){ console.log("[+] Done") } }) } )
Extracting all strings on this apk
ALT
Java.retain(obj) : duplicates the JavaScript wrapper obj for later use outside replacement method
Java.registerClass(spec): Create a new Javaclass and return a wrapper, where the specification is
namestring containing :: Specify the name of the class.
superClass: (Optional) Parent class. java.lang.ObjecOmission to be inherited from t.
implements: (Optional) An array of interfaces implemented by this class.
fields: (Optional) Object, which specifies the name and type of each field to be exposed.
methods: (Optional) Object, which specifies the method to be implemented.
Java.perform(function () { var hellojni = Java.registerClass({ name: 'com.example.frida_1demo.abcd' }); console.log(hellojni.hello.value); });
Java.isMainThread() :This API check the Programmer executing in main thread or not. When a Java program starts up, one thread begins running immediately. This is usually called the main thread of our program, because it is the one that is executed when our program begins.
Java.deoptimizeEverything() : forces the VM to execute everything with its interpreter.
Java.cast(handle, klass) : Create a JavaScript wrapper given the existing instance at the handle of given class klass as returned from
Java.use()
Java.perform(function () { var targetClass = Java.use("com.example.TargetClass"); // Replace with the desired class name var obj = targetClass.$new(); // Create an instance of the class // Perform the cast to String using Java.cast var strObj = Java.cast(obj, Java.use("java.lang.String")); // Now you can treat the object as a String and use its methods var length = strObj.length(); console.log("Length of the string object:", length); });
Java.array(type, elements) : Used to creates a Java array with elements of the specified type, from a JavaScript array elements
Java.perform(function () { var intArray = Java.array('int', [1, 2, 3, 4, 5]); // Create an int array var stringArray = Java.array('java.lang.String', ['apple', 'banana', 'cherry']); // Create a String array // Access and print array elements for (var i = 0; i < intArray.length; i++) { console.log("Element at index", i, "of intArray:", intArray[i]); } for (var j = 0; j < stringArray.length; j++) { console.log("Element at index", j, "of stringArray:", stringArray[j]); } });

Example of Method Overwrite

console.log("[+] Starting Frida") console.log("[+] Android Version: ", Java.androidVersion) Java.perform( function(){ let MainActivity = Java.use("com.apphacking.certificatepinning.MainActivity") MainActivity.onResume.implementation = function() { send("[+] onResume() is called.") this.onResume() } } )

Function Overloading

In the context of Frida and Android, function overloading refers to the ability to hook and modify multiple versions of a function with the same name but different parameter signatures. Function overloading is a concept from object-oriented programming languages where you can define multiple functions with the same name but different parameters.
To hook an overloaded function in Frida, you need to provide the exact parameter signature of the function you want to hook. For example, if you have an Android application with two overloaded versions of a function called "doSomething," where one version takes an integer parameter and the other takes a string parameter, you can hook each version separately by specifying the correct parameter type in your Frida script.
Example:
Java.perform(function () { var targetClass = Java.use('com.example.TargetClass'); // Hook the version of doSomething that takes an integer parameter targetClass.doSomething.overload('int').implementation = function (param) { // Your modifications here console.log('Hooked doSomething(int) with param:', param); // Call the original method this.doSomething(param); }; // Hook the version of doSomething that takes a string parameter targetClass.doSomething.overload('java.lang.String').implementation = function (param) { // Your modifications here console.log('Hooked doSomething(String) with param:', param); // Call the original method this.doSomething(param); }; });
Real World Example:
Java.perform(function () { var messagingClass = Java.use('com.example.MessagingClass'); // Hook the version of sendMessage that takes a string parameter for text messages messagingClass.sendMessage.overload('java.lang.String').implementation = function (message) { console.log('Intercepted text message:', message); // Call the original method this.sendMessage(message); }; // Hook the version of sendMessage that takes an image parameter for image messages messagingClass.sendMessage.overload('com.example.Image').implementation = function (image) { console.log('Intercepted image message:', image); // Call the original method this.sendMessage(image); }; // Hook the version of sendMessage that takes a video parameter for video messages messagingClass.sendMessage.overload('com.example.Video').implementation = function (video) { console.log('Intercepted video message:', video); // Call the original method this.sendMessage(video); }; });

Calling a Method

Calling static method and virtual method are differently. For static method you can just do this:
let MyClass = Java.use("com.example.package.MyClass") MyClass.myStaticMethod()
For virtual methods you usually need to create an instance of a class:
let MyClass = Java.use("com.example.package.MyClass") let MyObjectClass = MyClass.$new() MyObjectClass .myVirtualMethod()

Tasks

Change
faceNum
and
poolSize
parameters.
Use Frida to change dice app behavior to return only 6 in dices.
Solution
Create multiple player instance