Closed ExternalAddress4401 closed 1 year ago
Yeah, this is a bug I "discovered" few months ago, but I never filed an issue for that smh. It's not related to a specific application. See these release notes and the source code!
Ahh I see.
Specifically it looks like it starts at https://github.com/vfsfitvnm/frida-il2cpp-bridge/blob/1de863fee487bd7c67baa579ae11c2c12698beaa/src/il2cpp/structs/gc.ts#L67
Which throws Error: access violation accessing 0x0
But as I'm a total noob with all this I wouldn't even know where to begin attempting to find that function in something like Ghidra and tracing what might be happening.
I think for my case a temporary "solution" can be this ghetto generic wrapper to hook the constructor of any objects I used Il2Cpp.GC.choose for before and save them to variables until something better is found and can be replaced later.
const objects: Record<string, any> = {};
const collect = (library: string, klass: string) => {
const assembly = getAssembly(library);
assembly.class(klass).method(".ctor").implementation = function (...args) {
this.method(".ctor").invoke(...args);
if (!objects[klass]) {
objects[klass] = [];
}
objects[klass].push(this);
};
};
Sure there might be issues with that but I'll get to those later.
Beware of two things:
1) Don't use the class name as a key as there might be duplicates; use Il2Cpp.Class::handle::toInt32|toString
instead.
2) The objects you are pushing to the array may be garbage collected sooner or later, you can prevent so by using GC handles:
const handles: Record<string, Il2Cpp.GC.Handle[]> = {};
// ...
if (this instanceof Il2Cpp.Object) {
handles[klass].push(this.ref(true));
}
// ...
for (const handle of handles[klass]) {
const object = handle.target;
// ...
handle.free();
}
I don't know if that counts as a workaround or if it's the proper solution, but it looks like it's fixed :smile:
I don't think this issue should be closed. The thing still crashes.
We can greatly reduce the crashes by stopping garbage collection before using it, and then restarting garbage collection, this causes the game to drop frames / freeze, but its better than a crash -- this approach will still crash on occasion, but it goes from crash every time to crash randomly.
Il2Cpp.GC.stopWorld();
Il2Cpp.GC.choose()
Il2Cpp.GC.startWorld();
@ash47 Yeah that's what I did in the commit, however I could not reproduce the crashes. Would you elaborate (i.e. application name, platform, timing etc)?
Hi, I can give you anything you need, this crash happens in Bloons TD 6, if I select a class such as system.string, it's all good, if I select a class that is based on a monobehaviour then it crashes -- if I log inside of the for loop, it does work, I can see the logs for the instance, it crashes after the loop like a timing issue.
I'm using Windows.
I can buy you a copy of the game if that helps fix the issue.
I can provide the script or anything else, just let me know exactly what would be useful and I can provide it, I can test things too if required.
On Wed, 12 Apr 2023, 4:57 am vfsfitvnm, @.***> wrote:
@ash47 https://github.com/ash47 Yeah that's what I did in the commit, however I could not reproduce the crashes. Would you elaborate (i.e. application name, platform, timing etc)?
— Reply to this email directly, view it on GitHub https://github.com/vfsfitvnm/frida-il2cpp-bridge/issues/265#issuecomment-1503937977, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4UUDT4UD2VFWBIALDKGBDXAWSTFANCNFSM6AAAAAAWDJJNCY . You are receiving this because you were mentioned.Message ID: @.***>
Would you share the script you are using?
I have a full GUI set up for creating mods for games which lets me hook various modding engines including Frida, and for this mod, I tried out frida-il2cpp-bridge
, The mod launcher will load the script, it will pass in all the current options via updateOption
RPC call, it would call load but i dont have anything specifically hooked to run when load is run, and then when a user presses "Add Cash" button in the GUI, it runs the runAction
which ultiamtely runs the code. I am using the version of frida-il2cpp-bridge
that is sitting on NPM right now.
I'll re-write the code below to remove the stuff specific to my Mod Manager.
import "frida-il2cpp-bridge";
let totalToAdd = 0;
let addingIt = false;
async function addIt(amount) {
totalToAdd += amount;
if (isNaN(totalToAdd)) {
totalToAdd = 1000;
}
// If nothing to change, stop
if (totalToAdd === 0) return;
// only do it once at a time
if (addingIt) return;
addingIt = true;
// cache how much we need to add and set this to zero
let cacheAmountToAdd = totalToAdd;
totalToAdd = 0;
Il2Cpp.perform(() => {
// Stop the world
Il2Cpp.GC.stopWorld();
// do the stuff
let results = null;
try {
const assemblyCSharp = Il2Cpp.Domain.assembly("assembly-csharp").image;
results = Il2Cpp.GC.choose(
assemblyCSharp.class("Assets.Scripts.Simulation.Simulation")
);
} catch (e) {
// do nothing
console.log(e);
}
Il2Cpp.GC.startWorld();
if(results !== null) {
try {
results.forEach((instance) => {
// Get cash and amount to add
const cashIndex = -1;
const currentCash = instance.method('GetCash').invoke(cashIndex);
// Add it
instance.method('SetCash').invoke(currentCash + cacheAmountToAdd, cashIndex);
});
} catch(e) {
console.log(e);
}
}
// Wait a bit before we are done
setTimeout(() => {
addingIt = false;
addIt(0);
}, 1000);
});
}
async function handleAction(actionUuid, actionArgs) {
const addAmount = parseInt(optionValues['550f2ae8-8069-4411-b1c1-3d14a84a0a81']?.currentValue || '0');
if (isNaN(addAmount)) {
addAmount = 1000;
}
// get onto a new thread
setTimeout(() => {
addIt(addAmount);
}, 1);
}
const optionValues = {};
rpc.exports = {
updateOption: async (optionInfo) => {
if (!optionValues.hasOwnProperty(optionInfo.optionUuid)) optionValues[optionInfo.optionUuid] = {};
optionValues[optionInfo.optionUuid] = {
...optionValues[optionInfo.optionUuid],
...optionInfo,
}
if (typeof (handleOptionUpdate) === "function") {
handleOptionUpdate(optionInfo);
}
},
runAction: async (actionUuid, actionArgs) => {
if (typeof (handleAction) === "function") {
return await handleAction(actionUuid, actionArgs);
}
return null;
},
load: async () => {
if (typeof (handleLoad) === "function") {
return await handleLoad();
}
return null;
},
unload: async () => {
if (typeof (handleUnload) === "function") {
return await handleUnload();
}
return null;
},
};
I wrote the code the way I did because each time the "add cash" is called, it freezes the game, so, i made it collect all the times it is pressed and only execute it once per second to increase performance. I also played around with the order of starting and stopping the world, starting the world again after we've done the call to Il2Cpp.GC.choose
seems to be ok.
---- Here's a rewrite that might be more useful:
import "frida-il2cpp-bridge";
const cashIndex = -1; // hard coded to be for player 1
const amountOfCashToAdd = 1000;
Il2Cpp.perform(() => {
// Stop the world
Il2Cpp.GC.stopWorld();
const assemblyCSharp = Il2Cpp.Domain.assembly("assembly-csharp").image;
Il2Cpp.GC.choose(
assemblyCSharp.class("Assets.Scripts.Simulation.Simulation")
).forEach((instance) => {
// Get the current cash
const currentCash = instance.method('GetCash').invoke(cashIndex);
// Add the current cash and the amount we want to add
instance.method('SetCash').invoke(currentCash + amountOfCashToAdd, cashIndex);
});
Il2Cpp.GC.startWorld();
});
The thing is, this works fine, aside from the freezing, it's just that once every so often it will crash, it becomes much more obvious when using a GUI because it's really easy to keep clicking "Add Cash"
Please let me know if you need any other details.
The freezing probably happens because you are invoking GetCash
/SetCash
from the Frida threa, you might want to call it from the main thread. And maybe, you need to "restart the world" before invoking such methods.
Il2Cpp.perform(() => {
const AssemblyCSharp = Il2Cpp.Domain.assembly("Assembly-CSharp").image;
const AssetsScriptsSimulationSimulation = AssemblyCSharp.class("Assets.Scripts.Simulation.Simulation");
Il2Cpp.GC.stopWorld();
const instances = Il2Cpp.GC.choose(AssetsScriptsSimulationSimulation);
Il2Cpp.GC.startWorld();
Il2Cpp.attachedThreads[0].schedule(() => {
instances.forEach(instance => {
const currentCash = instance.method<number>("GetCash").invoke(cashIndex);
instance.method("SetCash").invoke(currentCash + amountOfCashToAdd, cashIndex);
});
});
});
I had a bit of a play around, I'm on Windows, it seems like Il2Cpp.attachedThreads[0]
doesn't work in Bloons TD6, it results in Error: access violation accessing 0x7d44
I did some quick debugging via:
console.log('1');
console.log('2', Il2Cpp);
console.log('3', Il2Cpp.attachedThreads);
console.log('4', Il2Cpp.attachedThreads[0]);
I get numbers 1, 2, and 3, there is no 4, just the Error: access violation accessing 0x7d44
Is there something such as Discord I can message you directly on?
Yeah, vfsfitvnm#7025
Thanks, I added you as a friend, I can't message you unless you accept.
On Sat, Apr 15, 2023 at 9:04 PM vfsfitvnm @.***> wrote:
Yeah, vfsfitvnm#7025
— Reply to this email directly, view it on GitHub https://github.com/vfsfitvnm/frida-il2cpp-bridge/issues/265#issuecomment-1509729824, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4UUDQ5HEK2HP7YFX6II4TXBJ6D7ANCNFSM6AAAAAAWDJJNCY . You are receiving this because you were mentioned.Message ID: @.***>
I suspect this might be some sort of anti frida tampering but I'm not smart enough to make that determination.
The newest version of https://apkcombo.com/beatstar/com.spaceapegames.beatstar/
Causes a crash with the following
I'm not smart enough to determine why this crashes. I've tried going to
0000000000a22d60
in Ghidra but what's there doesn't match what Frida is telling me is at that address and I'm struggling to determine which function this even is.Is this some sort of anti frida code at work or a library issue?