鎖定APP的目標類與函數
上一節小程介紹了怎麼獲取APP的所有類的結構信息,以這一步為基礎,下一步可以通過注入來做更多研究工作。
注入的最小單位是函數,實際上,編譯執行的程序在編譯後,類就不復存在了,留給小程的只是二進位代碼(指令或數據都是一樣的二進位代碼)。所幸的是,跟小程打交道的,並不是二進位代碼(那會困難很多),而是函數,而且是某個類的函數。
那麼,在用classdump拿到成千上萬個類與函數後,哪個函數才是小程關心的呢?怎麼鎖定它們呢?
本文介紹鎖定目標類與函數的可行的辦法。
基本上小程研究的目標APP都是有豐富的界面的,而小程關心的場景基本都是由特定的界面觸發,所以,從界面入手是個不錯的選擇。
有沒有辦法找出某個界面對應哪一個類呢?然後就可以在classdump拿到的眾多的類中仔細研究這個界面類擁有的函數與成員變數。
小程覺得目前最好的辦法就是使用Reveal工具。
(一)使用Reveal
先下載一個Reveal(有破解版本),比如1.6版本或1.5版本。
(1)拷貝libReveal.dylib到手機
Reveal最大的一個作用是把手機上的某個APP的界面同步顯示到電腦上,要做到這個效果,Reveal既要在電腦上運行,同時也要把一個「內鬼」打入到手機。這個打入手機的「內鬼」就是libReveal.dylib。
在電腦上運行Reveal後,點擊菜單Help,選擇Show Reveal Library in Finder,再點擊iOS Library,就可以找到libReveal.dylib。
拷貝到DynamicLibrary目錄下:
基本上,手機上的APP在啟動運行後,都可以載入DynamicLibraries裡面的動態庫(以dylib為後綴),至於載入哪個動態庫,則由plist文件決定。這個知識點很重要,這意味著你可以寫一個plist文件,讓某個APP在啟動時載入你寫的動態庫,這是注入的前提。
/Library/MobileSubstrate/DynamicLibraries,在手機成功越獄後就會存在。
(2)拷貝libReveal.plist到手機
找一個plist文件來修改,或者直接寫一個plist文件,命名為libReveal.plist。
libReveal.plist文件要指定讓哪個APP啟動時載入Reveal.dylib,比如:
Filter
Bundles
com.tencent.QQKSong
上面的plist內容,讓「全民k歌」啟動時,載入Reveal.dylib。
com.tencent.QQKSong,是「全民k歌」的BoudleID。至於目標APP的BoundleID是多少,有很多辦法可以查到,比如找到它的plist文件來查看,比如ps看進程信息,比如動態調試等等。
小白:如果不指定這個Filter呢,是不是所有的APP啟動時都載入?
小程:iOS8之前的版本是這樣的,但之後的版本都需要指定APP。所以,不管3724,加上這個Filter總是不會錯的。
之後,在電腦上再次啟動Reveal就可以連接目標APP,來分析界面類了。
比如,在手機上重啟全民k歌,在電腦上重啟Reveal並選擇菜單項,連接全民k歌:
可以看到,全民k歌的一個頁面是這樣(右下角的類名是重點):
(二)讓全民k歌自動切換至歌手頁面
為了「感性」一點,小程做一個演示,通過Reveal定位到全民k歌的目標類,並讓全民k歌啟動後自動切換至歌手頁面。
首先通過Reveal,定位到底部導航條的所在的viewcontroller類:
也就是KSRootTabBarController。
然後,通過查看classdump翻譯到的類結構中,找到這個類。可以看到,點擊「我要唱」按鈕,實際就是觸發KSRootTabBarController::onClickTabBarItem函數。
接著,就可以hook這個類了,讓目標APP自動跳轉。這一步的具體操作,小程會在後續詳細介紹,讀者只需要「感性」地知道這回事就好。
最終,自動跳轉的效果是這樣的:
GIF
以上小程講解了Reveal的使用。Reveal是定位目標類與函數的有效的辦法,除了這個辦法,還有一個辦法就是,觀察所有類的類名,猜測可能有關係的類(比如應該具備某個關鍵字),再注入這些類的函數並用NSLog輸出信息,或者動態調試觀察執行的流程,最終確定目標類與函數。
小程在這裡介紹動態調試目標APP的辦法,這個辦法在「漫天定位」的時候可能有用,實際大多數情況下可能不必用到。讀者可以在需要時再閱讀這部分內容。
目標:在電腦上遠程調試手機上的進程。
(一)問題(1)為什麼不用本地調試的方式?
可以在手機上安裝gdb,再使用gdb調試目標進程。但因為本地用gdb來調試可能會遇到很多gdb本身的問題(有一些可以解決,而有一些並不好解決),而lldb被蘋果支持,並用於替代gdb。所以,可考慮使用更好的工具,即lldb+debugserver來遠程調試目標進程。
(2)需要什麼工具?
電腦上需要lldb,如果mac電腦上安裝了xcode那就會有lldb。
手機上需要安裝debugserver。
(3)手機如何安裝debugserver?
debugserver在哪裡?
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/xx.xx(目標手機是什麼系統就選擇什麼版本的目錄),雙擊下面的文件:DeveloperDiskImage.dmg,在打開的finder中,usr/bin目錄中存放了debugserver。
怎麼定製手機上相應的debugserver?
(a)
雙擊打開DeveloperDiskImage.dmg後,
cd /Volumes/DeveloperDiskImage/Library/PrivateFrameworks,
scp -r ARMDisassembler.framework root@192.168.2.22:/System/Library/PrivateFrameworks/,
即把ARMDisassembler.framework拷貝到手機,之所以這樣做,是據別人的經驗,可以讓lldb看彙編代碼的效果更好,不妨照做。
(b)
把usr/bin中的debugserver拷貝到另一個目錄,為後續的簽名作準備。
(c)
創建一個entitlement.xml文件,內容如下:
com.apple.springboard.debugapplications
get-task-allow
task_for_pid-allow
run-unsigned-code
(d)
使用ldid來簽名:
ldid -Sentitlement.xml debugserver
在手機上測試是否可用(比如ssh到手機後輸入命令):
debugserver
(4)重簽名工具ldid怎麼安裝?
如果有安裝iOSOpenDev,那在目錄/opt/iOSOpenDev/bin/下面就有ldid。
如果沒有,那可以拉代碼下來安裝:
git clone git://git.saurik.com/ldid.git
cd ldid
git submodule update --init
./make.sh
which ldid #查看是否安裝成功
(5)怎麼使用debugserver與lldb?
在手機上啟動伺服器debugserver:
debugserver *:54321 -a "Spotify"
*表示偵聽任意ip的連接,54321為埠,-a連接目標進程的名稱(可用ps取得)。
然後,在電腦上啟動lldb並連接伺服器:
process connect connect://192.168.2.22:54321
如果手機端的debugserver提示:Waiting for debugger instructions for process 0,那說明已經連接上。
之後可以在lldb端發送命令,比如查看當前調試進程的模塊信息:
image list -o -f
注意,第一次image list -o -f,可能要等上幾分鐘。當有返回後,再輸入c,就可以讓程序繼續運行。
(6)如果覺得debugserver運行很慢,怎麼解決?
使用usbmuxd來啟用數據線來調試。
* 如何安裝與使用usbmuxd?
* 下載:http://7xibfi.com1.z0.glb.clouddn.com/uploads/default/original/2X/a/aa9cecf05b47d08a59324edeaaeea3f17e0608ee.zip
* 具體使用可以參考:http://www.jianshu.com/p/9333a706641a。
小程直接用lldb跟debugserver,第一次image list等了幾分鐘,整體還可以接受。
(7)怎麼調試?
* debugserver *:54321 -a "Spotify" --手機上啟動debugserver並附加到目標進程
* lldb --電腦上啟動lldb
* process connect connect://192.168.2.22:54321 --lldb連接debugserver
* image list -o -f --查看目標進程的所有模塊的信息,比如輸出:
[ 0] 0x000d8000 /var/containers/Bundle/Application/A80AF35B-DAD3-4D7E-B467-6C4230E32556/Spotify.app/Spotify(0x00000000000dc000)
第二個值為程序在內存中的基地址,記錄這個值。
* br s -a (0x000d8000+目標代碼的偏移地址)
* c --讓程序繼續執行
* (操作程序)讓程序觸發斷點。
* 使用lldb命令進行調試。
(二)示例
這裡簡單演示下怎麼調試Spotify這個APP。
從class-dump得到的信息,嘗試調試類SPTNowPlayingBarModel的pause函數,看看在播放bar條上暫停播放時會不會斷在這個函數上。
class-dump得到的pause的地址是0x00477ff1,在hopper上跳轉到這個地址(G),也可以看到-[SPTNowPlayingBarModel pause]的信息。
* 運行目標應用
* debugserver *:54321 -a "Spotify" 手機端啟動伺服器,之後都在客戶端lldb中操作。
* lldb
* process connect connect://192.168.2.22:54321
* image list -o -f 或 image list -o -f grep Spotify
這一步可能要等一會,在ipod5的6.0系統會很快,而在iphone5s的10.1.1系統則很慢。
* 從上面的命令找到基地址為:0x000e6000 (每次運行不一樣)
* 查看一下即將下斷點的代碼是怎麼樣的:dis -a "0x000e6000+0x00477ff1" 結果跟用hopper看到的一樣(0x0047ff1處)。
* br s -a "0x000e6000+0x00477ff1"
* c
* 讓程序觸發到斷點,使用lldb的命令查看數據或設置值,或單步等。
(三)lldb的常用命令
列印信息:
expr或e、po、p或print
display 表達式
undisplay 序號
斷點:
br l或breakpoint list#所有斷點
breakpoint set -a 函數地址,可簡寫:br s -a xxx #下斷點
br s --函數關鍵字(可模糊)
breakpoint set --shlib foo.dylib --name foo
br del 1#刪除斷點
br del 2 3 4#刪除幾個斷點
內存斷點
watchpoint set expression 地址
watchpoint set variable 變數名稱
條件斷點
watchpoint modify -c 表達式
單步:
s/si#單步進入函數
n/ni#單步
f#跳出函數
線程:
thread backtrace,或bt,或bt all #列出所有線程
模塊信息:
image list [-o -f]
image lookup -a expression
image lookup -a $pc
image lookup -r -n xxx
image lookup -r -n playPause #用來查看一個函數(關鍵字)的地址
image lookup -r -s
顯示地址處的代碼:
disassemble/dis -a 地址
dis -a $pc
dis -s 0x0002c000 -c 9 #後面的參數-c用來限制顯示的代碼數。
內存操作:
memory read [起始地址 結束地址]/寄存器 -outfile 輸出路徑
寄存器操作:
register read/格式
register write 寄存器名稱 數值
總結一下,本文重點在於使用Reveal,從界面入手,定位到關鍵類與函數,進而找到目標類與函數,為分析借鑒或注入提供基礎。對於動態調試,讀者未必需要掌握,如果它實際幫不到你的話。使用Reveal的難度係數為低,而動態調試的難度為中。
TAG:全球大搜羅 |