iOS逆向工程之Hopper+LLDB調試第三方App,hopperlldb
LLDB是Low Level Debugger的簡稱,在iOS開發的調試中LLDB是經常使用的,LLDB是Xcode內建的動態調試工具。使用LLDB可以動態調試你的應用程式,如果你不做其他的額外處理,因為debugserver缺少task_for_pid許可權,所以你只能使用LLDB來調試你自己的App。那麼本篇部落格中就要使用LLDB來調試從AppStore下載安裝的App,並且結合著Hopper來分析第三方App內部的結構。LLDB與Hopper的結合,會讓你看到不一樣的東西,本篇部落格就會和你一起領略LLDB與Hopper的魅力。
一、SSH的USB串連--usbmuxd
之前我們ssh串連iOS裝置是通過區域網路也就是WiFi來串連的,當網路環境不好的時候輸入個命令列都卡,所以我們需要一種更快的訪問iOS裝置的方式,那就是使用USB串連了。本篇部落格中無論是SSH串連iOS裝置還是LLDB串連iOS裝置,我們都使用USB的方式進行裝置的訪問,這樣速度就快的不行不行的了。本篇部落格的第一部分就是介紹如何使用USB進行裝置的SSH串連,這部分也是本篇部落格的基礎,不過內容還算是簡單。
1、擷取usbmuxd
usbmuxd雖然目前最新的版本是1.1.0,但是1.1.0版本和1.0.9版本僅支援Linux系統,也就是說我們的Mac還是得下載v1.0.8的版本,(usbmuxd-v1.0.8)。下載完後,將下載的檔案進行解壓,內容如下所示:
2.使用usbmuxd串連iOS越獄裝置(1) 使用usbmuxd轉寄介面
切換到上述檔案夾下的python-client目錄下,執行下方的命令,將iOS上的22連接埠轉寄到當前裝置的2221連接埠,如下所示。
./tcprelay.py -t 22:2221
下方是執行上述命令的結果:
(2) 使用ssh串連到越獄裝置
ssh root@localhost -p 2222
上述命令就是ssh串連的命令 -p後邊緊跟的是上述轉寄的連接埠,執行上述命令後,結果如下:
使用usbmuxd就可以通過USB來串連我們的iOS越獄裝置了,下方的LLDB串連iOS裝置也是通過USB串連的。具體請看下文。
二、配置debugserver
在做iOS開發時,在Mac上輸入LLDB的命令就可以控制iOS端的App,是因為在我們iOS用戶端中有一個debugserver伺服器。debugserver專門用來串連Mac端的LLDB用戶端,接收LLDB所提供的命令,並且進行相應的執行。如果你的iOS裝置進行過真機調試的話,裝置中就會被安裝上debugserver, 不過該debugserver只能用來調試你自己的相關應用。如果想要調試從AppStore中擷取的App的話,那麼我們需要對iOS裝置上的debugserver進行處理。該部分就是要處理我們的debugserver。
1.擷取debugserver
首先我們得找到iOS裝置中debugserver,並將其拷貝到Mac上進行處理,下方就是位於/Developer/usr/bin目錄下的debugserver。此debugserver只支援調試我們自己的App, 如果需要調試其他人的App的話,需要對此debugserver進行處理,處理過程見下文。
2.對debugserver進行瘦身
lipo -thin arm64 debugserver -output debugserver
進入到到Mac中debugserver所在的目錄下執行上述命令即可,-thin後方填寫你的測試機相應的ARM架構即可,因為我的測試機是iPhone 6 Plus, 是arm64的架構,所以此處填的參數是arm64, 如果你的是iPhone5的裝置,那麼就是armv7s了。
3.給debugserver添加task_for_pid許可權
給debugserver添加task_for_pid許可權後,我們就可以使用LLDB調試其他App了。此部分我們需要一個儲存配置資訊的xml檔案,該檔案的內容如下。你可以將下下方的文本進行拷貝,然後儲存成ent.xml即可。
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>com.apple.springboard.debugapplications</key> <true/> <key>get-task-allow</key> <true/> <key>task_for_pid-allow</key> <true/> <key>run-unsigned-code</key> <true/></dict></plist>
在給debugserver符許可權時,我們需要使用到ldid命令,如果你的Mac上沒有安裝ldid命令,那麼請用brew進行install。執行下方的命令列就可以給我們的debugserver賦上task_for_pid許可權。需要注意的是-S與ent.xml檔案名稱中是沒有空格的。
ldid -Sent.xml debugserver
下方就是我們處理debugserver的步驟,如下所示:
4、將debugserver拷貝到iOS裝置中
最後一步就是將處理好的debugserver拷貝到我們的越獄裝置中,並且給debugserver賦上可執行檔許可權。因為/Developer/usr/bin目錄下的debugserver是唯讀,所以你不能將處理好的debugserver拷貝到上述檔案,你要將處理好的debugserver拷貝到/usr/bin/目錄下(當然此處我們藉助IFunBox進行檔案的拷貝)。
將debugserver拷貝到/usr/bin目錄下後,執行下方的賦許可權的命令,將可執行檔許可權賦給debugserver,如下所示:
chmod +x debugserver
賦完許可權後,你就可以使用debugserver命令來開啟debuserver了,如下所示:
三、debugserver的開啟與LLDB的串連1.開啟debugserver
在越獄裝置中,我們就可以通過下方命令列來開啟debugserver了,我們此處以調試App為例。下方的命令就是啟動debugserver來監聽來自任何IP地址的接入,iOS裝置的接入連接埠是12345,所要調試的App為“WeChat”。命令如下:
debugserver *:12345 -a "WeChat"
在我們iOS裝置上執行上述命令的效果如下所示,執行完上述命令後,我們的iOS裝置就會等待Mac終端LLDB的接入。
2.LLDB串連debugserver
LLDB串連debugserver可以使用WIFI進行串連,可是WIFI是不穩定的,而且特別的慢,所以此處我們要使用usbmuxd進行LLDB和debugserver的串連。
(1)進行連接埠的轉寄
和第一部分中的內容相同,我們使用usbmuxd進行連接埠的轉寄,將上述的“12345”連接埠對接到Mac本地的某個連接埠,此處我們使用“12345”連接埠。進入到usbmuxd-1.0.8目錄下的python-client下執行下方的命令。
./tcprelay.py -t 12345:12345
具體操作步驟如下所示:
(2)Mac端LLDB的接入
進行連接埠轉寄後,接下來我們就開始進入lldb模式,然後進行debugserver的串連了。首先在terminal上輸入lldb命令,然後輸入下方的地址進行串連。因為我們使用usbmuxd進行了連接埠的轉寄,因此可以使用本地的環回測試地址來進行debugserver的串連。
process connect connect://127.0.0.1:12345
下方是串連後的結果,LLDB與debugserver建立完成後,我們就可以使用lldb來調試這個應用了。
四、Hopper + LLDB
在上篇部落格《iOS逆向工程之給WeChat脫殼》 我們已經給進行可脫殼處理,所以使用Hopper進行處理是沒有問題的。此部分我們就要將Hopper與LLDB結合在一起發揮其雙劍合璧的作用。該部分也算是本篇部落格中實戰的一部分。
1.查看線程中的WeChat
LLDB串連上debugserver後,我們首先使用下方的命令來查看當前進程中的所有模組。從這些輸出資訊中我們能找到“WeChat”這個進程在虛擬記憶體相對於模組基地址的位移量。
image list -o -f
lldb串連debugserver後,執行上述命令輸出的部分結果如下所示。下方中,第一個就是“WeChat”程式的相關資訊。左邊紅框就是ASLR位移量(隨機位移量),ASLR位移量其實就是虛擬記憶體的其真實位址,相對於模組基地址的位移量。右邊紅框中的地址就是位移後的地址。
在介紹地址這塊的東西是先熟悉一下下方的兩個概念:
- 模組在記憶體中的起始地址----模組基地址
- ASLR位移 ---- 虛擬記憶體起始地址與模組基地址的位移量
從下方的輸出結果我們可以知道:ASLR位移量 = 0x5b000, 模組位移後基地址 = 0x5f000
下方是使用Hopper開啟的解密後的的安裝包,其起始地址從中我們可以看出是0x4000, 這個地址就是模組位移前的地址,也就是模組在虛擬記憶體中的起始地址。從Hopper中我們可以知道:模組位移前的基地址=0x4000
從上面兩組資料我們可以得出:
模組位移後的基地址(0x5f000)= ASLR位移量(0x5b000)+ 模組位移前基地址(0x4000)
上面這個公式是尤為重要的,因為Hopper中顯示的都是“ 模組位移前基地址”,而LLDB要操作的都是“模組位移後的基地址”。所以從Hopper到LLDB,我們要做一個地址位移量的轉換。這個在下方會多次用到。當然,有一點需要注意的是Hopper與LLDB所選擇的AMR架構的位元得一致,要麼是32位,要麼都是64位,如果位元不匹配的話,那麼計算出來的記憶體位址肯定是不對的。
2、使用LLDB給登入添加斷點(1)、加斷點前的分析
“斷點”這個東西在iOS開發中可謂是經常使用的東西,接下來我們要做的就是給在點擊登入進行頁面跳轉時添加一個斷點。就是點擊左邊的登入按鈕往右邊頁面跳轉時添加一個斷點。我們暫且將斷點添加在右邊頁面的初始化方法中。
WCAccountPhoneLoginControlLogic”(我們可以翻譯一下英文,大概意思就是“手機帳號登入控制邏輯”)的類,從這個類的名字中我們不難推斷出該類極有可能就是我們要尋找的“手機帳號登入”頁面。
經過上述分析後,我們決定要給該類的“initWithData”(這肯定是個初始化方法)使用LLDB添加斷點。
(2)、定位斷點地址
經過第一步找到添加斷點的類中的方法後,接下來我們要計算出該方法的記憶體位址,然後使用LLDB給該地址添加斷點。通過Hopper我們很容易定位到上述的“initWithData:”方法,的位置,如下所示。下方中這個帶“星號”的地址就是“initWithData:”方法位移前的基地址。根據上面的公式我們很容易就可以計算出該方法“位移後的基地址”也就是真正的記憶體位址。演算法如下所示:
initWithData記憶體位址 = 0x1304b60 + 0x5b000(ALSR位移) = 0x135FB60
(3)、添加斷點
使用下述命令,給上述地址添加斷點。斷點添加後,點擊登入按鈕就會跳轉到“手機號登入”頁面就會執行該斷點,下方的紅框中就是“斷點”執行後的效果。從下方中我們可以看出該斷點的編號是1,Breakpoint後方就是斷點編號,該編號會在操作斷點是會用到,下方會給出執行個體。
br s -a 0x135FB60
(4)、斷點的逐步執行(ni, si)
你可以通過nexti (簡寫:ni)和stepi (簡寫:si)來進行單步的調試。ni遇到跳轉不會進入到跳轉中去,而si則會跳轉到相應的分支中去。下方就是通過si和ni進行單步調試的效果。
(5) 放開執行該斷點(c)
命令c可以執行該斷點, 上面這種情況如果執行c命令,因為只有一個斷點,該斷點執行後,就會跳轉到“手機號登陸頁面”。
(6)斷點的禁用和開啟
上面也有提到,上述建立的斷點的編號是1,我們要對該斷點進行禁用和開啟操作,具體命令如下所示:
br dis 1 -- 禁用(disable)編號為1的斷點
br en 1 -- 啟用(enable)編號為1的斷點
br dis -- 禁用所有斷點
br en -- 啟用所有斷點
具體操作結果如下, 當斷點禁用後,點擊登入按鈕就不會觸發該斷點了。當斷點重啟後,點擊登入按鈕還是會觸發該斷點的。具體效果如下所示:
(7) 斷點的刪除
br del 1 -- 刪除(delete)編號為1的斷點
br del -- 刪除所有斷點
3.輸出寄存器的值(p, po)
在iOS開發中,我們在使用LLDB調試時,經常會用到po命令來輸出某個變數或者常量的值。在使用LLDB調試WeChat時,我們也可以使用某些命令來輸出寄存器中的值。我們使用$來訪問某個寄存器中的值,並且使用p命令進行列印。下方就是通過p命令將r1寄存器中所存的內容進行列印,在列印之前將$r1進行類型轉換,po命令則輸出了Objective-C的對象,而p輸出的是C語言類型的資料。如下所示:
我們還可以將一個地址所存放的值進行列印,下方這個命令就是輸出了$sp指標所指的地址處所存放的值:
p/x $sp
4.修改寄存器中的值
我們不僅可以查看某些寄存器中的值,而且可修改寄存器中的中,通過下述命令我們就可以修改指的寄存器中的值。
register write 寄存器 值
接下來我們將要通過一個執行個體來實戰一下register write這個命令,通過在Hopper中對登入模組的分析,我們不難發現“WCAccountManualAuthControlLogic”這個類中的“handleAuthResponse:”方法就是用來處理“登入認證響應”的方法。也就是說“handleAuthResponse:”負責處理登入商務邏輯的網路響應,並且在這個函數的前邊有一個比較(cmp r0, r1), 根據r0和r1的比較結果來進行跳轉。
接下來我們要做的事情就是,在比較寄存器r0和r1中的值時我們要改變r1寄存器中的值,然後觀察App的運行效果。下方這個是隨便輸入手機號和密碼時所提示的內容。也就是正常的流程會彈出下方的框。
接下來我們要做的就是給0x1063a24 + 0x5b000 = 0x10BEA24 (cmp)這個記憶體位址添加斷點,然後去修改寄存器r1的值。下方就是給0x10BEA24這個記憶體址添加了斷點,並且在輸入手機號和密碼後,點擊登入會執行我們添加的斷點,如下所示。在斷點出我們清楚的看到了cmp r0, r1這行ARM指令。
接下來我們先將r0和r1中的值進行列印,$r0 = 8, $r1 = 351。然後我們將$r1中的值改成8,然後輸入c繼續執行,發現之前正常流程的alter就不會彈出來了,而是重新進行了一次網路請求。
上述樣本都是在32位系統上做的,如果你使用的是arm64架構的裝置,如iPhone6Plus,那麼你的地址會比上述地址要長一倍。下方兩個是使用iPhone 6 Plus越獄裝置做實驗的,可以和上述步驟進行一下對比,雖然有所不同,但是上述內容在調試下方內容時也是適用的。
本篇部落格的內容就到這兒吧,至此,你應該能將LLDB與Hopper結合起來使用了吧。今天我們以“”為例子,並沒有別的意思,只是想在真正的執行個體中實現一下。使用做實驗的原因就是做的足夠安全,畢竟的團隊還是很強大的。因為用我的越獄裝置做完上述實驗後,該越獄裝置已經不能登入帳號了,肯定是的後台監測到該“越獄裝置”的異常行為了,從而做了一些安全措施。
“攻與防”就像“矛與盾”相輔相成。