frida-il2cpp-bridge
frida-il2cpp-bridge copied to clipboard
Fix tracer return value parse error
When a method definition does not include an null value (example: MyClass getMyClass();
) but something went wrong (or null check was stripped by il2cpp compiler) it can return NULL
so Error: abort was called
will be thrown (I think because NativeFunction
expected pointer
return type but got null)
One more note, "real" IL2CPP code checks if the value is null (at least ghidra decompiler shows that), so nothing bad should happen
Before:
il2cpp:
0x00d42bc0 ┌─MyClass::.ctor(this = (MyClass))
0x00d42bc0 └─MyClass::.ctor
Error: abort was called
at callback (/home/commonuserlol/index.js:1316)
After:
il2cpp:
0x00d42bc0 ┌─MyClass::.ctor(this = (MyClass))
0x00d42bc0 └─MyClass::.ctor
il2cpp:
0x00d41af4 ┌─MyClass::OnEnable(this = SOME_VIEW(Clone) (MyClass))
0x00d41e48 │ ┌─MyClass::GetNextReward(this = SOME_VIEW(Clone) (MyClass))
0x00d41e48 │ └─MyClass::GetNextReward = null
0x00d422c8 │ ┌─MyClass::ParseReward(this = SOME_VIEW(Clone) (MyClass), reward = null)
0x00d421f0 │ │ ┌─MyClass::OnOkButton(this = null)
0x00d421f0 │ │ └─MyClass::OnOkButton
0x00d422c8 │ └─MyClass::ParseReward
0x00d41af4 └─MyClass::OnEnable
Thanks, I'm fine with that. But perhaps it would be more meaningful if we reported it, even a simple message is fine something like:
0x00d41e48 │ ┌─MyClass::GetNextReward(this = SOME_VIEW(Clone) (MyClass))
0x00d41e48 │ └─MyClass::GetNextReward = null [native IL2CPP excpetion occurred]
Bonus points if you make it red!
What do you think?
I haven’t seen something similar here, how would you implement a value check??
-
ret.equals(NULL) ? "err" : (ret == undefined ? "" : "formatted ret")
-
if (ret.equals(NULL)) ...; else if (...); else ...
Okay, now it looks like
0x00d41e48 │ ┌─MyClass::GetNextReward(this = SOME_VIEW(Clone) (MyClass))
0x00d41e48 │ └─MyClass::GetNextReward = null [native IL2CPP excpetion occurred]
Also colored like error from console.ts/raise
I did with 1st variant from my previous message (not commited yet)
const result = returnValue == undefined ? ""
: (returnValue.equals(NULL) ?
" = \x1b[0m\x1b[38;5;9mnull [native IL2CPP excpetion occurred]\x1b[0m"
: ` = \x1b[36m${fromFridaValue(returnValue, method.returnType)}\x1b[0m`);
If you prefer other style let me know
Correct impl is
const result = returnValue == undefined ? "" :
returnValue instanceof NativePointer ?
returnValue.equals(NULL)
? " = \x1b[0m\x1b[38;5;9mnull [native IL2CPP excpetion occurred]\x1b[0m"
: ` = \x1b[36m${fromFridaValue(returnValue, method.returnType)}\x1b[0m`
: ` = \x1b[36m${fromFridaValue(returnValue, method.returnType)}\x1b[0m`;
now tracer works (in prev piece of code it died when returnValue
was non-native pointer)
Still waiting for answer about codestyle
Hmm, this is how I would do it:
let returnValue;
let isError = false;
try {
returnValue = method.nativeFunction(...args);
} catch (_) {
isError = true;
}
if ((this as InvocationContext).threadId == threadId) {
// prettier-ignore
state.buffer.push(`\x1b[2m0x${paddedVirtualAddress}\x1b[0m ${`│ `.repeat(--state.depth)}└─\x1b[33m${method.class.type.name}::\x1b[1m${method.name}\x1b[0m\x1b[0m${returnValue == undefined ? "" : ` = \x1b[36m${fromFridaValue(returnValue, method.returnType)}`}\x1b[0m${isError ? " \x1b[38;5;9m[native IL2CPP excpetion occurred]\x1b[0m" : ""}`);
state.flush();
}
return isError ? NULL : returnValue;
However, I'm wondering what happens in case Frida expect us to return a number
(let's say an exception occurs within System.In32 Foo();
) - I believe we can't always return NULL
(a NativePointer
), can we?
Il2Cpp compiled code already have checks for NativePointer
but for number
it can be UB (undefined behavior) as c++ have it. Frida can interpret it as 0 afaik. Probably easiest way to check is replacing some method with Int32
return type (NativeFunction
provides exceptions
option which stops generating JS exception and app should handle it by itself) and throw some exception in it. But lemme test this tomorrow (here is 0:45)
Actually we can shift responsibility to the app (it will handle NULL), what do think about this? upd: nvm it crashes with this
Hmm, this is how I would do it:
let returnValue; let isError = false; try { returnValue = method.nativeFunction(...args); } catch (_) { isError = true; } if ((this as InvocationContext).threadId == threadId) { // prettier-ignore state.buffer.push(`\x1b[2m0x${paddedVirtualAddress}\x1b[0m ${`│ `.repeat(--state.depth)}└─\x1b[33m${method.class.type.name}::\x1b[1m${method.name}\x1b[0m\x1b[0m${returnValue == undefined ? "" : ` = \x1b[36m${fromFridaValue(returnValue, method.returnType)}`}\x1b[0m${isError ? " \x1b[38;5;9m[native IL2CPP excpetion occurred]\x1b[0m" : ""}`); state.flush(); } return isError ? NULL : returnValue;
However, I'm wondering what happens in case Frida expect us to return a
number
(let's say an exception occurs withinSystem.In32 Foo();
) - I believe we can't always returnNULL
(aNativePointer
), can we?
By the way, there's a typo here, it should be exception
not excpetion
.
I found method which already throws error (but target still works)
il2cpp: System.NullReferenceException: Object reference not set to an instance of an object.
at User.GetBalance (System.String a) [0x00000] in <00000000000000000000000000000000>:0
il2cpp:
0x00ee1ad4 ┌─User::GetBalance(this = name, a = "remove_ads")
0x00ee1ad4 └─User::GetBalance = null [native IL2CPP excpetion occurred]
Error: expected an integer // due returning NULL
after commit:
il2cpp:
0x00ee1ad4 ┌─User::GetBalance(this = name, a = "remove_ads")
0x00ee1ad4 └─User::GetBalance = 0 [native IL2CPP exception occurred]
// No Error: expected an integer
For other types it should work
Note: void
is fine with both NULL
and 0
(not sure it's your code or frida default behavior), tested with:
Backend.method("HandleResponse").implementation = function (req) {
this.method<void>("HandleResponse").invoke(req);
// let's imagine that an exception was caught (abort was called), we returning null
console.log("test: return NULL for System.Void HandleResponse(HttpRequest request);");
console.log(`isPrimitive for original ret (void): ${this.method<void>("HandleResponse").returnType.isPrimitive}`)
return NULL;
}
output:
test: return NULL for System.Void HandleResponse(HttpRequest request);
isPrimitive for original ret (void): false
// No error, 0 is ok too
const cm = ((globalThis as any).cm = new CModule(`int lol(void) { return 1; }`));
Interceptor.replace(cm.lol, new NativeCallback(() => NULL as any, "int", []));
console.log(new NativeFunction(cm.lol, "int", [])());
I get Error: expected an integer
Look, i pushed one more commit which does method.returnValue.isPrimitive ? 0 : NULL
(int/System.Int32 is primitive)
ping
Sorry for bothering. looks like you are familiar with this module. would you mind help my question in issue? https://github.com/vfsfitvnm/frida-il2cpp-bridge/issues/506 it tortures me, thanks in advance.
@vfsfitvnm please review this pr
@vfsfitvnm ping
@commonuserlol Hi, I think I'm having the same issue here. I've tried building your fork and there were 4 errors before, but now there are only 2 left. Could you please take a look? I'd really appreciate it.
- Error: abort was called
- at invokeRaw (structs/method.ts:234)
- at toString (structs/object.ts:58)
- at concat (native)
- at <anonymous> (tracer.ts:297)
- at map (native)
- at callback (tracer.ts:297)
- Error: access violation accessing 0x10
- at get length (structs/string.ts:26)
- at get content (structs/string.ts:5)
- at toString (structs/object.ts:58)
- at concat (native)
- at <anonymous> (tracer.ts:297)
- at map (native)
- at callback (tracer.ts:297)
@fakekey Hi, I haven't these error when tested, can you tell game name (or send link)
@fakekey Hi, I haven't these error when tested, can you tell game name (or send link)
I can suggest you take a look at games.flexus.trainminer, in fact there are the same mistakes that @fakekey said
I see. I think we shouldn't flood there. Please create issue on my repo or you can DM me at discord (same name as on github) to track it. I'll look into tomorrow
Hi @fakekey, @Vladik01-11 - reproduced, pushed and tested on games.flexus.trainminer
. Should be fine now
Confirmed by fakekey (via discord)
Reverted due return value parsed incorrectly.