一、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
根本没有相关加密字段,也就是它本身是没有加壳的。
这里手动拷贝是因为
frida
无法导出。dump.py -l
根本找不到到SpringBoard
应用,通过cycript
找到bundleId
砸壳dump.py com.apple.springboard
也无效。
那么我们可以直接dump
出它的头文件:
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。
编译源码的过程中发现
Use of undeclared identifier 'PLATFORM_IOSMAC'
:
查看对应宏定义用PLATFORM_MACCATALYST
替换PLATFORM_IOSMAC
成功。
虽然解决了报错问题,但是只生成了一个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
⚠️
Reveal
查看不了SpringBoard
,在设置中根本就没有SpringBoard
选项。
cycript
和LLDB
可以。
这个时候查看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
这个时候就成功导出了共享缓存库。
生成
dsc_extractor
可执行文件步骤:
(1).下载对应版本的MachO
源码 dyld源码
(2).修改代码,将dsc_extractor.cpp
最后一个#if
条件编译宏, 将#if 0
改为#if 1
(第653
行)。这个是苹果的测试宏。
(4).编译 ``dsc_extractor,clang++ -o dsc_extractor dsc_extractor.cpp dsc_iterator.cpp
(5).编译成功之后会得到dsc_extractor
这个文件,chmod +x dsc_extractor
添加执行权限
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(可以略过这个,这里只是为了做记录)
无意间搜到到别人分享的RuntimeBrowser
RuntimeBrowser
这个工具支持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
环境。 -
import
classdumpdyld
。 - 调用
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
安装到设备。
这个是
command + i
?但是这个工程是灰色不能点击,暂时不清楚这个怎么操作。
-
MonkeyDevkillProcessOnInstall
:安装插件后要杀掉的进程。 -
MonkeyDevPath
:MonkeyDev
安装路径。 -
MonkeyDevTheosPath
:theos
安装路径。
一般情况可以把
MonkeyDevDeviceIP
和MonkeyDevDevicePort
设置在.zshrc
中。export MonkeyDevDeviceIP=localhost export MonkeyDevDevicePort=12345
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)
:导出某个库的头文件
主程序通过
[NSBundle mainBundle]
导出。
其它通过identifier
[NSBundle bundleWithIdentifier:@"com.apple.UIKit"]
导出。-
classdumpdyld.dumpBundleForClass(xxx)
:根据库中的某个类导出整个库的头文件
-
参考:
https://www.reddit.com/r/jailbreakdevelopers/comments/e6cjxx/ios_13_springboard_headers/f9qgkhr/
https://github.com/nst/RuntimeBrowser