iOS 11上的破解入门教程

前言

我是个新手,接触iOS逆向破解没几个月,网上教程资料看了很多,知识点比较零散,使用的工具大都是针对iOS 10之前的,在iOS 11上基本无法使用,弯路走了不少,经过多次试验,最终摸索出了自己的破解思路和风格。
网上已经有很多基于CydiaSubstrate的越狱开发教程,使用theos、MonkeyDev进行开发非常方便、高效,而本文则是脱离CydiaSubstrate,以非Unity游戏《干掉月亮》(Shoot The Moon)为例,纯手工打造动态库,注入到游戏中实现破解,虽说效率不比使用CydiaSubstrate来的高,但是能学到很多基本的知识,非常适合新手入门。

准备工作

设备

  • 一部越狱的iPhone手机(iOS 11.0~11.1.2)
  • 一台Mac机

macOS上的工具

  • Xcode
  • Terminal
  • Console
  • ssh
  • scp
  • Homebrew
  • usbmuxd
  • macho_edit
  • Hopper Disassembler
  • Impactor

iOS上的工具

  • bfinject
  • applst

App

  • 《干掉月亮》(Shoot The Moon)

下载安装

Xcode

从App Store下载,或者从苹果开发者页面下载都可以,下载最新版即可。

Terminal

macOS自带。许多命令需要在终端上执行。

Console

macOS自带。查看手机日志。

ssh

macOS自带。通过电脑登录手机。

scp

macOS自带。在手机和电脑间互传文件。

Homebrew

Homebrew官网(brew.sh)下载安装。
用于安装各种命令行工具。

usbmuxd

打开Terminal,使用Homebrew安装。

$ brew install usbmuxd
# 或者
$ brew install libidevicemobile

将iPhone上的端口通过usb线映射到电脑上的端口。

macho_edit

https://github.com/Tyilo/macho_edit下载源码。
编译后可以得到macho_edit可执行文件。
用于编辑App的可执行文件。

Hopper Disassembler

请自行搜索下载安装。
用于反编译App。

Impactor

Cydia Impactor官网(www.cydiaimpactor.com)下载。
用于安装ipa到手机。

bfinject

https://github.com/BishopFox/bfinject下载。
根据说明文档,将bfinject放到iPhone中。
用来砸壳、动态库注入等。

applst

https://github.com/DeviLeo/AppList下载。
根据说明文档,将命令行版的applst放到iPhone中。
用于列出手机上安装的App及文档目录。

《干掉月亮》(Shoot The Moon)

App Store下载。
这是一款有趣好玩的游戏。

破解

开局一台机,过程全靠猜。
下面正式进入破解阶段,我会非常啰嗦地写下所有的步骤。
如果您有一定地开发经验,尽情跳过所有已会的步骤。

砸壳

从App Store下载下来的App都是加密的,直接复制出来是无法使用Hopper Disassembler看到任何代码的,所以我们需要砸壳解密。

使用usbmuxd

如果你的Wifi环境不是很理想,可以使用usbmuxd映射端口走usb线。
此处我们映射iPhone上的ssh端口22到macOS上的2222端口,输入以下命令。

$ iproxy 2222 22

ssh登录iPhone

默认密码是 alpine

# -p 后面跟端口,默认是22
$ ssh root@localhost -p 2222
root@localhost's password: # 输入密码,默认密码是alpine

查询名称及进程ID

启动《干掉月亮》,查询App名称和进程ID。
因为《干掉月亮》的英文名是《Shoot The Moon》,所以我们在搜索进程的时候使用grep来筛选出带有moon的输出行。

$ ps -e | grep moon
1975 ??         0:59.42 /var/containers/Bundle/Application/24546F40-53DA-4C49-81C0-DED0C13DB814/shootthemoon.app/shootthemoon

砸壳

有了App名称和进程ID,我们就可以使用bfinject来砸壳了。

# 以下两条命令二选一,推荐使用App名称

# 使用App名称
$ bash bfinject -P shootthemoon -L decrypt

# 使用进程ID
$ bash bfinject -p 1975 -L decrypt

