一、Tweak修改系统行为
有一个需求是去掉手机桌面的红点显示。首先必须清楚手机桌面也是一款App SpringBoard。
zaizai:~ root# ps -A | grep SpringBoard
26075 ?? 2:14.28 /System/Library/CoreServices/SpringBoard.app/SpringBoard
26859 ttys000 0:00.01 grep SpringBoard
直接拷贝SpringBoard:
scp -P 12345 root@localhost:/System/Library/CoreServices/SpringBoard.app/SpringBoard ./
查看加密信息:
otool -l SpringBoard | grep crypt
这个时候回发现SpringBoard根本没有相关加密字段,也就是它本身是没有加壳的。
class-dump -H SpringBoard -o ./Headers
2021-05-31 19:44:08.473 class-dump[28776:9098728] Unknown load command: 0x00000032
发现直接报错Unknown load command: 0x00000032,github上下载class-dump源码编译最新代码生成class-dump工具class-dump。
虽然解决了报错问题,但是只生成了一个CDStructures.h文件:

难道springboard全部改为了swift实现?

查看了下
SpringBoard发现只有75KB。这就有意思了,说明新版本SpringBoard只是一个壳真正的实现肯定不在SpringBoard .app中。
只好查看下MachO文件了,果然有一个SpringBoard私有库:

在手机上进入/System/Library/PrivateFrameworks/目录果然有SpringBoard.framework:
zaizai:/System/Library/PrivateFrameworks root# AXSpringBoardServerInstance.framework/
SpringBoard.framework/
SpringBoardFoundation.framework/
SpringBoardHome.framework/
SpringBoardServices.framework/
SpringBoardUI.framework/
SpringBoardUIServices.framework/
拷贝这个私有库:
scp -r -P 12345 root@localhost:/System/Library/PrivateFrameworks/SpringBoard.framework ./
但是结果却是让人失望的,在SpringBoard.framework中并没有相关的MachO文件,唯一有点关联的就是有个SBRendererService.xpc文件:

查看对应的MachO:

这个在上面搜索SpringBoard.framework的时候就已经见到了,继续导出SpringBoardFoundation.framework查看仍然没有什么有用的信息,随后将上面列出的所有framework尝试都没有找到有用的信息。
直接用Xcode附加SpringBoard:

发现红点显示逻辑的视图是
SBIconBadgeView
这个时候查看image list发现了加载了Xcode中的库:
/Users/zaizai/Library/Developer/Xcode/iOS DeviceSupport/14.0 (18A373)/Symbols/System/Library/PrivateFrameworks/SpringBoard.framework/SpringBoard

这个大小看起来有点靠谱,并且没有加密相关字段。
查看MachO文件:

这里明确了
SpringBoard依赖库的关系。尝试class-dump报错:
class-dump[44486:9544743] Error: Cannot find offset for address 0x201d7915448 in dataOffsetForAddress:
难道所有实现都是swift实现了?创建Tweak工程,尝试Hook SBIconBadgeView的实现:
%hook SBIconBadgeView
- (id)init{
return nil;
}
%end
直接在init的时候返回nil。这个时候Hook是成功的红点直接全部消失了。

这里其实就已经实现了隐藏系统角标的问题。
二、dump头文件
虽然上面已经处理完隐藏逻辑了,但是并不严谨。如果有更复杂的功能需要处理那么导出头文件是个重要的步骤。
2.1 方式一 dsc_extractor(可以略过这个,这里只是为了做记录)
既然上面都获取不到那么就要换个思路了,系统动态库都在共享缓存中,那么导出共享缓存中的动态库是不是就能class-dump了?
拷贝共享缓存
启动本机的Mach-O文件的时候/System/Library/PrivateFrameworks相应的库文件全部转移到了 /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64。那么直接拷贝共享缓存文件:
scp -P 12345 root@localhost:/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64 ./
解析共享缓存文件
在苹果dyld源码中有一个launch-cache/shared-cache(不同版本可能不太相同)文件夹可以用来解析动态库共享缓存。编译比较麻烦我这里直接用别人编译好的dsc_extractor文件。编译好的dsc_extractor
使用:
./dsc_extractor dyld_shared_cache_arm64 frameworks
这个时候就成功导出了共享缓存库。
dyld版本获取
#import <mach-o/dyld.h>
void printDyldVersion() {
int32_t version = NSVersionOfRunTimeLibrary("dyld");
NSLog(@"origin dyld version: %d",version);
if ( version != 0xFFFFFFFF ) {
printf("conventversion: (compatibility version %u.%u.%u, current version %u.%u.%u)\n",
(version >> 16),
(version >> 8) & 0xff,
(version) & 0xff,
(version >> 16),
(version >> 8) & 0xff,
(version) & 0xff);
}
}
class-dump需要的framework
这个时候仍然和从Xcode导出来的库报同样的错误。查资料说是只适用于iOS12之前的版本。
2.2 方式二 RuntimeBrowser(可以略过这个,这里只是为了做记录)
无意间搜到到别人分享的RuntimeBrowserRuntimeBrowser
这个工具支持iOS和macOS下载源码后需要我们自己编译build到手机上。在尝试的过程中遇到的问题比较多,并且在源码中过滤了我需要的库:

