lldb調(diào)試和chisel、DerekSelander、cycript的簡(jiǎn)單應(yīng)用

0x01 LLDB

Xcode 5發(fā)布之后,LLDB調(diào)試器已經(jīng)取代了GDB,成為了Xcode工程中默認(rèn)的調(diào)試器。它與LLVM編譯器一起,帶給我們更豐富的流程控制和數(shù)據(jù)檢測(cè)的調(diào)試功能。LLDB為Xcode提供了底層調(diào)試環(huán)境,其中包括內(nèi)嵌在Xcode IDE中的位于調(diào)試區(qū)域的控制面板,在這里我們可以直接調(diào)用LLDB命令。

基礎(chǔ)語(yǔ)法

<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
  • commandsubcommand:LLDB調(diào)試命令的名稱(chēng)。
  • action:執(zhí)行命令的操作。
  • options:命令的選項(xiàng)。
  • arguement:命令的參數(shù)。
  • []:表示命令是可選的。可有可無(wú)。

例:

breakpoint set -n test

command: breakpoint 表示斷點(diǎn)命令
action: set 表示設(shè)置斷點(diǎn)
option: -n 表示根據(jù)方法name設(shè)置斷點(diǎn)
arguement: test 表示方法名為test

基礎(chǔ)命令

  • 通過(guò)help命令查看相關(guān)lldb指令描述
(lldb) help
Debugger commands:
  apropos           -- List debugger commands related to a word or subject.
  breakpoint        -- Commands for operating on breakpoints (see 'help b' for
                       shorthand.)
  bugreport         -- Commands for creating domain-specific bug reports.
  command           -- Commands for managing custom LLDB commands.
  disassemble       -- Disassemble specified instructions in the current
                       target.  Defaults to the current function for the
                       current thread and stack frame.
  expression        -- Evaluate an expression on the current thread.  Displays
                       any returned value with LLDB's default formatting.
  frame             -- Commands for selecting and examing the current thread's
                       stack frames.
  • 通過(guò)apropos命令獲取具體命令的合法參數(shù)及含義
(lldb) apropos bt
The following commands may relate to 'bt':
  _regexp-bt -- Show the current thread's call stack.  Any numeric argument
                displays at most that many frames.  The argument 'all' displays
                all threads.
  bt         -- Show the current thread's call stack.  Any numeric argument
                displays at most that many frames.  The argument 'all' displays
                all threads.
  • 一些小指令
    c/continue: 繼續(xù)執(zhí)行
    po:expression命令選項(xiàng):-O 表示調(diào)用對(duì)象的discraption方法
    up: 往上走。
    n:?jiǎn)尾酵伦?。將子函?shù)當(dāng)做整體一步執(zhí)行。
    s: 單步運(yùn)行,遇到子函數(shù)會(huì)進(jìn)去。
    ni: 單步執(zhí)行會(huì)跳轉(zhuǎn)到指令內(nèi)部,匯編級(jí)別。
    finish:返回上層調(diào)用棧。
    bt: 查看當(dāng)前調(diào)用的堆棧信息。
    thread return:回滾。會(huì)改變代碼執(zhí)行流程。
    frame select 3:查看某個(gè)堆棧信息。
    image list:查看某塊列表。
    register read:讀取寄存器。
    register write:寫(xiě)入寄存器。
    Memory read:讀取內(nèi)存值。
    frame variable:可以打印當(dāng)前方法的所有參數(shù)變量。frame表示幀。
