前言
上一篇文章 iOS 逆向 - LLDB 中講述了 lldb
的一些基礎用法 , 並沒有涉及太多其他內容 , 逆向過程中常用的動態除錯方法其實還有一些 , 本文針對上篇文章和實際逆向中的運用進行一個補充 .
主要針對 Chisel
以及 Cycript
兩個部分 .
如果篇幅不長 , 我們來講一講自定義 cy
指令.
逆向除錯注意
-
有部分同學反應重簽名微信應用被封號的情況 . 這裡說明一下 , 微信 / 抖音 等應用是有防護和監測操作的 , 網上流傳部分破解微信防護的
logos
程式碼 , 筆者測試效果不是百分百 , 有需要的小夥伴可以評論留言 . -
除錯別人應用本身是會造成類似問題的 , 因此儘量不要登入個人使用賬號 , 如非必須登入 , 不要登入賬號 .
-
確實需要登入 , 登入一個小號 , 另外提前準備好另一個賬戶去解封 .
-
在越獄環境下 , 使用外掛的方式除錯 , 無須重簽名 , 被封號的機率是很低的 .
-
最後再強調一次 : 玩逆向只是為了更好地防護 .
Chisel
概述
Chisel 也是 Facebook
釋出的一個 lldb
的外掛 , 能夠做到幫助除錯和提供使用者自定義指令集的功能 . 接下來會具體闡述 .
安裝
命令 :
brew install chisel
( 還沒有安裝 HomeBrew 的自行安裝 )
安裝後使用 :
brew list
檢視安裝結果
配置
如何配置 ?
1 -- 開啟下載目錄
- 先找到下載的檔案 :
cd /usr/local/Cellar/chisel
open .
, 找到fblldb.py
指令碼檔案.cmd + opt + c
拷貝檔案路徑.
2 -- lldb配置指令碼路徑載入
- 如果你的使用者家目錄下沒有
.lldbinit
檔案 , 請查閱上一篇文章 iOS 逆向 - LLDB 中最後講自動啟用載入指令所述 , 使用vim
自行建立即可 . vim .lldbinit
s
進入編輯模式- 新增載入指令 :
command script import /usr/local/Cellar/chisel/1.8.1/libexec/fblldb.py
( 路徑換成你自己的 ) ESC
,:wq
儲存退出
使用
測試配置結果
先來簡單試一下配置成功了沒 .
注意 : 如果是正在執行的工程 , 那你需要使用 command source ~/.lldbinit
, 來重新載入一下 lldb
配置檔案 .
隨便開啟一個工程 , 進入斷點模式 , 輸入 pviews
.
pviews
就是 Chisel
提供的一個檢視檢視層級的命令 . 以上看到 , 我們已經配置成功了 .
常用指令
為了模擬逆向過程中實際動態除錯場景 . 我使用了我之前使用 MonkeyDev
重簽名的微信應用來演示指令 , 對重簽名不熟悉的同學可以閱讀一下 重籤應用除錯與程式碼修改 (Hook) , 與 shell 指令碼自動重簽名與程式碼注入 , 這兩篇文章 .
MonkeyDev
的安裝和使用我們就不多贅述了 , 畢竟重簽名原理理解了 , Monkey
其本質上也是利用指令碼自動重簽名 , 然後 程式碼注入 hook
部分整合了 Cydia Substrate
來做的.
我們所需要做的只是把砸過殼的 ipa
或者 app
包 放到指定資料夾下即可完成重簽名和程式碼注入 . 非常方便.
如果同學們關於 MonkeyDev
使用有問題可以留言告知 .
執行工程 .
pviews 圖層層級
開啟註冊頁面 , 暫停 . 進入斷點模式 ,
- 指令 :
pviews
- 結果 :
- 說明 :
pviews
可以幫助我們很清楚的看出圖層邏輯層級關係以及記憶體地址 .
pvc 檢視層級
-
指令 :
pvc
-
結果 :
-
說明 :
pvc
可以幫助我們很清楚的看出檢視控制器層級關係以及記憶體地址 .
pactions 事件查詢
使用 pviews
, 隨便找到一個按鈕 , 複製其記憶體地址 .
- 指令 :
pactions 0x10b06e5e0
- 結果 :
- 說明 :
pactions
可以拿到button
的target
以及action
, 在逆向時需要方法hook
經常會使用.
presbonder 響應鏈
使用 pviews
, 隨便找到一個按鈕 , 複製其記憶體地址 .
- 指令 :
presbonder 0x10b06e5e0
- 結果 :
- 說明 :
presbonder
可以檢視完整的響應鏈.
pclass 繼承鏈
使用 pclass
, 隨便找到一個類 , 複製其記憶體地址 .
- 指令 :
pclass 0x10b910600
- 結果 :
- 說明 :
pclass
可以檢視完整的繼承鏈.
pmethods 檢視類方法 / 例項方法
- 指令 :
pmethods 0x106b57bc0
- 結果 :
- 說明 :
pmethods
可以檢視類完整的類方法和例項方法.
pinternals 檢視成員變數
- 指令 :
pinternals 0x10b660df0
- 結果 :
fv / fvc
- 指令 :
fvc -v 0x10b8fe000
- 結果 :
- 說明 :
- 通過記憶體地址檢視類名 ( po 也可以 ) . , 檢視控制器用
fvc
, 檢視用fv
. fv + 類名
反之是一樣的 , 回去工程搜尋這個類 列印其記憶體地址 .
- 通過記憶體地址檢視類名 ( po 也可以 ) . , 檢視控制器用
重點 : taplog
- 指令 :
taplog
- 說明 :
taplog
輸入後會退出斷點模式 , 再點選螢幕上任何可響應檢視 , 會自動進入lldb
模式並列印按鈕. - 結果 :
重點 : flicker
- 指令 :
flicker 0x11827c040
- 說明 : 通過記憶體地址 , 在斷電模式下呼叫該指令會閃爍
view
, 非常方便除錯 , 以及確定該記憶體地址是否為我們想找到的那個檢視 .
重點 : vs
- 指令 :
vs 0x11827c040
- 說明 : 通過記憶體地址 , 進入除錯模式 , 當前view會被新增紅色以便檢視 .
- 結果 :
(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
裡
- 來到家目錄 , 找到
.lldbinit
, 新增一條指令 :
command script import /usr/local/Cellar/lldb_commands/dslldb.py
複製程式碼
路徑換成自己的 lldb_commands
資料夾路徑即可 .
工程來到 lldb
模式下 , 輸入 search UIView
, 即可測試有沒有配置成功 .
常用指令
methods 快速定位方法
找到檢視控制器地址 , 使用 methods 檢視其所有的例項方法和屬性 .
注意 : 由於逆向時 方法的符號是沒有恢復的 , 因此根據類名和方法名下斷點會失敗 .
函式呼叫棧
由於符號並沒有恢復 , 因此 bt
指令檢視函式呼叫棧時 , 會出現以下情況 .
( 後續會講如何利用工具恢復 Mach-O 的符號 )
那麼此時 , 利用 lldb_commands
提供的 sbt
指令 , 會幫助恢復一些符號 , 以便於檢視方法名稱 .
Mach-O Section 檢視
Section
指令可以讓我們快速檢視Mach-O
有哪些Section
段 .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
來檢視結果.
Cycript
概述
Cycript 是由 Cydia
( 熟悉越獄的同學應該都很清楚 ) 創始人 Saurik
推出的一款指令碼語言,Cycript
混合了 OC
、 JavaScript
語法的直譯器,這意味著我們能夠在一個命令中使用 OC
或者 JavaScript
,甚至兩者並用。
它能夠掛鉤 正在執行的程式,能夠在執行時修改很多東西。
到官網點選 Download SDK
即可下載 .
安裝
- 將下載後的資料夾放入
/opt/
裡即可 , 也可以自行選擇位置 . - 配置環境變數 .
- 注意 : 這裡根據你使用的是
zsh
還是bash
去相應的資原始檔配置 (家目錄下的.zshrc
/.bash_profile
) . - 筆者由於在
.zshrc
中也配置了bash_profile
的引用 , 因此兩處來配置這個環境變數都是可以的.
配置內容:
- 新增 :
export CY=/opt/cycript_0.9.594/
, 換成你自己的路徑. - export PATH= 中新增
:$CY
重啟 iTerm
, 輸入 cycript
, 即可檢視 .
如果有遇到 ruby
環境不對的同學 , 去下載對應版本的即可 . /System/Library/Frameworks/Ruby.framework/Versions
使用
在越獄環境下 , 是可以在 Cydia
直接安裝 Cycript
外掛的.
越獄手機截圖上傳有點麻煩 , 直接拍的.. 瑕疵請忽略 , 看個意思
那麼非越獄環境下 , 就要藉助 Cycript
提供的 iOS Framework
, 注入進去了 . 而且在 MonkeyDev
中 , 是預設已經做好了 Cycript
的注入的 .
而且新增了 預設 6666
埠號的監聽 .
也就是說只要用 MonkeyDev
來重籤跑起來的程式程式 , 6666
埠就可以附加使用了 ( 當然 , 在 Monkey
裡程式碼可以自己定義埠號 ) .
好了 說了這麼多 , 開始使用.
- 1 . 保證電腦和手機在同一個區域網內 ( 因為要做埠對映 )
- 2 . 執行
MonkeyDev
重簽名程式 / 或者直接開啟以前重簽好的工程 , 無須Xcode
執行也可以 - 3 . 終端輸入
cycript -r 192.168.0.116:6666
- 換成你自己的手機
ip
地址 - 另外 , 請不要將應用程式放到後臺 , 會影響附加.
- 換成你自己的手機
出現以下 , Congratulations , you're good to go !
提示
使用 tab
鍵 , 寫程式碼可以補全 .
UIWindow.keyWindow()
檢視當前 Window
.
UIApplication sharedApplication
指令 : [UIApplication sharedApplication ]
可簡寫為 UIApp
自定義變數
指令 : 自己 var
一個物件 , 而後我們就可以使用 .
另外注意 : 只要 APP
程式沒有掛 , 這個變數是一直存在的 .
# + 地址可以直接使用
#
+ 物件地址 可以直接呼叫物件的方法
檢視檢視層級
UIWindow.keyWindow().recursiveDescription().toString()
隨便找一個 , 例如註冊頁面有一個 .text
為 +86
的 label
, 拿到其記憶體地址 ,
命令 :
#0x10b95d800.text = "hhh"
複製程式碼
顯示結果
基於這種直接修改程式記憶體的方式 , 大家可以自己去玩一玩 . 比如登入了修改一下錢包餘額 , 然後改一改 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
這些指令是 Monkey
在 MDConfig.plist
中額外封裝了自定義的 cy
源的 . 也就是說使用越獄環境原本的 cycript
外掛是沒有這些指令可用的 .
那麼我們閒著也是閒著 , 我們也來自己寫一個 cy
源來玩一下 ?
自定義 cy 指令
-
在我們的
monkey
工程主工程target
中新建一個空檔案, 我這裡取名lb.cy
. -
Build Phases
-Copy Files
, 引入這個檔案 -
在空檔案新增我們想自己定義的指令 . 可以參照 Monkey 原本提供的那兩個來寫 raw.githubusercontent.com/AloneMonkey…
這裡我寫了一些我常用的指令 , 比如獲取
APPID
/APPPATH
, 當前跟檢視 , 當前頁面 這些 , 我貼在下面供大家參考 , 也可以拿去直接用 . -
重新執行工程 .
-
輸入我們自定義的指令 例如 :
LBCurrentVC()
-
提示沒找到指令 , 因為我們還沒有引入 ,
monkey
那兩個在config
中會自動引入. -
@import lb
-
再次輸入
LBCurrentVC()
, 得到結果.
//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 Debug
和cycript
除錯介面是非常常用的手段 , 因此 , 希望大家能熟練掌握這些技巧 .