frida-il2cpp-bridge
frida-il2cpp-bridge copied to clipboard
References to value types are created incorrectly
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
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
@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!
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);
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()));
@keinPlan Did it work?
yes if you do it properly it's working fine thx