(lldb) frame select 3
frame #3: 0x000000010af838e8 UIKitCore`forwardTouchMethod + 353
UIKitCore`forwardTouchMethod:
    0x10af838e8 <+353>: movq   -0x68(%rbp), %rdi
    0x10af838ec <+357>: movq   0x865bc5(%rip), %rbx      ; (void *)0x00000001076a0010: objc_release
    0x10af838f3 <+364>: callq  *%rbx
    0x10af838f5 <+366>: movq   -0x70(%rbp), %rdi

斷點(diǎn)相關(guān)

  • 通過(guò)函數(shù)名設(shè)置斷點(diǎn)
(lldb) breakpoint set -name action1
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) br list
Current breakpoints:
1: name = 'action1', locations = 0 (pending)
  • 設(shè)置OC方法斷點(diǎn)。
breakpoint set -n "[ViewController action1:]"

可同時(shí)設(shè)置多個(gè)。就可以同時(shí)啟用或者禁用某一組。

(lldb)breakpoint set -n "[ViewController action1:]" -n "[ViewController action2:]" -n "[ViewController action3:]
(lldb) br list
 Current breakpoints:
 1: names = {'[ViewController action1]', '[ViewController action1]', '[ViewController action2]', '[ViewController action2]', '[ViewController action3]', '[ViewController action3]'}, locations = 3, resolved = 3, hit count = 0
 1.1: where = LLDB`-[ViewController action1] + 12 at ViewController.m:42, address = 0x000000010a48f60c, resolved, hit count = 0
 1.2: where = LLDB`-[ViewController action2] + 12 at ViewController.m:45, address = 0x000000010a48f61c, resolved, hit count = 0
 1.3: where = LLDB`-[ViewController action3] + 12 at ViewController.m:48, address = 0x000000010a48f62c, resolved, hit count = 0
  • 禁用或者啟用某一組。(也可以禁用1.1)
breakpoint disable 1
breakpoint enable 1
  • 刪除某一組。(只能以組為單位的刪除)
breakpoint delete 1

下面這樣只能達(dá)到禁用效果

breakpoint delete 1.1
  • 給項(xiàng)目中所有某個(gè)方法設(shè)置斷點(diǎn),適合自定義方法。
breakpoint set --selector viewDidLoad
  • 給某個(gè)文件或者某個(gè)方法設(shè)置斷點(diǎn)
br set --file ViewController.m --selector action1:
  • 給系統(tǒng)所有帶有某個(gè)字符串的方法設(shè)置斷點(diǎn)。
br set -r xxx
  • 刪除斷點(diǎn)
breakpoint delete
br delete
  • 內(nèi)存斷點(diǎn)。類(lèi)似于KVO,可以監(jiān)聽(tīng)某塊內(nèi)存區(qū)域值的變化。
//監(jiān)聽(tīng)p1對(duì)象中的name字段的內(nèi)存區(qū)域。
watchpoint set variable p1->_name

也可以直接表達(dá)為具體的地址。

p &p1->_name
watchpoint set expression 0x0000600002d21c68

進(jìn)階用法

  • command指令。通過(guò)指令給斷點(diǎn)處添加額外指令的實(shí)現(xiàn)。
(lldb) breakpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> frame variable
> DONE

當(dāng)截住斷點(diǎn)時(shí),便會(huì)打印相關(guān)的指令。


  • hook指令。同上,只是針對(duì)每個(gè)斷點(diǎn)生效。每次截住斷點(diǎn),打印配置好的指令。這里指令一般是通用命令。
target stop-hook add -o "frame variable"

補(bǔ)充:

lldb有一個(gè)初始化文件.lldbinit,通過(guò)在文件中配置相關(guān)指令,使用lldb截住的斷點(diǎn)都可以執(zhí)行相關(guān)指令。

操作步驟:

1、vim ~/.lldbinit,進(jìn)入配置文件。
2、進(jìn)入編輯模式,將target stop-hook add -o "frame variable"命令輸入其中。保存即可生效。
3、在Xcode代碼中設(shè)置斷點(diǎn),截住時(shí)便會(huì)打印frame variable的方法參數(shù)信息。

0x02 chisel

chiselfacebook利用llbd官方的api,用腳本實(shí)現(xiàn)了一些特定的功能。

安裝

通過(guò)Homebrew進(jìn)行安裝

brew update
brew install chisel

vim ~/.lldbinit修改lldb初始化文件,將chisel的腳本文件的替身地址加入其中

command script import /usr/local/Cellar/chisel/1.8.0/libexec/fblldb.py

