Mod Uninty apps with Frida
Hi all!
This tutorial will help you understand quickly and easily how to mod Unity apps and games with Frida.
Introduction
According to Frida document, Frida is Greasemonkey for native apps, or, put in more technical terms, it’s a dynamic code instrumentation toolkit. It lets you inject snippets of JavaScript or your own library into native apps on Windows, macOS, GNU/Linux, iOS, Android, and QNX. Learn more
Explanation
"Frida" means "Free IDA", where Frida could be Ida’s sister, as IDA is a static analysis tool and Frida is a dynamic analysis toolkit.
It lets you inject snippets of JavaScript into native apps on Windows, Mac, Linux, iOS and Android. Frida also provides you with some simple tools built on top of the Frida API.
In other words, it allows you to inject your own code and to programmatically and interactively inspect and change running processes. Frida doesn’t need access to source code and can be used on iOS and Android devices that aren’t jailbroken or rooted. It lets you do all of this through APIs available from Objective-C, which are also exposed to higher-level languages through bindings.
Why is Frida?
As far as I know, Frida is a framework designed for developers, reverse-engineers, and security researchers to monitor and debug running processes. It also enables programmers, software and security professionals to execute their own JS scripts into other processes.
Game/App Modding might not meet the purpose on how Frida is made for, but due to its wonderful features, such as live debugging; powerfull instrumentation kit; simple syntax, simple setup that help beginers easier to implement and learn, etc. It can be a perfect method for modding if we understand the Frida's fundamental, so let's start!
Getting ready
Frida-tools
First, we would need to install Frida-tools on Windows/Mac/Linux in order to use the CLI.
Requirement
- Python, Python3
- Pip, Pip3
Install with Pip
pip install frida-tools
Testing via cmd/terminal
Open cmd/powershell or terminal and type:
frida-ps
This will list all the running processes of our current OS.
Install Frida-server
To communicate with Frida-tools from client-side, let's install Frida-server on whichever device we want to analyze. In this case, it's a Android device.
Requirement
- Rooted device
- ADB is enabled and authorized
First off, download the latest frida-server from the releases page and uncompress it. (PS: Remember to uncompress the file before push it to your phone! )
In this tutorial, we will be doing it on Android device that has arm64-v8a ABI, so we need to find and download frida-server-xx.xx.xx-android-arm64.xz
. After uncompressing, we should rename the file to frida-server
and push to data/local/tmp
Install the server manually via ADB
Let's install and start the server by following this Frida document
adb push frida-server /data/local/tmp/
adb shell
su
chmod 755 /data/local/tmp/frida-server
/data/local/tmp/frida-server &
Install the server via MagiskFrida module or Frida server app
The process of installing and updating Frida server could be done automatically by a Magisk module or an Android app published on Google Play.
With Magisk module, just open Magisk app, go to Download tab, find and install the MagiskFrida module then restart the device. This method is highly recommended since
MagiskFrida
is continuously developing, the server itself is automatically started every time the device boots and also get updated whenever there's a new version released.With Frida server app by
shingle
, find it on Google Play with packageIDme.shingle.fridaserver
. Aftersu
granted, we can now download and start the Frida-server easily.
Testing via cmd/terminal
Open cmd/powershell or terminal and type:
frida-ps -U
This -U
option means USB or remote device, so that we should see the processes of our Android device.
Mod our first Unity app
This tutorial comes with a sample Unity app that designed for learning Frida, so let's begin by downloading the apk file.
Hook the script to desired app
First, let's create a Javascript file and write down this simple code:
console.log("Hello World!")
After that, we need to make Frida listen to our app by inputting its packageID, then use -l
to hook the custom Javascript file, see this cmd:
frida -U <com.company.someapp> -l <some-script.js>
If the cmd above executes successfully, we will see console output Hello World!
string.
To spawn the app then listen to it right away, which is very helpful for early instrumentation, use -f
frida -U -f <com.company.someapp> -l <some-script.js>
While spawning, Frida will pause the app for early instrumentation purpose, so we need %resume
to resume it. Or we can do it automatically by adding --no-pause
at the end of cmd, also use -Uf
for brevity.
frida -Uf <com.company.someapp> -l <some-script.js> --no-pause
Note:
Apk that built from latest version of Unity Engine (including the sample app in this tutorial) will crash the server if we don't use
-f
, so make sure to add that option in cmd line.Early instrumentation will need a callback wrapper, because the module (libil2cpp.so) may not be able to load before the script's executing. See the example code below:
function awaitForCondition(callback) {
var i = setInterval(function () {
var addr = Module.findBaseAddress('libil2cpp.so');
console.log("Address found:", addr);
if (addr) {
clearInterval(i);
callback(+addr);
}
}, 0);
}
var il2cpp = null;
Java.perform(function () {
awaitForCondition(function (base) {
il2cpp = ptr(base);
// do something
})
})
- The
-l <some-script.js>
is optional, Frida CLI is a REPL interface so we just need to paste the whole script into cmd line to execute it, but that is not ideally for large amount of codes.
Write the first script
Learning Frida script is not difficult since it supports Javascript API and others high-level programming language. Let's take a look at Javascript API document.
Clone this repo, npm install
then create new .js
file inside of project folder so we can get code completion, type checking, inline docs, refactoring tools, etc.
Here're some features that we're going to mainly focus on for modding Unity app:
- findBaseAdrress(
lib name
) - load(
path
)
- attach(
address
,callback
) - replace(
adress
,callback
)
- NativePointer(
offset | decimal
)
- readInt() | readFloat() | readutf16String() | readByteArray(
decimal
) | readPointer() - writeInt(
decimal
) | writeFloat(decimal
) | writeUtf16String('some string'
) | writeByteArray(hex
) | writePointer(ptr
)
NativeFunction(
address
,