# 输出结果
[+] Liberios detected
[+] Injecting into '/var/containers/Bundle/Application/24546F40-53DA-4C49-81C0-DED0C13DB814/shootthemoon.app/shootthemoon'
[+] Getting Team ID from target application...
[+] Thinning dylib into non-fat arm64 image
[+] Signing injectable .dylib with Team ID A6DVR4V967 and platform entitlements...
[bfinject4realz] Calling task_for_pid() for PID 1975.
[bfinject4realz] Calling thread_create() on PID 1975
[bfinject4realz] Looking for ROP gadget... found at 0x1826574e0
[bfinject4realz] Fake stack frame at 0x10b624000
[bfinject4realz] Calling _pthread_set_self() at 0x182897804...
[bfinject4realz] Returned from '_pthread_set_self'
[bfinject4realz] Calling dlopen() at 0x182657460...
[bfinject4realz] Returned from 'dlopen'
[bfinject4realz] Success! Library was loaded at 0x1c41e2d00
[+] So long and thanks for all the fish.

命令执行完成并不表示砸壳已经完成,需要以App界面的提示为准。
下图为砸壳中,务必保持屏幕常亮。

下图为砸壳完成。

如果你会使用NetCat,选择YES,将砸壳后的App传到电脑上。否则选择No,稍后使用scp来传输文件。
砸壳后的文件在App的文档目录下,名字为decrypted.ipa。

注意
1、bfinject命令必须在bfinject目录下执行,在其它目录下执行会出错。
2、如果砸壳过程中出现闪退,按Ctrl+C退出,重启App,再执行一遍砸壳命令即可。

找到《干掉月亮》的App文档目录

此处使用工具applst查询App的文档目录。

# applst默认会列出所有用户安装的App以及BundleID,
# 使用grep过滤只显示《干掉月亮》的BundleID
$ applst | grep shoot
$ applst com.pipsqueakgames.shootmoon
# 此处会显示《干掉月亮》的相关信息
localizedName:
Shoot Moon

shortVersionString:
1.6

vendorName:
Shaun Coleman

applicationIdentifier:
com.pipsqueakgames.shootmoon

itemID:
809390893

itemName:
Shoot The Moon

teamID:
A6DVR4V967

bundleURL:
file:///private/var/containers/Bundle/Application/24546F40-53DA-4C49-81C0-DED0C13DB814/shootthemoon.app

dataContainerURL:
file:///private/var/mobile/Containers/Data/Application/99B25BD3-77BA-47F0-9503-46F912C5A2E4

applicationType:
User

其中 bundleURL 就是App的目录,dataContainerURL 是App的文档目录,数据、缓存、临时文件都存储在这个目录。
注意:少数情况下,一个App可能拥有 多个dataContainerURL,applst只能列出其中一个。

传输砸壳后的文件

电脑上输入以下命令,将Documents目录下的decrypted-app.ipa传到电脑上。

# -P 后面跟端口,默认是22
$ scp -P 2222 root@localhost:/private/var/mobile/Containers/Data/Application/99B25BD3-77BA-47F0-9503-46F912C5A2E4/Documents/decrypted-app.ipa ./
root@localhost's password: # 输入密码,与ssh登录时的密码相同    

破解

解压砸壳后的ipa

如下图,选中ipa文件,右键选择解压工具解压,使用系统默认的解压工具即可。

备份可执行文件

解压后得到一个文件夹叫Payload,里面有shootthemoon.app,其实是一个文件夹。
如下图,选中后右键选择显示包内容。

找到 shootthemoon 没有后缀名的文件,这是一个砸壳后的Mach-O格式的可执行文件。
复制一份到其它目录,准备拿它开刀。

使用Hopper Disassembler

启动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,所有的破解代码都写在这个分类中。
工程目录结构如下图。

ShootMoonHacker.h

这个文件是创建工程时自动创建的,我们将需要公开的类全部写在这个文件里即可。

#import <UIKit/UIKit.h>

//! Project version number for ShootMoonHacker.
FOUNDATION_EXPORT double ShootMoonHackerVersionNumber;

//! Project version string for ShootMoonHacker.
FOUNDATION_EXPORT const unsigned char ShootMoonHackerVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <ShootMoonHacker/PublicHeader.h>
#import <HackerLoader.h>
#import <NSObject+Hacker.h>

