Frida module to dump, manipulate and hijack any IL2CPP application at runtime with a high level of abstraction.
import Il2Cpp from "frida-il2cpp-bridge";;
async function main() {
await Il2Cpp.initialize();
const domain = await Il2Cpp.Domain.get();
const TestAssembly = domain.assemblies["Test.Assembly"].image;
TestAssembly.classes.TestClass.methods.testMethod.intercept({
onLeave(returnValue) {
const testObject = returnValue.value as Il2Cpp.Object;
testObject.fields.testField.value = 100;
testObject.methods.newTestMethod.invoke(false, Il2Cpp.String.from("testString"));
}
});
TestAssembly.classes.NewTestClass.trace();
}
main().catch(error => console.log(error.stack));
It should support Unity versions from 5.3.0
to 2021.1.0
. I couldn't test them
all, please file a bug in case something doesn't work as expected. Thanks to Il2CppInspector
for providing the headers.
- Linux (not tested)
- Android
- Windows (may need a revision, I just hook
LoadLibraryW
for now) - iOS (missing test device and early instrumentation knowledge)
npm install --save-dev frida-il2cpp-bridge
You may need to include "moduleResolution": "node"
in your tsconfig.json
.
First things first: read the docs.
Initialization
Dump
Print all the strings on the heap
Find every instance of a certain class
String manipulation
Array manipulation
Class tracing
Method interception
import Il2Cpp from "frida-il2cpp-bridge";;
async function main() {
await Il2Cpp.initialize();
const domain = await Il2Cpp.Domain.get();
}
main().catch(error => console.log(error.stack));
Il2Cpp.dump(domain, `/full/path/to/file.cs`);
This will produce something like:
// mscorlib.dll
class <Module>
{
}
// mscorlib.dll
class Locale : System.Object
{
static System.String GetText(System.String msg); // 0x01fbb020
static System.String GetText(System.String fmt, System.Object[] args); // 0x01803a38
}
Il2Cpp.choose(YourClass).forEach(instance => {
// Do whatever you want
});
// Alternatively
Il2Cpp.choose2<Il2Cpp.Array<Il2Cpp.Object>>(YourArrayClass).forEach(instance => {
// Do whatever you want 2
});
const corlib = domain.assemblies.mscorlib.image;
const StringClass = corlib.classes["System.String"];
Il2Cpp.choose<Il2Cpp.String>(StringClass).forEach(str => {
console.log(str.handle, str.content);
});
const str = Il2Cpp.String.from("Hello");
console.log(str, str.length); // Hello 5
str.content = "Goodbye";
console.log(str, str.length); // Goodbye 7
It's not possible to add or remove an array element at the moment.
const corlib = domain.assemblies.mscorlib.image;
const StringClass = corlib.classes["System.String"];
const arr = Il2Cpp.Array.from<Il2Cpp.String>(StringClass, [
Il2Cpp.String.from("One"), Il2Cpp.String.from("Two"), Il2Cpp.String.from("Three")
]);
console.log(arr.length); // 3
for (const str of arr) {
console.log(str); // One .. Two .. Three
}
arr.set(0, Il2Cpp.String.from("Zero"));
console.log(arr.get(0)); // Zero
const corlib = domain.assemblies.mscorlib.image;
const StringClass = corlib.classes["System.String"];
StringClass.trace();
It will log something like:
[il2cpp] 0x015ed550 get_Chars
[il2cpp] 0x005602f0 FastAllocateString
[il2cpp] 0x00ab497c wstrcpy
[il2cpp] 0x01a62bc0 IsNullOrEmpty
[il2cpp] 0x015ed550 get_Chars
[il2cpp] 0x015ed550 get_Chars
You can trace single methods as well.
You can replace any of the parameters and the return value by reassigning them.
const corlib = domain.assemblies.mscorlib.image;
const StringClass = corlib.classes["System.String"];
StringClass.methods.IsNullOrEmpty.intercept({
onEnter(instance, parameters) {
console.log(parameters.value.value);
},
onLeave(returnValue) {
returnValue.value = !(returnValue.value as boolean);
}
});