測(cè)試你的第一個(gè)選項(xiàng)
代碼已經(jīng)足夠了. 是時(shí)候測(cè)試一下腳本了.
與你在前面章節(jié)中用過(guò)的reload_script
腳本不同的是, 你將會(huì)用一個(gè)可替代的手段去重載這段腳本.
回到Xcode中并且創(chuàng)建一個(gè)symbolic
斷點(diǎn).
確保Breakpoint Navigator
選項(xiàng)欄是選中狀態(tài), 然后點(diǎn)擊左下角的+按鈕, 然后選擇Symbolic breakpoint
...或者使用快捷鍵? + Ctrl + \
也可以.
在Symbol
這一行中輸入dlopen
.
添加兩個(gè)actions
. 第一個(gè)action添加下面的指令:
br dis 1
在另一個(gè)action中添加你的bar
指令:
bar -n "-[NSUserDefaults objectForKey:]"
然后勾選Automatically continue after evaluating actions
.
在所有的事情都做完之后你的斷點(diǎn)看起來(lái)應(yīng)該是下面這個(gè)樣子:
你能弄清楚你剛剛在干什么吧?你在
dlopen
的C函數(shù)處創(chuàng)建了一個(gè)斷點(diǎn).如果我想在“my”代碼開始執(zhí)行的地方設(shè)置一個(gè)斷點(diǎn), 或者在逆向一個(gè)app之前, 這是一個(gè)在lldb中hook任何自定義指令的很好的方法.我不是使用
main
函數(shù)的粉絲, 而且主要的可執(zhí)行文件的main
標(biāo)簽在編譯之后的可執(zhí)行文件里可能被去掉.我們知道dlopen
的斷點(diǎn)一定會(huì)被觸發(fā)而且在我的代碼執(zhí)行之前也會(huì)被觸發(fā).那些
actions
都是干什么的呢?第一個(gè)action的作用是擺脫dlopen
斷點(diǎn).你不用刪除它, 你只用禁用它就可以了.這就是dlopen
被稱為公平的完美原因.一旦你設(shè)置了LLDB邏輯,就需要擺脫這個(gè)斷點(diǎn). 提到使用1
是因?yàn)檫@是你為這個(gè)session創(chuàng)建的第一個(gè)斷點(diǎn), 這個(gè)斷點(diǎn)在運(yùn)行一次之后就會(huì)被禁用.在那之后, 你在
NSUSerDefaults’s objectForKey:
方法處創(chuàng)建了一個(gè)沒有正則表達(dá)式的斷點(diǎn). 我們期望這個(gè)方法返回一個(gè)id或者nil, 那么讓我們來(lái)看一下讀到我們NSUserDefaults
里的RWDevCon
app是什么吧.構(gòu)建并運(yùn)行這個(gè)程序.
如果你還沒有深入的了解過(guò)這款app, 那么你將會(huì)得到很多nil值.這就意味著這個(gè)方法確實(shí)在這個(gè)app里讀到了一些代碼.
點(diǎn)擊任意一個(gè)工作空間, 調(diào)出詳細(xì)視圖控制器.
在你你繼續(xù)工作之前, 先用? + K
清空一下lldb窗口.
然后,點(diǎn)擊Add to my Schedule
同時(shí)注意觀察控制臺(tái)的輸出.
你會(huì)看到有一個(gè)對(duì)象在某個(gè)時(shí)間被加入到
NSUserDefaults
中.
添加有參數(shù)的選項(xiàng)
你已經(jīng)學(xué)了如何添加沒有參數(shù)的選項(xiàng).現(xiàn)在你要學(xué)習(xí)的是添加有參數(shù)的選項(xiàng)了.
接下來(lái)要學(xué)的選項(xiàng)是--module
選項(xiàng), 這個(gè)選項(xiàng)可以用來(lái)指定你的正則表達(dá)式在某個(gè)模塊范圍內(nèi)查詢.這與用來(lái)指定斷點(diǎn)所在模塊的-s
選項(xiàng)非常類似. 你可以回到第四章中去復(fù)習(xí)這些內(nèi)容.
在BreakAfterRegex.py
腳本文件中回到generateOptionParser
函數(shù)下面, 添加下面的代碼:
#1
parser.add_option("-m", "--module",
#2
action="store",
#3
default=None,
#4
dest="module",
help="Filter a breakpoint by only searching within a specified Module")
- 第一行, 你添加了一個(gè)新的選項(xiàng)
-m
和--module
給OptionParser實(shí)例. - 在上一個(gè)例子中
action
的值是"store_true"
, 這一次它的值是"store". 這就意味著這個(gè)選項(xiàng)期望能有一個(gè)參數(shù). - 第三行, 這個(gè)參數(shù)的默認(rèn)值是None.
- 第四行, 這個(gè)屬性的名字將會(huì)是
module
.
回到breakAfterRegex
函數(shù)中, 找到下面的代碼:
if options.non_regex:
breakpoint = target.BreakpointCreateByName(clean_command)
else:
breakpoint = target.BreakpointCreateByRegex(clean_command)
給這些函數(shù)添加一個(gè)options.module
參數(shù).
if options.non_regex:
breakpoint = target.BreakpointCreateByName(clean_command,
options.module)
else:
breakpoint = target.BreakpointCreateByRegex(clean_command,
options.module)
那么這么做有什么用呢?現(xiàn)在我們來(lái)打印一下BreakpointCreateByRegex
方法的簽名(signature).在lldb中輸入下面的內(nèi)容:
(lldb) script help (lldb.SBTarget.BreakpointCreateByRegex)
這將會(huì)輸出這個(gè)函數(shù)的少量文檔. 盡管這個(gè)方法沒有幫助文檔, 他會(huì)給出這個(gè)方法的簽名列表.
下面這個(gè)簽名值得討論一下:
BreakpointCreateByRegex(SBTarget self, str symbol_name_regex, str module_name=None) -> SBBreakpoint
注意一下最后一個(gè)參數(shù)module_name=None
. 事實(shí)上它是一個(gè)可選參數(shù), 如果你不給他傳參數(shù), 那么module_name
的默認(rèn)值是none.也就是說(shuō), OptionParser
在解析這個(gè)選項(xiàng)的時(shí)候, 你可以將options.module
作為參數(shù)傳到BreakpointCreateByRegex
方法中.
是時(shí)候來(lái)檢測(cè)一下效果了, 保存你編輯過(guò)的腳本. 回到Xcode中修改dlopen
符號(hào)斷點(diǎn). 用下面的代碼替換第二個(gè)action:
bar @objc.*.init -m RWDevCon
確保'Con'中的'C'是大寫的.
這會(huì)為所有被swift繼承了Objective-C對(duì)象的初始化方法里創(chuàng)建一個(gè)正則表達(dá)式斷點(diǎn).你過(guò)濾了一下只在RWDevCon
模塊中查詢這些斷點(diǎn).
運(yùn)行這個(gè)程序并檢查所有被swift繼承了的Objective-C對(duì)象.
快速看一眼輸出內(nèi)容.你將會(huì)看到觸發(fā)了很多__ObjC.NSEntityDescription
.這就說(shuō)明在swift中寫了很多CoreData
相關(guān)的邏輯, 對(duì)嗎?
完全正確.
清空屏幕然后在工作臺(tái)中列表的單元格上點(diǎn)擊, 然后看詳細(xì)視圖控制器上會(huì)彈出什么.
你將會(huì)得到一個(gè)所有被swift繼承了的Objective-C對(duì)象的列表. 找到叫做Person
的類.
把內(nèi)存地址復(fù)制到剪切板上.
在粘貼內(nèi)存地址之前, 我們先來(lái)踢去一下
Person
類實(shí)現(xiàn)的所有方法.既然它是一個(gè)Objective-C的子類, 那它就適用于你前面創(chuàng)建的指令.在lldb中輸入下面內(nèi)容:
(lldb) methods Person
這將通過(guò)Objective-C
的runtime提取出Person類實(shí)現(xiàn)的所有方法.
你可以試著在任何一個(gè)有效的Person
實(shí)例上執(zhí)行這些方法.
讓我們來(lái)賭一把.觀察Person
類實(shí)現(xiàn)的方法, 這里有一個(gè)方法名字叫fullName
.
記住這些信息, 因?yàn)檫@些信息會(huì)在最后的例子中用到.
現(xiàn)在你在bar
指令中添加了一個(gè)選項(xiàng), 這個(gè)選項(xiàng)允許你添加一個(gè)條件, 在函數(shù)的斷點(diǎn)執(zhí)行完之后進(jìn)行判斷. 如果是true
, 執(zhí)行就會(huì)停止.如果是false
,執(zhí)行就會(huì)繼續(xù).
你會(huì)把這個(gè)條件賦值給fullName
, 知道遇到“Ray Wenderlich"
才會(huì)停止.