iOS 逆向 - lldb高階篇 Chisel 與 Cycript

李斌同學發表於2019-11-28

前言

上一篇文章 iOS 逆向 - LLDB 中講述了 lldb 的一些基礎用法 , 並沒有涉及太多其他內容 , 逆向過程中常用的動態除錯方法其實還有一些 , 本文針對上篇文章和實際逆向中的運用進行一個補充 .

主要針對 Chisel 以及 Cycript 兩個部分 .

如果篇幅不長 , 我們來講一講自定義 cy 指令.

逆向除錯注意

  • 有部分同學反應重簽名微信應用被封號的情況 . 這裡說明一下 , 微信 / 抖音 等應用是有防護和監測操作的 , 網上流傳部分破解微信防護的 logos 程式碼 , 筆者測試效果不是百分百 , 有需要的小夥伴可以評論留言 .

  • 除錯別人應用本身是會造成類似問題的 , 因此儘量不要登入個人使用賬號 , 如非必須登入 , 不要登入賬號 .

  • 確實需要登入 , 登入一個小號 , 另外提前準備好另一個賬戶去解封 .

  • 在越獄環境下 , 使用外掛的方式除錯 , 無須重簽名 , 被封號的機率是很低的 .

  • 最後再強調一次 : 玩逆向只是為了更好地防護 .

Chisel

概述

Chisel 也是 Facebook 釋出的一個 lldb 的外掛 , 能夠做到幫助除錯和提供使用者自定義指令集的功能 . 接下來會具體闡述 .

安裝

命令 : brew install chisel

( 還沒有安裝 HomeBrew 的自行安裝 )

安裝後使用 : brew list 檢視安裝結果

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

配置

如何配置 ?

1 -- 開啟下載目錄

  • 先找到下載的檔案 : cd /usr/local/Cellar/chisel
  • open . , 找到 fblldb.py 指令碼檔案.
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • cmd + opt + c 拷貝檔案路徑.

2 -- lldb配置指令碼路徑載入

  • 如果你的使用者家目錄下沒有 .lldbinit 檔案 , 請查閱上一篇文章 iOS 逆向 - LLDB 中最後講自動啟用載入指令所述 , 使用 vim 自行建立即可 .
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • vim .lldbinit
  • s 進入編輯模式
  • 新增載入指令 : command script import /usr/local/Cellar/chisel/1.8.1/libexec/fblldb.py ( 路徑換成你自己的 )
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • ESC , :wq 儲存退出

使用

測試配置結果

先來簡單試一下配置成功了沒 .

注意 : 如果是正在執行的工程 , 那你需要使用 command source ~/.lldbinit , 來重新載入一下 lldb 配置檔案 .

隨便開啟一個工程 , 進入斷點模式 , 輸入 pviews .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

pviews 就是 Chisel 提供的一個檢視檢視層級的命令 . 以上看到 , 我們已經配置成功了 .

常用指令

為了模擬逆向過程中實際動態除錯場景 . 我使用了我之前使用 MonkeyDev 重簽名的微信應用來演示指令 , 對重簽名不熟悉的同學可以閱讀一下 重籤應用除錯與程式碼修改 (Hook) , 與 shell 指令碼自動重簽名與程式碼注入 , 這兩篇文章 .

MonkeyDev 的安裝和使用我們就不多贅述了 , 畢竟重簽名原理理解了 , Monkey 其本質上也是利用指令碼自動重簽名 , 然後 程式碼注入 hook 部分整合了 Cydia Substrate 來做的.

我們所需要做的只是把砸過殼的 ipa 或者 app 包 放到指定資料夾下即可完成重簽名和程式碼注入 . 非常方便.

如果同學們關於 MonkeyDev 使用有問題可以留言告知 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

執行工程 .

pviews 圖層層級

開啟註冊頁面 , 暫停 . 進入斷點模式 ,

  • 指令 : pviews
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 說明 : pviews 可以幫助我們很清楚的看出圖層邏輯層級關係以及記憶體地址 .
pvc 檢視層級
  • 指令 : pvc

  • 結果 :

    iOS 逆向 - lldb高階篇 Chisel 與 Cycript

  • 說明 : pvc 可以幫助我們很清楚的看出檢視控制器層級關係以及記憶體地址 .

