frida-il2cpp-bridge icon indicating copy to clipboard operation
frida-il2cpp-bridge copied to clipboard

References to value types are created incorrectly

Open vfsfitvnm opened this issue 1 year ago • 6 comments

Discussed in https://github.com/vfsfitvnm/frida-il2cpp-bridge/discussions/420

Originally posted by keinPlan October 28, 2023 got a problem with a function (UnityVersion 2021.3.22f1).

if i'm calling the function the parameter seems to be passed in a wrong format.

// The Function
System.Void Init(Rk.Rubens.Map.StartMarchCrypt& action);

// STRUCT
struct Rk.Rubens.Map.StartMarchCrypt : System.ValueType
{
    Rk.Rubens.IAtom StartPoint; // 0x10
    Rk.Rubens.IAtom TargetAtom; // 0x18
    System.UInt32 OilCount; // 0x20
    Rk.IList<Rk.Rubens.IAtomHero> TakeHeroes; // 0x28
    System.UInt32 ApproxFinishTime; // 0x30
    System.Void .ctor(Rk.Rubens.IAtom startPoint, Rk.Rubens.IAtom targetAtom, System.UInt32 oilCount, Rk.IList<Rk.Rubens.IAtomHero> takeHeroes, System.UInt32 approxFinishTime); // 0x03682ea0
}

i did replace the implementaion and just read the data from the handle passed:

GAME CALLING: Handle: ->Rk.Rubens.Map.StartMarchCrypt HandleType: 0xc2814eeca0 MemDump 7xuint64:

  1967449137824 	// StartPoint
  1968403908576 	// TargetAtom
  1			// OilCount
  1969275494240		// TakeHeroes
  1698436712		// ApproxFinishTime  --- struct Rk.Rubens.Map.StartMarchCrypt END 
  1968609456641	
  8011462212112905538

ME CALLING: Handle: ->Rk.Rubens.Map.StartMarchCrypt HandleType: 0x1ca52c92180 MemDump 7xuint64:

  1966467562432 	// il2cpp pointer ? 
  0			// il2cpp pointer ? 
  1962936266352 	// StartPoint
  1968403908576 	// TargetAtom
  1 			// OilCount
  1967398819952 	// TakeHeroes
  30 			// ApproxFinishTime --- struct Rk.Rubens.Map.StartMarchCrypt END 

also can't read fields of struct the game created... but if reading direct from memory the data are there but without the il2cpp pointers at the start.

currently i just overwritte the il2cpp pointer @ the begining of the struct with the struct data befor passing it to the function ... messy but seems to work

vfsfitvnm avatar Oct 29 '23 12:10 vfsfitvnm

I'm not 100% the following findings are related to this issue, however, it looks I incorrectly create references:

Given the following IL2CPP code

// System.Int32 System.Decimal::Compare(System.Decimal,System.Decimal)
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t Decimal_Compare_m90C94AD2C713181DF0E92B3B6F9D56BD494E862C (Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8  ___d10, Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8  ___d21, const RuntimeMethod* method)
{
	static bool s_Il2CppMethodInitialized;
	if (!s_Il2CppMethodInitialized)
	{
		il2cpp_codegen_initialize_method (Decimal_Compare_m90C94AD2C713181DF0E92B3B6F9D56BD494E862C_MetadataUsageId);
		s_Il2CppMethodInitialized = true;
	}
	{
		IL2CPP_RUNTIME_CLASS_INIT(Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8_il2cpp_TypeInfo_var);
		int32_t L_0 = Decimal_FCallCompare_mC72E1B2721A5D80C32071FE8180EC96259A0A6EA((Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8 *)(&___d10), (Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8 *)(&___d21), /*hidden argument*/NULL);
		return L_0;
	}
}
// System.Int32 System.Decimal::FCallCompare(System.Decimal&,System.Decimal&)
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR int32_t Decimal_FCallCompare_mC72E1B2721A5D80C32071FE8180EC96259A0A6EA (Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8 * ___d10, Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8 * ___d21, const RuntimeMethod* method)
{
	typedef int32_t (*Decimal_FCallCompare_mC72E1B2721A5D80C32071FE8180EC96259A0A6EA_ftn) (Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8 *, Decimal_t44EE9DA309A1BF848308DE4DDFC070CAE6D95EE8 *);
	using namespace il2cpp::icalls;
	return  ((Decimal_FCallCompare_mC72E1B2721A5D80C32071FE8180EC96259A0A6EA_ftn)mscorlib::System::Decimal::FCallCompare) (___d10, ___d21);
}

