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
1.03k stars 202 forks source link

The 'this' value is of incorrect type?I don't quite understand why ‘this’ is String Type? #181

Closed lshain closed 2 years ago

lshain commented 2 years ago

Il2Cpp.perform(() => { log('Il2Cpp.perform.... ');

const assemblyCSharp = Il2Cpp.Domain.assembly("Assembly-CSharp").image;

/* Test C# Code
public class TestA : MonoBehaviour
{
    private void Awake()
    {
        Test1("AAAA");
    }

    void Test1(string aa)
    {
        Debug.LogError("Application.version: " + Application.version);
        Debug.LogError("Application.unityVersion: " + Application.unityVersion);
        Debug.LogError("Application.persistentDataPath: " + Application.persistentDataPath);
        Debug.LogError("Application.dataPath: " + Application.dataPath);
        Debug.LogError(aa);
    }
}
*/
const TestA = assemblyCSharp.class("TestA");
const Test1 = TestA.method("Test1");
log('Test1: ' + Test1); // -> Test1: System.Void Test1(System.String aa);

// System.Void Test1(System.String aa);
Test1.implementation = function (value: Il2Cpp.String): void {
    console.log('this: ' + this); // -> this: AAAA   -- The ‘this’ value is of incorrect type?
    console.log('value: ' + value); // // value: "AAAA"

    //value.content = "--!--";

    // <--- onEnter
    // why ‘this’ is String Type? or how to call the original method?
    this.method("Test1").invoke(value); // -> il2cpp: couldn't find method Test1 in String, did you mean Trim?
    // <--- onLeave

};

});

vfsfitvnm commented 2 years ago

What's the output of Test1.fridaSignature?

lshain commented 2 years ago

Test1: System.Void Test1(System.String aa); // 0x00277aa8 Test1.fridaSignature: pointer,pointer this: AAAA value: "AAAA" il2cpp: couldn't find method Test1 in String, did you mean Trim?

package.json: "devDependencies": { "@types/frida-gum": "^17.1.0", "@types/node": "^16.4.8", "frida-compile": "^10.0.0", "frida-il2cpp-bridge": "^0.7.11" }

vfsfitvnm commented 2 years ago

Would you give more context? E.g. is the application yours? If not, what's the name? This looks awkward... this is set here, args[0] can't be a TestA instance...

lshain commented 2 years ago

Just a Unity test app, Unity Version is 2021.3.0f1, Contains only the following c# code, Build with IL2CPP:

public class TestA : MonoBehaviour
{
    private void Awake()
    {
        Test1("AAAA");
    }

    void Test1(string aa)
    {
        Debug.LogError("Application.version: " + Application.version);
        Debug.LogError("Application.unityVersion: " + Application.unityVersion);
        Debug.LogError("Application.persistentDataPath: " + Application.persistentDataPath);
        Debug.LogError("Application.dataPath: " + Application.dataPath);
        Debug.LogError(aa);
    }
}

ts Code:

"use strict";

import "frida-il2cpp-bridge";

Il2Cpp.perform(() => {
    log('Il2Cpp.perform.... ');

    const assemblyCSharp = Il2Cpp.Domain.assembly("Assembly-CSharp").image;

    /*
    public class TestA : MonoBehaviour
    {
        private void Awake()
        {
            Test1("AAAA");
        }

        void Test1(string aa)
        {
            Debug.LogError("Application.version: " + Application.version);
            Debug.LogError("Application.unityVersion: " + Application.unityVersion);
            Debug.LogError("Application.persistentDataPath: " + Application.persistentDataPath);
            Debug.LogError("Application.dataPath: " + Application.dataPath);
            Debug.LogError(aa);
        }
    }
    */
    const TestA = assemblyCSharp.class("TestA");
    const Test1 = TestA.method("Test1");
    log('Test1: ' + Test1); // -> Test1: System.Void Test1(System.String aa);
    log('Test1.fridaSignature: ' + Test1.fridaSignature); 

    // System.Void Test1(System.String aa);
    Test1.implementation = function (value: Il2Cpp.String): void {
        console.log('this: ' + this); // -> this: AAAA
        console.log('value: ' + value); // // value: "AAAA"

        //value.content = "--!--";

        // <--- onEnter
        this.method("Test1").invoke(value); // -> il2cpp: couldn't find method Test1 in String, did you mean Trim?
        // <--- onLeave

    };
});
vfsfitvnm commented 2 years ago

At the moment, I can't test custom C# code unfortunately. However, I cannot reproduce in 2021.2.7f1... honestly I don't have a clue. Would you be able to dig a little? E.g: what does this print?

const replacement = new NativeCallback((instance, string) => {
    console.log("this:", new Il2Cpp.Object(instance));
    console.log("string:", new Il2Cpp.String(string));
}, "void", ["pointer", "pointer"]);

Interceptor.replace(Test1.virtualAddress, replacement);
lshain commented 2 years ago

Il2Cpp.perform(() => { const assemblyCSharp = Il2Cpp.Domain.assembly("Assembly-CSharp").image;

/*
public class TestA : MonoBehaviour
{
    private void Awake()
    {
        Test1("AAAA");
    }

    void Test1(string aa)
    {
        Debug.LogError("Application.version: " + Application.version);
        Debug.LogError("Application.unityVersion: " + Application.unityVersion);
        Debug.LogError("Application.persistentDataPath: " + Application.persistentDataPath);
        Debug.LogError("Application.dataPath: " + Application.dataPath);
        Debug.LogError(aa);
    }
}
*/
const TestA = assemblyCSharp.class("TestA");
const Test1 = TestA.method("Test1");
log('Test1: ' + Test1); // -> Test1: System.Void Test1(System.String aa); // 0x00277aa8
log('Test1.fridaSignature: ' + Test1.fridaSignature); // -: Test1.fridaSignature: pointer,pointer

const replacement = new NativeCallback((instance, string) => {
    console.log("this:", new Il2Cpp.Object(instance)); // -> this: AAAA
    console.log("string:", new Il2Cpp.String(string)); // -> string: "AAAA"
}, "void", ["pointer", "pointer"]);

Interceptor.replace(Test1.virtualAddress, replacement);

});

