我是个新手,接触iOS逆向破解没几个月,网上教程资料看了很多,知识点比较零散,使用的工具大都是针对iOS 10之前的,在iOS 11上基本无法使用,弯路走了不少,经过多次试验,最终摸索出了自己的破解思路和风格。 网上已经有很多基于CydiaSubstrate的越狱开发教程,使用theos、MonkeyDev进行开发非常方便、高效,而本文则是脱离CydiaSubstrate,以非Unity游戏《干掉月亮》(Shoot The Moon)为例,纯手工打造动态库,注入到游戏中实现破解,虽说效率不比使用CydiaSubstrate来的高,但是能学到很多基本的知识,非常适合新手入门。
从App Store下载,或者从苹果开发者页面下载都可以,下载最新版即可。
macOS自带。许多命令需要在终端上执行。
macOS自带。查看手机日志。
macOS自带。通过电脑登录手机。
macOS自带。在手机和电脑间互传文件。
从Homebrew官网(brew.sh) 下载安装。 用于安装各种命令行工具。
打开Terminal,使用Homebrew安装。
将iPhone上的端口通过usb线映射到电脑上的端口。
从https://github.com/Tyilo/macho_edit 下载源码。 编译后可以得到macho_edit可执行文件。 用于编辑App的可执行文件。
请自行搜索下载安装。 用于反编译App。
从Cydia Impactor官网(www.cydiaimpactor.com) 下载。 用于安装ipa到手机。
从https://github.com/BishopFox/bfinject 下载。 根据说明文档,将bfinject放到iPhone中。 用来砸壳、动态库注入等。
从https://github.com/DeviLeo/AppList 下载。 根据说明文档,将命令行版的applst放到iPhone中。 用于列出手机上安装的App及文档目录。
从App Store 下载。 这是一款有趣好玩的游戏。
开局一台机,过程全靠猜。 下面正式进入破解阶段,我会非常啰嗦地写下所有的步骤。 如果您有一定地开发经验,尽情跳过所有已会的步骤。
从App Store下载下来的App都是加密的,直接复制出来是无法使用Hopper Disassembler看到任何代码的,所以我们需要砸壳解密。
如果你的Wifi环境不是很理想,可以使用usbmuxd映射端口走usb线。 此处我们映射iPhone上的ssh端口22到macOS上的2222端口,输入以下命令。
默认密码是 alpine 。
启动《干掉月亮》,查询App名称和进程ID。 因为《干掉月亮》的英文名是《Shoot The Moon》,所以我们在搜索进程的时候使用grep来筛选出带有moon的输出行。
有了App名称和进程ID,我们就可以使用bfinject来砸壳了。
命令执行完成并不表示砸壳已经完成,需要以App界面的提示为准。 下图为砸壳中,务必保持屏幕常亮。
下图为砸壳完成。 如果你会使用NetCat,选择YES,将砸壳后的App传到电脑上。否则选择No,稍后使用scp来传输文件。 砸壳后的文件在App的文档目录下,名字为decrypted.ipa。
注意 : 1、bfinject命令必须在bfinject目录下执行,在其它目录下执行会出错。 2、如果砸壳过程中出现闪退,按Ctrl+C退出,重启App,再执行一遍砸壳命令即可。
此处使用工具applst查询App的文档目录。
其中 bundleURL 就是App的目录,dataContainerURL 是App的文档目录,数据、缓存、临时文件都存储在这个目录。注意 :少数情况下,一个App可能拥有 多个dataContainerURL ,applst只能列出其中一个。
电脑上输入以下命令,将Documents目录下的decrypted-app.ipa传到电脑上。
如下图,选中ipa文件,右键选择解压工具解压,使用系统默认的解压工具即可。
解压后得到一个文件夹叫Payload,里面有shootthemoon.app,其实是一个文件夹。 如下图,选中后右键选择显示包内容。 找到 shootthemoon 没有后缀名的文件,这是一个砸壳后的Mach-O格式的可执行文件。 复制一份到其它目录,准备拿它开刀。
启动Hopper Disassembler,菜单栏中选择File->Read Executable to Disassembler...,或者按下快捷键Command+Shift+O,弹出的对话框中选择 shootthemoon 文件,再点击OK。 紧接着会弹出如下图所示的对话框,保持默认选项,点击OK。 等待解析完成后,File->Save或Command+S保存一下,以免闪退后又要重新解析。
虽然Hopper Disassembler已经为我们解析出来大部分的方法名,但是我们不知道该从何下手。 与其瞎忙活,不如先玩一盘再说。 哇哦,10分!在游戏中靠近顶部的地方有一条隐隐约约的横线,当目标正好压在这根线上时,击中会得到10分,连着击中3次10分可以获得双倍分数的加成,而在那根线下面击中时分数会递减,加成也会消失,那就有思路了,我们把那根线移到最下面,这样我们就可以不管目标在哪个位置时,击中都能获得10分。 让我们回到Hopper Disassembler,在左侧列表中输入“ten”,看看能不能搜索到有关设置10分线位置的方法。 哇哦,运气太好了,这么容易就找到了,MainEnemy 类里面有一个方法名叫做 tenPointLineY 的方法,看名字应该是10分线的Y坐标,如果能将Y坐标改为屏幕最底部就能达到目的了。 嗯?你想直接改汇编代码?这对于新手来说太难了,我们用高级语言来实现我们想法。
打开Xcode,创建一个Project,选择Cocoa Touch Framework,名字就叫ShootMoonHacker。 首先新建文件,选择Cocoa Touch Class,名字叫HackerLoader,继承NSObject类,动态库加载时初始化的代码写在这个类中。 再新建一个文件,选择Objective-C File,文件类型选择Category,类选择NSObject,名字叫Hacker,所有的破解代码都写在这个分类中。 工程目录结构如下图。
这个文件是创建工程时自动创建的,我们将需要公开的类全部写在这个文件里即可。
我们在文件的最后追加我们之前创建的类和分类。 import时记得使用尖括号,而不是双引号。
我们在分类头文件中声明一个方法,叫hack,提供给HackerLoader类调用,执行破解代码。
现在我们开始改写 tenPointLineY 方法。 我们可以使用Objective-C运行时的方法交换来达到我们的目的。 此处我事先封装了交换方法的代码,参数是原始方法名、原始类名、新方法、新类、是否为类方法。 虽然我们目前只需要交换 tenPointLineY 这一个方法,但是考虑到以后可能需要交换多个方法,为了避免出现大量重复的代码,所以封装一下,同时也能让代码看起来整洁一点。
注 :使用方法交换需要导入<objc/runtime.h>
头文件。
接下来编写我们自己的 tenPointLineY 方法,名字就叫 my_tenPointLineY 。
[self my_tenPointLineY] 这一句,其实调用的是原始的 tenPointLineY 方法,因为在交换方法后,原始的 tenPointLineY 方法名对应的是 my_tenPointLineY 方法的代码段,而 my_tenPointLineY 方法名对应的是 tenPointLineY 方法的代码段。 具体可以搜索 Method Swizzling 来了解方法交换的细节。 我们不知道把10分线的Y坐标改为多少才是我们想要的效果,先改个200试试看。 添加NSLog打印日志是为了确定代码是否按我们预期的那样执行了。
写完 my_tenPointLineY ,开始交换方法,也就是实现我们最开始声明的hack方法。
原始的 tenPointLineY 是属于 MainEnemy 类的,my_tenPointLineY 是属于 NSObject 类的,是实例方法。
我们希望在动态库加载时自动执行破解代码的话,需要这么写。
不要忘记导入头文件 NSObject+Hacker.h 。
方法 entry 的名字不是固定的,可以是你想要的任何符合命名规则的名字。 关键的是 __attribute__((constructor)) ,当动态库被加载的时候,会自动执行拥有此属性的方法。
在编译之前,有些工程配置需要修改。
如下图,iOS Deployment Target 改为 iOS 11.0 。
如下图,签名 改为 None 。
如下图,编译目标 改为 Generic iOS Device 。
配置修改完成后,按下Command+B编译。 编译成功后,就能得到ShootMoonHacker.framework。
展开Products目录,右键ShootMoonHacker.framework,选择Show in Finder。
新开一个Terminal窗口,输入 cd空格 ,然后把 ShootMoonHacker.framework 目录拖到Terminal窗口上,如下图。
松开后,效果如下图。
按下回车键后,我们需要将 ShootMoonHacker 文件传到iPhone上,命令如下。
我猜测 tenPointLineY 这个方法会在开始游戏时调用,所以先将游戏退到初始界面,然后回到通过电脑ssh登录到iPhone上的Terminal窗口,进入到bfinject目录,输入如下命令注入我们的动态库。
注意 : 1、bfinject命令必须在bfinject目录下执行,在其它目录下执行会出错。 2、如果注入过程中出现闪退,按Ctrl+C退出,重启App,再执行一遍注入命令即可。
注入成功后,我们开始游戏看一下效果。
就像游戏中的肥蜜蜂一样,我皱起了眉头。 怎么没有任何效果,是不是动态库没有执行?
让我们在电脑上打开Console,选择iPhone,重启游戏再执行一遍注入。 由于日志太多,我们在搜索栏输入“>>>”,按下回车,可以在列表中看到我们注入成功的代码。右键选择我们动态库输出的那行日志,选择Show Process 'shootthemoon',这样列表就会隐藏其它和我们App无关的日志了。 日志告诉我们,注入成功了,但是10分线的位置为什么没有改变呢?开始游戏的时候似乎也没有调用我们的代码。难道10分线的位置是启动游戏时候调用的?抱着试试看的想法,我们在游戏启动的瞬间执行注入命令,不过这么做会导致游戏卡住然后闪退,只能换一种方式了。
还记得我们的 shootthemoon.app 目录吗?把我们的动态库文件 ShootMoonHacker 复制到这个目录下。 接下来我们要使用 macho_edit 工具来修改 shootthemoon 可执行文件的动态库列表。 还记得我们备份出来的那个 shootthemoon 文件吗?我们对它下手。 执行如下命令。
选择 2 Load command edit ,编辑列表。
选择 1 List load commands ,看看原始的列表有哪些内容。
能看到加载了很多系统的动态库。 选择 3 Insert load command ,将我们的动态库注入到文件中。
选择 1 LC_LOAD_DYLIB ,注入动态库。
输入 @executable_path/ShootMoonHacker ,按下回车。@executable_path 指的是 shootthemoon 这个可执行文件所在的目录,也就是 shootthemoon.app 这个目录。
选择 1 List load commands ,看看编辑是否成功。
如果列表中最后一项不是输入的那样,如上结果后面跟着其它字符,说明编辑出现了bug,需要退出 macho_edit ,删除错误内容,再重新插入。
选择6,取消编辑。
选择3,退出。重新再次编辑。
选择2,编辑。
选择2,删除。
选择50,删除bug导致的错误的动态库路径。
选择3,删除后重新插入动态库。
选择1,动态库。
输入动态库路径 @executable_path/ShootMoonHacker 。
选择1,确认插入的动态库路径是否正确。
选择6,动态库路径插入正确,可以退出编辑。
选择3,退出。
把编辑好的 shootthemoon 文件覆盖掉 shootthemoon.app 目录下的原始文件。 然后在Finder中,右键 Payload 文件夹,选择 压缩"Payload" ,得到 Payload.zip 压缩包,将其重命名为 shootthemoon_hacked.ipa 。 打开 Impactor 工具,将 shootthemoon_hacked.ipa 文件拖到 Impactor 的 install Cydia Extender 的位置上。 松开后,会要求输入你的 Apple ID 和 密码 ,然后会将ipa安装到手机上。注意 : 1、Apple账号需要关闭二次验证功能,否则Impactor会安装失败。 2、安装前务必先卸载原来的App,因为从App Store下载的App签名与Impactor安装的App签名不同,所以无法覆盖安装。推荐 :申请一个新的Apple账号,专门用于Impactor安装ipa。
安装完成后,赶紧启动游戏看一下效果。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!