让 iPhone 上显示学期周数(二) — — 定位锁屏界面目标方法

第一篇文章中我们确定了要修改两处 UI 元素,本篇先来定位锁屏界面显示时间处的目标方法。

定位 Framework

控制锁屏界面的 Framework 显然应该是私有的,查看设备的 /System/Library/PrivateFrameworks 目录,寻找可疑目标。

推断目标方法应在与 SpringBoard 有关的 framework 内,因此初步确定 SpringBoardFoundation.framework 和 SpringBoardUI.framework 等几个含有关键词的目标进行尝试。这里以 SpringBoardFoundation.framework 为例。

定位目标方法

接下来的思路是提取 framework 库文件(即二进制文件)中的头文件信息,从中寻找目标方法的定义。

获取库文件

如果直接进入 SpringBoardFoundation.framework 这个目录,会发现其中并没有二进制文件。这是因为出于某些考虑, iOS 把许多库文件存进了 cache 里,它位于 /System/Library/Caches/com.apple.dyld/dyld_shared_cache_armx。

先将 cache 文件拷贝至 Mac,然后从这里下载 dyld_decache 工具,解压之后赋予其可执行权限,然后开始从 cache 中提取二进制文件。

~  chmod +x ./dyld_decache\[v0.1c\]
~ ./dyld_decache\[v0.1c\] -o ./decached_binaries/ ./dyld_shared_cache_armx

这样提取出的所有文件都存放在了 ./decached_binaries/ 这个目录,在其中找到 SpringBoardFoundation.framework/SpringBoardFounation 这个二进制文件,准备提取头文件。

获取头文件

class-dump 可以利用 Objective-C 的 runtime 特性,将存储在 Mach-O 文件中的头文件信息提取出来并生成 .h 文件。

安装后用如下命令 dump 准备好的二进制库文件。

~  class-dump -S -s -H ./SpringBoardFoundation -o ./dumped_headers/

这样 ./dumped_headers 这个目录下就存放了该 framework 所有的头文件。

定位目标文件及方法

iOS 的命名通常都很规范,大多数类名可以望文生义。因为我们要修改的是锁屏界面显示日期处,故直接在生成的头文件中寻找含有 lock 字样的文件。很快发现有一个名为 SBFLockScreenDateView.h 的文件高度可疑,打开查看其内容

容易发现 SBFLockScreenDateView 类具有一个 NSString 类型的属性 _customSubtitleText,对应还有一个 setter 方法 - (void)setCustomSubtitleText:(id)arg1 withColor:(id)arg2;,从命名上看与我们的需求高度吻合,所以初步确定这个方法就是我们要找的目标方法。

目标方法测试

我们已经根据确定了一个「嫌疑人」,现在马上编写 WeekCount 插件测试这个方法。创建工程的部分与上篇文章中的基本无异,因此略过。

Makefile

这里在 Makefile 中需要注意的有两点:一是用 WeekCount_PRIVATE_FRAMEWORKS 字段引入 SpringBoardFounation;二是 iOS 9.3 的 SDK 移除了 Private Framework,所以如果 Xcode 版本高于 7.3,则需要手动下载 9.2 版本的 SDK,并在 TARGET 字段指定,具体方法可参见这个链接。最终内容如下。

THEOS_DEVICE_IP = IOSIP
ARCHS = armv7 arm64
TARGET = iphone:clang:9.2:8.0

include theos/makefiles/common.mk

TWEAK_NAME = WeekCount
WeekCount_FILES = Tweak.xm
WeekCount_FRAMEWORKS = UIKit
WeekCount_PRIVATE_FRAMEWORKS = SpringBoardFoundation

include $(THEOS_MAKE_PATH)/tweak.mk

after-install::
install.exec "killall -9 SpringBoard"

Tweak.xm

根据之前的分析,在 Tweak.xm 中 hook SBFLockScreenDateView 类的 — (void)setCustomSubtitleText:(id)arg1 withColor:(id)arg2; 方法。根据命名规则,参数 arg1 和 arg2 应分别是日期的文本和文字颜色,因此用 %orig 命令修改 arg1。

#import <UIKit/UIKit.h>

%hook SBFLockScreenDateView


- (void)setCustomSubtitleText:(id)arg1 withColor:(id)arg2 {
%orig(@"WeekCount", arg2);
}

%end

测试结果

编译安装插件,设备注销后可看到目标位置已经由日期变成了 WeekCount 这个测试字符串,至此我们已经成功找到了该位置对应的方法。

补充

大多数情况下,寻找目标方法很难一次命中,难免需要在各个 framework 中间不断尝试,每次都要手动 dump 显然稍显复杂。好在已经有人把所有的 iOS 头文件 dump 出来放到了这里,因此还有一个更为简便的方法是直接在这个 repo 中搜索关键词。

当然,这样尽管方便,但不能保证头文件是最新的、和自己的设备相符的(尤其是在 iOS 版本刚刚更新的时间点),所以掌握 dump 头文件的方法仍然是必要的。