lshain commented 2 years ago

64-bit apk + 64-bit arm frida_server unity version: 2020.3.29f1

Il2Cpp.perform.... Test1: System.Void Test1(System.String aa); // 0x004ddc80 Test1.fridaSignature: pointer,pointer this: AAAA string: "AAAA"

64-bit apk + 64-bit arm frida_server unity version: 2021.2.7f1 Il2Cpp.perform.... Test1: System.Void Test1(System.String aa); // 0x0025fe84 Test1.fridaSignature: pointer,pointer this: AAAA string: "AAAA"

32-bit apk + 64-bit arm frida_server Il2Cpp.perform.... Test1: System.Void Test1(System.String aa); // 0x001c5a1c Test1.fridaSignature: pointer,pointer Error: access violation accessing 0x4 at (frida/runtime/core.js:141) at get name (node_modules/frida-il2cpp-bridge/dist/il2cpp/structs/class.js:116) at call (native) at (node_modules/decorator-cache-getter/dist/index.js:9) at (node_modules/frida-il2cpp-bridge/dist/utils/utils.js:37) at method (node_modules/frida-il2cpp-bridge/dist/il2cpp/structs/object.js:35) at toString (node_modules/frida-il2cpp-bridge/dist/il2cpp/structs/object.js:71) at join (native) at r (frida/runtime/console.js:30) at log (frida/runtime/console.js:9) at (agent/index.ts:48)

lshain commented 2 years ago

package.json: "devDependencies": { "@types/frida-gum": "^18.0.0", "@types/node": "^17.0.31", "frida-compile": "^10.2.5", "frida-il2cpp-bridge": "^0.7.13" }

tsconfig.json: "compilerOptions": { "target": "es2020", "module": "commonjs", "allowJs": true, "noEmit": true, "strict": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true }

last apk(delete the zip suffix): testA.apk.zip

vfsfitvnm commented 2 years ago
64-bit apk + 64-bit arm frida_server
unity version: 2020.3.29f1

Il2Cpp.perform....
Test1: System.Void Test1(System.String aa); // 0x004ddc80
Test1.fridaSignature: pointer,pointer
this: AAAA
string: "AAAA"

At this level, frida-il2cpp-bridge is not involved at all - this may be a Frida bug (but I don't think so). Are you using an emulator, by chance?

lshain commented 2 years ago

All tests are performed on the MI 8 UD

vfsfitvnm commented 2 years ago

I don't know, I can't reproduce. Other than updating frida-compile to 10.2.5, I don't have ideas. Does this happen with all methods?

Also, try this:

const replacement = new NativeCallback((instance, string) => {
    console.log("NativeCallback", instance, string);
    console.log("this:", new Il2Cpp.Object(instance));
    console.log("string:", new Il2Cpp.String(string));
}, "void", ["pointer", "pointer"]);

Interceptor.replace(Test1.virtualAddress, replacement);
lshain commented 2 years ago

Il2Cpp.perform.... Test1: System.Void Test1(System.String aa); // 0x0025fe84 Test1.fridaSignature: pointer,pointer NativeCallback 0x6f60053810 0x6f60053810 this: AAAA string: "AAAA"

vfsfitvnm commented 2 years ago

NativeCallback 0x6f60053810 0x6f60053810

As you can see, these addresses are the same: they are the same objects. As I said previously, this aspect is not related to frida-il2cpp-bridge, so I'll mark this issue as invalid - a close will follow in the next days.

That method looks compiled correctly, this is how ghidra decompiles it - it looks fair:

void FUN_0025fe84(undefined8 param_1,undefined8 param_2)

{
  undefined8 uVar1;

  if ((DAT_0077e541 & 1) == 0) {
    thunk_FUN_001ce078(&DAT_00744f58);
    thunk_FUN_001ce078(&DAT_0074d430);
    thunk_FUN_001ce078(&DAT_0074d440);
    thunk_FUN_001ce078(&DAT_0074d448);
    thunk_FUN_001ce078(&DAT_0074d438);
    DAT_0077e541 = 1;
  }
  uVar1 = FUN_004efd18(0);
  uVar1 = FUN_00323914(DAT_0074d448,uVar1,0);
  if (*(int *)(DAT_00744f58 + 0xe0) == 0) {
    thunk_FUN_00173f34(DAT_00744f58);
  }
  FUN_004f0ec0(uVar1,0);
  uVar1 = FUN_004efcf0(0);
  uVar1 = FUN_00323914(DAT_0074d440,uVar1,0);
  FUN_004f0ec0(uVar1,0);
  uVar1 = FUN_004efcc8(0);
  uVar1 = FUN_00323914(DAT_0074d438,uVar1,0);
  FUN_004f0ec0(uVar1,0);
  uVar1 = FUN_004efca0(0);
  uVar1 = FUN_00323914(DAT_0074d430,uVar1,0);
  FUN_004f0ec0(uVar1,0);
  FUN_004f0ec0(param_2,0);
  return;
}

I guess this is a Frida issue, is it up to date?.