一、Tweak修改系統(tǒng)行為
有一個需求是去掉手機桌面的紅點顯示。首先必須清楚手機桌面也是一款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
這個時候回發(fā)現(xiàn)SpringBoard根本沒有相關(guān)加密字段,也就是它本身是沒有加殼的。
這里手動拷貝是因為
frida無法導(dǎo)出。dump.py -l根本找不到到SpringBoard應(yīng)用,通過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
發(fā)現(xiàn)直接報錯Unknown load command: 0x00000032,github上下載class-dump源碼編譯最新代碼生成class-dump工具class-dump。
編譯源碼的過程中發(fā)現(xiàn)
Use of undeclared identifier 'PLATFORM_IOSMAC':
class-dump源碼編譯
查看對應(yīng)宏定義用PLATFORM_MACCATALYST替換PLATFORM_IOSMAC成功。
雖然解決了報錯問題,但是只生成了一個CDStructures.h文件:

難道springboard全部改為了swift實現(xiàn)?

查看了下
SpringBoard發(fā)現(xiàn)只有75KB。這就有意思了,說明新版本SpringBoard只是一個殼真正的實現(xiàn)肯定不在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 ./
但是結(jié)果卻是讓人失望的,在SpringBoard.framework中并沒有相關(guān)的MachO文件,唯一有點關(guān)聯(lián)的就是有個SBRendererService.xpc文件:

查看對應(yīng)的MachO:

這個在上面搜索SpringBoard.framework的時候就已經(jīng)見到了,繼續(xù)導(dǎo)出SpringBoardFoundation.framework查看仍然沒有什么有用的信息,隨后將上面列出的所有framework嘗試都沒有找到有用的信息。
直接用Xcode附加SpringBoard:

發(fā)現(xiàn)紅點顯示邏輯的視圖是
SBIconBadgeView
??
Reveal查看不了SpringBoard,在設(shè)置中根本就沒有SpringBoard選項。
cycript和LLDB可以。
這個時候查看image list發(fā)現(xiàn)了加載了Xcode中的庫:
/Users/zaizai/Library/Developer/Xcode/iOS DeviceSupport/14.0 (18A373)/Symbols/System/Library/PrivateFrameworks/SpringBoard.framework/SpringBoard

這個大小看起來有點靠譜,并且沒有加密相關(guān)字段。
查看MachO文件:

這里明確了
SpringBoard依賴庫的關(guān)系。嘗試class-dump報錯:
class-dump[44486:9544743] Error: Cannot find offset for address 0x201d7915448 in dataOffsetForAddress:
難道所有實現(xiàn)都是swift實現(xiàn)了?創(chuàng)建Tweak工程,嘗試Hook SBIconBadgeView的實現(xiàn):
%hook SBIconBadgeView
- (id)init{
return nil;
}
%end
直接在init的時候返回nil。這個時候Hook是成功的紅點直接全部消失了。

這里其實就已經(jīng)實現(xiàn)了隱藏系統(tǒng)角標(biāo)的問題。
二、dump頭文件
雖然上面已經(jīng)處理完隱藏邏輯了,但是并不嚴(yán)謹(jǐn)。如果有更復(fù)雜的功能需要處理那么導(dǎo)出頭文件是個重要的步驟。
2.1 方式一 dsc_extractor(可以略過這個,這里只是為了做記錄)
既然上面都獲取不到那么就要換個思路了,系統(tǒng)動態(tài)庫都在共享緩存中,那么導(dǎo)出共享緩存中的動態(tài)庫是不是就能class-dump了?
拷貝共享緩存
啟動本機的Mach-O文件的時候/System/Library/PrivateFrameworks相應(yīng)的庫文件全部轉(zhuǎn)移到了 /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(不同版本可能不太相同)文件夾可以用來解析動態(tài)庫共享緩存。編譯比較麻煩我這里直接用別人編譯好的dsc_extractor文件。編譯好的dsc_extractor
使用:
./dsc_extractor dyld_shared_cache_arm64 frameworks
這個時候就成功導(dǎo)出了共享緩存庫。
生成
dsc_extractor可執(zhí)行文件步驟:
(1).下載對應(yīng)版本的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添加執(zhí)行權(quán)限
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導(dǎo)出來的庫報同樣的錯誤。查資料說是只適用于iOS12之前的版本。
2.2 方式二 RuntimeBrowser(可以略過這個,這里只是為了做記錄)
無意間搜到到別人分享的RuntimeBrowserRuntimeBrowser
這個工具支持iOS和macOS下載源碼后需要我們自己編譯build到手機上。在嘗試的過程中遇到的問題比較多,并且在源碼中過濾了我需要的庫:

