iOS逆向學習之十二(iOS簽名機制)
iOS簽名機制的作用
在學習了上一章什麼是加密解密、數字簽名以及證書之後,現在我們再來學習iOS的簽名機制就事半功倍了。其實iOS簽名機制的作用就是保證安裝到使用者手機上的App都是經過Apple官方允許的。當然,越獄裝置除外。
在平時開發時,不管是真機除錯,還是釋出App到App Store,我們都需要經過以下幾個步驟
- 首先,在Mac上生成CertificateSigningRequest.certSigningRequest檔案
- 然後在Apple開發者網站上獲取到ios_development.cer或者ios_distribution.cer證書檔案,前者是開發證書,後者是釋出證書
- 在Apple開發者網站上註冊device,新增App ID。
- 選擇device、App ID以及開發證書或者生產證書,生成mobileprovision檔案
經過以上的一系列操作之後,我們最終就可以拿到ios_development.cer或者ios_distribution.cer證書檔案,以及mobileprovision描述檔案。通過安裝這些檔案到Mac上就可以進行真機除錯。當然,如果你在Xcode中勾選了Automatically manage signing選項,那麼Xcode會自動幫我們執行以上的所有操作。
那麼,以上操作每一步執行的作用是什麼?最後獲得的ios_development.cer或者ios_distribution.cer證書檔案,以及mobileprovision
iOS簽名流程
要想知道上述所有檔案的具體作用,就需要了解iOS簽名的完整流程。在使用Xcode編譯、執行完專案之後,Xcode其實幫我們做了簽名操作。
其實就是利用codeSign指令對.app檔案進行簽名操作。
準備
想要執行iOS簽名流程,就需要有以下公鑰、私鑰資訊
- Mac裝置的公鑰和私鑰,一般是Mac裝置自己生成
- Apple官方的私鑰,儲存在Apple的後臺
- Apple官方的公鑰,每一臺iPhone出廠之後都會儲存Apple官方的公鑰資訊
簽名流程分析
iOS專案在編譯完成之後會生成.app檔案,App的簽名操作從拿到.app檔案開始
- 第一步,拿到.app檔案之後,使用Mac私鑰對.app檔案進行簽名操作,生成的簽名檔案存放在.app目錄下的_CodeSignature/CodeResources
- 第二步,使用Apple私鑰對Mac公鑰進行簽名,生成證書檔案
- 第三步,將第二步獲取到的證書檔案和devices、app id以及entitlement一起使用Apple私鑰再次進行簽名,生成mobileprovision檔案,也就是描述檔案。
- 第四步,將第一步簽名過後的.app檔案和第三步生成的mobileprovision檔案一起壓縮生成ipa安裝包。
- 最後,完整的簽名打包過程如下
驗證流程
對.app安裝包進行簽名之後,壓縮生成ipa安裝包,然後將ipa安裝包安裝到iPhone上時,會進行簽名驗證操作。
- 第一步,使用iPhone上存放的Apple公鑰驗證mobileprovision檔案中的簽名。
- 第二步,簽名驗證成功之後,拿到mobileprovision中存放的devices、app id以及entitlement資訊。同時拿到證書檔案。然後使用Apple公鑰對證書中的簽名進行驗證。驗證成功之後拿到Mac公鑰。
- 第三步,拿到Mac公鑰之後,使用Mac公鑰對App的簽名檔案進行驗證,如果驗證成功,則表明當前的App的原始碼沒有被篡改過。然後此App就能安裝到iPhone上
- 最後得到完整的簽名加驗證流程,如下
在檔案mobileprovision中,有devices、app id以及entitlement資訊,它們的作用分別是:
- devices標識著哪些裝置可以安裝此App,如果裝置不在devices中,安裝會失敗。
- app id,指定此標識的App才能安裝,如果App的唯一標識和此app id不對應,則安裝失敗。
- entitlement中存放在App所具備的許可權資訊,如果App所使用的許可權和entitlement中存放的許可權不一致,也會產生問題。
簽名實際操作流程
上文學習了iOS簽名的具體流程,現在,我們就來一步步的執行實際的操作來對我們所學的簽名流程進行驗證。同時瞭解ios_development.cer或者ios_distribution.cer證書檔案,以及mobileprovision等檔案的實際作用。
- 第一步,在Mac上生成CertificateSigningRequest.certSigningRequest檔案,其實這個檔案就是Mac裝置的公鑰。
- 第二步,登入Apple開發者網站,獲取證書,安裝到Mac裝置。
- 建立certificates
此步驟就是利用Apple的私鑰,對Mac公鑰進行簽名,生成證書檔案ios_development.cer和ios_distribution.cer
- 第三步,生成mobileprovision
- 建立mobileprovision,可以選擇開發環境或者生產環境
此步驟就是將devices、app id、entitlement和證書檔案,通過Apple私鑰進行簽名,生成最後的mobileprovision檔案。而且生成的mobileprovision檔案就決定了當前App可以安裝的裝置有哪些,可以安裝的App的BundleId,以及App所擁有的許可權。
Apple官方驗證流程
Apple官方驗證流程如下:
重簽名
當我們逆向了一款App,為App編寫了相應的外掛,並且安裝到了我們自己的越獄手機上。但是,逆向過的App只能在自己越獄手機上使用,如果我們想要將App和我們自己編寫的外掛重新打包,安裝到未越獄的iPhone上,那就需要學習如何對App進行重簽名。
學習重簽名之前,需要注意幾點
- 第一,安裝包中的可執行檔案必須是進行過脫殼操作的,重簽名才會生效,不然會安裝失敗
- 第二,重簽名所需要的mobileprovision檔案必須是付費開發者賬號申請的才可以,免費開發者賬號無法進行重簽名。
- 第三,.app包中的所有動態庫(.framework,.dylib)、AppExtension(PlugIns資料夾,拓展名是appex)、WatchApp(Watch資料夾)等都需要進行重簽名操作
CodeSign指令重簽名
具體步驟
- 首先,需要準備一個embedded.mobileprovision檔案(必須是付費開發者賬號生成的,裡面的appid、device等需要匹配),然後將此檔案放入.app包中
生成mobileprovision檔案有兩種方式,第一種是通過Xcode自動生成,在編譯後的App包中可以找到,第二種是到Apple官網生成,上文有詳細步驟。
- 從embedded.mobileprovision檔案中提取出entitlements.plist許可權檔案,具體有兩步:
// 首先從embedded.mobileprovision檔案中匯出許可權資訊,存放到temp.plist中
security cms -D -i embedded.mobileprovision > temp.plist
# 然後使用PlistBuddy工具將temp.plist轉換成Entitlements格式的檔案entitlements.plist
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' temp.plist > entitlements.plist
複製程式碼
- 檢視Mac上可以使用的證書,獲取到證書的Identity,後續簽名需要使用,具體指令如下
security find-identity -v -p codesigning
複製程式碼
得到的結果如下
➜ ~ security find-identity -v -p codesigning
1) D9E2802126C89BF6BF6621064FC5547F895FC25E "iPhone Developer: [email protected] (KT9PJDKFVG)"
複製程式碼
- 對.app包中的所有動態庫、AppExtension等進行重簽名,前提是修改過這些動態庫或者AppExtension,如果沒有修改的動態庫或者AppExtension,可以跳過此步驟。指令如下:
# -fs 是 -f -s 的縮寫
codesign -fs 證書ID xxx.dylib
複製程式碼
- 對.app包進行簽名,需要使用到之前生成的entitlements.plist檔案,指令如下:
codesign -fs 證書ID --entitlements entitlements.plist xxx.app
複製程式碼
Theos外掛重簽名
使用Xcode編譯自己的專案,我們知道原始碼,所以知道怎麼修改Mach-O檔案,但是如果我們在逆向別人的App時,是不知道別人的原始碼的,所以無法直接修改Mach-O檔案。
之前的文章中學習了Theos,知道了怎麼通過建立Tweak專案來修改App的行為,具體流程可以檢視iOS逆向學習之六(Theos實戰演練)這篇文章。
Tweak載入方式
建立了Tweak專案之後,通過Cydia安裝到越獄手機上,然後就可以改變App的行為。具體是怎麼實現的呢?
- 首先,Tweak專案經過編譯生成的是一個dylib動態庫檔案,存放在Tweak專案目錄.theos/obj/debug/目錄下。
- 執行make package打包之後生成對應的deb檔案,存放在packages目錄下。
- 執行make install之後,會通過Cydia安裝到手機上,dylib檔案存放在~/Library/MobileSubstrate/DynamicLibraries/目錄下。
- 在App啟動之後,會同時將dylib載入到記憶體中,App中如果訪問被我們hook的類中的方法,會直接執行dylib中的方法。
動態庫的注入
Tweak專案本質上是生成動態庫,而且動態庫不是存放在.app目錄下,所以,想要將我們逆向過的App安裝到別人的手機上,首先需要做的就是將Tweak專案生成的動態庫注入到App中的可執行檔案中,也就是Mach-O檔案中。
可以使用insert_dylib庫來將動態庫注入到Mach-O檔案中,可以通過insert_dylib庫主頁下載insert_dylib工具。在Release環境下編譯,得到命令列工具,將命令列工具放在/usr/local/bin目錄下。
insert_dylib庫用法
insert_dylib的本質其實就是往Mach-O檔案的Load Commands中添加了一個LC_LOAD_DYLIB或者LC_LOAD_WEAK_DYLIB。具體注入方法如下:
insert_dylib 動態庫載入路徑 Mach-O檔案 --all-yes --weak
複製程式碼
- --weak選項表示,即使當前注入的動態庫找不到,App也不會報錯
- --all-yes選項表示,後面所有的選項都選yes
檢視Mach-O的動態庫依賴資訊
檢視動態庫依賴資訊兩種方式
- 通過otool檢視Mach-O的動態庫依賴資訊
otool -L Mach-O檔案
複製程式碼
- 通過MachOView檢視Mach-O的動態庫依賴資訊
更改動態庫的載入地址
在向Mach-O檔案中注入動態庫之後,需要更改Mach-O檔案中動態庫的載入地址,否則在App執行時,會因為找不到動態庫報錯。
可以使用install_name_tool指令來修改Mach-O檔案中動態庫的載入地址:
install_name_tool -change 舊地址 新地址 Mach-O檔案
複製程式碼
需要注意的是,上述指令中的新地址,必須填寫全路徑地址,但是我們不知道App安裝到手機上後dylib存放的具體地址,因此,可以使用以下兩個常用的環境變數:
- @executable_path代表可執行檔案所在的目錄,也就是Mach-O檔案所在目錄。我們將dylib和可執行檔案放在同一個目錄下,然後將地址修改為@executable_path/dylib名稱。這就表示在載入動態庫時,到可執行檔案所在的目錄下去尋找動態庫。
- @loader_path代表動態庫所在的目錄,此環境變數一般在動態庫依賴其它動態庫的情況下使用,如果我們需要注入的動態庫還依賴其它動態庫,那麼就需要將所依賴的動態庫和原動態庫存放在同一目錄下,然後更改動態庫的載入地址為@loader_path/動態庫名稱。這表示到原動態庫所在目錄下載入需要依賴的動態庫。
Theos開發的動態庫外掛注意事項
- 我們使用Theos開發的動態庫外掛(dylib)因為使用過Cydia安裝的,所以它預設是依賴CydiaSubstrate外掛的。CydiaSubstrate外掛存放目錄為iPhone的/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate。
- 如果想要將我們開發的動態庫外掛打包到ipa中, 就需要同時將CydiaSubstrate一起打包到ipa中,並且需要修改CydiaSubstrate的載入地址。
重簽名GUI工具
iReSign
可以點選下載iReSign原始碼,執行裡面的Mac應用,提供.app包的路徑、entitlements.plist路徑和embedded.mobileprovision的路徑,就可以對.app進行重簽名,然後打包生成ipa檔案。
iOS App Signer
可以點選下載iOS App Signer原始碼,選擇Release環境進行編譯,拿到編譯後的Mac應用,就可以直接使用。只需提供.app包的路徑和embedded.mobileprovision的路徑
重簽名練習
練習一、使用codesign指令對Xcode生成.app檔案進行重簽名
現在我們就來使用Xcode生成.app包,然後一步一步實現重簽名的過程。
- 首先,建立iOS專案TestSign,在ViewController中增加如下程式碼
#import "ViewController.h"
@interface ViewController ()
@property (weak,nonatomic) IBOutlet UILabel *labelA;
@property (weak,nonatomic) IBOutlet UILabel *labelB;
@property (weak,nonatomic) IBOutlet UILabel *labelResult;
@end
@implementation ViewController
int a = 10;
int b = 20;
- (void)viewDidLoad {
[super viewDidLoad];
self.labelA.text = [NSString stringWithFormat:@"%d",a];
self.labelB.text = [NSString stringWithFormat:@"%d",b];
self.labelResult.text = [NSString stringWithFormat:@"%d",a + b];
}
@end
複製程式碼
- 執行專案在介面上可以看到10 + 20 = 30的效果,此時,拿到編譯Product目錄下生成的.app包TestSign.app
- 此時,建立PayLoad資料夾,將TestSign.app放到資料夾中,壓縮PayLoad資料夾,修改壓縮檔案的字尾為.ipa,將PayLoad.ipa直接安裝到手機上,是可以直接安裝成功的,因為編譯的時候就是選擇的是真機編譯,而且mobileprovision檔案也是由Xcode自動生成,包含當前裝置。
- 通過之前的學習我們知道,在Mach-O檔案中,全域性變數存放的位置是__DATA段,使用MachOView開啟estSign.app中的可執行檔案,如下
<font color=red>0A</font>轉換成10進位制就是10,對應著全域性變數a,<font color=red>14</font>轉換成10進位制就是20,對應著全域性變數b。
複製程式碼
-
直接修改Mach-O檔案,將全域性變數a的值修改成14,也就是將a的值改為20。
-
重新壓縮生成.ipa檔案,使用iFunBox將ipa安裝到同一臺手機上,會出現安裝失敗提示,原因是.app中的Mach-O檔案已經被篡改,它的簽名也將會失效。所以安裝在iPhone上時對App的簽名驗證也會失敗,所以會導致安裝失敗。此時就需要對.app進行重簽名。
-
拿到TestSign.app包中的embedded.mobileprovision檔案,從embedded.mobileprovision檔案中提取出entitlements.plist檔案,指令如下:
// 首先從embedded.mobileprovision檔案中匯出許可權資訊,存放到temp.plist中
security cms -D -i embedded.mobileprovision > temp.plist
# 然後使用PlistBuddy工具將temp.plist轉換成Entitlements格式的檔案entitlements.plist
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' temp.plist > entitlements.plist
複製程式碼
- 通過以下指令獲取到證書ID,要與embedded.mobileprovision檔案中的證書保持一致:
security find-identity -v -p codesigning
複製程式碼
- 使用codesign指令對.app包進行重簽名
codesign -fs 1BEA2FE8783A297CF30B7728849EB225231D67E8 --entitlements entitlements.plist TestSign.app
複製程式碼
出現上圖效果,表明重簽名成功,此時,重新壓縮TestSign.app檔案,得到ipa,安裝到之前的手機上就可以安裝成功,同時,因為全域性變數a的值被我們改成了20,所以介面上展示的是20 + 20 = 40。至此,重簽名操作完成。
練習二、Theos開發外掛,注入騰訊視訊App,安裝到非越獄手機
下面為騰訊視訊開發Theos外掛,然後注入到騰訊視訊App,並且重新生成ipa,安裝到非越獄手機上,
- 首先,下載騰訊視訊App,可以通過愛思助手、PP助手下載已經進行過脫殼的App,或者通過App Store下載,然後使用Clutch工具進行脫殼操作。注意,必須要是脫殼後的App才能進行重簽名,否則簽名無效。
- 連線手機,使用Reveal檢視騰訊視訊的頁面,找到主介面QLHomeController。具體流程可參考iOS逆向學習之二(Mac遠端操控iPhone)和iOS逆向學習之三(Cycript)
- 建立Tweak專案。使用nic.pl指令建立Tweak專案,按步驟填寫所需要的資訊。注意MobileSubstrate Bundle filter填寫騰訊視訊App的bundle ID,通過Cycript獲取到bundle ID為com.tencent.live4iphone。
- 在Tweak.x檔案中新增如下程式碼,效果是在騰訊視訊首頁彈出一個Alert彈框。
@interface QLHomeController
- (id)presentViewController:(id)controller animated:(BOOL)animated completion:(id)completion;
@end
%hook QLHomeController
- (void)viewDidAppear:(BOOL)animated{
%orig;
UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"提示框" message:@"測試Theos外掛" preferredStyle:UIAlertControllerStyleAlert];
[alertVc addAction:[UIAlertAction actionWithTitle:@"關閉" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertVc animated:YES completion:nil];
}
%end
複製程式碼
- 執行make package && make install,對Tweak專案進行打包,打包完成後通過Cydia安裝到iPhone上,等待iPhone重啟,然後開啟騰訊視訊,在首頁就可以看到我們寫的彈框了。
-
獲取以下簽名需要的一些檔案
- 在iPhone上的/var/mobile/Containers/Bundle/Application/目錄下找到live4iphone.app安裝包。
- 在/Library/MobileSubstrate/DynamicLibraries/目錄下找到我們編寫的外掛所生成的動態庫test_live.dylib。
- 在/Library/Frameworks/CydiaSubstrate.framework/目錄下找到test_live.dylib依賴的動態庫CydiaSubstrate。
- 獲取之前練習中使用的embedded.mobileprovision檔案。
-
將test_live.dylib、CydiaSubstrate和embedded.mobileprovision檔案複製到live4iphone.app包中。
-
向live4iphone.app包中的可執行檔案中注入動態庫
insert_dylib @executable_path/test_live.dylib live4iphone --all-yes --weak live4iphone
複製程式碼
出現以下結果表明注入成功:
➜ live4iphone.app insert_dylib @executable_path/test_live.dylib live4iphone --all-yes --weak live4iphone
live4iphone already exists. Overwrite it? [y/n] y
Binary is a fat binary with 2 archs.
LC_CODE_SIGNATURE load command found. Remove it? [y/n] y
LC_CODE_SIGNATURE load command found. Remove it? [y/n] y
Added LC_LOAD_WEAK_DYLIB to all archs in live4iphone
複製程式碼
使用otool指令或者MachOView檢視騰訊視訊的可執行檔案,可以發現動態庫已經注入到Mach-O檔案當中
- 修改CydiaSubstrate庫的路徑
- 通過otool檢視test_live.dylib的依賴庫資訊。
- 此時發現<font color=red>CydiaSubstrate</font>的路徑是<font color=red>/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate</font>,但是在非越獄手機上是沒有這個路徑的,所以需要通過install_name_tool指令來改變動態庫的載入路徑。
```
install_name_tool -change /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate @loader_path/CydiaSubstrate test_live.dylib
```
再次檢視<font color=red>test_live.dylib</font>的依賴庫資訊,會發現<font color=red>CydiaSubstrate</font>庫的路徑變成了<font color=red>@loader_path/CydiaSubstrate</font>
複製程式碼
- 分別對test_live.dylib和CydiaSubstrate進行重簽名
➜ live4iphone.app codesign -fs 1BEA2FE8783A297CF30B7728849EB225231D67E8 test_live.dylib
test_live.dylib: replacing existing signature
➜ live4iphone.app codesign -fs 1BEA2FE8783A297CF30B7728849EB225231D67E8 CydiaSubstrate
CydiaSubstrate: replacing existing signature
➜ live4iphone.app
複製程式碼
- 使用iOS App Signer對live4iphone.app進行重簽名,生成ipa安裝包
- 將live4iphone.ipa安裝到非越獄手機上,可以發現安裝成功,並且進入之後出現彈框。