Closed carmebar closed 2 years ago
Interceptor.attach(getRandomSeedID.virtualAddress, {
onEnter(args) {
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
const min = new Il2Cpp.String(args[1]);
const max = new Il2Cpp.String(args[2]);
console.log(min);
console.log(max);
}
});
0x724c699f40 0x724c699f60 0x724c699f40 "1" "3"
args[0]
and args[2]
are the same address... Issue with frida? Any guidance to further debug?
At the moment I'm able to just change args[1]
and args[2]
to change the behaviour in the way I wanted. Just wondering if its an issue with the library or I'm doing something wrong.
Thanks for the report. Would you try an older Frida version (15.x
)?
Can confirm same issue.
frida --version
15.2.2
Unfortunately the game won't load because it requires the google play services, which I don't have. Put this code in the implementation: what does it print?
console.log(typeof this);
console.log(this instanceof Il2Cpp.Object);
if (this instanceof Il2Cpp.Object) {
console.log(this.class.type.name);
}
Thank you for trying to run the APK. Here's the output:
object true System.String
As expected by the pointers... 🤔
There are two possibilities: GetRandomSeedID
is either a static method (but I cannot detect it somehow) or it doesn't use this
, so the compiler performed an optimization and made it static (only in practice). The former can be verified by getting the flags of the C# object of such method, the latter can be verified using a disassembler and looking at how many parameters are being passed (2 or 3). I personally think it's the latter
This is the whole definition of the class dumped by il2cpp-bridge (same signatures as the ones extracted with il2cppdumper from the binary):
// Assembly-CSharp
class Seed : System.Object
{
Shop_PotList_Detail m_DetailView; // 0x10
System.Int32 pot_id; // 0x18
Cooltime LoadPotCoolTimeData; // 0x20
System.DateTime coolTime; // 0x28
System.TimeSpan cTime; // 0x30
System.TimeSpan totalT; // 0x38
System.Int32 totalTime; // 0x40
System.Boolean UpdateTime; // 0x44
System.String load_show_str; // 0x48
System.Boolean m_EventSeed; // 0x50
System.Collections.Generic.Dictionary<System.String,System.String> getDB; // 0x58
System.String selectSeedIndex; // 0x60
System.Action onCompleteShowInfoHandler; // 0x68
System.String UpdateGroupSeedString; // 0x70
System.Void .ctor(Shop_PotList_Detail arg_obj_DetailView, System.Int32 arg_PotID, System.String arg_seedIndex, System.Boolean arg_Event); // 0x013a5184
System.Void ShowInfo(System.Action _onCompleteShowInfoHandler); // 0x013b8b80
System.Void BuyItem(); // 0x013b8b88
System.Void PlantSeed(); // 0x013b8b8c
System.Void PlantSeedAll(); // 0x013b8b90
System.Void PlantSeedError(); // 0x013b8b94
System.Void Update(); // 0x013b8b98
System.Void SeedInfoDetail(); // 0x013a58c4
System.Collections.Generic.Dictionary<System.String,System.String> GetGroupSeedID(System.String seed1, System.String seed2, System.String seed3, System.Int32 prob1, System.Int32 prob2, System.Int32 prob3); // 0x013a7b28
System.Collections.Generic.Dictionary<System.String,System.String> GetRandomSeedID(System.String min, System.String max); // 0x013a7d50
System.Boolean PurchasedAlready(); // 0x013b8eb0
System.Void SaveNewUserDataResult(BuyIllustResult result); // 0x013b8fac
System.Void OnComfirmSeedingNow(ClientConsts.ConfirmList _result); // 0x013b9abc
System.Void SyncDataNow(); // 0x013b94b4
System.Void SyncIllustData(); // 0x013b97b8
System.Void GetUserDataResult(GetCurrencyResult result); // 0x013b9b50
System.Void GetSeedResult(GetIllustResult result); // 0x013b9dd8
System.Void Fail_Connect(System.Int32 result, System.String message); // 0x013a89e4
System.Void Fail_ConnectSeeding(System.Int32 result, System.String message); // 0x013ba014
System.Void ShowLoading(System.Boolean arg_Flag); // 0x013ba07c
}
And the decompiled method from Ghidra
Seems you are right about first param (this) not being used inside the method.
So I would go for:
const dummySeed = assem.class("Seed").alloc();
getRandomSeedID.implementation = function (min : Il2Cpp.String, max: Il2Cpp.String) : Il2Cpp.Object {
return dummySeed.method<Il2Cpp.Object>("GetRandomSeedID").invoke(max, max);;
};
Yes, that works as expected. I am not able to cache the allocated class because sometimes it gets overriden by other classes and fails randomly with errors like:
il2cpp: couldn't find method GetRandomSeedID in TypeFilter, did you mean Invoke?
Thank you for walking me through the debugging. Hopefully this issue will help somebody in the future like the rest of issues helped me to learn the library 👍🏿
I am not able to cache the allocated class because sometimes it gets overriden by other classes and fails randomly with errors like
Right! That occurs because dummySeed
is allocated in the frida thread; but when the Il2Cpp.perform
call ends, the frida thread gets detached from the il2cpp world, so the memory gets reassigned.
You could try:
let dummySeed: Il2Cpp.Object?;
getRandomSeedID.implementation = function (min : Il2Cpp.String, max: Il2Cpp.String) : Il2Cpp.Object {
if (dummySeed == null) dummySeed = assem.class("Seed").alloc();
return dummySeed!.method<Il2Cpp.Object>("GetRandomSeedID").invoke(max, max);;
};
or
const dummySeed = assem.class("Seed").alloc();
dummySeed.ref(true);
getRandomSeedID.implementation = function (min : Il2Cpp.String, max: Il2Cpp.String) : Il2Cpp.Object {
return dummySeed.method<Il2Cpp.Object>("GetRandomSeedID").invoke(max, max);;
};
(https://github.com/vfsfitvnm/frida-il2cpp-bridge/issues/183#issuecomment-1119483815).
I am curious to know if both works!
Probably related to #181.
UnityVersion: 2020.3.34f1 APK: [REDACTED] Frida version: latest (16.0.1) Frida server: latest (16.0.1) arm64 Compiled with:
esbuild --bundle --outfile=_.js --watch index.ts
Running on: real hardwaregetRandomSeedID.fridaSignature
Any indications on how to keep troubleshooting or any extra information that I can gather to help debug the issue?
Thank you for your time and work on the library. Much appreciated.