frida-il2cpp-bridge
frida-il2cpp-bridge copied to clipboard
Il2Cpp.Thread::id doesn't work on Windows
I'm trying to schedule a function on the main thread but looks like there's no way to do it.
This is what i've tried:
Il2Cpp.perform(() => {
// This never gets called
Il2Cpp.scheduleOnInitializerThread(() => {
console.log("Test");
});
// Producing error: access violation accessing 0x...
Il2Cpp.currentThread?.schedule(() => {
console.log("Test");
});
// Producing error: access violation accessing 0x...
Il2Cpp.attachedThreads[0].schedule(() => {
console.log("Test");
});
});
Am i missing something?
Il2Cpp.scheduleOnInitializerThread
is a last resort, its wobbly and probably won't work (however, make sure the app is in the foreground).
Il2Cpp.currentThread?.schedule
is not expected to work (in you specific scenario), since the Frida thread lacks of a synchronization context.
Il2Cpp.attachedThreads[0].schedule
should not throw an exception (access violation is a bad thing), what's the Unity version of that app? If on Android, what's the package name?
It's a Unity app, version 2020.3.32f1
Yeah of course it's a Unity app, but what's its name? I can try to download it so it can reproduce the issue
Unfortunately I cannot test right now it as I don't have Windows. Would you attach the whole stack trace? So I can at least know where the problem is (I expect here or here).
Yeah, here it is:
Error: access violation accessing 0x2a24
at <anonymous> (frida/runtime/core.js:138)
at get idOffset (node_modules/frida-il2cpp-bridge/dist/il2cpp/structs/thread.js:19)
at call (native)
at <anonymous> (node_modules/decorator-cache-getter/dist/index.js:9)
at get id (node_modules/frida-il2cpp-bridge/dist/il2cpp/structs/thread.js:28)
at schedule (node_modules/frida-il2cpp-bridge/dist/il2cpp/structs/thread.js:92)
at <anonymous> (agent/index.ts:133)
at perform (node_modules/frida-il2cpp-bridge/dist/il2cpp/base.js:176)
Looks like there is a problem getting the thread id.
Yep, unfortunately there's no straightforward way to retrieve the native thread id of a System.Threading.Thread
. I'll see what I can do for Windows.
However, why are you using such function (schedule
)? I expect it to be seldomly used
Well, let me explain:
If invoke my function when i load the agent, everything works great, i.e:
Il2Cpp.perform(() => {
const myClass = ...
myClass.method("myMethod").invoke(); // <- works!
});
BUT, this is not my case. I need to invoke my methods on demand, after the agent has been loaded, using the Messages system.
Il2Cpp.perform(() => {
recv("myCommand", onCommandReceived);
});
function onCommandReceived(): void {
const myClass = ...
myClass.method("myMethod").invoke(); // <- this produces access violation error
}
I'm pretty sure the problem occurs when the method is called from another thread, that's why i was looking for a way to schedule actions on the main thread.
I don't remember if recv
is blocking (I guess not), so, when onCommandReceived
is invoked, Il2Cpp.perform
is already finished, hence the Frida thread is not attached to Il2Cpp anymore.
You should do the following instead:
recv("myCommand", () => Il2Cpp.perform(onCommandReceived));
function onCommandReceived(): void {
const myClass = ...
myClass.method("myMethod").invoke();
}
You can verify the connection between the caller thread and Il2Cpp with the property Il2Cpp::currentThread
: if it returns null, then the caller thread is not attached, hence the access violation error.
However, if you still get an access violation error, then you are right: you must ensure to call myMethod
from the main thread.
How could I not think about it before... Now it works! Thank you so much.
Perfect! I'll leave this issue open until I fix the thread id thing.
Hi @vfsfitvnm,
I tried adding this code in base.js:
static scheduleOnMainThread(block) {
const CurrentThreadIsMainThread = this.internalCall("UnityEngine.Object::CurrentThreadIsMainThread", "bool", []);
return new Promise(resolve => {
const listener = Interceptor.attach(Il2Cpp.Api._threadCurrent, () => {
if (CurrentThreadIsMainThread() ) {
listener.detach();
const result = block();
setImmediate(() => resolve(result));
}
});
});
}
and then I ran this script:
Il2Cpp.scheduleOnMainThread(()=>{
console.log('mainthread:', Il2Cpp.currentThreadIsMainThread())
})
I got this output:
Address found: 0x79f88c2000
mainthread: 1
Not sure how to properly test and verify that it is working properly and I'm not sure if UnityEngine.Object::CurrentThreadIsMainThread
is consistent across platforms and unity versions.
I just tried it on an invoke that always causes my game to freeze and now it works and doesn't freeze anymore.
Created a pull request
@wildsheepz Thanks for looking at this, but your solution only is a partial solution to the problem. The problem here is https://github.com/vfsfitvnm/frida-il2cpp-bridge/blob/master/src/il2cpp/structs/thread.ts#L14 throwing an access violation. We need to find a way to find the thread id offset - so we can get the thread id of every thread - checking if the current thread is the main one is merely a workaround.
I don't have windows now so I can't test, but adding a try/catch on that line may be sufficient:
for (let i = 0; i < 1024; i++) {
try {
const candidate = handle.add(i).readS32();
if (candidate == currentThreadId) {
return i;
}
} catch (e: any) {
}
}