冰指的是用戶態,火指的是內核態。如何突破像冰箱一樣的用戶態沙盒最終到達并控制如火焰一般燃燒的內核就是《iOS冰與火之歌》這一系列文章將要講述的內容。但在講主線劇情前,我們今天先聊一聊分支劇情 - 在非越獄的iOS上進行App Hook。利用這個技術,你可以在非越獄的iOS系統上實現各種hook功能(e.g., 微信自動搶紅包,自動聊天機器人,游戲外掛等),但寫這篇文章的目的并不是鼓勵大家使用外掛,更不是鼓勵大家去賣外掛,所以千萬不要用這個技術去做一些違法的事情。
《iOS冰與火之歌》系列的目錄如下:
另外文中涉及代碼可在我的github下載:
https://github.com/zhengmin1989/iOS_ICE_AND_FIRE
要是看過我寫的安卓動態調試七種武器之離別鉤 – Hooking(上)http://drops.wooyun.org/tips/9300 和 安卓動態調試七種武器之離別鉤 – Hooking(下)http://drops.wooyun.org/papers/10156 的同學應該知道在android進行hook的方法可以是五花八本的。其實在iOS上進行hook的方式也有很多,但是大多數都需要越獄后才能實現(比如大家最常用的Cydia Substrate),今天我就來介紹一種不需要越獄就能hook iOS app方法,也就是Mach-O LC_LOAD_DYLIB Hook
。這種方法是通過修改binary本身來加載第三方dylib并實現hook,具體思路是:
提取ipa中的二進制文件 -> 修改二進制文件的Load Commands列表,加入要hook的dylib –> hook.dylib在函數constructor函數中完成對特定函數的hook->對修改后的ipa進行簽名,打包和安裝。
首先我們先來看一下我們將要進行注入的目標app,這個app非常簡單,就是調用上一章講過的talker這個類輸出一句”Hello, iOS!”。
在Products文件夾中我們能夠看到IceAndFire.app這個文件,也就是編譯完后的app:
IceAndFire.app其實就是個文件夾,里面可以看到很多的資源文件(簽名信息,圖片等),但最重要的東西就是與文件夾同名的IceAndFire這個二進制文件了。我們可以用xxd命令來看一下里面的內容:
這個二進制文件里保存了IceAndFire這個app的所有邏輯,但是直接看二進制編碼太辛苦了,這里我推薦一個叫做MachOView的軟件(可以在我的github里下載),通過這個軟件就可以看到整個MachO文件的結構了:
在Load Commands這個數據段里,我們可以看到IceAndFire這個二進制文件會在啟動的時候自動加載Foundation, libobjc.A.dylib等動態庫。如果我們對MachO這個文件的Load Commands結構體進行修改,是不是就可以讓IceAndFire這個app在啟動的時候加載我們自定義的用來hook的dylib呢?沒錯,這個想法是可行的。并且我們只要在dylib的構造函數里完成相應的hook邏輯,就可以在app啟動的時候對指定函數進行hook操作了。
那么如何修改MachO的結構體呢?用010 editor等二進制編輯器的確是一種方法,但實在是麻煩了點。好消息是金正日小分隊已經把自動注入dylib的工具幫我們寫好了。這個叫yololib的工具可以幫我們直接進行dylib的注入:https://github.com/KJCracks/yololib。但作者只放出了源碼沒有放出binary,我幫大家編譯了一份扔到了我的github上(https://github.com/zhengmin1989/iOS_ICE_AND_FIRE)。
編譯好yololib后,我們只需要在mac上執行:
#!bash
./yololib [binary]?[dylib file]
./yololib [被插入dylib的二進制文件] [要插入的dylib]
命令即可完成dylib的注入,如圖所示:
現在我們再用MachOView看一下IceAndFire這個二進制文件就會看到hook1.dylib已經被我們成功注入進去了:
“@executable_path/hook1.dylib
”的意思是二進制文件會在當前目錄下對hook1.dylib進行加載,所以我們編譯好的hook1.dylib要和二進制文件一起放到IceAndFire.app文件夾里,這樣才能保證我們在運行app的時候hook1.dylib能夠被正確的加載。
修改好了app二進制文件的Load Commands結構體后,我們再來看看如何構造進行hook的第三方dylib。因為app自己肯定不會主動調用第三方dylib中的函數,所以如果我們想要讓第三方dylib進行hook操作就要把hook的邏輯寫到構造函數里。實現構造函數很簡單,只要在函數前聲明 ”__attribute__((constructor)) static
” 即可,我們先寫個”Hello, Ice and Fire!”測試一下:
編譯好dylib文件后,我們將這個dylib文件與app一起簽名、打包、安裝。然后我們運行一下程序就可以看到我們注入的dylib庫已經在程序啟動的時候成功加載并執行了。
下一步就是要實現對特定函數的hook。在這里我推薦使用CaptainHook這個framework。作者已經幫我們實現了hook所需要的各種宏,只要按照如下步驟就可以完成針對特定函數的hook:
比如我們想要hook Talker這個class里的say method,讓app在調用say的時候修改method的參數,讓say的話都變成”Hello, Android!”,我們只需要這樣編寫dylib的源碼:
#!objc
#import <CaptainHook/CaptainHook.h>
CHDeclareClass(Talker);
CHMethod(1, void, Talker, say, id, arg1)
{
NSString* tmp=@"Hello, Android!";
CHSuper(1, Talker, say, tmp);
}
__attribute__((constructor)) static void entry()
{
NSLog(@"Hello, Ice And Fire!");
CHLoadLateClass(Talker);
CHClassHook(1, Talker,say);
}
CHMethod()這個宏的格式是:參數的個數,返回值的類型,類的名稱,selector的名稱,selector的類型,selector對應的參數的變量名。
CHClassHook()這個宏的格式是:參數的個數,返回值的類型,類的名稱,selector的名稱。
編寫完代碼后,我們對源碼進行編譯,將生成的dylib文件與app一起簽名、打包、安裝。然后我們運行一下程序就可以看到我們注入的dylib庫已經成功的hook了say method了,原本應該輸出”Hello, iOS!”,已經被我們成功的變成了”Hello, Android!”:
我們知道越獄后的iPhone有一個很重要的特性就是可以關閉app的簽名校驗,關掉簽名校驗后,App Store上的app(無論是收費的還是免費的)就可以隨意盜版并且免費安裝了。但是在非越獄的iPhone上,系統要求app必須要有合法的簽名,負責無法進行安裝。其實除了AppStore上的app有合法的簽名外,我們還可以使用開發者證書或者企業證書來讓沒有合法簽名的app擁有合法的簽名。
當我們擁有開發者帳號并且在機器上安裝了證書的話,就可以在Keychain Access這個工具中看到我們的簽名信息:
我們接下來要干的事情就是使用這個開發者證書來對我們修改后的IceAndFire .app進行簽名。步驟如下:
首先先保證IceAndFire.app文件夾下有正確的embedded.mobileprovision文件:
如果沒有的話,可以去蘋果的開發者中心(developer.apple.com)生成。如果是個人開發者要注意將iOS設備的UDID加到開發者的設備列表中再生成embedded.mobileprovision文件,如果是企業證書則沒有設備數量的限制。
正確的編寫簽名時使用的Entitlements.plist:
這里最需要注意的就是application-identifier要包含正確的Team ID (可以在開發者中心查看) 和對應的Bundle ID。
使用codesign對hook的dylib進行簽名:
#!bash
codesign -f -s "iPhone Developer: [email protected] (XXXXXXXXX)" IceAndFire.app/hook1.dylib
使用codesign對app進行簽名:
#!bash
codesign -f -s "iPhone Developer: [email protected] (XXXXXXXXX)" --entitlements Entitlements.plist IceAndFire.app
使用xcrun將IceAndFire.app打包成IceAndFire.ipa:
#!bash
xcrun -sdk iphoneos PackageApplication -v IceAndFire.app -o ~/iOSPwn/hook/github/IceAndFire.ipa
使用itunes或者mobiledevice進行安裝。
成功的話會顯示”OK”。然后就可以在非越獄的手機上使用我修改后的app了。
通過上面幾節的介紹,我們已經將非越獄app hook的流程走過一遍了,但這時候有人會問:”你hook的app是自己寫的,你當然知道應該hook哪個函數了,我想hook的app都是App Store上的,并沒有源碼,我該怎么辦?”其實這個問題也不難解決。只要用好class-dump和ida即可。
Class-dump是一款可以用來dump頭文件工具:
比如我們想要dump XXX的頭文件,只需要執行:
#!bash
./class-dump -H -o header XXX
經過dump后,所有的頭文件都會保存在”header”這個文件夾中:
每個頭文件中都包含了類和方法的聲明:
可以看到,利用class-dump能夠很好的幫助我們了解app的內部結構。但是class-dump只能獲取app的頭文件,并不能知道每個方法具體的邏輯,這時候我們就需要用到ida了。
利用ida我們可以獲取到一個方法具體的邏輯,不過這需要你對arm匯編有一定的了解:
比如上圖所示的函數就是調用NSLog(@”%@\n”)
來向控制臺輸出參數的內容。只有了解了某個函數具體是做什么的,我們能才知道如何hook這個函數。
至于微信自動搶紅包的插件無非就是hook了接收微信消息的函數,然后判斷消息中有沒有紅包,有的話就直接調用打開紅包的函數即可。但因為這篇文章的主要目的是介紹非越獄手機的app hook,而不是鼓勵大家使用外掛,所以具體實現的細節就不公布了,有興趣的同學可以自己嘗試寫一個。雖然效果沒有機械流那么酷炫,但的確省時省力啊。
通過這篇文章我們可以看到,即使是在非越獄的iOS系統上依然可以玩出很多的花樣,因此各大it廠商不要盲目的相信非越獄iOS系統的安全性。針對紅包和支付等比較重要的邏輯一定要有混淆和加固,針對app本身一定要有完整性校驗。不然好心的白帽子可能只是寫個自動搶紅包的外掛玩玩,但是黑客就可能利用這種技術開發各種外掛來牟取暴利或者讓用戶在無意當中安裝上帶有后門的app,隨后會發生什么就只有天知道了。
最后感謝我的同事黑雪和耀刺對這篇文章的幫助和指導。
PS: 文中涉及代碼可在我的github下載:
https://github.com/zhengmin1989/iOS_ICE_AND_FIRE