所以就不過多研究這個庫了。安裝好后在
iOS下如下:
對于某些庫是能直接在手機端瀏覽頭文件的。貌似也是
iOS12支持的比較好。
2.3 方式三 classdump-dyld(推薦)
上面自己沒有能夠成功class-dump springboard庫的頭文件,那么嘗試搜下看有沒有其他人在iOS14上成功恢復(fù)。找到了iOS14runtime頭文件。
在別人分享出來的Headers文件中發(fā)現(xiàn)了以下信息:
/*
* 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環(huán)境。 -
importclassdumpdyld。 - 調(diào)用
classdumpdyld導(dǎo)出頭文件:-
classdumpdyld.dumpClass(xxx):導(dǎo)出某個類的頭文件 -
classdumpdyld.dumpBundle(xxx):導(dǎo)出某個庫的頭文件 -
classdumpdyld.dumpBundleForClass(xxx):根據(jù)庫中的某個類導(dǎo)出整個庫,適用于我們并不清楚類屬于哪個庫的情況。
-
2.3.3 classdump-dyld 使用
通過SBIconBadgeView導(dǎo)出整個庫的頭文件:
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 創(chuàng)建工程
除了通過theos寫tweak工程,也可以通過Monkey來寫。
創(chuàng)建Monkey Tweak工程Logos Tweak:

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

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

直接將Hook代碼拷貝過來:

簽名信息配置:

3.3 編譯安裝配置

-
MonkeyDevBuildPackageOnAnyBuild: 每次build都生成deb包。 -
MonkeyDevClearUiCacheOnInstall:安裝的時候清除緩存。 -
MonkeyDevCopyOnBuild:build時將deb包拷貝到設(shè)備的/var/root/MonkeyDevBuilds/目錄。 -
MonkeyDevDeviceIP:目標(biāo)設(shè)備的ip地址,默認(rèn)USB連接,localhost。 -
MonkeyDevDevicePassword:目標(biāo)設(shè)備的ssh登錄密碼,默認(rèn)為空使用免密碼登錄。 -
MonkeyDevDevicePort:目標(biāo)設(shè)備的端口,默認(rèn)22。和自己的映射端口相關(guān),這里設(shè)置為12345。 -
MonkeyDevInstallOnAnyBuild:每次編譯安裝,一般設(shè)置為NO。安裝時設(shè)置為YES。 -
MonkeyDevInstallOnProfiling:點擊Profile才將deb安裝到設(shè)備。
這個是
command + i?但是這個工程是灰色不能點擊,暫時不清楚這個怎么操作。
-
MonkeyDevkillProcessOnInstall:安裝插件后要殺掉的進程。 -
MonkeyDevPath:MonkeyDev安裝路徑。 -
MonkeyDevTheosPath:theos安裝路徑。
一般情況可以把
MonkeyDevDeviceIP和MonkeyDevDevicePort設(shè)置在.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工程
創(chuàng)建新的Tweak工程后目錄結(jié)構(gòu)如下:

編譯make后多了一個.theos文件夾:

其中存放
.dylib文件。外層的.dylib是Fat類型的,是其它單一架構(gòu)的合集。
打包make package后會生成packages文件夾,里面存放.deb文件:

并且
.theos文件夾下也有一個packages,這個文件夾中是一個記錄build號的文件:

-
.deb可以理解為類似.ipa,.deb通過cydia下發(fā)安裝插件。.ipa通過AppStore下發(fā)安裝App。 -
.ipa安裝的是.app,.deb安裝的是.dylib。
安裝好的.dylib在/Library/MobileSubstrate/DynamicLibraries目錄中:

除了
.dylib文件外,還有一個對應(yīng)的.plist。這個plist記錄了要附加的進程bundleid。
4.2 驗證
那么要注入動態(tài)庫有兩種方式:LC_LOAD_DYLIB寫入MachO和DYLD_INSERT_LIBRARIES注入。
如果是LC_LOAD_DYLIB那么MachO中肯定有對應(yīng)的記錄。
由于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

確認(rèn)
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動態(tài)庫如下:
[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并不需要拷貝。
總結(jié)
-
class-dump導(dǎo)出頭文件class-dump -H MachO文件 -o 頭文件路徑
-
classdump-dyld導(dǎo)出系統(tǒng)庫頭文件手機端需要安裝
classdump-dyld插件進入
cycript環(huán)境導(dǎo)入classdumpdyld-
調(diào)用
classdumpdyld導(dǎo)出頭文件-
classdumpdyld.dumpClass(xxx):導(dǎo)出某個類的頭文件 -
classdumpdyld.dumpBundle(xxx):導(dǎo)出某個庫的頭文件
主程序通過
[NSBundle mainBundle]導(dǎo)出。
其它通過identifier[NSBundle bundleWithIdentifier:@"com.apple.UIKit"]導(dǎo)出。-
classdumpdyld.dumpBundleForClass(xxx):根據(jù)庫中的某個類導(dǎo)出整個庫的頭文件
-
參考:
https://www.reddit.com/r/jailbreakdevelopers/comments/e6cjxx/ios_13_springboard_headers/f9qgkhr/
https://github.com/nst/RuntimeBrowser