我们在文件的最后追加我们之前创建的类和分类。
import时记得使用尖括号,而不是双引号。

NSObject+Hacker.h

我们在分类头文件中声明一个方法,叫hack,提供给HackerLoader类调用,执行破解代码。

#import <Foundation/Foundation.h>

@interface NSObject (Hacker)

- (void)hack;

@end

NSObject+Hacker.m

现在我们开始改写 tenPointLineY 方法。
我们可以使用Objective-C运行时的方法交换来达到我们的目的。
此处我事先封装了交换方法的代码,参数是原始方法名、原始类名、新方法、新类、是否为实例方法。
虽然我们目前只需要交换 tenPointLineY 这一个方法,但是考虑到以后可能需要交换多个方法,为了避免出现大量重复的代码,所以封装一下,同时也能让代码看起来整洁一点。

- (void)exchangeMethod:(NSString *)methodName ofClass:(NSString *)className toMethod:(SEL)method ofClass:(Class)class isInstanceMethod:(BOOL)isInstanceMethod {
    Class c = NSClassFromString(className);
    SEL sel = NSSelectorFromString(methodName);
    Method om = isInstanceMethod ? class_getInstanceMethod(c, sel) : class_getClassMethod(c, sel);
    Method nm = isInstanceMethod ? class_getInstanceMethod(class, method) : class_getClassMethod(class, method);
    method_exchangeImplementations(om, nm);
}

:使用方法交换需要导入<objc/runtime.h>头文件。

#import <objc/runtime.h>

接下来编写我们自己的 tenPointLineY 方法,名字就叫 my_tenPointLineY

- (float)my_tenPointLineY {
    NSLog(@">>>>> my_tenPointLineY");
    float ret = [self my_tenPointLineY];
    NSLog(@">>>>> my_tenPointLineY ret: %f", ret);
    ret = 200;
    NSLog(@">>>>> change tenPointLineY ret to: %f", ret);
    return ret;
}

[self my_tenPointLineY] 这一句,其实调用的是原始的 tenPointLineY 方法,因为在交换方法后,原始的 tenPointLineY 方法名对应的是 my_tenPointLineY 方法的代码段,而 my_tenPointLineY 方法名对应的是 tenPointLineY 方法的代码段。
具体可以搜索 Method Swizzling 来了解方法交换的细节。
我们不知道把10分线的Y坐标改为多少才是我们想要的效果,先改个200试试看。
添加NSLog打印日志是为了确定代码是否按我们预期的那样执行了。

写完 my_tenPointLineY,开始交换方法,也就是实现我们最开始声明的hack方法。

- (void)hack {
    NSString *className = @"MainEnemy";
    Class selfClass = [self class];

    // TenPointLineY
    [self exchangeMethod:@"tenPointLineY" ofClass:className
                toMethod:@selector(my_tenPointLineY) ofClass:selfClass
        isInstanceMethod:YES];
}

原始的 tenPointLineY 是属于 MainEnemy 类的,my_tenPointLineY 是属于 NSObject 类的,是实例方法。

HackerLoader.m

我们希望在动态库加载时自动执行破解代码的话,需要这么写。

static void __attribute__((constructor)) entry(void) {
    NSLog(@">>>>> Code Injected <<<<<");
    NSObject *obj = [[NSObject alloc] init];
    [obj hack];
}

不要忘记导入头文件 NSObject+Hacker.h

#import "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上,命令如下。

$ scp -P 2222 ShootMoonHacker root@localhost:~/
root@localhost's password: # 输入密码

我猜测 tenPointLineY 这个方法会在开始游戏时调用,所以先将游戏退到初始界面,然后回到通过电脑ssh登录到iPhone上的Terminal窗口,进入到bfinject目录,输入如下命令注入我们的动态库。

# 注入动态库
$ bash bfinject -P shootthemoon -l ~/ShootMoonHacker

