0332睡到0756睡到1030睡到1114,睡觉好舒服,梦到有了能心照不宣的队友。
1
2
| /** Gets the method identified by the given name and parameter count. */
method<T extends Il2Cpp.Method.ReturnType>(name: string, parameterCount?: number): Il2Cpp.Method<T>;
|
frida-il2cpp-bridge 的 method() 只支持按名字 + 参数数量筛选,没有直接按参数类型 overload 的 API。
1
2
3
| public static AbilityCompatibility GetAbilityCompatibility(TSKBattleNote attackNote, TSKBattleNote targetNote, TeamType attackTeam) { }
public static AbilityCompatibility GetAbilityCompatibility(int attribute_1, int attribute_2, TeamType attackTeam) { }
|
所以这个要怎么办呢。
这样:
1
2
3
4
5
6
| const method = TSKBattleUtility.methods.find(
(m) =>
m.name === "GetAbilityCompatibility" &&
m.parameters[0].type.name === "TSKBattleNote" &&
m.parameters[1].type.name === "TSKBattleNote"
);
|
…
1
2
3
| [23:00:05] args[8] kind (TSKBattleAttack.AttackType) = 0x1
...
[23:00:09] args[8] kind (TSKBattleAttack.AttackType) = 0x21500000004
|
有时正常而有时发狂的enum,下面明显是4,但有了很怪的前缀。原因是用 arg.toString() 打印 NativePointer,所以该用toInt32()?但enum又无法根据type直接看出来..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| const TSKBattleUtility = Il2Cpp.domain
.assembly("Assembly-CSharp")
.image.class("TSKBattleUtility");
const GetAbilityCompatibility = TSKBattleUtility.methods.find(
(m) =>
m.name === "GetAbilityCompatibility" &&
m.parameters[0].type.name === "TSKBattleNote" &&
m.parameters[1].type.name === "TSKBattleNote"
);
if (!GetAbilityCompatibility) {
log("GetAbilityCompatibility not found");
return;
}
log(GetAbilityCompatibility.toString());
const result = GetAbilityCompatibility?.invoke(attack, defence, teamType);
log(`GetAbilityCompatibility result = ${result?.toString()}`);
|
这样能调用游戏内的函数,但其实如果只是想监听想知道数值的话直接trace会更方便。而且一开始log时[00:04:54] GetAbilityCompatibility result = None,我还以为我invoke失败了,后来才发现是因为返回值就是None:
1
2
3
4
5
| enum AbilityCompatibility {
None = 0,
Week = 1,
Resist = 2,
}
|
不过trace时也会有噪音,我可能只想知道TSKBattleCalculationManager.CaluculationNormalDamage中TSKBattleUtility.GetAbilityCompatibility的情况,但后者也有可能在别的地方被调用到,会污染log..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| fVar15 = (float)TSKBattleCalculationManager$$FluctuationOffset(0);
fVar16 = (float)TSKBattleCalculationManager$$RushOffset(rushCount,attack,*compatibility,0)
;
critical = TSKBattleNote$$GetAttack(attack,0,0);
fVar17 = (float)TSKBattleCalculationManager$$AttributeOffset
(*compatibility,attack,beforeRushCount,0);
fVar18 = (float)TSKBattleCalculationManager$$CriticalOffset
(*isCritical != '\0',attack,param_10,beforeRushCount,0);
fVar19 = (float)TSKBattleCalculationManager$$DownOffset(defence,attack,0);
if (*(int *)(System.Math_TypeInfo + 0xe0) == 0) {
FUN_180297050();
}
dVar20 = ceil((double)((float)critical * fVar17 * fVar18 * skillValue * fVar16 * fVar15 *
fVar19));
|
TSKBattleCalculationManager.CaluculationNormalDamage中上述这些变量都是和涉及到最终伤害的数据,一开始还想自己invoke来算,现在想来还是trace可能更方便,进入TSKBattleCalculationManager.CaluculationNormalDamage时全局变量设计个flag,然后trace后面的函数就行了?
用il2cpp和ghidra时可以直接看offset然后
1
2
3
| const teamPtr = attack.handle.add(0x28).readPointer();
const team = new Il2Cpp.Object(teamPtr);
const teamType = team.handle.add(0x28).readS32();
|
如果只用frida动态分析的话可能还得打印class然后field(’team')
1
2
3
| [01:00:13] enter TSKBattleCalculationManager.FluctuationOffset
[01:00:13] FluctuationOffset (CaluculationNormalDamage) = 0x215f3268a50
[01:00:13] {"pc":"0x7fff353a5430","sp":"0x2142f7ef08","rax":"0x215f3268a50","rcx":"0x11","rdx":"0x0","rbx":"0x0","rsp":"0x2142f7ef08","rbp":"0x2142f7f289","rsi":"0x215a0e6c0f4","rdi":"0x2158a42b240","r8":"0xd16a77a6","r9":"0x2130007e180","r10":"0x30","r11":"0x2142f7ee40","r12":"0x17","r13":"0x17","r14":"0x0","r15":"0x2158da52b40","rip":"0x7fff353a5430"}
|
float TSKBattleCalculationManager$$FluctuationOffset(void)没能通过trace得到正确返回值,打印log(JSON.stringify(this.context));也没看到像是正确的值,它似乎给的是"rax":"0x215f3268a50",而float不存在这里?Windows x64 ABI决定float存在XMM0。ghidra中return时也是180c2df37 f3 0f 58 05 ... ADDSS XMM0,dword ptr [DAT_183b16444],但我好像获取不到它。
1
2
| const f = new NativeFunction(method.virtualAddress, "float", []);
console.log(f());
|
直接调用测试得到的结果:
1
2
3
4
5
6
| 1.0091999769210815
1.0003000497817993
1.0069999694824219
1.003999948501587
1.0053000450134277
1.003600001335144
|
基本可以说明[wiki]中的1.00倍~1.01倍(100%~101%)の範囲内で変動する。是对的;也说明这里的retval是有问题的。所以是frida-il2cpp-bridge的问题吗?
frida-il2cpp-bridge的某个pr中作者有写:
1
2
3
4
5
| The main reason why I didn't use `Interceptor.attach` is because Frida doesn't support reading/writing floating-point values (`System.Single`, `System.Double`) - such values live in different CPU registers, and Frida doesn't expose them (yet). So, the only option was `Interceptor.replace`.
Ref: https://github.com/frida/frida/issues/266#issuecomment-294729705
However, a lot of time passed since that, and Frida might have implemented the support for it.
|
似乎是这个问题,但也不太像。。一翻log翻到了更诡异的:
1
2
3
| [23:30:11] args[4] skillValue (System.Single) = 4.320000171661377
...
[23:30:11] [CaluculationNormalDamage]: baseAttack=2747 attack=5659(ignore charge) critical=2350 beforeRushCount=2 skillValue=2.802596928649634e-45
|
怎么同一时间还能不同值的,我急了。288+144=432,说明前者的skillValue肯定是读对了,但后面是怎么回事。
前者是直接function dumpArgs(method: Il2Cpp.Method, args: InvocationArguments) {,后者是function parseArgument(arg: NativePointer, typeName: string): unknown {,本质都是value = intBitsToFloat(arg.toString());,所以是后面读错寄存器了?为什么前面能是正确的??
…这里是我的问题 const skillValue = parseArgument(args[4], "float");被我写成 const skillValue = parseArgument(args[3], "float");了,生气了。。反正参数中是能正确读float的。明天再说吧。