轉載: URL http://dev.qq.com/topic/577e0acc896e9ebb6865f321ios
若是您有耐心看完這篇文章,您將懂得如何着手進行app的分析、追蹤、注入等實用的破解技術,另外,經過「入侵」,將幫助您理解如何規避常見的安全漏洞,文章大綱:spring
敏銳的嗅覺 有時候經過一個函數名,一個類名,就能大體的判斷出它的做用,這就是嗅覺;功力已臻化境時,甚至可使用第六感判斷出一些注入點shell
面對失敗的勇氣 破解有時候很耗時,和程序開發正好相反,它耗時不是耗在寫代碼上,而是耗在尋找注入點和逆向工程上,有可能你花了3天時間去找程序的破綻,可是最終的破解代碼可能就2行,不到一分鐘就搞定了;可是你也須要作好面對失敗的準備,若是路選錯了,有可能你這3天徹底是在浪費腦細胞windows
洪荒之力 洪荒之力-即入侵過程當中須要藉助的各類工具,工欲善其事,必先利其器,工具都是前人智慧的結晶,能用工具解決的,毫不要手動去搞api
iOS的入侵離不開越獄開發,一切的破解、入侵都是創建在越獄的基礎上的,若是沒有拿到系統級權限,一切的想法都是空談了,固然,市面上存在免越獄的破解補丁,可是它的開發過程,也是基於越獄環境的xcode
在iOS的黑客界,要作破解或越獄開發,就必須瞭解tweak,它是各類破解補丁的統稱,在google上,若是你想搜索一些越獄開發資料或者開源的破解補丁代碼,它是最好的關鍵字。sass
iOS的tweak大體分爲兩種:安全
第一種是在cydia上發佈的,須要越獄才能安裝,大部分是deb格式的安裝包,iOS在越獄後,會默認安裝一個名叫mobilesubstrate的動態庫,它的做用是提供一個系統級的入侵管道,全部的tweak均可以依賴它來進行開發,目前主流的開發工具備theos和iOSOpenDev,前者是採用makefile的一個編譯框架,後者提供了一套xcode項目模版,能夠直接使用xcode開發可調試,可是這個項目已經中止更新了,對高版本的xcode支持很差,你們酌情選擇(本文中的例子所有采用theos)微信
第二種是直接打包成ipa安裝包,並使用本身的開發證書或者企業證書籤名,不需越獄也能夠安裝,可直接放到本身的網站上,可實如今線安裝;對於沒有越獄的手機,因爲權限的限制,咱們是沒有辦法寫系統級的tweak的,例如springboard的補丁是無法運行的,這種tweak大可能是針對某個app,把目標app進行修改注入處理,再從新簽名和發佈,有點相似於windows軟件的xxx破解版、xxx免註冊版網絡
沒有越獄的機器因爲系統中沒有mobilesubstrate這個庫,咱們有二個選擇,第一個是直接把這個庫打包進ipa當中,使用它的api實現注入,第二個是直接修改彙編代碼;第一個適用於較爲複雜的破解行爲,並且越獄tweak代碼能夠複用,第二種適用於破解一些if…else…之類的條件語句
下面的圖展現的就是oc屆著名的method swizzling技術,他就是iOS的注入原理,相似於windows的鉤子,因此咱們注入也稱爲hook
Mobilesubstrate爲了方便tweak開發,提供了三個重要的模塊:
MobileHooker 就是用來作上面所說的這件事的,它定義一系列的宏和函數,底層調用objc-runtime和fishhook來替換系統或者目標應用的函數
MobileLoader 用來在目標程序啓動時根據規則把指定目錄的第三方的動態庫加載進去,第三方的動態庫也就是咱們寫的破解程序,他的原理下面會簡單講解一下
Safe mode 相似於windows的安全模式,好比咱們寫的一些系統級的hook代碼發生crash時,mobilesubstrate會自動進入安全模式,安全模式下,會禁用全部的第三方動態庫
上面講到了mobileloader,他是怎麼作到把第三方的lib注入進目標程序的呢?這個咱們要從二進制文件的結構提及,從下面的圖來看,Mach-O文件的數據主體可分爲三大部分,分別是頭部(Header)、加載命令(Load commands)、和最終的數據(Data)。mobileloader會在目標程序啓動時,會根據指定的規則檢查指定目錄是否存在第三方庫,若是有,則會經過修改二進制的loadCommands,來把本身注入進全部的app當中,而後加載第三方庫。
爲了讓你們看的更清楚,下面我用machoview來打開一個真實的二進制文件給你們看看,能夠看出,二進制當中全部引用到的動態庫都放在Load commands段當中,因此,經過給這個段增長記錄,就能夠注入咱們本身寫的動態庫了
那麼問題來了,在這裏插入咱們本身的動態庫有什麼用?咱們本身寫的代碼沒有執行的入口,咱們同樣沒發乾壞事,嗯,恭喜你問到點子上了,咱們還須要一個"main"函數來執行咱們本身的代碼,這個"main"函數在oc裏面稱爲構造函數,只要在函數前聲明 「attribute((constructor)) static」 便可,有了它咱們就能夠發揮想象力,進行偷天換日干點壞事了:
#import <CaptainHook/CaptainHook.h> CHDeclareClass(AnAppClass); CHMethod(1, void, AnAppClass, say, id, arg1) { NSString* tmp=@"Hello, iOS!"; CHSuper(1, AnAppClass, say, tmp); } __attribute__((constructor)) static void entry() { NSLog(@"Hello, Ice And Fire!"); CHLoadLateClass(AnAppClass); CHClassHook(1, AnAppClass,say); }
到這裏爲止,咱們已經知道了怎麼在目標程序注入本身的代碼,那麼咱們怎麼知道須要hook哪些方法?怎麼找到關鍵點進行實際的破解呢?下面講一下常見的app入侵分析方法
###iOS逆向分析方法
逆向分析最經常使用的有三種方法:
網絡分析 經過分析和篡改接口數據,能夠有效的破解經過接口數據來控制客戶端行爲的app,經常使用的抓包工具備Tcpdump, WireShark, Charles等,windows平臺有fidller
靜態分析 經過砸殼、反彙編、classdump頭文件等技術來分析app行爲,經過這種方式能夠有效的分析出app實用的一些第三方庫,甚至分析出app的架構等內容,經常使用的工具備dumpdecrypted(砸殼)、hopper disassembler(反彙編)、class_dump(導頭文件)
動態分析 有靜就有動,萬物都是相生相剋的,動態分析指的是經過分析app的運行時數據,來定位注入點或者獲取關鍵數據,經常使用的工具備cycript(運行時控制檯)、 lldb+debugserver(遠程斷點調試)、logify(追蹤)
上面講了不少原理性的東西,相信你們已經看的不耐煩了,下面咱們一塊兒動點真格的,咱們從頭開始,一步一步的作一個微信的自動搶紅包插件,固然,網上可能已經有相關的開源代碼了,可是我這裏要講的是,這些代碼是怎麼得出來的,我麼重點講一講分析過程
一臺越獄的手機,並裝有如下軟件
一臺蘋果電腦,並裝有如下軟件
###尋找注入點
首先咱們要作的就是把微信的殼砸掉,砸殼實際上是爲了把它的頭文件classdump出來,由於從appstore下載的app二進制都是通過加密的,直接進行classdump操做是啥也看不出來的
xxx$ cp /usr/lib/dumpdecrypted.dylib /path/to/app/document xxx$ DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /path/to/WeChat
執行完這幾行命令後,會在微信的documents目錄生成一個WeChat.decrypted文件,這就是砸殼後的二進制文件;固然了,這一步不是必須的,咱們能夠直接從91或者pp助手下載一個已經砸過殼的版本
要想實現自動搶紅包,咱們必須找到收到紅包消息的handler方法,怎麼入手呢?咱們先從界面出發,進入微信的消息首發窗口:
ps aux | grep WeChat
cycript -p pidxxx
UIApp.keyWindow.recursiveDescription().toString()
最終的輸出以下,內容太多,你們確定看不清楚,不過不要緊,這個不是重點,這裏只是展現一下打印的結果形式:
咱們能夠隨機的選取一個節點不要太靠樹葉,也不要太靠樹根,例如我選的是標紅的部分,把這個節點的內存地址copy出來,這個內存地址,就表明了這個節點的view對象,ios開發的老油條們都知道,經過view的nextResponder方法,能夠找出它所屬的視圖控制器ViewController,因此我麼在cycript的控制檯中持續輸入以下的命令:
看到沒有,經過四個nextResponder方法調用,我麼找到了當前聊天窗口的ViewController類名,他就是BaseMsgContentViewController,如今咱們縮小了目標範圍,下面咱們還須要繼續縮小範圍,要找到具體的消息處理函數才行。
要繼續縮小範圍,就得祭起神器Logify了,它是theos的一個模塊,做用就是根據頭文件自動生成tweak,生成的tweak會在頭文件的全部方法中注入NSLog來打印方法的入參和出參,很是適合追蹤方法的調用和數據傳遞
如今咱們根據此前砸殼後class_dump出來的頭文件,找到BaseMsgContentViewController在pc終端執行以下命令:
logify.pl /path/to/BaseMsgContentViewController.h > /out/to/Tweak.xm
輸出的tweak文件大概是這個樣子的:
這裏帶百分號的關鍵字,例如 %hook、%log、%orig 都是mobilesubstrate的MobileHooker模塊提供的宏,其實也就是把method swizzling相關的方法封裝成了各類宏標記,使用起來更簡單,你們想要更深刻了解各類標記,能夠google一下logos語言
上面咱們用logify生成了一個tweak代碼,咱們要把它安裝到手機上,首先須要使用theos進行編譯,安裝了theos以後,在pc終端輸入nic.pl:
首先選擇項目模版固然是tweak啦,而後是項目名稱、做者,後面兩個選項要注意:
最後一切都完成後,在當前目錄會生成下列文件:
把上面logify生成的tweak文件覆蓋到當前目錄,並用文本編輯器打開makefile文件,在文件的開頭增長你的ios設備的ip地址和ssh端口:
最後在pc終端進入項目目錄,輸入 make package install 命令:
期間會讓你輸入設備的ssh密碼,越獄機器的默認ssh密碼是alpine,make命令會生成deb安裝包,放在debs目錄,咱們若是想對外發布本身的插件,能夠把生成的安裝包上傳到cydia便可
安裝成功後再次進入微信的聊天界面,並使用另一個微信在羣裏發個普通消息,鏈接xcode打開越獄機器控制檯,查看輸出,會發現有相似下面的輸出:
Jun 7 09:56:13 Administratorde-iPhone WeChat[85972] <Notice>: [1;36m[WxMsgPreview] [m[0;36mTweak.xm:308[m [0;30;46mDEBUG:[m -[<BaseMsgContentViewController: 0x15e0c9a00> addMessageNode:{m_uiMesLocalID=2, m_ui64MesSvrID=0, m_nsFromUsr=ccg*675~9, m_nsToUsr=1037957572@chatroom, m_uiStatus=1, type=1, msgSource="(null)"} layout:1 addMoreMsg:0]
看出來了吧,消息處理函數是BaseMsgContentViewController的**addMessageNode:layout:addMoreMsg:**方法,你們能夠看出,方法的參數內容也打印出來了
到目前爲止,我麼已經把範圍縮小到了具體的函數,看起來注入點已經找到了,可是請你們思考一下,若是咱們在這個函數中注入搶紅包邏輯,那咱們的tweak會不會有什麼致命的缺陷?
是的,由於BaseMsgContentViewController這個類是微信羣聊天窗口對應的controller,我麼必須進入到羣的聊天界面,這個類纔會建立,若是不進入聊天窗口,咱們的插件就不生效了,並且,即便進入聊天窗口,也只是能自動槍當前羣的紅包而已,其餘羣就無能爲力了,是否是有點low?
因此爲了使咱們的插件顯得上流一些,我麼還要繼續追根溯源,尋找消息的源頭,這裏就用到了lldb遠程調試,使用lldb打斷點的方式,經過調用棧,咱們能夠就能夠看到當消息來到時,方法的調用順序,找到最早執行的消息處理函數。
要在剛剛追蹤到的**addMessageNode:layout:addMoreMsg:**方法中打斷點,首先咱們得知道它在運行時的內存地址,那麼內存地址怎麼來呢?有這麼一個公式:
首先偏移量咱們能夠經過反彙編工具hooper來查,在pc上用hooper打開微信的二進制文件(注意,打開時會讓你選擇armv7或者arm64,這須要根據你越獄手機的cpu類型來選,必定要和你的手機一致),hooper的界面很是簡潔,左側有個搜索框,能夠輸入函數名,直接找到函數在二進制中的位置
經過左側的搜索框搜addMessageNode關鍵字,找到它的偏移量是0x00000001017d7c6c:
找到了偏移量,還須要進程的基地址,這個地址須要連lldb,因此下面講一下如何鏈接lldb進行遠程調試,先ssh進越獄手機的終端,在終端輸入以下命令(注意,你的手機必須連xcode調試過纔會有這個命令):
debugserver *:19999 -a WeChat
而後在pc端新起一個終端窗口,輸入以下命令來鏈接手機端進行調試:
lldb -> process connect connect://deviceIP:19999
若是鏈接成功,會進入lldb的控制檯,咱們在lldb的控制檯輸入以下命令來獲取微信進程的基地址:
image list -o -f
執行這個命令會打印不少行數據,像下面圖中這樣,我麼要找到微信的二進制文件所在的行,記錄它的內存地址0X00000000000E800:
到這裏咱們兩個地址都找到了,再經過br命令打斷點:
br s -a '0X00000000000E800+0x00000001017d7c6c'
打好斷點後繼續向羣裏面發消息,咱們會發現進程被斷掉了,這時輸入bt指令,就能夠看到當前的調用棧,就像下圖這樣:
分析堆棧的時候,重點找出模塊時WeChat的項,這些都是微信模塊的方法調用,有了堆棧,咱們須要根據堆棧的內存地址找出它的具體函數名,思路仍是先根據上面講到的公式來計算出棧地址在二進制中的偏移量,而後用hooper找到偏移量對應的函數名
例如根據箭頭所指的內存地址和剛剛獲得的進程基地址,計算偏移量:
0x0000000101ad02f4 – 0x00000000000e8000 = 1019E82F4
而後在hooper中搜索這個地址,獲得結果以下:
最終把全部的棧都進行還原,得出調用棧是這個樣子的:
-[CMessageMgr MainThreadNotifyToExt:]: –> -[BaseMsgContentLogicController OnAddMsg:MsgWrap:]: ——> -[RoomContentLogicController DidAddMsg:] ———-> -[BaseMsgContentLogicController DidAddMsg:] —————-> -[BaseMsgContentViewController addMessageNode:layout:addMoreMsg:]:
CMessageMgr這個類浮出水面了,是時候發揮黑客的嗅覺了,根據方法名咱們能判斷出MainThreadNotifyToExt:這個方法僅僅是用來發送通知的,若是hook這個方法,咱們是拿不到消息內容的
因爲這裏多是一個異步調用,用斷點的方式,可能已經打印不出來棧信息了,因此還得使用logify來繼續追蹤CMessageMgr這個類,講過的內容我就不重複了,直接獲得最終的消息處理函數:
-(void)AsyncOnAddMsg:(id)message MsgWrap:(CMessageWrap* )msgWrap
上一節咱們已經找到了hook的關鍵點,那麼該如何去實現搶的動做?一樣咱們須要結合動態分析和靜態分析,首先獲得紅包消息體的數據特徵,而後再分析處理消息的關鍵點
首先咱們的代碼須要分辨哪些纔是紅包消息,方法很簡單,用logify追蹤BaseMsgContentViewController,而後向微信羣發一個紅包,觀察手機日誌輸出,咱們能夠看出消息的數據結構中有個type字段,值是49,這個type應該就是標記消息類型的,若是不肯定,能夠再發個圖片或者文本之類的消息,這個值是不一樣的:
Administratorde-iPhone WeChat[47410] <Notice>: [1;36m[WxMsgPreview] [m[0;36mTweak.xm:308[m [0;30;46mDEBUG:[m -[<BaseMsgContentViewController: 0x15e0c9a00> addMessageNode:{m_uiMesLocalID=16, m_ui64MesSvrID=1452438635530425509, m_nsFromUsr=1037957572@chatroom, m_nsToUsr=ccg*675~9, m_uiStatus=4, type=49, msgSource="<msgsource> <silence>0</silence> <membercount>3</membercount> </msgsource> "} layout:1 addMoreMsg:0]
如今咱們能分辨消息類型了,重點來了,怎麼實現搶這個事呢,可能聰明人已經猜到了,從ui入手,先找到微信自己的搶紅包函數,咱們本身來給它構造參數並調用他不就好了?
把紅包點開後,用cycript打印出當前view的層次,就像下面這個,一眼就能夠看到重點,WCRedEnvelopesReceiveHomeView就是開紅包彈框的類名
知道類名後,用cycript追蹤它,點擊開紅包,在日誌中找到了下圖中的內容,從名字來看,這是一個事件處理函數,咱們如今要作的,就是把他還原成oc代碼,真正實現搶紅包功能
Administratorde-iPhone WeChat[91173] <Notice>: [1;36m[WxMsgPreview] [m[0;36mTweak.xm:8[m [0;30;46mDEBUG:[m -[<WCRedEnvelopesReceiveHomeView: 0x13cdda8c0> OnOpenRedEnvelopes]
怎麼把他還原成oc代碼,真正實現搶紅包功能呢?還得藉助一點點彙編技能,只是一點點而已,由於如今的反彙編工具已經很強大了,咱們不須要挨個去看寄存器了
在pc上用hooper打開微信的二進制文件,搜索OnOpenRedEnvelopes,查看彙編代碼,注意在圖片中最後一行調用了一個WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes函數
繼續搜索WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes這個方法,找到它的彙編代碼
最終反解出的代碼以下,是否是很簡單?
NSString *nativeUrl = [[msgWrap m_oWCPayInfoItem] m_c2cNativeUrl]; nativeUrl = [nativeUrl substringFromIndex:[@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length]]; NSDictionary *nativeUrlDict = [%c(WCBizUtil) dictionaryWithDecodedComponets:nativeUrl separator:@"&"];
繼續往下看, 在這裏前面三行建立了一個mutable dictionary:
最終獲得的代碼以下:
NSMutableDictionary *args = [[%c(NSMutableDictionary) alloc] init]; [args setObject:nativeUrlDict[@"msgtype"] forKey:@"msgType"]; [args setObject:nativeUrlDict[@"sendid"] forKey:@"sendId"]; [args setObject:nativeUrlDict[@"channelid"] forKey:@"channelId"];
繼續往下看從箭頭所指的幾處,咱們能夠看見,它的代碼是這樣的,共分爲四步
最終還原的到的代碼以下:
CContactMgr *contactManager = [[%c(MMServiceCenter) defaultCenter] getService:[%c(CContactMgr) class]]; CContact *selfContact = [contactManager getSelfContact];
繼續往下看,這裏使用剛剛獲得的selfcontact來獲取displayname和headimgurl,並把它們設置到剛剛的字典裏面了,key分別是nickname和headimg
最終的代碼:
[args setObject:[selfContact getContactDisplayName] forKey:@"nickName"]; [args setObject:[selfContact m_nsHeadImgUrl] forKey:@"headImg"];
接着看,接下來這兩段就比較蛋疼了,徹底是從內存地址裏面取的值,我也不知道他從哪裏來,怎麼辦呢?有沒有不懂彙編就能搞定它的捷徑呢,答案是有!
最終的結果以下:
[args setObject:nativeUrl forKey:@"nativeUrl"]; [args setObject:xxx forKey:@"sessionUserName"];
繼續往下看,接下來這一段仍是用mmservicecenter來獲取WCRedLogicMgr對象,而後調用WCRedLogicMgr的open方法來拆紅包,能夠想象open方法的參數就是上面咱們辛苦組裝的字典
代碼以下:
[[[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]] OpenRedEnvelopesRequest:args];
到這裏,咱們再總結一下咱們上面分析的過程…
最終的搶紅包代碼合併起來以下:
#import "WxMsgPreview.h" %hook CMessageMgr -(void)AsyncOnAddMsg:(id)message MsgWrap:(CMessageWrap* )msgWrap { %log; %orig; if(msgWrap.m_uiMessageType == 49){ CContactMgr *contactManager = [[%c(MMServiceCenter) defaultCenter] getService:[%c(CContactMgr) class]]; CContact *selfContact = [contactManager getSelfContact]; if ([msgWrap.m_nsContent rangeOfString:@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao"].location != NSNotFound) { // 紅包 NSString *nativeUrl = [[msgWrap m_oWCPayInfoItem] m_c2cNativeUrl]; nativeUrl = [nativeUrl substringFromIndex:[@"wxpay://c2cbizmessagehandler/hongbao/receivehongbao?" length]]; NSDictionary *nativeUrlDict = [%c(WCBizUtil) dictionaryWithDecodedComponets:nativeUrl separator:@"&"]; NSMutableDictionary *args = [[%c(NSMutableDictionary) alloc] init]; [args setObject:nativeUrlDict[@"msgtype"] forKey:@"msgType"]; [args setObject:nativeUrlDict[@"sendid"] forKey:@"sendId"]; [args setObject:nativeUrlDict[@"channelid"] forKey:@"channelId"]; [args setObject:[selfContact getContactDisplayName] forKey:@"nickName"]; [args setObject:[selfContact m_nsHeadImgUrl] forKey:@"headImg"]; [args setObject:nativeUrl forKey:@"nativeUrl"]; [args setObject:msgWrap.m_nsFromUsr forKey:@"sessionUserName"]; [[[%c(MMServiceCenter) defaultCenter] getService:[%c(WCRedEnvelopesLogicMgr) class]] OpenRedEnvelopesRequest:args]; } } }