重啟Xcode,即可使用。

常用命令概覽

  • 打印視圖層級(jí)
(lldb) pviews 0x170391b90
<UIView: 0x170391b90; frame = (0 20; 320 633); autoresize = W; layer = <CALayer: 0x170a23960>>
   | <UIImageView: 0x1701ffd00; frame = (0 -20; 320 568); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x170a235e0>>
  • 打印視圖上級(jí)
 pviews -u 0x17438fe50
  • 打印整個(gè)界面的層級(jí)關(guān)系
pviews
  • 查看響應(yīng)者鏈
presponder 0x150867780
  • 查看按鈕綁定的事件
(lldb) pactions 0x150867780
<WCAccountLoginControlLogic: 0x174a915d0>: onFirstViewRegester
  • 查看按鈕的繼承關(guān)系
(lldb) pclass 0x150867780
FixTitleColorButton
   | UIButton
   |    | UIControl
   |    |    | UIView
   |    |    |    | UIResponder
   |    |    |    |    | NSObject
  • 列出某個(gè)對(duì)象的方法列表
(lldb) pmethods 0x14f787f20
Class Methods:
No methods were found
  • 打印對(duì)象的所有屬性及屬性值。
 pinternals 0x136a11200
  • 根據(jù)控制器的名稱(chēng)找到這個(gè)名稱(chēng)當(dāng)前所有的地址。
(lldb) fvc WCAccountMainLoginViewController
0x136a11200 WCAccountMainLoginViewController
//根據(jù)地址顯示控制器信息。
(lldb) fvc -v 0x136a11200
Found the owning view controller.
<WCAccountMainLoginViewController: 0x136a11200>
  • 快速定位到某個(gè)組件。

taplogcontinue,點(diǎn)擊頁(yè)面的某個(gè)控件,便會(huì)自動(dòng)打印控件的相關(guān)信息。

(lldb) taplog 
Process 2388 resuming
<WCUITextField: 0x136685ee0; baseClass = UITextField; frame = (10 0; 217 44); text = ''; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x170a5fdd0>; layer = <CALayer: 0x1708320e0>>
  • 閃爍一次控件,方便開(kāi)發(fā)者定位控件。
flicker 0x1378b2880
  • 動(dòng)態(tài)查看控件相關(guān)信息.

enter之后,輸入相關(guān)的指令可查看對(duì)應(yīng)的一些控件信息,并且會(huì)給控件更改backgroundColor來(lái)標(biāo)注控件。

(lldb) vs 0x1378b2880 
Use the following and (q) to quit.
(w) move to superview   找到父控件。
(s) move to first subview   找到第一個(gè)subview
(a) move to previous sibling  向前移動(dòng)
(d) move to next sibling  向后移動(dòng)。
(p) print the hierarchy  打印層級(jí)

0x03 DerekSelander

安裝

點(diǎn)擊DerekSelander下載腳本文件。通過(guò)在.lldbinit中完成配置即可使用。

常見(jiàn)命令

  • 查找UIImageView的對(duì)象。
search UIImageView   
<_UINavigationBarBackIndicatorView: 0x1703e0d00; frame = (8 11.5; 13 21); alpha = 0; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x170837c00>>
<UIImageView: 0x1703e0900; frame = (0 0; 130 47); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x17023e420>>
<UIImageView: 0x1743ebf00; frame = (0 501.5; 320 2.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x17522a3a0>>
<UIImageView: 0x1701fe500; frame = (0 64; 320 0); hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x170635ba0>>
  • 列出某個(gè)類(lèi)所有的方法和屬性。(方法后面的地址為imp的地址。)
(lldb) methods 0x174897200
<WCAccountLoginControlLogic: 0x174897200>:
in WCAccountLoginControlLogic:
    Properties:
        @property (readonly) unsigned long hash;
        @property (readonly) Class superclass;
        @property (readonly, copy) NSString* description;
        @property (readonly, copy) NSString* debugDescription;
    Instance Methods:
        - (void) startLogic; (0x102565bdc)
        - (void) stopLogic; (0x102566144)
        - (void) onOneClickLoginSwitchAccount; (0x1025669d4)
        - (void) onOneClickLoginProblem; (0x102566a08)
        - (void) onOneClickLoginGoToSecurityCenter; (0x102566a40)
        - (void) onOneClickLoginGoToHelpCenter; (0x102566a78)
  • 通過(guò)內(nèi)存地址給方法下斷點(diǎn)。
(lldb) b -a 0x1025662c0
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol142407$$WeChat, address = 0x00000001025662c0
  • 通過(guò)sbt查看函數(shù)調(diào)用棧、并得到去符號(hào)后的信息。
(lldb)sbt
frame #0 : 0x1025662c0 WeChat`-[WCAccountLoginControlLogic onFirstViewLogin] 
frame #1 : 0x187970d34 UIKit`-[UIApplication sendAction:to:from:forEvent:] + 96
frame #2 : 0x1030de4e0 WeChat`___lldb_unnamed_symbol189001$$WeChat ... unresolved womp womp + 460
frame #3 : 0x187959e48 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 612

0x04 cycript

cycript是一個(gè)允許開(kāi)發(fā)者使用OCJS結(jié)合語(yǔ)法查看及修改運(yùn)行時(shí)App內(nèi)存信息的工具,通過(guò)它我們可以注入程序、查看程序界面、調(diào)用程序函數(shù)驗(yàn)證自己的想法等。同lldb不同、當(dāng)開(kāi)始使用cycript時(shí)它會(huì)常駐內(nèi)存,所以也更方便我們?nèi)?dòng)態(tài)調(diào)試。

配置安裝

1、在~/.zshrc文件中鏈接bash_profile.

source .bash_profile

2、在官網(wǎng)中下載SDK文件。在bash_profile中配置dycript。

3、安裝完成,通過(guò)cycript命令即可進(jìn)入cycript界面了。

界面調(diào)試

通過(guò)MonkeyDev運(yùn)行重簽應(yīng)用,通過(guò)查看自動(dòng)生成的動(dòng)態(tài)庫(kù)文件,我們可得到對(duì)應(yīng)的端口號(hào)。

查看手機(jī)IP,通過(guò)cycript -r 192.168.0.105:6666連接手機(jī)。當(dāng)出現(xiàn)如下界面,便可以自由調(diào)試了。在這里你可以編寫(xiě)OCJS語(yǔ)言、達(dá)到對(duì)應(yīng)用程序的調(diào)試。

下面展示一些簡(jiǎn)單的調(diào)試命令:

  • 打印當(dāng)前UIApplication的信息。
cy# [UIApplication sharedApplication]
#"<UIApplication: 0x10ba038e0>"

cy# UIApp
#"<UIApplication: 0x10ba038e0>"
  • 定義一個(gè)變量keywindow來(lái)保存當(dāng)前的window,接著便可以通過(guò)變量keywindow直接打印有關(guān)window的屬性。
cy# var keyWindow = UIWindow.keyWindow()
#"<iConsoleWindow: 0x10b856e70; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x282910540>; layer = <UIWindowLayer: 0x282656340>>"

cy# keyWindow.rootViewController
#"<MMUINavigationController: 0x10c165e00>"
  • 通過(guò)#,查看內(nèi)存地址的詳細(xì)信息。
cy# #0x10c165e00
#"<MMUINavigationController: 0x10c165e00>"
  • 拿到某個(gè)對(duì)象的成員變量的key和value
cy# *#0x10c165e00

只獲取key