pactions 事件查詢

使用 pviews , 隨便找到一個按鈕 , 複製其記憶體地址 .

  • 指令 : pactions 0x10b06e5e0
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 說明 : pactions 可以拿到 buttontarget 以及 action , 在逆向時需要方法 hook 經常會使用.
presbonder 響應鏈

使用 pviews , 隨便找到一個按鈕 , 複製其記憶體地址 .

  • 指令 : presbonder 0x10b06e5e0
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 說明 : presbonder 可以檢視完整的響應鏈.
pclass 繼承鏈

使用 pclass , 隨便找到一個類 , 複製其記憶體地址 .

  • 指令 : pclass 0x10b910600
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 說明 : pclass 可以檢視完整的繼承鏈.
pmethods 檢視類方法 / 例項方法
  • 指令 : pmethods 0x106b57bc0
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 說明 : pmethods 可以檢視類完整的類方法和例項方法.
pinternals 檢視成員變數
  • 指令 : pinternals 0x10b660df0
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
fv / fvc
  • 指令 : fvc -v 0x10b8fe000
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 說明 :
    • 通過記憶體地址檢視類名 ( po 也可以 ) . , 檢視控制器用 fvc , 檢視用 fv .
    • fv + 類名 反之是一樣的 , 回去工程搜尋這個類 列印其記憶體地址 .
重點 : taplog
  • 指令 : taplog
  • 說明 : taplog 輸入後會退出斷點模式 , 再點選螢幕上任何可響應檢視 , 會自動進入 lldb 模式並列印按鈕.
  • 結果 :
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
重點 : flicker
  • 指令 : flicker 0x11827c040
  • 說明 : 通過記憶體地址 , 在斷電模式下呼叫該指令會閃爍 view , 非常方便除錯 , 以及確定該記憶體地址是否為我們想找到的那個檢視 .
重點 : vs
  • 指令 : vs 0x11827c040
  • 說明 : 通過記憶體地址 , 進入除錯模式 , 當前view會被新增紅色以便檢視 .
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 結果 :
(lldb) vs 0x1120f3390

Use the following and (q) to quit.
(w) move to superview    // 來到當前檢視的父檢視
(s) move to first subview // 來到當前檢視的第一個子檢視
(a) move to previous sibling  // 來到當前檢視同級關係下的前一個檢視
(d) move to next sibling     // 來到當前檢視同級關係下的後一個檢視
(p) print the hierarchy   // 列印當前檢視的層級結構

<FixTitleColorButton: 0x1120f3390; baseClass = UIButton; frame = (20 112; 374 47); clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x1118c0900>>
複製程式碼
  • 退出 vs 除錯模式 , q 指令 .
提示

最後三條指令在逆向過程中非常常用 , 大家多加練習與掌握 .

lldb_commands 外掛

這個 LLDB 外掛叫 lldb_commands . 地址為 github.com/DerekSeland…

安裝

  • 直接 Clone 或者下載 , 把 lldb_commands 資料夾儲存起來 , 我這邊是放到 /usr/local/Cellar

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

  • 來到家目錄 , 找到 .lldbinit , 新增一條指令 :
command script import /usr/local/Cellar/lldb_commands/dslldb.py
複製程式碼

路徑換成自己的 lldb_commands 資料夾路徑即可 .

工程來到 lldb 模式下 , 輸入 search UIView , 即可測試有沒有配置成功 .

常用指令

methods 快速定位方法

找到檢視控制器地址 , 使用 methods 檢視其所有的例項方法和屬性 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

注意 : 由於逆向時 方法的符號是沒有恢復的 , 因此根據類名和方法名下斷點會失敗 .

函式呼叫棧

由於符號並沒有恢復 , 因此 bt 指令檢視函式呼叫棧時 , 會出現以下情況 .

( 後續會講如何利用工具恢復 Mach-O 的符號 )

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

那麼此時 , 利用 lldb_commands 提供的 sbt 指令 , 會幫助恢復一些符號 , 以便於檢視方法名稱 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

Mach-O Section 檢視
  • Section 指令可以讓我們快速檢視 Mach-O 有哪些 Section 段 .
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • Section 可新增其他指令來檢視 Mach-O 具體內容 .
  # Dump the Mach-O segments to the main executable
  (lldb) section

  # Dump the Mach-O segments to UIKit
  (lldb) section UIKit

  # Dump the Mach-O sections of the __TEXT segment of UIKit
  (lldb) section UIKit __TEXT

  # Get the load address of all the hard-coded uint8_t * strings in the UIKit binary
  (lldb) section UIKit __TEXT.__cstring -l

  # Get the entitlements for the executable (simulator only, entitlements for actual app in __LINKEDIT)
  (lldb) section  __TEXT.__entitlements

  # Get all the load address to the lazy symbol stubs in the main executable
  (lldb) section  __DATA.__la_symbol_ptr -l
複製程式碼

效果如下 , 大家可以結合 MachOView 來檢視結果.

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

Cycript

概述

Cycript 是由 Cydia ( 熟悉越獄的同學應該都很清楚 ) 創始人 Saurik 推出的一款指令碼語言,Cycript 混合了 OCJavaScript 語法的直譯器,這意味著我們能夠在一個命令中使用 OC 或者 JavaScript,甚至兩者並用。

它能夠掛鉤 正在執行的程式,能夠在執行時修改很多東西。

到官網點選 Download SDK 即可下載 .

安裝

  • 將下載後的資料夾放入 /opt/ 裡即可 , 也可以自行選擇位置 .
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript
  • 配置環境變數 .
  • 注意 : 這裡根據你使用的是 zsh 還是 bash 去相應的資原始檔配置 (家目錄下的 .zshrc / .bash_profile) .
  • 筆者由於在 .zshrc 中也配置了 bash_profile 的引用 , 因此兩處來配置這個環境變數都是可以的.
    iOS 逆向 - lldb高階篇 Chisel 與 Cycript

配置內容:

  • 新增 : export CY=/opt/cycript_0.9.594/ , 換成你自己的路徑.
  • export PATH= 中新增 :$CY

重啟 iTerm , 輸入 cycript , 即可檢視 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

如果有遇到 ruby 環境不對的同學 , 去下載對應版本的即可 . /System/Library/Frameworks/Ruby.framework/Versions

使用

在越獄環境下 , 是可以在 Cydia 直接安裝 Cycript 外掛的.

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

越獄手機截圖上傳有點麻煩 , 直接拍的.. 瑕疵請忽略 , 看個意思

那麼非越獄環境下 , 就要藉助 Cycript 提供的 iOS Framework , 注入進去了 . 而且在 MonkeyDev 中 , 是預設已經做好了 Cycript 的注入的 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

而且新增了 預設 6666 埠號的監聽 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

也就是說只要用 MonkeyDev 來重籤跑起來的程式程式 , 6666 埠就可以附加使用了 ( 當然 , 在 Monkey 裡程式碼可以自己定義埠號 ) .

好了 說了這麼多 , 開始使用.

  • 1 . 保證電腦和手機在同一個區域網內 ( 因為要做埠對映 )
  • 2 . 執行 MonkeyDev 重簽名程式 / 或者直接開啟以前重簽好的工程 , 無須 Xcode 執行也可以
  • 3 . 終端輸入 cycript -r 192.168.0.116:6666
    • 換成你自己的手機 ip 地址
    • 另外 , 請不要將應用程式放到後臺 , 會影響附加.

出現以下 , Congratulations , you're good to go !

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

提示

使用 tab 鍵 , 寫程式碼可以補全 .

UIWindow.keyWindow()

檢視當前 Window .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

UIApplication sharedApplication

指令 : [UIApplication sharedApplication ] 可簡寫為 UIApp

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

自定義變數

指令 : 自己 var 一個物件 , 而後我們就可以使用 .

另外注意 : 只要 APP 程式沒有掛 , 這個變數是一直存在的 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

# + 地址可以直接使用

# + 物件地址 可以直接呼叫物件的方法

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

檢視檢視層級

UIWindow.keyWindow().recursiveDescription().toString()

隨便找一個 , 例如註冊頁面有一個 .text+86label , 拿到其記憶體地址 ,

命令 :

#0x10b95d800.text = "hhh"
複製程式碼

顯示結果

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

基於這種直接修改程式記憶體的方式 , 大家可以自己去玩一玩 . 比如登入了修改一下錢包餘額 , 然後改一改 frame , 練習一下 .

以下指令結果我就不一一貼圖了 , 文章太長 , 不便閱讀 , 大家自己嘗試 .

獲取頁面上所有控制元件

choose(UIButton)

choose(UILabel)

隱藏 / 顯示狀態列

[UIApp setStatusBarHidden:YES]

APP角標

[UIApp setApplicationIconBadgeNumber: 99]

獲取Bundle ID

APPID 結果 :

@"com.libin.LBMonkeyApp"

頁面層級

pviews()

pvcs()

根據按鈕地址獲取按鈕 target & Action

pactions (#0x10b29da40)

結果 :

"<WCAccountRegisterViewController: 0x10b9d9800> onAgreementCheckBoxClick:"
複製程式碼

根據按鈕地址獲取響應鏈

rp(#0x10b29da40)

退出cy 除錯模式

control + d

注意:

pviews / pvc / pactions / rp 這些指令是 MonkeyMDConfig.plist 中額外封裝了自定義的 cy 源的 . 也就是說使用越獄環境原本的 cycript 外掛是沒有這些指令可用的 .

iOS 逆向 - lldb高階篇 Chisel 與 Cycript

那麼我們閒著也是閒著 , 我們也來自己寫一個 cy 源來玩一下 ?

自定義 cy 指令

  • 在我們的 monkey 工程主工程 target 中新建一個空檔案, 我這裡取名 lb.cy .

  • Build Phases - Copy Files , 引入這個檔案

    iOS 逆向 - lldb高階篇 Chisel 與 Cycript

  • 在空檔案新增我們想自己定義的指令 . 可以參照 Monkey 原本提供的那兩個來寫 raw.githubusercontent.com/AloneMonkey…

    這裡我寫了一些我常用的指令 , 比如獲取 APPID / APPPATH , 當前跟檢視 , 當前頁面 這些 , 我貼在下面供大家參考 , 也可以拿去直接用 .

  • 重新執行工程 .

  • 輸入我們自定義的指令 例如 : LBCurrentVC()

  • 提示沒找到指令 , 因為我們還沒有引入 , monkey 那兩個在 config 中會自動引入.

  • @import lb

  • 再次輸入 LBCurrentVC() , 得到結果.

    iOS 逆向 - lldb高階篇 Chisel 與 Cycript

//IIFE 匿名函式自執行表示式

(function(exports){

     APPID = [NSBundle mainBundle].bundleIdentifier,
     APPPATH = [NSBundle mainBundle].bundlePath,

     //如果有變化,就用function去定義!!
     LBRootvc = function(){
        return UIApp.keyWindow.rootViewController;
     };

     LBKeyWindow = function(){
        return UIApp.keyWindow;
     };

    LBGetCurrentVCFromRootVc = function(rootVC){
        var currentVC;
        if([rootVC presentedViewController]){
            rootVC = [rootVC presentedViewController];
        }

        if([rootVC isKindOfClass:[UITabBarController class]]){
            currentVC = LBGetCurrentVCFromRootVc(rootVC.selectedViewController);
        }else if([rootVC isKindOfClass:[UINavigationController class]]){
            currentVC = LBGetCurrentVCFromRootVc(rootVC.visibleViewController);
        }else{
            currentVC = rootVC;
        }

        return currentVC;
    };

    LBCurrentVC = function(){
        return LBGetCurrentVCFromRootVc(LBRootvc());
    };
 
})(exports);
複製程式碼

小提示

  • 使用 cycript 某一個控制元件 / 物件 記憶體地址時 , 要注意其生命週期 , 例如用一個 label 的記憶體地址除錯 , 你頁面退出 , 又重進 , 是重新建立了物件的 , 以前的記憶體地址也會無法再使用 . 不要忘記這點 .

  • cycript 使用需要同區域網每次要連線 , 或者其他我們需要自定義的初始化動作 , 我們可以將其寫到一個指令碼中 , 配置到 zsh / bash 環境變數中即可 , 大家可以玩一玩 , 有問題大家一起探討一下 .

  • 逆向過程中 , View Debugcycript 除錯介面是非常常用的手段 , 因此 , 希望大家能熟練掌握這些技巧 .

相關文章