vfsfitvnm / frida-il2cpp-bridge

A Frida module to dump, trace or hijack any Il2Cpp application at runtime, without needing the global-metadata.dat file.
https://github.com/vfsfitvnm/frida-il2cpp-bridge/wiki
MIT License
974 stars 199 forks source link

il2cpp-bridge source code theory question #239

Closed Chensem closed 1 year ago

Chensem commented 1 year ago

I want to know some deep theory about il2cpp-bridge

Il2Cpp.perform

image

https://github.com/vfsfitvnm/frida-il2cpp-bridge/blob/e99fb8a2d4812a52e40e6cf2f8ceb5a6b4db6e14/src/il2cpp/base.ts#L204

especially this line , the line run on il2cpp thread ? if this line run on il2cpp thread , i can under stand to code below this line , if not , i can't understand

wish to get your reply

vfsfitvnm commented 1 year ago

Hi!

1

if (Il2Cpp.Api._getCorlib().isNull()) {
    await new Promise<void>(resolve => {
        const interceptor = Interceptor.attach(Il2Cpp.Api._init, {
            onLeave() {
                interceptor.detach();
                setImmediate(resolve);
            }
        });
    });
}

This snippet waits for Il2Cpp to be initialized. When libil2cpp.so (or whatever) is loaded into memory and its library constructors are invoked, we still need to wait for the call to il2cpp_init to finish.

int il2cpp_init(const char* domain_name)
{
    // Use environment's default locale
    setlocale(LC_ALL, "");

    return Runtime::Init(domain_name);
}

Runtime::Init initializes the Il2Cpp world (metadata, types and other things). If we try to invoke a il2cpp_* function before the initialization process finishes, we'll mostly run into problems or errors.

Il2Cpp.Api._getCorlib().isNull() is a workaround to check whether the call to il2cpp_init is already finished. If it's not, we hook il2cpp_init and wait. When the invocation finishes, we are here:

onLeave() {
    interceptor.detach();
    setImmediate(resolve);
}

We detach the interceptor and call resolve so the await for the Promise ends. We must use setImmediate, otherwise our code will keep executing in the same thread who called il2cpp_init (we don't want that to happen).

2

let thread = this.currentThread;
const isForeignThread = thread == null;

if (thread == null) {
    thread = Il2Cpp.Domain.attach();
}

try {
    const result = block();
    return result instanceof Promise ? await result : result;
} catch (e: any) {
    (globalThis as any).console.log(e);
    throw e;
} finally {
    if (isForeignThread) {
        thread.detach();
    }
}

This snippet makes sure block is invoked in a Il2Cpp thread. this.currentThread (which calls il2cpp_thread_current) returns a null pointer if the caller thread is not attached to Il2Cpp (= it's not a Il2Cpp thread). So, if the caller thread isn't attached to Il2Cpp, isForeignThread is true.

When isForeignThread is true, we are responsible to attach the caller thread to Il2Cpp (Il2Cpp.Domain.attach()) and to detach it later (thread.detach()). This ensures block is executed in a thread attached to Il2Cpp.

Chensem commented 1 year ago

oh , thank you so much , i understand the code via debug the snippet and another question

static attach() {
    return new Il2Cpp.Thread(Il2Cpp.Api._threadAttach(this));
}

why here _threadAttach function can pass an object ? _threadAttach function is NativeFunction and the argument shoule be nativePointer ? can replace by this.handle
the NativeFunction detect the argument internal ? if the argument is an object , it will pick up the object's handle ?

Chensem commented 1 year ago

oh , thank you so much , i understand the code via debug the snippet and another question

static attach() {
    return new Il2Cpp.Thread(Il2Cpp.Api._threadAttach(this));
}

why here _threadAttach function can pass an object ? _threadAttach function is NativeFunction and the argument shoule be nativePointer ? can replace by this.handle the NativeFunction internal detect the argument ? if the argument is an object , it will pick up the object's handle ?

I see get object() method

vfsfitvnm commented 1 year ago

You can pass a NativePointerValue, which is a NativePointer or ObjectWrapper.

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/frida-gum/index.d.ts#L1624

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/frida-gum/index.d.ts#L1597