旅行的青蛙Unity游戏逆向修改--iOS篇

前言

之前写了一篇分析Android C#脚本的文章,主要也是为了给这篇文章做铺垫吧,因为在iOS现在都是IL2CPP模式,C#脚本已经被转成了c代码。所以要单独分析iOS的话难度会大很多,如果从Android的C#脚本入手的话,因为iOS和Android脚本都是一样的话,可以从Android分析的函数名来对应iOS的c函数然后进行hook修改。

提取ipa

首先从越狱设备上面提取旅行青蛙的ipa包,使用frida-ios-dump一键提取即可。由于是日文名字,先通过./dump.py -l把名字列出来,然后复制名字或者通过bundle id去dump就可以了。

IL2CPP符号还原

由于使用IL2CPP选项编译unity游戏,会生成cpp的代码,直接使用IDA看是看不到函数和函数名的,而且游戏中使用的字符串都被保存在global-metadata.dat的资源文件里。首先要通过提取global-metadata.dat文件里面的字符串对对应的c函数进行符号还原。具体也有现成的文章:还原使用IL2CPP编译的unity游戏的symbol,github上面也有线程的项目也做这件事情Il2CppDumper。下载release的工具,运行Il2CppDumper.exe并依次选择il2cpp的可执行文件和global-metadata.dat文件,然后选择Auto(Plus)模式,将生成dump.cs文件和script.py脚本。使用IDA打开可执行文件然后使用script.py脚本即可还原符号。

1
2
3
4
5
6
7
Making method name...
Make method name done
Setting String...
Set string done
Making function...
Make function done, please wait for IDA to complete the analysis
Script finish !

根据函数hook代码

还原之后就可以根据之前分析到的函数名来hook对应的代码了,首先是三叶草的数目通过SuperGameMaster.CloverPointStock()获取的,在IDA搜索CloverPointStock如下:

接着就可以直接hook这个函数了,由于要inline hook目前是在越狱机器上面,后面会讲到非越狱机器hook的方案。使用MonkeyDev新建一个Logos Tweak项目,清空.xm的内容并写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#import <substrate.h>
#import <dlfcn.h>
#import <mach-o/dyld.h>

int (*old_clover_point_stock)(void);

int new_clover_point_stock(void)
{
return 9999;
}

%ctor
{
@autoreleasepool
{
unsigned long clover_point_stock = _dyld_get_image_vmaddr_slide(0) + 0x100093A2C;
MSHookFunction((void *)clover_point_stock, (void *)&new_clover_point_stock, (void **)&old_clover_point_stock);


}
}

然后在build settings里面设置端口和设备密码然后command + b安装就能看到效果了,其它函数的hook也是一样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#import <substrate.h>
#import <dlfcn.h>
#import <mach-o/dyld.h>

int (*old_clover_point_stock)(void);

int new_clover_point_stock(void)
{
return 9999;
}

int (*old_ticket_stock)(void);

int new_ticket_stock(void)
{
return 9999;
}

void (*old_lotterycheck)(uint64_t obj);

void new_lotterycheck(uint64_t obj)
{
*(int*)(obj + 80) = rand() % 4 + 1;
}

uint64_t (*old_new_clover_object)(uint64_t obj, int index, uint64_t cloverData, uint64_t cloversObj, int fourLeafFlag);

uint64_t new_new_clover_object(uint64_t obj, int index, uint64_t cloverData, uint64_t cloversObj, int fourLeafFlag)
{
return old_new_clover_object(obj,index,cloverData,cloversObj,1);
}

%ctor
{
@autoreleasepool
{
unsigned long clover_point_stock = _dyld_get_image_vmaddr_slide(0) + 0x100093A2C;
MSHookFunction((void *)clover_point_stock, (void *)&new_clover_point_stock, (void **)&old_clover_point_stock);

unsigned long ticket_stock = _dyld_get_image_vmaddr_slide(0) + 0x100093AA4;
MSHookFunction((void *)ticket_stock, (void *)&new_ticket_stock, (void **)&old_ticket_stock);

unsigned long lotterycheck = _dyld_get_image_vmaddr_slide(0) + 0x100086CF4;
MSHookFunction((void *)lotterycheck, (void *)&new_lotterycheck, (void **)&old_lotterycheck);

unsigned long new_clover_object = _dyld_get_image_vmaddr_slide(0) + 0x100037100;
MSHookFunction((void *)new_clover_object, (void *)&new_new_clover_object, (void **)&old_new_clover_object);
}
}

这里有一个函数RaffelPanel$$LotteryCheck要修改里面的result的值,就要根据汇编或者伪代码来看result的赋值是在什么位置了,该函数通过F5获得的伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
__int64 __fastcall RaffelPanel__LotteryCheck(__int64 a1)
{
__int64 v1; // x19
__int64 v2; // x0
__int64 v3; // x0
int v4; // w20
int v5; // w23
signed int v6; // w24
__int64 v7; // x0
__int64 result; // x0

v1 = a1;
if ( !(byte_10137EDBB & 1) )
{
sub_100DEAD34(6810LL);
byte_10137EDBB = 1;
}
v2 = qword_101439198;
if ( *(_BYTE *)(qword_101439198 + 266) & 1 && !*(_DWORD *)(qword_101439198 + 188) )
{
sub_100DFF71C();
v2 = qword_101439198;
}
if ( !*(_QWORD *)(*(_QWORD *)(v2 + 160) + 192LL) )
LABEL_17:
sub_100DE28B4();
v3 = sub_1000FB954();
v4 = Random__Range_71094(0LL, 0LL, v3, 0LL);
v5 = 0;
*(_DWORD *)(v1 + 80) = 0; this.result = Rank.White; //默认都是白色的球
v6 = -1;
while ( 1 )
{
v7 = qword_101439198;
if ( *(_BYTE *)(qword_101439198 + 266) & 1 )
{
if ( !*(_DWORD *)(qword_101439198 + 188) )
{
sub_100DFF71C();
v7 = qword_101439198;
}
}
if ( !*(_QWORD *)(*(_QWORD *)(v7 + 160) + 192LL) )
goto LABEL_17;
result = sub_1000FB954();
v5 += result;
if ( v4 < v5 ) //if (num < num2)
break;
if ( ++v6 >= 4 )
return result;
}
*(_DWORD *)(v1 + 80) = v6 + 1; //this.result = (Rank)i;
return result;
}

这里的*(_DWORD *)(v1 + 80)的位置其实就是this.result所以直接修改80偏移位置的值就可以了。

总结

总结来说的话,要分析iOS里面转换后的脚本C代码还是不容易的,如果能够根据Android C#脚本分析的结果然后对iOS的符号进行恢复一下的话,就可以直接根据Android分析到的函数直接来Hook iOS对应的函数来修改参数或者值了。不过这里还是在越狱设备上面进行的hook,然后会讲到非越狱设备同样也可以进行静态的hook操作。

AloneMonkey wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!