所以就不过多研究这个库了。安装好后在
iOS下如下:
对于某些库是能直接在手机端浏览头文件的。貌似也是
iOS12支持的比较好。
2.3 方式三 classdump-dyld(推荐)
上面自己没有能够成功class-dump springboard库的头文件,那么尝试搜下看有没有其他人在iOS14上成功恢复。找到了iOS14runtime头文件。
在别人分享出来的Headers文件中发现了以下信息:
/*
* This header is generated by classdump-dyld 1.0
* on Thursday, September 24, 2020 at 12:37:36 AM British Summer Time
* Operating System: Version 14.0 (Build 18A373)
* Image Source: /System/Library/PrivateFrameworks/SpringBoardHome.framework/SpringBoardHome
* classdump-dyld is licensed under GPLv3, Copyright © 2013-2016 by Elias Limneos.
*/
看到是通过classdump-dyld生成的头文件classdump-dyld。
2.3.1 安装
cydia中直接搜索classdump-dyld插件进行安装。

2.3.2 classdump-dyld API
#cycript -p SpringBoard
@import net.limneos.classdumpdyld;
classdumpdyld.dumpClass(SpringBoard);
@"Wrote file /tmp/SpringBoard.h"
classdumpdyld.dumpBundle([NSBundle mainBundle]);
@"Wrote all headers to /tmp/SpringBoard"
// Dump any bundle other than the main bundle
classdumpdyld.dumpBundle([NSBundle bundleWithIdentifier:@"com.apple.UIKit"]);
@"Wrote all headers to /tmp/UIKit"
// Dump any image loaded in the process using any class name it contains
classdumpdyld.dumpBundleForClass(CallBarControllerModern);
@"Wrote all headers to /tmp/CallBar7"
- 进入手机端
cycript环境。 -
importclassdumpdyld。 - 调用
classdumpdyld导出头文件:-
classdumpdyld.dumpClass(xxx):导出某个类的头文件 -
classdumpdyld.dumpBundle(xxx):导出某个库的头文件 -
classdumpdyld.dumpBundleForClass(xxx):根据库中的某个类导出整个库,适用于我们并不清楚类属于哪个库的情况。
-
2.3.3 classdump-dyld 使用
通过SBIconBadgeView导出整个库的头文件:
cycript -p SpringBoard
@import net.limneos.classdumpdyld;
cy# classdumpdyld.dumpBundleForClass(SBIconBadgeView);
@"Wrote all headers to /tmp/SpringBoardHome"
可以确定SBIconBadgeView在SpringBoardHome.framework中,从手机端拷贝文件:
scp -r -P 12345 root@localhost:/tmp/SpringBoardHome/ ./SpringBoardHome_Headers/
这个时候就正常dump出头文件可以分析了。

三、Monkey写Tweak
3.1 创建工程
除了通过theos写tweak工程,也可以通过Monkey来写。
创建Monkey Tweak工程Logos Tweak:

3.2 工程配置
配置要附加的进程Package->Library->MobileSubstrate->DynamicLibraries->***.plist:

其中的
Bundles就是要附加的进程,直接在这里配置就好了。
配置.xm文件的type:

直接将Hook代码拷贝过来:

签名信息配置:

3.3 编译安装配置

-
MonkeyDevBuildPackageOnAnyBuild: 每次build都生成deb包。 -
MonkeyDevClearUiCacheOnInstall:安装的时候清除缓存。 -
MonkeyDevCopyOnBuild:build时将deb包拷贝到设备的/var/root/MonkeyDevBuilds/目录。 -
MonkeyDevDeviceIP:目标设备的ip地址,默认USB连接,localhost。 -
MonkeyDevDevicePassword:目标设备的ssh登录密码,默认为空使用免密码登录。 -
MonkeyDevDevicePort:目标设备的端口,默认22。和自己的映射端口相关,这里设置为12345。 -
MonkeyDevInstallOnAnyBuild:每次编译安装,一般设置为NO。安装时设置为YES。 -
MonkeyDevInstallOnProfiling:点击Profile才将deb安装到设备。
-
MonkeyDevkillProcessOnInstall:安装插件后要杀掉的进程。 -
MonkeyDevPath:MonkeyDev安装路径。 -
MonkeyDevTheosPath:theos安装路径。
3.4 安装运行
配置MonkeyDevInstallOnAnyBuild为YES,然后command + b安装。
这个时候就和直接使用Tweak效果相同了。
错误处理
1.building for iOS, but linking in .tbd file (/opt/theos/vendor/lib/CydiaSubstrate.framework/CydiaSubstrate.tbd) built for iOS Simulator, file '/opt/theos/vendor/lib/CydiaSubstrate.framework/CydiaSubstrate.tbd' for architecture arm64