# 输出结果
[+] Liberios detected
[+] Injecting into '/var/containers/Bundle/Application/24546F40-53DA-4C49-81C0-DED0C13DB814/shootthemoon.app/shootthemoon'
[+] Getting Team ID from target application...
[+] Thinning dylib into non-fat arm64 image
[+] Signing injectable .dylib with Team ID A6DVR4V967 and platform entitlements...
[bfinject4realz] Calling task_for_pid() for PID 3593.
[bfinject4realz] Calling thread_create() on PID 3593
[bfinject4realz] Looking for ROP gadget... found at 0x1826574e0
[bfinject4realz] Fake stack frame at 0x10b3c8000
[bfinject4realz] Calling _pthread_set_self() at 0x182897804...
[bfinject4realz] Returned from '_pthread_set_self'
[bfinject4realz] Calling dlopen() at 0x182657460...
[bfinject4realz] Returned from 'dlopen'
[bfinject4realz] Success! Library was loaded at 0x1c0151b40
[+] So long and thanks for all the fish.

注意
1、bfinject命令必须在bfinject目录下执行,在其它目录下执行会出错。
2、如果注入过程中出现闪退,按Ctrl+C退出,重启App,再执行一遍注入命令即可。

注入成功后,我们开始游戏看一下效果。

就像游戏中的肥蜜蜂一样,我皱起了眉头。
怎么没有任何效果,是不是动态库没有执行?

查看日志

让我们在电脑上打开Console,选择iPhone,重启游戏再执行一遍注入。

由于日志太多,我们在搜索栏输入“>>>”,按下回车,可以在列表中看到我们注入成功的代码。右键选择我们动态库输出的那行日志,选择Show Process 'shootthemoon',这样列表就会隐藏其它和我们App无关的日志了。

日志告诉我们,注入成功了,但是10分线的位置为什么没有改变呢?开始游戏的时候似乎也没有调用我们的代码。难道10分线的位置是启动游戏时候调用的?抱着试试看的想法,我们在游戏启动的瞬间执行注入命令,不过这么做会导致游戏卡住然后闪退,只能换一种方式了。

换个姿势插入动态库

还记得我们的 shootthemoon.app 目录吗?把我们的动态库文件 ShootMoonHacker 复制到这个目录下。
接下来我们要使用 macho_edit 工具来修改 shootthemoon 可执行文件的动态库列表。
还记得我们备份出来的那个 shootthemoon 文件吗?我们对它下手。
执行如下命令。

$ macho_edit shootthemoon
# 输出内容
Thin mach-o binary:
	arm64 arch (offset 0x0)

1 Fat mach-o configuration
2 Load command edit
3 Exit

Select an option: 2

选择 2 Load command edit,编辑列表。

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 1

选择 1 List load commands,看看原始的列表有哪些内容。

