Skip to content

Conversation

@rubenvereecken
Copy link
Contributor

@rubenvereecken rubenvereecken commented Jan 9, 2025

This isn't quite ready, but I wanted to start the conversation.

onEnter and onLeave (after Frida's own) are a bit less heavy-handed than replacing the whole implementation, and don't require explicit calls to the original method if you don't want to change output values. They're useful for inspection.

It's now possible to do something like

Il2Cpp.corlib.class('System.String').method('Concat', 1).onEnter = (strings) => {
  console.log(strings);
}

Il2Cpp.corlib.class('System.String').method('Concat', 1).onLeave = (retval) => {
  return Il2Cpp.string('new return value');
}

However, one of my real reasons for writing them is that set implementation appears to break some methods. There are some types and values for which the sequence of fromFridaValue -> toFridaValue isn't idempotent, and breaks. I'll send a separate PR once I've fully figured out what's happening, but for now this PR helps me debug.

I'll leave some comments inline. Also, I'm super not precious about what this API should look and feel like.

}

/** @internal */
export function read(pointer: NativePointer, type: Il2Cpp.Type): Il2Cpp.Field.Type {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was an interesting one. Parameters are different from eg Fields, in that they don't need to be dereferenced first. If you get a pointer that's meant to be a Il2Cpp.String, then no need to call ptr.readPointer() first.

Made this new behaviour backward compatible.

raise(`couldn't read the value from ${pointer} using an unhandled or unknown type ${type.name} (${type.typeEnum}), please file an issue`);
}

/** @internal */
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Err unrelated but could we have these exposed in the API? Bit more low level but super useful.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That method is indeed here - if you invoke it, it works. It just isn't exposed (yet?)!

wrapOnLeave(block: OnLeaveCallback<T>): ((this: InvocationContext, retval: InvocationReturnValue) => void) {
return (retval: InvocationReturnValue) => {
// TODO grab `this` pointer during `onEnter`
const thisObject = this.class
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we derive this from the first argument sometimes, I suppose we'll always have to hijack both onEnter and onLeave at the same time, otherwise we can't propagate this to onLeave. Edit coming up...

@vfsfitvnm
Copy link
Owner

vfsfitvnm commented Jan 10, 2025

The main reason why I didn't use Interceptor.attach is because Frida doesn't support reading/writing floating-point values (System.Single, System.Double) - such values live in different CPU registers, and Frida doesn't expose them (yet). So, the only option was Interceptor.replace.

Ref: frida/frida#266 (comment)

However, a lot of time passed since that, and Frida might have implemented the support for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants