【騰訊Bugly乾貨分享】iOS 黑客技術大揭祕

本文來自於騰訊bugly開發者社區,非經做者贊成,請勿轉載,原文地址:http://dev.qq.com/topic/5791d...ios

「8小時內拼工做,8小時外拼成長」這是你們共同的理想。除了天天忙於工做外,咱們都但願能更多地區吸取領域內的新知識與新技能,從而走向人生巔峯。c++

Dev Club 是一個交流移動開發技術,結交朋友,擴展人脈的社羣,成員都是通過審覈的移動開發工程師。每週都會舉行嘉賓分享,話題討論等活動。算法

上一期咱們邀請了騰訊 WXG iOS 開發工程師「姚海波」分享了《微信讀書 iOS性能優化》xcode

本期,咱們邀請了騰訊 CDG iOS 開發工程師「何兆林」爲你們分享《iOS黑客技術大揭祕》安全

分享內容簡介

在黑客的世界裏,沒有堅不可破的防禦系統,也沒有無往不勝、所向披靡的入侵利器,有時候看似簡單的問題,破解起來也許花上好幾天、好幾個月,有時候看似很 low 的工具每每能解決大問題;咱們以實現微信自動搶紅包爲引子,逐步展開 iOS 黑客入侵經常使用的幾種武器,並簡單的講解一些經常使用的反入侵策略,以及如何破解反入侵策略,雖然搶紅包的破解代碼網上有不少,可是咱們要講的是這些代碼是用什麼工具分析出的,爲何要這樣寫?性能優化

內容大致框架微信

  1. 讓目標程序破繭而出 -- dumpdecrypted架構

  2. 運行時分析 -- cycript併發

  3. 追蹤神器 -- logifyapp

  4. 反彙編工具 -- hopper

  5. 斷點調試工具 -- lldb+debugserver

  6. 注入工具 -- insert_dylib + install_name_tool

分享人介紹: 何兆林 通信充值與彩票業務部 iOS開發工程師。

負責過的產品:QQ彩票、QQ電影票 iOS客戶端,目前主要負責 QQ彩票 iOS客戶端的開發。

下面是本期分享內容整理。


你們晚上好,我是來自 CDG的 jolinhe,目前在作qq彩票項目,負責 iOS側的插件化和總體架構。

越獄和入侵是個人業餘愛好,正好08和09年的時候,在網龍積累了一些越獄開發經驗,因此今天跟你們分享一下 iOS入侵方面的工具和手段。

爲了不紙上談兵:

  • 第一部分咱們就拿微信來開刀,講解一下如何運用這些工具來找到和實現自動搶紅包功能的;

  • 第二部分咱們再總結一下經常使用的入侵原理和反入侵方法。

開始以前,咱們須要把環境搞起,硬件方面,須要:

  1. 一臺越獄手機

  2. 一臺裝了開發環境的 Mac電腦

軟件方面內容提綱已經列出來了,須要注意的是它們有一些是手機端的,一些是 PC端的,用到的時候我會細講。

羣裏不乏老司機,這裏我假設你們對於 ssh、theos、class_dump、hook這些工具和技術都已經有所涉獵了,若是有不清楚的,歡迎私下交流。

1、微信實現自動搶紅包功能

言歸正傳,要實現自動化搶紅包,首先咱們須要理清思路:

  • 第一步:須要攔截微信的消息流,在哪裏攔截?

  • 第二步:在這些信息流中分辨出哪些消息是紅包?

  • 第三步:在合適的地方插入本身的「搶」紅包代碼。

一、讓目標程序破繭而出——dumpdecrypted

由於直接從 AppStore下載下來的二進制文件都是加了殼的,因此爲了讓它的內核破繭而出,咱們須要砸殼操做。

因此第一個工具就是 dumpdecrypted,這個工具是手機端的,能夠經過 cydia安裝,安裝後文件路徑是:

/usr/lib/dumpdecrypted.dylib

在實際使用時,能夠經過 pp助手把這個文件 copy到目標程序的 documents目錄,而後ssh進手機終端,cd到 documents目錄,執行:

DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib WeChatPath

執行完後會再 documents目錄生成一個砸完殼後的二進制文件

二、運行時分析——cycript