删除
CydiaSubstrate.tbd中i386和x86_64两项。
四、Tweak原理
4.1 分析Tweak工程
创建新的Tweak工程后目录结构如下:

编译make后多了一个.theos文件夹:

其中存放
.dylib文件。外层的.dylib是Fat类型的,是其它单一架构的合集。
打包make package后会生成packages文件夹,里面存放.deb文件:

并且
.theos文件夹下也有一个packages,这个文件夹中是一个记录build号的文件:

-
.deb可以理解为类似.ipa,.deb通过cydia下发安装插件。.ipa通过AppStore下发安装App。 -
.ipa安装的是.app,.deb安装的是.dylib。
安装好的.dylib在/Library/MobileSubstrate/DynamicLibraries目录中:

除了
.dylib文件外,还有一个对应的.plist。这个plist记录了要附加的进程bundleid。
4.2 验证
那么要注入动态库有两种方式:LC_LOAD_DYLIB写入MachO和DYLD_INSERT_LIBRARIES注入。
如果是LC_LOAD_DYLIB那么MachO中肯定有对应的记录。
由于SpringBoard不好查看,直接Monkey重签名微信,然后 Tweak Logos工程附加重签名的微信分析。
MonkeyBadgeHidden代码如下:
#import <UIKit/UIKit.h>
%hook UIView
+ (void)load {
NSLog(@"\n\n\n UIView ????????\n\n\n");
NSLog(@"\n\n\n UIView ????????\n\n\n");
NSLog(@"\n\n\n UIView ????????\n\n\n");
}
%end
Monkey附加工程中如下:
%hook UIViewController
+ (void)load {
NSLog(@"\n\n\n UIViewController ????????\n\n\n");
NSLog(@"\n\n\n UIViewController ????????\n\n\n");
NSLog(@"\n\n\n UIViewController ????????\n\n\n");
}
%end

确认
LC_LOAD_DYLIB只有libMonkeyDemoDylib.dylib。
配置DYLD_PRINT_LIBRARIES打印下加载的库:

找到
libMonkeyDemoDylib.dylib和MonkeyBadgeHidden.dylib:
dyld: loaded: <E2661470-9027-3E13-B71E-433B032D4A7E> /private/var/containers/Bundle/Application/B54EACB0-2790-42F4-A1F8-246116BC14BF/MonkeyDemo.app/Frameworks/libMonkeyDemoDylib.dylib
dyld: loaded: <DDC6FCBA-BD5C-3A2B-AE59-BED0B7A67135> /Library/MobileSubstrate/DynamicLibraries/MonkeyBadgeHidden.dylib
在这里也可以看到libMonkeyDemoDylib.dylib是从App的Frameworks中加载的,MonkeyBadgeHidden.dylib是从MobileSubstrate中加载的。
image list查看,MonkeyBadgeHidden插件如下:
[681] 48733794-72F0-3137-AFBE-704DA9B60E0D 0x0000000114ee4000 /Library/MobileSubstrate/DynamicLibraries/MonkeyBadgeHidden.dylib
/System/Volumes/Data/Users/zaizai/Library/Developer/Xcode/DerivedData/MonkeyBadgeHidden-ajtwpltzqpaxyldhfqsxakdgneuo/Build/Products/Debug-iphoneos/MonkeyBadgeHidden.dylib.dSYM/Contents/Resources/DWARF/MonkeyBadgeHidden.dylib(0x0000000114ee4000)
libMonkeyDemoDylib动态库如下:
[105] 67B122C5-FD9E-3C6A-AB0F-2A0287A6DCDD 0x000000010dfc0000 /Users/zaizai/Library/Developer/Xcode/DerivedData/MonkeyDemo-ezelbbqeimtnqtbanmlksmtowvin/Build/Products/Debug-iphoneos/MonkeyDemo.app/Frameworks/libMonkeyDemoDylib.dylib
这就可以看出MonkeyBadgeHidden.dylib是从/Library/MobileSubstrate/DynamicLibraries/MonkeyBadgeHidden.dylib拷贝的,而libMonkeyDemoDylib并不需要拷贝。
总结
-
class-dump导出头文件class-dump -H MachO文件 -o 头文件路径
-
classdump-dyld导出系统库头文件手机端需要安装
classdump-dyld插件进入
cycript环境导入classdumpdyld-
调用
classdumpdyld导出头文件-
classdumpdyld.dumpClass(xxx):导出某个类的头文件 -
classdumpdyld.dumpBundle(xxx):导出某个库的头文件
-
classdumpdyld.dumpBundleForClass(xxx):根据库中的某个类导出整个库的头文件
-
参考:
https://www.reddit.com/r/jailbreakdevelopers/comments/e6cjxx/ios_13_springboard_headers/f9qgkhr/
https://github.com/nst/RuntimeBrowser