the following script

Il2Cpp.perform(() => {
    const Decimal = Il2Cpp.corlib.class("System.Decimal");

    const x = Decimal.alloc().unbox();
    x.method(".ctor").invoke(44);

    const offset = Il2Cpp.corlib.class("System.Int32").valueTypeSize * 2;

    console.log("Direct:", x.handle.add(offset).readInt());
    console.log("frida-il2cpp-bridge reference:", Il2Cpp.reference(x).handle.add(offset).readInt());

    Interceptor.attach(Decimal.method("FCallCompare").virtualAddress, args => {
        console.log("IL2CPP reference:", args[0].add(offset).readInt());
    });

    x.method("CompareTo").overload(Decimal.type.name).invoke(x);
});

logs

Direct: 44
frida-il2cpp-bridge reference: 0 !!! WRONG
IL2CPP reference: 44

vfsfitvnm avatar Oct 29 '23 13:10 vfsfitvnm

@keinPlan Could you test if the commit above fixes the issue for you? I don't know how you are invoking that method, by the way, so I'm unsure whether this is related to your issue!

vfsfitvnm avatar Oct 29 '23 14:10 vfsfitvnm

No didn't help still got the offset in the object created by me.

Thats the code i use for testing:

    var parameter = _types.StartMarchCrypt.alloc();
    // .ctor(Rk.Rubens.IAtom startPoint, Rk.Rubens.IAtom targetAtom, System.UInt32 oilCount, Rk.IList<Rk.Rubens.IAtomHero> takeHeroes, System.UInt32 approxFinishTime);
    parameter.method(".ctor").invoke(
        // Rk.Rubens.IAtom startPoint
        playerHometown.obj,
        // Rk.Rubens.IAtom targetAtom,
        targets[0].Atom,
        // System.UInt32 oilCount,
        1,
        // Rk.IList<Rk.Rubens.IAtomHero> takeHeroes,
        heroList,
        // System.UInt32 approxFinishTime
        10,
    );

    var cmd = diContainer.method<Il2Cpp.Object>("Instantiate").inflate(_types.StartMarchCryptCommand).invoke(new NativePointer(0));
 
     cmd.method("Init").implementation =  (x) => {     
         var t = (x as Il2Cpp.Reference<Il2Cpp.ValueType>).value;
         console.log("Handle:" + t);
         console.log("HandleType:" + t.handle);
     
         console.log("MemDump:")
         var test1 = new DataView(t.handle.readByteArray(48 + 8 + 8) as ArrayBuffer);
         console.log("StartPoint:       " + test1.getBigInt64(0, true))  // StartPoint
         console.log("TargetAtom:       " + test1.getBigInt64(8, true))  // TargetAtom
         console.log("OilCount:         " + test1.getBigInt64(16, true)) // oil
         console.log("TakeHeroes:       " + test1.getBigInt64(24, true)) // TakeHeroes
         console.log("approxFinishTime: " + test1.getBigInt64(32, true)) // approxFinishTime
         console.log(" :                " + test1.getBigInt64(40, true)) 
         console.log(" :                " + test1.getBigInt64(48, true)) 
      
         console.log("-------")        
     };
     
     cmd.method("Init").invoke(parameter);

keinPlan avatar Oct 29 '23 19:10 keinPlan

Init takes a reference to a struct - but you are passing an object instead. This is how you should invoke it instead:

cmd.method("Init").invoke(Il2Cpp.reference(parameter.unbox()));

vfsfitvnm avatar Oct 29 '23 20:10 vfsfitvnm

@keinPlan Did it work?

vfsfitvnm avatar Nov 01 '23 10:11 vfsfitvnm

yes if you do it properly it's working fine thx

keinPlan avatar Nov 01 '23 11:11 keinPlan