砸完殼以後,咱們再 dump出頭文件,可是微信的類太多了,頭文件有幾百個,如此多的頭文件,讓人眼花繚亂,因此要找到突破口,咱們得縮小範圍,我喜歡用的思路是從 ui入手,先找到微信對話界面的 controller,而後再追蹤 controller中對應的消息處理函數。

這樣第二個工具 cycript 隆重出場了,它也是個手機端的工具,用於查看 app運行時數據,大夥兒能夠經過 cydia安裝,安裝完以後,ssh到越獄手機的終端。

先找到微信的進程id:ps aux | grep WeChat
再執行:cycript -p 微信的pid

完成了注入,進入到 cycript提供的終端。

這時候進入在手機上進入微信的聊天窗口,例如:

而後在 cycript的終端輸入:

UIApp.keyWindow.recursiveDescription().toString()

結果以下:

打印的是當前的ui樹,隨便找一個節點(樹的中間,爲何要在中間,你們能夠思考下),copy它的內存地址,例如 0x14da3f000

執行:[#0x14da3f000 nextResponder]

會輸出當前節點的下一級事件響應鏈,而後對輸出節點再次調用 nextResponder,直到找到它的視圖控制器 xxxcontroller

看到了吧,微信的聊天頁對應的類名是 BaseMsgContentViewController

三、追蹤神器 - logify

目前爲止咱們已經鎖定了大體的突破口,下面還需繼續追蹤,找到這個類裏面的消息處理函數,這裏的思路就是經過向羣裏發送一個消息,而後觀察 controller中哪些方法被調用了。

第三個工具 Logify就是幹這個事情的,它是 theos的一個組件,和 theos一塊兒安裝在 pc端的,在 pc的終端輸入:

logify.pl /path/to/BaseMsgContentViewController.h > /out/path/to/Tweak.xm

打開生成的 tweak.xm文件,能夠看到它其實就是 hook了這個類全部的方法,在方法中注入了 nslog,打印方法的入參和返回值,最後把這個文件用 theos打包並安裝到手機中,再次向羣裏發消息,手機連上 xcode觀察手機控制檯輸出。

輸出內容不少,須要仔細過濾一下,例如咱們發的消息是一個文本「test」,在控制檯搜索它,你會在它附近找到下面這個函數調用:

addMessageNode:layout:addMoreMsg

從函數名字來看,這個應該就是用來處理消息數據的,從表面來看咱們已經找到消息的攔截點了,可是你們想一下,若是咱們hook這個方法,在裏面自動搶紅包,會有什麼致命的缺陷?

老司機們已經看出來了吧,這個方法是 BaseMsgContentViewController類裏面的,而這個類進入聊天頁面纔會被建立。

hook這裏會有兩個缺陷:

  • 第一,必須進特定的羣的聊天頁面才能生效;

  • 第二,兩個羣同時有紅包來,無法併發的搶。

爲了追求更加上流的功能,咱們須要再向上追溯消息的源頭,最好 hook那種微信啓動後就存在的對象,怎麼追溯呢?

個人思路是經過在這個方法中設置斷點,經過調用棧,來找到上層的調用者。

四、反彙編工具——hopper & 斷點調試工具——lldb + debugserver

第五個工具 lldb + debugserver顧名思義,debugserver是手機端的(只要你的手機有連過 xcode進行 debug,這個玩意就自動有了),用於監聽 pc端 lldb的鏈接,來實現遠程調試。

這個工具要和第四個 hooper(反彙編工具)結合起來用。

首先 ssh進手機的終端,輸入:

debugserver *:19999 -a WeChat

監聽 lldb的鏈接

而後打開pc的終端,啓動 lldb並鏈接:

lldb
process connect connect://deviceIP:19999

若是鏈接成功,咱們就正式進入 debug狀態了。

那麼問題來了,要精確的設置斷點,必須知道這個函數的內存地址,這個內存地址怎麼搞出來呢?

有個公式:

內存地址=進程內存基地址+函數在二進制中的偏移量

上面咱們已經連上了 lldb調試環境,獲取基地址在 lldb中輸入下面的命令:

image list -o -f

這時會輸出不少行數據,找到文件名爲 WeChat的模塊地址,這裏第一行就是了:

偏移量須要藉助 hooper,pc端的反彙編利器,用 hooper打開微信的二進制文件,等幾分鐘,反彙編完成後,在搜索框輸入剛找到的函數名: addMessageNode,定位到相應的彙編代碼,第一列就是偏移量了:

兩個參數都找到後,在lldb中輸入:

br s -a ‘基地址+偏移量’

而後用 「br l」 確認一下斷點是否設置成功

進入聊天界面,再次向羣發送一個消息,會發現 ui卡住了,觀察 lldb控制檯,會提示進程被斷住了,在 lldb中繼續輸入 bt指令,重點觀察模塊名是 WeChat的棧,可是因爲沒有符號表,咱們只能看到棧的內存地址:

想要把內存地址還原成函數名,須要兩步:

  • 第一要把內存地址轉換成二進制文件偏移量

  • 第二步再使用 hooper根據偏移量找到函數名

也就是上面公式的逆向過程:

函數在二進制中的偏移量=內存地址 - 進程內存基地址

這裏咱們棧地址是0x0000000101ad02f4,基地址是0x00000000000e8000,作下減法,獲得結果0x1019E82F4,而後在 hooper中搜索這個地址就能獲得方法名:

以此類推,最後獲得棧頂的調用是這個:

[CMessageMgr MainThreadNotifyToExt:]

CMessageMgr這個類,從名字來看,就是消息管理器,並且是單例的,咱們終於找到了真正須要 hook的類,可是這個方法是用來異步發送通知的,不像是消息的源頭,因此咱們用上面說的 logify組件繼續追蹤一下這個類

過程再也不重複講,最後的目標終於浮出水面:

(void)AsyncOnAddMsg:(id)message MsgWrap:(CMessageWrap* )msgWrap

第一步消息的攔截點終於找到了,接下來是第二步:

第二步:咱們須要知道哪些消息是紅包,這個就比較簡單,向羣裏發一個普通消息和紅包消息,經過 logify組件觀察 message參數的內容,發現它裏面有一個 type字段,若是是紅包,值是49,其餘語音和文本各不相同,因此,這裏輕鬆搞定。

第三步:須要實現搶紅包代碼,這一步稍微複雜一點,先講一下思路,首先進入微信開紅包的界面:

根上面講過的同樣,經過 cycript+logify咱們能夠輕鬆拿到開紅包的入口函數,下一步,咱們須要本身從 AsyncOnAddMsg的參數中構造搶紅包函數的入參。

找注入點我就再也不重複講,直接上結果:

[WCRedEnvelopesReceiveHomeView OnOpenRedEnvelopes]

這顯然是一個事件處理函數,它裏面確定會調用真正的拆紅包邏輯

因此咱們打開 hooper,找到這個方法而後觀察方法體,發現它在最後調用了一個 selector:

WCRedEnvelopesReceiveHomeViewOpenRedEnvelopes

這應該是真正的拆紅包邏輯,可是這個 selector沒有參數,因此我猜測這個方法的做用應該是本身封裝拆紅包所需的數據,並調用底層服務來拆紅包。

在hooper中搜索這個方法,觀察一下,果真是這樣的:

函數開始部分的彙編代碼都是在構造dictionary,只有在最後調用了一個能夠函數:

這裏說明一下,因爲微信這一塊的代碼全都是 oc的,而 hooper能夠直接將 oc反彙編到接近源碼的水平,因此,還原過程基本不須要掌握多少彙編知識,具體的還原過程能夠看下我以前發的文章:http://blog.csdn.net/heiby/ar...

最後一步,編寫 tweak,替換 AsyncOnAddMsg函數並把本身的成果注入進去就 ok了。

六、注入工具——insert_dylib + install_name_tool

對於越獄機器來講,到這裏已經大功告成了,可是想要在非越獄機器上跑,還須要幾個步驟:

在非越獄機上面,一切都要靠本身,首先手動把你的庫注入到目標二進制中,這一步使用 insert_dylib就能夠了,它運行在 pc端,在命令行 cd到微信的二進制目錄,執行命令:

insert_dylib @executable_path/xxx.dylib WeChat

由於咱們的 hook代碼是基於 cydiaSubstrate的,用l -L xxx.dylib來檢查一下你的 tweak,這個依賴庫在正版機上是沒有的,咱們須要把它從越獄機充 cp出來和你的 tweak一塊兒拖進目標 app目錄,並經過install_name_tool命令修改你的 tweak中對他的引用路徑。

最後,用 codesign命令對微信 bundle裏面全部的 dylib進行簽名:

codesign -f -s "iPhone Developer:xxx" xxx.dylib

打包成ipa:

xcrun -sdk iphoneos PackageApplication -v WeChat.app -o ~/WeChat.ipa

再使用 iResign對 ipa進行簽名,就能夠安裝到非越獄的機器上了。

注意一下,若是你不想使用 iResign,在執行 xcrun以前,還須要對微信的二進制文件進行簽名:

codesign -f -s "iPhone Developer:xxx」 —entitlements Entitlements.plist WeChat.app

常常有人問 Entitlements.plist文件怎麼寫?照着這個來就好了,把 teamed和 bundle id改爲你本身的:

簽名完成後能夠經過ldid命令查看簽入的內容:

ldid -e /path/to/WeChat

到這裏入侵圓滿結束!

2、經常使用入侵原理和反入侵方法總結

下面咱們總結一下用到的幾個工具:

  • dumpdecrypted

  • insert_dylib

  • cycript

第一個工具是砸殼用的,代碼是開源的,並且至關簡單,它並無破解 appstore的加密算法,而是把本身注入到已經經過系統加載器解密的 mach-o文件,再把解密後的內存數據 dump出來,詳細的原理這篇文章寫的很清楚:http://bbs.iosre.com/t/dumpde...

那他是怎麼注入的呢?就是利用了 iOS系統中 DYLD_INSERT_LIBRARIES這個環境變量,若是設置了 DYLD_INSERT_LIBRARIES 環境變量,那麼在程序運行時,動態連接器會先加載該環境變量所指定的動態庫;也就是說,這個動態庫的加載優先於任何其它的庫,包括 libc。

因爲這個環境變量指定的動態庫加載的時機實在是太早了,因此對於 app來講,除了代碼混淆外,無良策;

可是咱們能夠在代碼中經過判斷環境變量來檢測是否是被注入:

charchar *env = getenv("DYLD_INSERT_LIBRARIES");

若是方法返回非空,咱們能夠作一些上報之類的。

後面兩個工具都是用來注入的

insert_dylib經過向 mach-o文件的 loadcommand段插入 LC_LOAD_DYLIB數據來加載第三方庫。

對於 insert_dylib,咱們能夠經過在 Xcode的Build Settings中找到「Other Linker Flags」在其中加上-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null指令來繞過 dylib加載額外的第三方庫,具體的原理可參考 http://bbs.iosre.com/t/tweak-...

可是破解這一招也很是簡單,上面的連接也說了,用 0xED打開二進制文件,把__RESTRICT全局替換成其它名字便可。

cycript我的感受是比較神奇的,它在進程運行時動態注入。

沒有細看它的源碼,網上資料稱,它經過 taskfor_pid函數獲取目標進程句柄,而後經過在進程內建立新線程並執行本身的代碼。

對於 cycript這種 bt的行爲,利用系統的 root權限在進程中建立線程並執行本身的代碼,目前還沒想到好的對策,若是有老司機有方法,但願能指導一下~

最後再說說 lldb反調試,網上大多都提到 ptrace系列函數,原理我就很少說,這裏主要講如何繞過它,有兩種方法,先說第一種,直接經過彙編代碼修改 ptrace的第一個參數,這樣咱們須要先知道在哪裏調用了 ptrace。

用backboard服務啓動啓動目標程序:

debugserver -x backboard *:19999 /path/to/binary

在pc端用lldb鏈接:

process connect connect://deviceIP:19999

而後在lldb中下符號斷點

b ptrace,

在lldb中輸入c命令以後看ptrace第一行代碼的位置,繼續輸入命令:

p/x $lr

找到函數返回地址,而後用:

image list -o -f

找到目標程序的基地址,這樣就能用上面說的公式計算出偏移量,最後在 hooper中找到調用 ptrace的彙編代碼

找到彙編代碼的位置後,把 ptrace的第一個參數 1F,替換成 0A便可,下面是個人調試過程:

手機端:

pc端:

第二種簡單又暴力,注入tweak,讓 ptrace直接返回0便可,具體的代碼很是簡單,直接發出來:

//Tweak.xm
#import <substrate.h>
#import <mach-o/dyld.h>
#import <dlfcn.h>

static int (*oldptrace)(int request, pid_t pid, caddr_t addr, int data);
static int newptrace(int request, pid_t pid, caddr_t addr, int data){
    printf("newptrace:%d\n\n",request);
    return 0;
}
%ctor {
    printf("Tweak for SkipPtrace  Start!\n\n");
    MSHookFunction((void *)MSFindSymbol(NULL,"_ptrace"), (void *)newptrace, (void **)&oldptrace);
}

總結

入侵的路有不少條,關鍵是要在開始階段定好目標,並理清思路,否則很容易走進各類死衚衕,還要學會面對失敗,不要一條路走不通就放棄,最後呢,咱們要善於藉助各類工具,能用工具,幹嗎還要去費力氣。

今天分享就到這裏,下面你們有問題能夠提問哦!

問答環節

Q1:其實我挺好奇,這個能被破解,應該也會有被封堵的問題吧?

咱們這裏只是僞造了本身的參數,並調用微信原有的邏輯自動拆紅包,因此技術上出了微信更新版本,是封不了的,可是若是你搶的太暴力,帳號有可能被封,這裏咱們能夠經過隨機的延遲等操做來避免

Q2:我在分析 UI時候多用了一個 reveal的工具。

reveal的可視化作的比較強大,用來分析 ui很不錯,有須要能夠用

Q3:想問問那些安全類軟件是如何防止 tweak,對微信支付寶的進程保護?

防止tweak上面我也講到一些,不過都是從目標 app的代碼層級來說的,例如反 gdb和反注入,對於安全類軟件來講,在非越獄的系統上,基本起不了做用,在越獄機上面,因爲有 root權限,這時就能作不少事情了,例如檢查 mach-o文件的 loadcommand、檢查 DYLD_INSERT_LIBRARIES這種環境變量等

Q4:各類微信分身版能被微信後臺準確的識別出來嗎?若是能夠識別,有哪些方方法能夠去識別?

您指的是一機多裝吧,ios系統經過 app的 bundleid來惟一識別一個 app,分身版大可能是經過改 bundleid並從新簽名和發佈,在代碼中能夠經過監控本身 info.plist裏面的 bundle id來識別是否被篡改,可是這也是不可靠的,由於黑客們仍是能夠經過 hook你的監控函數來繞開

Q5:看到你很多是根據方法名字面上來猜測它的意思,要是有的 app代碼寫的爛,咱們反而很差弄了吧?還有若是紅包的核心代碼是用 C/C++的代碼,這是否是就不能這樣反彙編了?

確實會這樣,代碼寫得爛,有點相似於「代碼混淆」,會增長入侵的難度,若是核心代碼是 c/c++,在彙編層面會增長閱讀難度,可是隻是難度增長了而已

Q6:對於哪些包括 watch和 extension的 App,在重簽名時有哪些須要注意的呢? 重簽名安裝成功但啓動就閃退多是什麼緣由?

有 extension的app,在重簽名時,須要記住每一個 extension都須要用一樣的證書單獨簽名,而後再對外層的 ipa進行簽名,重簽名後啓動閃退,能夠經過 xcode鏈接設備,點擊 window->devices查看設備控制檯,在控制檯,會輸出你那一個 dylib校驗失敗,還有失敗的緣由

Q7:聽你分析逆向這麼容易,那 ios防反編譯有哪些方案?

ios防止反編譯主要仍是代碼混淆,可是混淆的代價你們是知道的,並且混淆也不能徹底阻止入侵,因此會得不償失,由於這樣,如今 ios開發不多用;還有一個方案就是反注入,上面講過的,可是很容易被繞過

Q8:Android的一個安全加固保護是經過設置 PTRACEME來防止外部程序來 ptrace本身,若是 iOS APP也這樣設置了,會不會致使其中一些逆向機制失效呢?

ptrace只是反調試的,不會影響逆向過程

更多精彩內容歡迎關注bugly的微信公衆帳號:

騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!

相關文章
相關標籤/搜索