Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: illegal instruction on initialization step. #33

Closed
truelecter opened this issue Sep 13, 2021 · 6 comments
Closed

Error: illegal instruction on initialization step. #33

truelecter opened this issue Sep 13, 2021 · 6 comments

Comments

@truelecter
Copy link

truelecter commented Sep 13, 2021

Currently for third.party.app you'll receive Error: illegal instruction when calling

Il2Cpp.perform(() => {
    // ... literally anything. 
   console.log('It works');
})

when spawning app with frida -U -f third.party.app -l _.js --no-pause --runtime=v8.

After debugging this error, I found that issues is with Il2Cpp.Api._getCorlib() call and for Il2Cpp.perform line 83 particularly:

await forModule(this.il2CppModuleName);
if (Il2Cpp.Api._getCorlib().isNull()) {
await new Promise<void>(resolve => {
const interceptor = Interceptor.attach(Il2Cpp.Api._init, {

Currently I fixed this with simple and ugly patch right before if statement

function wa(milis) {
    return new Promise(resolve => setTimeout(resolve, milis));
}

while (true) {
    try {
        Il2Cpp.Api._getCorlib()
        break;
    } catch (e) {
        console.log(e);
        await wa(100);
    }    
} 

Any suggestions what am I doing wrong?

OS: Android 9
Magisk: 23 (23000), App: 23 (23000)
Frida server: 15.1.1-1 (from magisk module). Tried with static binaries from frida/frida (this one exactly) as well

@vfsfitvnm
Copy link
Owner

Hi, thanks for the issue.

I installed that game and there is no issue with frida-il2cpp-bridge: the game actually has its libil2cpp.so encrypted/obfuscated.
In fact, you won't able to analyze it with a disassembler.

This is the output of inform(hexdump(Il2Cpp.Api._getCorlib, { length: 4 }));:
Screenshot_20210913_154134

The first line is printed when the app is spawned: as you can see, 0x0c4341e7 is not a valid instruction, that's why the illegal istruction error occurs. After a while, the second line is printed. In that occasion, there will be 0x3e570014: this is a valid instructon.

My guess is this app has some sort of protection against static analysis.
The fastest solution here is what you already came up with:

while (true) {
    try {
        Instruction.parse(Il2Cpp.Api._getCorlib);
        break;
    } catch (e) {
        await new Promise(resolve => setTimeout(resolve, 100));
    }    
}

@truelecter
Copy link
Author

So, from code perspective, instead of patching library code in node modules I can put same while-true block before Il2Cpp.perform? Or in this case Il2Cpp.perform will be obsolete?

And another offtopic question regarding obfuscation: is it possible that after startup, game deobfuscates il2cpp.so in memory and dump will be statically-analyzable?

@vfsfitvnm
Copy link
Owner

So, from code perspective, instead of patching library code in node modules I can put same while-true block before Il2Cpp.perform? Or in this case Il2Cpp.perform will be obsolete?

Yes, in this case you can't use await, but this one will work:

const interval = setInterval(() => {
    try {
        Instruction.parse(Il2Cpp.Api._getCorlib);

        clearInterval(interval);

        Il2Cpp.perform(main);
    } catch (e) {}
}, 100);

function main() {
    console.log("Ready!", Il2Cpp.unityVersion);
}

And another offtopic question regarding obfuscation: is it possible that after startup, game deobfuscates il2cpp.so in memory and dump will be statically-analyzable?

What does "dump" refer to (il2cpp dump - classes, methods- or deobfuscated/decrypted libil2cpp.so dump)?

@truelecter
Copy link
Author

Thanks for the code snippet!

What does "dump" refer to (il2cpp dump - classes, methods- or deobfuscated/decrypted libil2cpp.so dump)?

libil2cpp.so dump from game process memory which (as I understand) should be decrypted when game fully launches

@vfsfitvnm
Copy link
Owner

libil2cpp.so dump from game process memory which (as I understand) should be decrypted when game fully launches

Well, technically something along these lines should work:

function main() {
    console.log("Ready!", Il2Cpp.unityVersion);

    const path = `${Il2Cpp.Dumper.defaultDirectoryPath}/libil2cpp.so`;
    const file = new File(path, "w");
    
    for (const range of Il2Cpp.module.enumerateRanges("---")) {
        file.write(range.base.readByteArray(range.size)!);
    }
    
    file.flush();
    file.close(); 

    console.log(`File saved to ${path}`);
}

Just tested and (kind of?) works on ghidra. I said kind of because I didn't perform a full-analysis and so there could be problems.

@truelecter
Copy link
Author

Thanks a lot!

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

No branches or pull requests

2 participants