天天看點

【騰訊Bugly幹貨分享】iOS黑客技術大揭秘

本文來自于騰訊bugly開發者社群,非經作者同意,請勿轉載,原文位址:http://dev.qq.com/topic/5791da152168f2690e72daa4

“8小時内拼工作,8小時外拼成長”這是大家共同的理想。除了每天忙于工作外,我們都希望能更多地區吸收領域内的新知識與新技能,進而走向人生巅峰。

Dev Club 是一個交流移動開發技術,結交朋友,擴充人脈的社群,成員都是經過稽核的移動開發工程師。每周都會舉行嘉賓分享,話題讨論等活動。

上一期我們邀請了騰訊 WXG iOS 開發工程師“姚海波”分享了《微信讀書 iOS性能優化》。

本期,我們邀請了騰訊 CDG iOS 開發工程師“何兆林”為大家分享《iOS黑客技術大揭秘》。

如何加入 Dev Club?

移動端開發經驗 >= 2 年,微信掃描下方群管理微信二維碼,備注姓名-公司(或産品) 申請加入。

分享内容簡介:

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

内容大體架構:

  1. 讓目标程式破繭而出 -- dumpdecrypted
  2. 運作時分析 -- cycript
  3. 追蹤神器 -- logify
  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目錄生成一個砸完殼後的二進制檔案

    2、運作時分析——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。

    3、追蹤神器 -- 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那種微信啟動後就存在的對象,怎麼追溯呢?

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

    4、反彙編工具——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/article/details/51792151

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

    6、注入工具——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

    到這裡入侵圓滿結束!

    二、常用入侵原理和反入侵方法總結

    下面我們總結一下用到的幾個工具:
  • dumpdecrypted
  • insert_dylib
  • cycript

    第一個工具是砸殼用的,代碼是開源的,而且相當簡單,它并沒有破解 appstore的加密算法,而是把自己注入到已經通過系統加載器解密的 mach-o檔案,再把解密後的記憶體資料 dump出來,詳細的原理這篇文章寫的很清楚:http://bbs.iosre.com/t/dumpdecrypted/465

    那他是怎麼注入的呢?就是利用了 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-app-app-tweak/438

    但是破解這一招也非常簡單,上面的連結也說了,用 0xED打開二進制檔案,把

    __RESTRICT

    全局替換成其它名字即可。

    cycript個人感覺是比較神奇的,它在程序運作時動态注入。

    沒有細看它的源碼,網上資料稱,它通過 taskfor_pid函數擷取目标程序句柄,然後通過在程序内建立新線程并執行自己的代碼。

    對于 cycript這種 bt的行為,利用系統的 root權限在程序中建立線程并執行自己的代碼,目前還沒想到好的對策,如果有老司機有方法,希望能指導一下~

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

    用backboard服務啟動啟動目标程式:

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

    在pc端用lldb連接配接:

    然後在lldb中下符号斷點

    b ptrace,

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

    p/x $lr

    找到函數傳回位址,然後用:

    找到目标程式的基位址,這樣就能用上面說的公式計算出偏移量,最後在 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 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧!