不一

iOS上的Unity逆向与弓箭传说游戏修改

字数统计: 2.4k阅读时长: 9 min
2019/10/18 Share

玩游戏又玩不过了,后面的怪血好厚,这游戏没有拿到国内版号不能氪金,只能看广告拿钻石(可想而知装备来得有多不容易)。所以开工之前要先做好心理准备,即是因为不能氪金所以才来破解的。本文是面向非越狱iPhone,因为我的日常使用手机没有越狱,在地铁上玩游戏总不能还从包里再掏出一个越狱手机来吧。再说了其实流程差不多。

工具

  • 越狱iPhone(非必须)
    因为最后要安装到非越狱iPhone上,所以只是用于APP砸壳,但是砸壳APP可以在例如PP助手之类的下载得到,所以也就非必须了。
  • 非越狱iPhone
    作为破解后安装的目标机器。
  • IPAPatch
    用于重新打包签名ipa并安装到非越狱iPhone上,非常强大。
  • IDA pro
    这个就不解释了,至于为什么不用Hopper,后面会看到需要IDA来执行python脚本,给函数绑定符号
  • Il2CppDumper
    Unity转换成IL2CPP的符号表提取工具,但是只能在Windows运行。
  • 汇编码转换器
    如其名,很方便根据汇编代码生成机器码的工具

开始逆向

砸壳

为了得到脱壳的可执行文件和应用数据,即脱壳ipa文件,有两种方式:

恢复符号

解压砸壳获取的ipa文件得到cnarchero.app文件,右键显示包内容可以得到APP的资源,首要分析目标是一个46M的二进制可执行文件,这个尺寸在做逆向上还是很恐怖的,所以及早拖到ida里面分析,因为需要好一段时间。


等ida分析完成之后会发现并不能得到太多有用信息,比如最有用的就是函数名,但是ida分析得到的函数名都是形如sub_xxxx,只表示了该函数的静态地址偏移,并没有带来功能方面的任何信息。

是因为基于Unity的iOS二进制文件不同于基于Objc-C下的,其符号表是另外加载。扩展一点说就是Unity在生成iOS二进制的时候,将开发者编写的C#代码编译成Cpp代码,成为IL2CPP。在解压ipa得到的app包中可以得到IL2CPP的符号表,在xxx.app/Data/Managed/Metadata目录下的global-metadata.dat文件。

接下去要使用Il2CppDumper提取符号表,因为只提供了windows下的工具,所以打开虚拟机。打开Il2CppDumper.exe需要提供两个文件,分别是可执行文件即46M的cnarchero,及其IL2CPP的符号表即global-metadata.dat文件,然后输入Unity的版本。

我哪个知道是什么版本开发的??又不是我写的!!网上搜了一圈没有逆向Unity版本的资料,而本身对Unity正向开发也完全不了解,看来只能猜了,先去Unity官网看一下有哪些版本。

好了本文可以结束了,作者当场去世——(R.I.P 2019.10)
话说后来发现三位的版本号其实只要猜前面两位就可以,因此需要猜的数量的少了很多,从2019.2开始往前推,最后猜到正确的版本号是2018.4,然后选择使用的模式,官方推荐是3.Auto(Plus),最后得到了两个输出文件script.pydump.cs

其中script.py可以用于ida,对IL2CPP表中的无名函数进行标记成形成类名$$函数名,逆向工程就有头绪了。另一个文件dump.cs是提取的方便阅读的符号表,可以使用任意文本编辑器打开,就像阅读源码的头文件一样。

在IDA中选择File - Script file...选择刚才获取的script.py对函数进行命名,完成之后可以看到真的是耳目一新,瞬间清醒如上数学课被点名回答问题一样。

流程分析

分析dump.cs可以了解程序中的类及其属性和方法,如本文想要修改角色的攻击力,这样来降低游戏难度(不要问为什么不直接修改钻石数量,问就是服务器数据同步改不了)。搜索的过程就忽略了,总之最后根据搜索得到的结果顺藤摸瓜得到关于角色攻击力的逻辑类图如下:

如果用MVC模式来理解的话,EntityHero是Controller,而EntityData就是Model,在EntityData中有方法GetAttack返回值是long,猜想应该就是返回攻击力的函数,这时就回到IDA去查看函数的具体逻辑。

由于平时做的逆向都是基于x86和x64的,和ARM64汇编有一定语法差异,推荐阅读参考目录下的ARM64汇编基础。因为ARM是RISK指令,所以每条指令长度一致,这使得修改机器码更容易,至少不需要NOP

然后发现居然有三个GetAttack相关的方法:GetAttackBaseGetAttackBase_4313...GetAttack,但可以发现GetAttackBaseGetAttack函数都有调用GetAttackBase_4313...方法,而GetAttackBase_4313...的返回值基于自身的计算结果,因此尝试修改GetAttackBase_4313...函数的返回值来一劳永逸。

越狱tweaks编写

顺便提一下越狱tweak编写,给自己做记录使用,对于非越狱手机可以跳过本节(因为本节原本就是写完文章之后才想起来强行插入的)。使用的工具是MonkeyDev,安装和使用方式都在其官方wiki上非常详尽了。
首先打开Xcode新建logos工程,工程设置如下,因为是USB连接,使用iproxy把手机22端口映射到本地2222端口,并设置了安装完之后重启cnarchero也就是弓箭传说app。

