從獲得APP的所有類聲明,到鎖定目標類與函數,現在是時候注入函數了。
所謂“注入函數”,小程的意思是讓APP執行到小程寫的代碼中,跟“鉤子”的概念一致。小程把個叫作iOS上的hook的技術。
本文介紹iOS注入函數的辦法。
在藉助框架之前,先介紹一個簡單的注入辦法,你可以“感性”地認識到“動態綁定”所帶來的注入。
(一)動態綁定的一個示例
(1)鎖定注入點
隨便找一個APP,classdump拿到所有類的結構信息。
比如,“微信”有一個類是這樣聲明的:
這個類繼承於UIViewController,也就是有viewDidLoad這個消息處理函數。這裏演示把MMUIViewController::viewDidLoad函數給替換掉,讓它執行到新的函數中。
(2)寫注入代碼
先找一個熟悉的編輯器,創建一個文件,命名爲hookwx.m,然後在裏面添加這樣的代碼:
然後是編譯的事。可以直接使用xcode來編譯出來.o文件,也可以用clang來編譯出來.o文件。比如,小程演示時使用的是iphone4手機,也就是armv7指令集,所以可以這樣編譯出obj文件:
clang -c hookwx.m -arch armv7 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk
再使用ld來鏈接成動態庫(dylib):
ld -dylib -lsystem -lobjc -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/ -o hookwx.dylib hookwx.o -framework Foundation -framework UIKit -ios_version_min 6.0
以上編譯鏈接命令的參數跟小程使用的sdk版本有關,你如果嘗試的話應該選擇對應的參數。
(3)拷貝dylib到DynamicLibraries
scp hookwx.dylib [email protected]:/Library/MobileSubstrate/DynamicLibraries/
這時,還要創建一個plist,來指定讓哪個APP來加載這個dylib(對於ios8.0以後的系統需要指定哪個APP),具體操作參照上一篇文章--介紹reveal的使用時創建了plist,就是這個操作了。“微信”的bundleID是“com.tencent.xin”或“com.tencent.xin1”,指定讓它加載這個dylib(可指定若干個bundleID)。
記得把plist文件也拷貝到DynamicLibraries目錄中。
(4)驗證效果
啓動“微信”,使用socat觀察log輸出,可以看到:
Dec 2 11:22:05 810 MicroMessenger[974] <Warning>: =======in initialize=================
...
Dec 2 11:24:48 810 MicroMessenger[974] <Warning>: -------------in new_viewDidLoad----------
也就是,“微信”加載了小程寫的dylib,而且也執行到新的函數中。
以上的例子,只是“感性地”知道注入的辦法,而在實際使用場景,更應該藉助一些成熟的可以做到注入的框架。
小程較常見的兩個框架,一個叫fishhook,一個叫MobileSubstrate。
fishhook,是facebook的一個開源工具,可以在運行時修改目標函數的地址,讓控制點執行到自己的代碼。因爲需要知道目標函數的名字,這對於c運行時庫的函數來說是適用的,或者對於能定位到函數名的情況也是適用的,但對於連名字都拿不到的情況(比如只能定位到代碼地址)就不適用。如果只能拿到函數的地址,那可以考慮用MobileSubstrate的MSHookFunction來做到注入。
MobileSubstrate(也叫CydiaSubstrate,以下簡稱爲MS),最大的一個作用,是可以動態綁定新的執行函數,這個功能已經能滿足我們大部分的需求。比如,MS提供的函數MSHookMessageEx,可以用來對oc代碼進行hook,原理上利用了oc的runtime特性(運行時替換執行函數)。
MS提供的函數MSHookFunction,可以用來對c代碼進行hook,比如很多APP在寫文件時都會用到write或fwrite函數,那通過對這兩個函數進行hook,就能看到寫入文件的數據,可以這樣寫代碼:
但直接使用MS的函數,並不是本文介紹的重點。從“實用”的角度,小程要介紹的是iOSOpenDev的使用。
(二)iOSOpenDev的使用
theos跟iOSOpenDev,都是對MS庫進行封裝的開發工具包,這樣的工具包可以簡化開發的操作。這裏介紹iOSOpenDev的使用。
安裝iOSOpenDev後,就可以使用xcode來完成插件的開發,或者簡單地生成dylib庫。
(1)安裝iOSOpenDev
包裝包地址:
如果你安裝成功,則不用參考小程失敗的例子。
以下是小程安裝失敗並動手解決的例子。
安裝時失敗,/var/log/system.log裏面記錄着“安裝器遇到了一個錯誤,導致安裝失敗。請聯繫軟件製造商以獲得幫助。”。
雖然安裝失敗,但是在/opt下面還是創建了三個目錄(紅框內):
在iOSOpenDevSetup/bin裏面已經有一個shell腳本:iod-setup,這個是安裝的腳本。
直接運行iod-setup來安裝:sudo ./iod-setup base
發現總是在下載某個東西時失敗,打開iod-setup來定位,發現有三個downloadGithubTarball的地方,
直接註釋掉,然後手動去下載這三個東西,並拷貝到iOSOpenDev目錄:
分別下載下面三個地址的zip包:
https://github.com/kokoabim/iOSOpenDev
https://github.com/kokoabim/iOSOpenDev-Xcode-Templates
https://github.com/kokoabim/iOSOpenDev-Framework-Header-Files
解壓上面下載的zip包,拷貝:
sudo cp -r iosopendev-master/* /opt/iosopendev/
在iosopendev目錄裏面,sudo mkdir templates,然後:
sudo cp -r iosopendev-xcode-templates-master/* /opt/iosopendev/templates
在iosopendev目錄裏面,sudo mkdir frameworks,然後:
sudo cp -r iosopendev-framework-header-files-master/* /opt/iosopendev/frameworks
再次安裝:
sudo ./iod-setup base
指定最新xcode sdk:
sudo ./iod-setup sdk -sdk iphoneos
小程還遇到另一種情況:在一臺imac上,xcode8.3.2,安裝包失敗後,直接sudo ./iod-setup base,成功。
所以,上面不成功的情況,有可能是從github下載時網絡上失敗導致。
最終安裝成功,表現爲:
1.
~/library/developer/xcode 裏面會多出
Templates/iosopendev
2.
打開 ~/.bash_profile
會看到:
export iOSOpenDevPath=/opt/iOSOpenDev
export iOSOpenDevDevice=
export PATH=/opt/iOSOpenDev/bin:$PATH
3.
啓動xcode,新建工程,多出一個“iOSOpenDev”的模板。
就算使用iOSOpenDev,也有必要安裝theos,否則編譯時會有提示:
Preparing to run Xcode Build Phase for Logos Processor...
Failed to locate Logos Processor. Is Theos installed? If not, see http://iphonedevwiki.net/index.php/Theos/Getting_Started.
安裝theos很簡單(可以安裝完iOSOpenDev後,再安裝theos):
brew install dpkg ldid
sudo Git clone --recursive https://github.com/theos/theos.git /opt/theos
(2)使用iOSOpenDev的示例
創建項目,iOSOpenDev -> Logos Tweak (安裝後會有圖標)。
在項目中,可以找到一個後綴爲xm的文件,這個文件就是寫代碼的地方。iOSOpenDev會根據xm的內容編譯到mm中(xm不是必須要有的,但xm的語法比mm中的好懂多了)。
xm文件裏面的#error會提示你拷貝個libsubstrate.dylib過來。到/opt/iosopendev/lib裏面把libsubstrate.dylib拉到項目的Frameworks目錄。
再拉進一個UIKit.framework,因爲SpringBoard在UIKit裏面聲明,而這個例子就是對SpringBoard進行hook。
SpringBoard是系統的組件,在啓動機子時加載。
清空xm文件,寫代碼:
小程這裏用的UIAlertView是舊sdk支持的,如果是新版本的sdk,應該使用新的“提示框”類。
編譯,成功的話,會生成對應的動態鏈接庫,即xx.dylib。然後是plist文件,在項目中找到xx.plist,對它進行修改,指定讓哪一個APP啓動時加載這個dylib。
然後,把dylib與plist拷貝到DynamicLibraries目錄(小程有多次提到了)。其實,xcode可以在編譯後自行拷貝到手機,只需要這樣配置下項目(當然也要保證電腦可以ping到手機):
在build settings頁面,查找iOSOpenDevDevice,把內容設置爲IP,比如:192.168.1.101 ,讓xcode知道往哪臺手機安裝應用。然後編譯並安裝到手機:Project->Build For->Profiling。
注意,編譯時,目標設備哪一項,應該選擇Generic iOS Device, 否則會遇到一堆錯誤(選擇真機或模擬器都可能一堆編譯錯誤)。
安裝後可以到cydia的“已安裝”中看有沒有你的應用,也可以ssh到手機後查看:
dpkg -l
重啓機子(killall springboard),啓動時會看到彈出的alertview。
總結一下,本文內容較多,但主體是iOSOpenDev的使用,這個是注入APP的有效的工具。