[i for(i in *#0x157f64480)]
  • 查看某個(gè)類(lèi)的層級(jí)關(guān)系;
#0x157f64480.recursiveDescription()

格式化輸出一波:

#0x157f64480.recursiveDescription().toString()
  • 查看當(dāng)前界面所有為某個(gè)類(lèi)的對(duì)象
cy# choose(UIButton)
[#"<FixTitleColorButton: 0x157f64480; baseClass = UIButton; frame = (20 18; 130 47); clipsToBounds = YES; opaque = NO; autoresize = RM; layer = <CALayer: 0x170a2f100>>",#"<UIButton: 0x157f6b6d0; frame = (234 20; 86 49); opaque = NO; autoresize = LM; layer = <CALayer: 0x1748284e0>>",#"<FixTitleColorButton: 0x1590af6e0; baseClass = UIButton; frame = (170 18; 130 47); clipsToBounds = YES; opaque = NO; autoresize = LM; layer = <CALayer: 0x170e2ae80>>"]
  • 動(dòng)態(tài)修改某個(gè)控件的值
#0x10b1caa00.text="123123"
"123123"

高級(jí)用法:

因?yàn)槲覀兪褂玫?code>monkeyDev自動(dòng)鏈接了兩個(gè)cy文件,在文件中實(shí)現(xiàn)了對(duì)某些方法的自定義函數(shù)名以方便我們快捷log。


猴神配置的cy文件的鏈接:
MS.cy
md.cy
下面簡(jiǎn)單列舉以下文件提到的一些命令:

  • 打印視圖層級(jí)和控制器層級(jí)。
pviews() 
pvcs
  • 打印對(duì)象所綁定的方法。
cy# pactions(#0x1053163b0)
"<CustomViewController: 0x1053133f0> showChangeLog:"
  • 打印響應(yīng)鏈
cy# rp(#0x1053163b0)
`<UIButton: 0x1053163b0; frame = (127.5 175; 120 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x1c4039aa0>>
<UIView: 0x1053205d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x1c403bd80>>
<CustomViewController: 0x1053133f0>
<UIWindow: 0x105313b60; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x1c425bae0>; layer = <UIWindowLayer: 0x1c40394e0>>
<UIApplication: 0x105406120>
<AppDelegate: 0x1c002b1a0>`
自定義cy文件,實(shí)現(xiàn)自定義命令

通過(guò)借鑒猴神的自定義命令的實(shí)現(xiàn),我們可以完成一些常規(guī)命令的自定義,方便使用。

1、新建名為dyTest.cy文件。編寫(xiě)相關(guān)命令。

更改文件的類(lèi)型

2、定義指令名和方法實(shí)現(xiàn)。

//匿名函數(shù)自執(zhí)行表達(dá)式
(function(exports){


//這種直接賦值的變量定義適用于不會(huì)發(fā)生值改變的情況。因?yàn)檫@樣在程序加載的時(shí)候變量的值就固定了。
APPID = [NSBundle mainBundle].bundleIdentifier,
APPPATH = [NSBundle mainBundle].bundlePath,

//如果值有變化,可借鑒于函數(shù)實(shí)現(xiàn)的方式。
DyRootvc = function(){
return UIApp.keyWindow.rootViewController;
};


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

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

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

return currentVC;
};


DyCurrentVC = function(){
return DyGetCurrentVCFromRootVc(DyRootvc());
};

})(exports);
//匿名函數(shù)自執(zhí)行表達(dá)式

3、copy file 文件,保證其運(yùn)行時(shí)能在Frameworks文件夾中。


4、實(shí)踐自定義命令。

cy# @import dyTest
{}
cy# APPID
@"com.WeChat.signXcode.dyTest"
cy# APPPATH
@"/private/var/mobile/Containers/Bundle/Application/D4AA77EB-FC58-4189-84CF-114A9EA2BB1E/dyTest.app"
cy# DyRootvc()
#"<MMUINavigationController: 0x14dfd9730>"
cy# DyKeyWindow()
#"<iConsoleWindow: 0x14f005a80; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x170849f30>; layer = <UIWindowLayer: 0x174420aa0>>"
cy# DyGetCurrentVCFromRootVc(#0x14dfd9730)
#"<WCAccountLoginFirstViewController: 0x14ded9e00>"
cy# DyCurrentVC()
#"<WCAccountLoginFirstViewController: 0x14ded9e00>"

參考鏈接

The LLDB Debugger
chisel-github
MonkeyDev
cycript
DerekSelander

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容