编辑tweak的plist文件,写明需要注入的进程bundle id为正版弓箭传说的id,可以在app的plist文件中找到。

编写工程名.mm文件,用于hook,内容如注释所示,其中0x10118BDD4是函数GetAttackBase_4313...的静态地址,可以从dump.cs文件中获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#line 1 "/Users/wc/Desktop/enarcwc/enarcwc/enarcwc.xm"
#import <substrate.h>
#import <dlfcn.h>
#import <mach-o/dyld.h>

// 用于保存原函数指针
int (*old_get_AttackValue)();

// 自创的新版函数
int new_get_AttackValue()
{
return 10000;
}


static __attribute__((constructor)) void _logosLocalCtor_735b90b4(int __unused argc, char __unused **argv, char __unused **envp)
{
@autoreleasepool
{
// 执行hook
unsigned long attack = _dyld_get_image_vmaddr_slide(0) + 0x10118BDD4;
MSHookFunction((void *)attack, (void *)&new_get_AttackValue, (void **)&old_get_AttackValue);
}
}

Command-B一下就把tweak安装到越狱机器上了。

非越狱修改

修改程序流程

GetAttackBase_4313...函数最后几行汇编代码如下:

1
2
3
4
5
6
7
8
9
il2cpp:000000010118BED0 01 10 2E 1E                 FMOV            S1, #1.0
il2cpp:000000010118BED4 00 28 21 1E FADD S0, S0, S1
il2cpp:000000010118BED8 00 08 28 1E FMUL S0, S0, S8
il2cpp:000000010118BEDC 00 00 38 9E FCVTZS X0, S0
il2cpp:000000010118BEE0 FD 7B 43 A9 LDP X29, X30, [SP,#0x30]
il2cpp:000000010118BEE4 F4 4F 42 A9 LDP X20, X19, [SP,#0x20]
il2cpp:000000010118BEE8 F6 57 41 A9 LDP X22, X21, [SP,#0x10]
il2cpp:000000010118BEEC E9 23 C4 6C LDP D9, D8, [SP],#0x40
il2cpp:000000010118BEF0 C0 03 5F D6 RET

函数的返回值是64位int,因此通过X0寄存器返回,如果是32位int的话就是W0寄存器。所以可以对上诉代码的第4行进行修改,反正原本也是对X0寄存器进行操作,尝试修改为例如MOV X0, 0xFFFF之类的,就相当于返回一个很大的攻击力值。使用汇编码转换器生成该汇编语句的机器码,也是个windows工具,希望你虚拟机还开着,记得选择AArch64模式,得到机器码。

在IDA中选择该行汇编语句,选择Edit - Patch program - Change byte...把原有的机器码进行更换,每条ARM指令只有32位,即4字节,所以不要修改多了。修改后的GetAttackBase_4313...最后几行如下:

1
2
3
4
5
6
7
8
9
il2cpp:000000010118BED0 01 10 2E 1E                 FMOV            S1, #1.0
il2cpp:000000010118BED4 00 28 21 1E FADD S0, S0, S1
il2cpp:000000010118BED8 00 08 28 1E FMUL S0, S0, S8
il2cpp:000000010118BEDC E0 FF 9F D2 MOV X0, #0xFFFF
il2cpp:000000010118BEE0 FD 7B 43 A9 LDP X29, X30, [SP,#0x30]
il2cpp:000000010118BEE4 F4 4F 42 A9 LDP X20, X19, [SP,#0x20]
il2cpp:000000010118BEE8 F6 57 41 A9 LDP X22, X21, [SP,#0x10]
il2cpp:000000010118BEEC E9 23 C4 6C LDP D9, D8, [SP],#0x40
il2cpp:000000010118BEF0 C0 03 5F D6 RET

最后选择Edit - Patch program - Apply patches to input file...生成新的可执行文件。

打包重签名

复原ipa包:

  1. 把上一步得到的新的可执行文件放在app包中,覆盖原有的可执行文件
  2. Payload文件夹右键-压缩得到Payload.zip
  3. Payload.zip重命名为app.ipa
    从github上获取IPAPatch,把app.ipa放到IPAPatch的Assets目录下,覆盖原有的app.ipa文件。

    打开xcode工程文件,删掉IPAPatchEntry.mload方法对示例代码的调用,插上非越狱手机运行即可,当然还需要使用个人开发者帐号进行签名。

运行效果


打怪当然是秒杀的,被怪打也是被秒杀的,因为怪和主角使用的Model都是EntityData,所以一改全改了,不过还是极大降低了难度,其次夸赞一下这游戏的架构设计还是不错的。

参考

原创旅行的青蛙Unity游戏逆向修改Android&iOS-『iOS安全』-看雪安全论坛
膜改一下旅行的蛤–iOS Unity3D 游戏修改实战 - Essence Sharing | 干货分享 - iOSRE
免越狱版 iOS 抢红包插件 - Swiftyper
iOS汇编入门教程(一)ARM64汇编基础 - 掘金

CATALOG
  1. 1. 工具
  2. 2. 开始逆向
    1. 2.1. 砸壳
    2. 2.2. 恢复符号
    3. 2.3. 流程分析
    4. 2.4. 越狱tweaks编写
    5. 2.5. 非越狱修改
      1. 2.5.1. 修改程序流程
      2. 2.5.2. 打包重签名
    6. 2.6. 运行效果
  3. 3. 参考