arm64 arch (offset 0x0):
	0: LC_SEGMENT_64: __PAGEZERO
	1: LC_SEGMENT_64: __TEXT
	2: LC_SEGMENT_64: __DATA
	3: LC_SEGMENT_64: __LINKEDIT
	4: LC_DYLD_INFO_ONLY
	5: LC_SYMTAB
	6: LC_DYSYMTAB
	7: LC_LOAD_DYLINKER: /usr/lib/dyld
	8: LC_UUID: 6f33289b-6f33-3328-9b6d-3c3a847d7f7f
	9: LC_VERSION_MIN_IPHONEOS: 0.0.0
	10: LC_SOURCE_VERSION
	11: LC_MAIN: 0x242d8
	12: LC_ENCRYPTION_INFO_64
	13: LC_LOAD_DYLIB: /usr/lib/libz.1.dylib
	14: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreText.framework/CoreText
	15: LC_LOAD_DYLIB: /System/Library/Frameworks/EventKit.framework/EventKit
	16: LC_LOAD_DYLIB: /System/Library/Frameworks/EventKitUI.framework/EventKitUI
	17: LC_LOAD_DYLIB: /System/Library/Frameworks/WebKit.framework/WebKit
	18: LC_LOAD_DYLIB: /System/Library/Frameworks/Social.framework/Social
	19: LC_LOAD_DYLIB: /System/Library/Frameworks/Security.framework/Security
	20: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreTelephony.framework/CoreTelephony
	21: LC_LOAD_DYLIB: /System/Library/Frameworks/MessageUI.framework/MessageUI
	22: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreData.framework/CoreData
	23: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreMedia.framework/CoreMedia
	24: LC_LOAD_DYLIB: /System/Library/Frameworks/StoreKit.framework/StoreKit
	25: LC_LOAD_DYLIB: /System/Library/Frameworks/AdSupport.framework/AdSupport
	26: LC_LOAD_DYLIB: /System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration
	27: LC_LOAD_DYLIB: /System/Library/Frameworks/GameKit.framework/GameKit
	28: LC_LOAD_DYLIB: /System/Library/Frameworks/OpenAL.framework/OpenAL
	29: LC_LOAD_DYLIB: /System/Library/Frameworks/UIKit.framework/UIKit
	30: LC_LOAD_DYLIB: /System/Library/Frameworks/QuartzCore.framework/QuartzCore
	31: LC_LOAD_DYLIB: /System/Library/Frameworks/AVFoundation.framework/AVFoundation
	32: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics
	33: LC_LOAD_DYLIB: /System/Library/Frameworks/OpenGLES.framework/OpenGLES
	34: LC_LOAD_DYLIB: /System/Library/Frameworks/Foundation.framework/Foundation
	35: LC_LOAD_DYLIB: /System/Library/Frameworks/AudioToolbox.framework/AudioToolbox
	36: LC_LOAD_DYLIB: /usr/lib/libobjc.A.dylib
	37: LC_LOAD_DYLIB: /usr/lib/libc++.1.dylib
	38: LC_LOAD_DYLIB: /usr/lib/libSystem.B.dylib
	39: LC_LOAD_DYLIB: /System/Library/Frameworks/CFNetwork.framework/CFNetwork
	40: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
	41: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreMotion.framework/CoreMotion
	42: LC_LOAD_DYLIB: /System/Library/Frameworks/CoreVideo.framework/CoreVideo
	43: LC_LOAD_DYLIB: /System/Library/Frameworks/GLKit.framework/GLKit
	44: LC_LOAD_DYLIB: /System/Library/Frameworks/MediaPlayer.framework/MediaPlayer
	45: LC_LOAD_DYLIB: /System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices
	46: LC_FUNCTION_STARTS
	47: LC_DATA_IN_CODE
	48: LC_CODE_SIGNATURE

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 3

能看到加载了很多系统的动态库。
选择 3 Insert load command,将我们的动态库注入到文件中。

Select the cmd you want to insert:
1 LC_LOAD_DYLIB
2 LC_LOAD_WEAK_DYLIB
3 LC_RPATH
4 Cancel

Select an option: 1

选择 1 LC_LOAD_DYLIB,注入动态库。

Dylib path: @executable_path/ShootMoonHacker

输入 @executable_path/ShootMoonHacker,按下回车。
@executable_path 指的是 shootthemoon 这个可执行文件所在的目录,也就是 shootthemoon.app 这个目录。

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 1

选择 1 List load commands,看看编辑是否成功。

49: LC_LOAD_DYLIB: @executable_path/ShootMoonHackers/MediaP

如果列表中最后一项不是输入的那样,如上结果后面跟着其它字符,说明编辑出现了bug,需要退出 macho_edit,删除错误内容,再重新插入。

# 此处已省略无关内容
49: LC_LOAD_DYLIB: @executable_path/ShootMoonHackers/MediaP

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 6

选择6,取消编辑。

1 Fat mach-o configuration
2 Load command edit
3 Exit

Select an option: 3

选择3,退出。重新再次编辑。

$ macho_edit shootthemoon
# 输出内容
Thin mach-o binary:
	arm64 arch (offset 0x0)

1 Fat mach-o configuration
2 Load command edit
3 Exit

Select an option: 2

选择2,编辑。

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 2

选择2,删除。

Select a load command to remove:

# 此处已省略无关内容
50 LC_LOAD_DYLIB: @executable_path/ShootMoonHackers/MediaP
51 Cancel

Select an option: 50

选择50,删除bug导致的错误的动态库路径。

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 3

选择3,删除后重新插入动态库。

Select the cmd you want to insert:
1 LC_LOAD_DYLIB
2 LC_LOAD_WEAK_DYLIB
3 LC_RPATH
4 Cancel

Select an option: 1

选择1,动态库。

Dylib path: @executable_path/ShootMoonHacker

