在上篇文章從fishhook看runtime,hook系統C函式中已經提到了利用LLDB的部分命令。在我們玩逆向的時候在大多數時候其實是拿不到原始碼的。所以瞭解一些LLDB來輔助我對別人APP的學(破)習(壞),是非常有必要的。
自從開始玩逆向,總是會有一些大佬給我發一些轉賬資訊(為什麼不是發紅包?紅包金額有限制,拿不出手),金額還挺大。
都是類似於這樣的。
那麼道友們想不想都收到這樣的紅包呢?看完這篇文章,你就可以。如果沒有人轉給你,評論區告訴我,我給你轉,說到做到!
廢話不多說,這篇文章內容很簡單,非常容易理解,但是需要記的東西比較多,理財師強烈推薦點個小心心,以備不時之需 O(∩_∩)O哈哈~。
今天的DEMO也比較簡單,可以在點選這裡下載到: LLDB
本文將介紹的內容如下:
- LLDB
- 自制LLDB指令碼
- chisel
- DerekSelander-LLDB
- 實操竄改微信紅包
一、LLDB
預設內建於Xcode中的動態除錯工具。標準的 LLDB 提供了一組廣泛的命令,旨在與老版本的 GDB 命令相容。 除了使用標準配置外,還可以很容易地自定義 LLDB 以滿足實際需要。
命令格式如下
<command> [<subcommand> [<subcommand>...]] + <action> + [-options [option-value]] + [argument [argument...]]
複製程式碼
[]
表示命令是可選的,可以有也可以沒有<command>
(命令)和<subcommand>
(子命令):LLDB
除錯命令的名稱。命令和子命令按層級結構來排列:一個命令物件為跟隨其的子命令物件建立一個上下文,子命令又為其子命令建立一個上下文,依此類推。<action>
:我們想在前面的命令序列的上下文中執行的一些操作。<options>
:行為修改器(action modifiers)。通常帶有一些值。<argument>
:根據使用的命令的上下文來表示各種不同的東西。
The full lldb command names are often long, but any unique short form can be used. Instead of "breakpoint set", "br se" is also acceptable. 一般lldb的命令會很長,但是隻要能夠想出足夠斷,並且又能代表唯一性的縮寫,那麼縮寫命令也是同一生效的如:
breakpoint set
==br se
LLDB的所有命令在 LLVM官網或者Apple官網 都可以查詢到。筆者會在這篇文章中列舉一些比較常用的命令。
1、斷點設定
命令名稱 | 命令參樣例 |
---|---|
使用名稱設定斷點 | breakpoint set --name main |
使用記憶體地址設定斷點 | breakpoint -a 0xXXXXXXXX |
刪除斷點 | breakpoint delete 1 |
使斷點失效/生效 | breakpoint disable/enable 2 |
檢視所有斷點 | breakpoint list |
OC中所有命名中包含為Test4的方法設定斷點 | breakpoint set -r Test4 |
隨意上兩張樣式圖:
附帶一張官網截圖,這些命令都可以在 這 找到
2、斷點命令
命令名稱 | 命令參樣例 |
---|---|
給某一個斷點增加命令 | breakpoint command add 1 |
檢視所有斷點命令 | breakpoint command list |
刪除斷點命令 | breakpoint command delete 1 |
使某個斷點命令生效/失效 | breakpoint command enable/disable |
給某一個斷點增加命令 | breakpoint command delete |
給某一處斷點加上一段程式碼,使其每次被斷住的時候都可以自動執行終端程式碼,如下圖:
3、執行程式碼
expression
就是執行程式碼的命令,也就是常用的p
,按照官網所說的縮寫唯一性原則e
、expr
,也是可以的。
如圖:
4、檢視堆疊,流程控制
命令名稱 | 命令參樣例 |
---|---|
檢視當前所有堆疊 | bt |
返回上一步堆疊 | up |
檢視某一條堆疊 | frame select 1 |
檢視當前堆疊的引數 | frame variable |
堆疊回滾到上一條 | thread return |
程式繼續執行 | c |
單步下一步 | n |
進入下一個函式(方法) | s |
彙編級別的單步下一步 | ni |
彙編級別的進入下一個函式(方法) | si |
同樣是一張樣式圖:
5、記憶體斷點
某個屬性地址只要有改變,就觸發斷點。相當於對某個屬性設定了KVO。
命令名稱 | 命令參樣例 |
---|---|
直接觀察一個變數 | watchpoint set variable global_var |
直接觀察一個變數的地址 | watchpoint set expression -- 0xxxxxx |
刪除斷點 | watchpoint delete 1 |
使斷點失效/生效 | watchpoint disable/enable 2 |
檢視所有斷點 | watchpoint list |
5、庫檔案image
命令名稱 | 命令參樣例 |
---|---|
檢視工程中使用的庫(包括MachO自己) | image list |
查詢可執行檔案或共享庫的原始地址 | image lookup --address 0x0000000100000de0 |
輸出NSURL的成員變數及屬性資訊。 | image lookup --type NSURL |
匯出可執行檔案和共享庫的所有符號表 | image dump symtab |
7、HOOK每個斷點
給每個斷點,都執行一段程式碼。
命令名稱 | 命令參樣例 |
---|---|
增加一個HOOK | target stop-hook add -o "frame variable" |
直接所有HOOK | target stop-hook list |
刪除HOOK | target stop-hook disable 1 |
使HOOK失效/生效 | target stop-hook disable/enable 2 |
8、暫存器&&記憶體
命令名稱 | 命令參樣例 |
---|---|
顯示當前執行緒所有暫存器的值 | register read --all |
將0x01寫入x2暫存器 | register write x2 0x01 |
讀取記憶體0x0002A8A2D中的的值 | memory read 0x0002A8A2D (縮寫x 0x0002A8A2D ) |
9、支援Python
例如:
script print "Here is some text"
複製程式碼
二、自制LLDB指令碼
1、.lldbinit
LLDB本質上就跟一個程式(或者說程式)一樣,每次啟動LLDB的時候都會主動載入一個初始化檔案,這個檔案就是.lldbinit
,他的地址位於根目錄下:
~/
複製程式碼
如果你的根目錄沒有這個檔案,那就只用touch
建立一個吧
// 建立.lldbinit
touch ~/.lldbinit
// 寫入
vi ~/.lldbinit
// 檢視
cat ~/.lldbinit
複製程式碼
在.lldbinit
中寫入如下程式碼
target stop-hook add -o "frame variable"
複製程式碼
重啟Xcode,執行工程,在任意一個地方加上斷點。
會發現當斷點斷住的時候,自動執行了frame variable
讀到這就有一個很有意思的事情了:
.lldbinit
可以幫我們預載入部分命令,LLDB
又支援Python語法,那麼是不是可以將部分Python的程式碼封裝起來,再利用.lldbinit
的機制,進而就可以實現用我們自己的封裝好的程式碼,讓我們更方便的使用LLDB
?
說幹就幹。
2、指令碼實操
之前我們使用過命令image list
命令檢視,檢視App執行後再記憶體中的首地址(ASLR
),這個地址其實是加上了pagezero
的值,其實使用命令image list -o
可以直接檢視ASLR
,如圖:
import lldb
import re
# 獲取ASLR偏移地址
def fy_get_ASLR(debugger, command, result, internal_dict):
# 獲取'image list -o'命令的返回結果
interpreter = lldb.debugger.GetCommandInterpreter()
returnObject = lldb.SBCommandReturnObject()
interpreter.HandleCommand('image list -o', returnObject)
output = returnObject.GetOutput();
# 正則匹配出第一個0x開頭的16進位制地址
match = re.match(r'.+(0x[0-9a-fA-F]+)', output)
if match:
print match.group(1)
else:
return None
# And the initialization code to add your commands
def __lldb_init_module(debugger, internal_dict):
# 'command script add fy_get_ASLR' : 給lldb增加一個'fy_get_ASLR'命令
# '-f lldbPyDemo.fy_get_ASLR' : 該命令呼叫了lldbPyDemo檔案的fy_get_ASLR函式
debugger.HandleCommand('command script add fy_get_ASLR -f lldbPyDemo.fy_get_ASLR')
print 'The "fy_get_ASLR" python command has been installed and is ready for use.'
複製程式碼
同樣的,每次都主動載入lldbPyDemo.py
也有點煩,將其路徑加入.lldbinit
中,即可實現每次自動載入。
LLDB所有開放出來的介面都可以在官方網站中找到,有興趣的同學可以研究研究。
三、chisel
Chisel is a collection of LLDB commands to assist in the debugging of iOS apps. Chisel是一個使用者Debug iOS Apps 的LLDB命令集合
以上介紹來自chisel官網。
由於其支援brew
,安裝使用的方法很簡單:
brew update
brew install chisel
複製程式碼
如果本地沒有.lldbinit
檔案,先在根目錄建立再開啟,否則直接開啟
// 建立.lldbinit檔案
touch .lldbinit
// 開啟.lldbinit檔案
open .lldbinit
複製程式碼
然後在.lldbinit
檔案中追加如下命令
command script import /path/to/fblldb.py
複製程式碼
重啟Xcode
即可使用。
以下挑了一些實用的命令:
命令名稱 | 命令描述 | iOS | OS X |
---|---|---|---|
pviews | 列印當前KeyWindow的所有View | Yes | Yes |
pvc | 列印當前KeyWindow的所有VC | Yes | No |
fv | 根據正規表示式列印查詢並列印View | Yes | No |
fvc | 根據正規表示式列印查詢並列印VC | Yes | No |
show/hide | 在不需要continue的情況下顯示/隱藏View或者Layer | Yes | Yes |
bmessage | 在類的方法或例項的方法上設定符號斷點,就算沒有實現顯示的實現該剛發,也會觸發。如viewWillAppear這個方法,在當前控制器中沒有實現它,但是又想在呼叫它的時機觸發中斷。 | Yes | Yes |
wivar | 相當於watchpoint | Yes | Yes |
下圖是pviews和pvc的用例圖:
四、DerekSelander-LLDB
DerekSelander-LLDB
同chisel
一樣是一個LLDB的指令碼集合,大部分功能一致,但DerekSelander-LLDB
有一個非常好用的功能:
sbt
:檢視當前堆疊,並且儘可能的恢復符號表!
這就很牛逼了,要知道在我們逆向的過程中,大部分研究的APP都是已經去符號的!
[站外圖片上傳中...(image-57c79-1554654483282)]
具體使用同樣也可以參照官網: DerekSelander-LLDB
DerekSelander-LLDB
的安裝過程沒有chisel
那麼花哨,不需要用到brew,安裝過程如下(官網複製的,我就不翻譯了):
1、 To Install, copy the lldb_commands folder to a dir of your choosing.
2、 Open up (or create) ~/.lldbinit
3、 Add the following command to your ~/.lldbinit file: command script import /path/to/lldb_commands/dslldb.py
複製程式碼
五、實操竄改微信紅包
LLDB既然這麼好用,那麼我們就利用LLDB來繼續分析一下我們可愛的微信,O(∩_∩)O哈哈~。
利用之前文章iOS逆向(4)-程式碼注入,竊取微信密碼講到的方法,直接利用Xcode將微信執行在手機上。
隨意讓一個小夥伴自己的微訊號發一個最大的紅包(0.01元),進入聊天頁面如下圖:
在這個地方斷住程式,進入LLDB的除錯頁面。
pviews
,會發現終端列印出了非常多的檢視結構。直接搜尋紅包金額0.01
,找到對應的Label
,如下圖:從上圖可以發現顯示金額的控制元件是MMUILabel
,很像一個UILabel,而且地址為0x10e6c7d00
。
在根據LLDB的p
命令執行更改文字的程式碼,如下:
p ((UILabel *)0x10e6c7d00).text = @"¥88888.88"
複製程式碼
然後程式繼續執行。可以看到微信的金額已經被改!此時的金額只是一個靜態被改變的字串而已,實際上並不會讓我們多一分錢或者少一分錢。
在普通的生活中,逆向其實是一件非常有意思的事情,在增加自己的知識面的同時,也能給予我們很多的歡樂,想想看這樣一張截圖往朋友圈一放是不是賊有面子。哈哈,也許你的朋友圈中各種紅包轉賬截圖也是這樣來的呢?尤其是經常發這樣的轉賬資訊的代購們。
六、總結
這片文章的內容其實非常簡單,首先介紹了一下LLDB的一下基本用法,從而得知其可以支援Python語法,又有.lldbinit
檔案可以幫我們自動載入指令碼,所有就有了一個簡單的LLDB指令碼案例,之後又引出facebook出品的Chiesl
和DerekSelander-LLDB
兩個指令碼集合。最後就是利用LLDB進行一些簡單的UI分析和執行簡單的程式碼了。
但是,每次使用LLDB都需要斷住程式,體驗不是很好。那是不是有一種能力,可以讓程式在正常執行的時候,我們也可以對APP進行實時的動態分析呢?
答案是肯定的,神器Cycript
就是這麼一個存在,在下一篇文章中,將會圍繞Cycript
講解一些逆向工程中非常非常重要的內容!