frida-il2cpp-bridge-my
Version:
(my:Support IOS)Frida module to dump, manipulate and hijack any IL2CPP application at runtime with a high level of abstraction.
310 lines (309 loc) • 13.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.dump = exports.allocRawValue = exports.readRawValue = exports.writeFieldValue = exports.readFieldValue = exports.getOrNull = void 0;
const console_1 = require("../utils/console");
const variables_1 = require("./variables");
const array_1 = require("./structs/array");
const object_1 = require("./structs/object");
const string_1 = require("./structs/string");
const value_type_1 = require("./structs/value-type");
const type_enum_1 = require("./structs/type-enum");
function getOrNull(handle, Class) {
return handle.isNull() ? null : new Class(handle);
}
exports.getOrNull = getOrNull;
function isCoherent(value, type) {
switch (type.typeEnum) {
case type_enum_1._Il2CppTypeEnum.VOID:
return value == undefined;
case type_enum_1._Il2CppTypeEnum.BOOLEAN:
return typeof value == "boolean";
case type_enum_1._Il2CppTypeEnum.I1:
case type_enum_1._Il2CppTypeEnum.U1:
case type_enum_1._Il2CppTypeEnum.I2:
case type_enum_1._Il2CppTypeEnum.U2:
case type_enum_1._Il2CppTypeEnum.I4:
case type_enum_1._Il2CppTypeEnum.U4:
case type_enum_1._Il2CppTypeEnum.CHAR:
case type_enum_1._Il2CppTypeEnum.R4:
case type_enum_1._Il2CppTypeEnum.R8:
return typeof value == "number";
case type_enum_1._Il2CppTypeEnum.I8:
return typeof value == "number" || value instanceof Int64;
case type_enum_1._Il2CppTypeEnum.U8:
return typeof value == "number" || value instanceof UInt64;
case type_enum_1._Il2CppTypeEnum.I:
case type_enum_1._Il2CppTypeEnum.U:
case type_enum_1._Il2CppTypeEnum.PTR:
return value instanceof NativePointer;
case type_enum_1._Il2CppTypeEnum.VALUETYPE:
if (type.class.isEnum)
return typeof value == "number";
return value instanceof value_type_1._Il2CppValueType;
case type_enum_1._Il2CppTypeEnum.CLASS:
case type_enum_1._Il2CppTypeEnum.GENERICINST:
case type_enum_1._Il2CppTypeEnum.OBJECT:
return value instanceof object_1._Il2CppObject;
case type_enum_1._Il2CppTypeEnum.STRING:
return value instanceof string_1._Il2CppString;
case type_enum_1._Il2CppTypeEnum.SZARRAY:
return value instanceof array_1._Il2CppArray;
default:
console_1.raise(`isCoherent: "${type.name}" (${type_enum_1._Il2CppTypeEnum[type.typeEnum]}) has not been handled yet. Please file an issue!`);
}
}
function readFieldValue(pointer, type) {
if (pointer.isNull()) {
return undefined;
}
switch (type.typeEnum) {
case type_enum_1._Il2CppTypeEnum.VOID:
return undefined;
case type_enum_1._Il2CppTypeEnum.BOOLEAN:
return !!pointer.readS8();
case type_enum_1._Il2CppTypeEnum.I1:
return pointer.readS8();
case type_enum_1._Il2CppTypeEnum.U1:
return pointer.readU8();
case type_enum_1._Il2CppTypeEnum.I2:
return pointer.readS16();
case type_enum_1._Il2CppTypeEnum.U2:
return pointer.readU16();
case type_enum_1._Il2CppTypeEnum.I4:
return pointer.readS32();
case type_enum_1._Il2CppTypeEnum.U4:
return pointer.readU32();
case type_enum_1._Il2CppTypeEnum.CHAR:
return pointer.readU16();
case type_enum_1._Il2CppTypeEnum.I8:
return pointer.readS64();
case type_enum_1._Il2CppTypeEnum.U8:
return pointer.readU64();
case type_enum_1._Il2CppTypeEnum.R4:
return pointer.readFloat();
case type_enum_1._Il2CppTypeEnum.R8:
return pointer.readDouble();
case type_enum_1._Il2CppTypeEnum.I:
case type_enum_1._Il2CppTypeEnum.U:
case type_enum_1._Il2CppTypeEnum.PTR:
return pointer.readPointer();
case type_enum_1._Il2CppTypeEnum.VALUETYPE:
return type.class.isEnum ? pointer.readS32() : new value_type_1._Il2CppValueType(pointer, type.class);
case type_enum_1._Il2CppTypeEnum.CLASS:
case type_enum_1._Il2CppTypeEnum.GENERICINST:
case type_enum_1._Il2CppTypeEnum.OBJECT:
return new object_1._Il2CppObject(pointer.readPointer());
case type_enum_1._Il2CppTypeEnum.STRING:
return new string_1._Il2CppString(pointer.readPointer());
case type_enum_1._Il2CppTypeEnum.SZARRAY:
return new array_1._Il2CppArray(pointer.readPointer());
default:
console_1.raise(`readFieldValue: "${type.name}" (${type_enum_1._Il2CppTypeEnum[type.typeEnum]}) has not been handled yet. Please file an issue!`);
}
}
exports.readFieldValue = readFieldValue;
function writeFieldValue(pointer, value, type) {
if (!isCoherent(value, type)) {
console_1.raise(`A "${type.name}" is required, but a "${Object.getPrototypeOf(value).constructor.name}" was supplied.`);
}
switch (type.typeEnum) {
case type_enum_1._Il2CppTypeEnum.VOID:
pointer.writePointer(NULL);
break;
case type_enum_1._Il2CppTypeEnum.BOOLEAN: {
pointer.writeU8(+value);
break;
}
case type_enum_1._Il2CppTypeEnum.I1:
pointer.writeS8(value);
break;
case type_enum_1._Il2CppTypeEnum.U1:
pointer.writeU8(value);
break;
case type_enum_1._Il2CppTypeEnum.I2:
pointer.writeS16(value);
break;
case type_enum_1._Il2CppTypeEnum.U2:
pointer.writeU16(value);
break;
case type_enum_1._Il2CppTypeEnum.I4:
pointer.writeS32(value);
break;
case type_enum_1._Il2CppTypeEnum.U4:
pointer.writeU32(value);
break;
case type_enum_1._Il2CppTypeEnum.CHAR:
pointer.writeU16(value);
break;
case type_enum_1._Il2CppTypeEnum.I8: {
const v = value instanceof Int64 ? value.toNumber() : value;
pointer.writeS64(v);
break;
}
case type_enum_1._Il2CppTypeEnum.U8: {
const v = value instanceof UInt64 ? value.toNumber() : value;
pointer.writeS64(v);
break;
}
case type_enum_1._Il2CppTypeEnum.R4:
pointer.writeFloat(value);
break;
case type_enum_1._Il2CppTypeEnum.R8:
pointer.writeDouble(value);
break;
case type_enum_1._Il2CppTypeEnum.I:
case type_enum_1._Il2CppTypeEnum.U:
case type_enum_1._Il2CppTypeEnum.PTR:
pointer.writePointer(value);
break;
case type_enum_1._Il2CppTypeEnum.VALUETYPE:
if (type.class.isEnum)
pointer.writeS32(value);
else
pointer.writePointer(value.handle);
break;
case type_enum_1._Il2CppTypeEnum.STRING:
pointer.writePointer(value.handle);
break;
case type_enum_1._Il2CppTypeEnum.CLASS:
case type_enum_1._Il2CppTypeEnum.OBJECT:
case type_enum_1._Il2CppTypeEnum.GENERICINST:
pointer.writePointer(value.handle);
break;
case type_enum_1._Il2CppTypeEnum.SZARRAY:
pointer.writePointer(value.handle);
break;
default:
console_1.raise(`writeFieldValue: "${type.name}" (${type_enum_1._Il2CppTypeEnum[type.typeEnum]}) has not been handled yet. Please file an issue!`);
}
}
exports.writeFieldValue = writeFieldValue;
function readRawValue(pointer, type) {
if (pointer == undefined) {
return;
}
switch (type.typeEnum) {
case type_enum_1._Il2CppTypeEnum.VOID:
return;
case type_enum_1._Il2CppTypeEnum.BOOLEAN:
return !!+pointer;
case type_enum_1._Il2CppTypeEnum.I1:
return +pointer;
case type_enum_1._Il2CppTypeEnum.U1:
return +pointer;
case type_enum_1._Il2CppTypeEnum.I2:
return +pointer;
case type_enum_1._Il2CppTypeEnum.U2:
return +pointer;
case type_enum_1._Il2CppTypeEnum.I4:
return +pointer;
case type_enum_1._Il2CppTypeEnum.U4:
return +pointer;
case type_enum_1._Il2CppTypeEnum.CHAR:
return +pointer;
case type_enum_1._Il2CppTypeEnum.I8:
return int64(pointer.toString());
case type_enum_1._Il2CppTypeEnum.U8:
return int64(pointer.toString());
case type_enum_1._Il2CppTypeEnum.R4:
return pointer.readFloat();
case type_enum_1._Il2CppTypeEnum.R8:
return pointer.readDouble();
case type_enum_1._Il2CppTypeEnum.I:
case type_enum_1._Il2CppTypeEnum.U:
case type_enum_1._Il2CppTypeEnum.PTR:
return pointer.isNull() ? NULL : pointer.readPointer();
case type_enum_1._Il2CppTypeEnum.VALUETYPE:
return type.class.isEnum ? +pointer : new value_type_1._Il2CppValueType(pointer, type.class);
case type_enum_1._Il2CppTypeEnum.STRING:
return pointer.isNull() ? undefined : new string_1._Il2CppString(pointer);
case type_enum_1._Il2CppTypeEnum.CLASS:
case type_enum_1._Il2CppTypeEnum.GENERICINST:
case type_enum_1._Il2CppTypeEnum.OBJECT:
return new object_1._Il2CppObject(pointer);
case type_enum_1._Il2CppTypeEnum.SZARRAY:
return new array_1._Il2CppArray(pointer);
default:
console_1.raise(`readRawValue: "${type.name}" (${type_enum_1._Il2CppTypeEnum[type.typeEnum]}) has not been handled yet. Please file an issue!`);
}
}
exports.readRawValue = readRawValue;
function allocRawValue(value, type) {
if (!isCoherent(value, type)) {
console_1.raise(`A "${type.name}" is required, but a "${Object.getPrototypeOf(value).constructor.name}" was supplied.`);
}
switch (type.typeEnum) {
case type_enum_1._Il2CppTypeEnum.VOID:
return NULL;
case type_enum_1._Il2CppTypeEnum.BOOLEAN:
return ptr(+value);
case type_enum_1._Il2CppTypeEnum.I1:
return ptr(value);
case type_enum_1._Il2CppTypeEnum.U1:
return ptr(value);
case type_enum_1._Il2CppTypeEnum.I2:
return ptr(value);
case type_enum_1._Il2CppTypeEnum.U2:
return ptr(value);
case type_enum_1._Il2CppTypeEnum.I4:
return ptr(value);
case type_enum_1._Il2CppTypeEnum.U4:
return ptr(value);
case type_enum_1._Il2CppTypeEnum.CHAR:
return ptr(value);
case type_enum_1._Il2CppTypeEnum.I8: {
const v = value instanceof Int64 ? value.toNumber() : value;
return ptr(v);
}
case type_enum_1._Il2CppTypeEnum.U8: {
const v = value instanceof UInt64 ? value.toNumber() : value;
return ptr(v);
}
case type_enum_1._Il2CppTypeEnum.R4:
return Memory.alloc(4).writeFloat(value);
case type_enum_1._Il2CppTypeEnum.R8:
return Memory.alloc(8).writeDouble(value);
case type_enum_1._Il2CppTypeEnum.PTR:
case type_enum_1._Il2CppTypeEnum.I:
case type_enum_1._Il2CppTypeEnum.U:
return value;
case type_enum_1._Il2CppTypeEnum.VALUETYPE:
return type.class.isEnum ? ptr(value) : value.handle;
case type_enum_1._Il2CppTypeEnum.STRING:
return value.handle;
case type_enum_1._Il2CppTypeEnum.CLASS:
case type_enum_1._Il2CppTypeEnum.OBJECT:
case type_enum_1._Il2CppTypeEnum.GENERICINST:
return value.handle;
case type_enum_1._Il2CppTypeEnum.SZARRAY:
return value.handle;
default:
console_1.raise(`allocRawValue: "${type.name}" (${type_enum_1._Il2CppTypeEnum[type.typeEnum]}) has not been handled yet. Please file an issue!`);
}
}
exports.allocRawValue = allocRawValue;
function dump(filePath) {
if (variables_1.domain == undefined) {
console_1.raise("Not yet initialized!");
}
if (filePath == undefined) {
const coreModuleName = "UnityEngine.CoreModule" in variables_1.domain.assemblies ? "UnityEngine.CoreModule" : "UnityEngine";
const applicationMethods = variables_1.domain.assemblies[coreModuleName].image.classes["UnityEngine.Application"].methods;
const persistentDataPath = applicationMethods.get_persistentDataPath.invoke().content;
const getIdentifierName = "get_identifier" in applicationMethods ? "get_identifier" : "get_bundleIdentifier";
const identifier = applicationMethods[getIdentifierName].invoke().content;
const version = applicationMethods.get_version.invoke().content;
filePath = `${persistentDataPath}/${identifier}_${version}.cs`;
}
const file = new File(filePath, "w");
for (const assembly of variables_1.domain.assemblies) {
console_1.inform(`Dumping ${assembly.name}...`);
for (const klass of assembly.image.classes) {
file.write(klass.toString());
}
}
file.flush();
file.close();
console_1.ok(`Dump saved to ${filePath}.`);
}
exports.dump = dump;