输入动态库路径 @executable_path/ShootMoonHacker

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 1

选择1,确认插入的动态库路径是否正确。

arm64 arch (offset 0x0):
	# 此处已省略无关内容
	49: LC_LOAD_DYLIB: @executable_path/ShootMoonHacker

1 List load commands
2 Remove load command
3 Insert load command
4 Move load command
5 Remove code signature
6 Cancel

Select an option: 6

选择6,动态库路径插入正确,可以退出编辑。

1 Fat mach-o configuration
2 Load command edit
3 Exit

Select an option: 3

选择3,退出。

打包安装

把编辑好的 shootthemoon 文件覆盖掉 shootthemoon.app 目录下的原始文件。
然后在Finder中,右键 Payload 文件夹,选择 压缩"Payload",得到 Payload.zip 压缩包,将其重命名为 shootthemoon_hacked.ipa
打开 Impactor 工具,将 shootthemoon_hacked.ipa 文件拖到 Impactorinstall Cydia Extender 的位置上。

松开后,会要求输入你的 Apple ID密码,然后会将ipa安装到手机上。
注意
1、Apple账号需要关闭二次验证功能,否则Impactor会安装失败。
2、安装前务必先卸载原来的App,因为从App Store下载的App签名与Impactor安装的App签名不同,所以无法覆盖安装。
推荐:申请一个新的Apple账号,专门用于Impactor安装ipa。

胜败乃兵家常事

安装完成后,赶紧启动游戏看一下效果。

好尴尬啊~虽然10分线出现在了底部,但是分数还是没变。看来 tenPointLineY 只是修改位置,并不会影响到分数。

大侠请重新来过

既然改10分线没有用,那就简单粗暴点,直接改分数吧。
让我们回到Hopper Disassembler,在左侧列表中输入“point”,看看能不能搜索到和分数相关的方法,貌似没有,换个词试试,搜索“score”看看。

第一条 -[GamePlay updateScore:] 貌似看上去可能或许大概应该好像就是修改分数的方法,尝试一下?尝试一下!
回到Xcode,在 NSObject+Hacker.m 文件中添加以下代码。

- (void)my_updateScore:(int)score {
    NSLog(@">>>>> my_updateScore: %d", score);
    score = 10000;
    NSLog(@">>>>> my_updateScore change score to: %d", score);
    [self my_updateScore:score];
    NSLog(@">>>>> my_updateScore done");
}

我们把分数固定在10000分,先看看效果如何。
然后修改hack方法,如下。

- (void)hack {
    NSString *className = @"GamePlay";
    Class selfClass = [self class];

    // UpdateScore
    [self exchangeMethod:@"updateScore:" ofClass:className
                toMethod:@selector(my_updateScore:) ofClass:selfClass
        isInstanceMethod:YES];
}

注意:updateScore是带参数的,对应的字符串后面要有冒号。
例子- (NSInteger)plus:(NSInteger)a and:(NSInteger)b; 方法对应的字符串就是"plus:and:"。

编译通过后,将动态库传到iPhone上,用bfinject注入,玩一下看看。

哈哈哈哈哈,这个貌似有点过分了。虽然和预期想的不太一样,以为updateScore修改的是左下角的总分数,阴差阳错地修改了击中时获得的分数,既然如此我们把分数固定在10分就可以了。
修改代码后,重新编译,再次注入,看看效果。

完美。我们有了如此完美的动态库,可以有两种选择。
1、针对越狱机器,我们保留从App Store下载的版本,需要时通过bfinject注入即可。
2、针对非越狱机器,我们用macho_edit插入动态库,重新打包安装。

总结

基本知识点

1、usbmuxd的使用
2、ssh与scp的使用
3、使用bfinject砸壳
4、Hopper Disassembler的使用
5、Method Swizzling,方法交换
6、动态库的编写
7、使用bfinject实时注入动态库
8、使用macho_edit插入动态库
9、重新打包ipa
10、Impactor的使用

其它

1、如果有苹果开发者账号,可以使用 iOS App Signer 对ipa重签名,然后使用Xcode安装即可。

posted @ 2022-11-22 10:53  DeviLeo  阅读(1037)  评论(0编辑  收藏  举报