靜態(tài)分析是指對二進制包進行反編譯匿值,分析靜態(tài)的代碼邏輯。
本文內(nèi)容包括:app 砸殼過程边酒、工具和環(huán)境的坑经柴、導(dǎo)出 OC 頭文件、使用 hopper 和 IDA 反編譯甚纲、arm 寄存器功能口锭、靜態(tài)分析經(jīng)驗、推薦的 IDA 插件介杆、如何分析系統(tǒng)庫。
對 app 砸殼解密
從 App Store 下載的 app 是經(jīng)過加密的韭寸,需要對其進行解密后春哨,才能進行分析。如果你懶得砸殼恩伺,可以直接去各種蘋果助手下載越獄版 app赴背,那些是已經(jīng)解密過的。但是如果要找的 app 在助手上沒有晶渠,就只能自己砸殼了凰荚。
砸殼可以使用 dumpdecrypted,也可以使用更簡單的 clutch褒脯。這里用 dumpdecrypted 講解便瑟。步驟如下。
1.下載 dumpdecrypted
從https://github.com/AloneMonkey/dumpdecrypted下載源碼番川,編譯出一個 dumpdecrypted.dylib 文件到涂。這個版本的 dumpdecrypted 添加了對 framework 的 dump。
2.安裝 openSSH
iOS 9及以下系統(tǒng)颁督,在 Cydia 里安裝 openSSH 即可践啄。
iOS 10越獄自帶了 openSSH,但是默認是關(guān)閉的沉御,需要做一點修改屿讽。
如果是用的 yalu 越獄:
- 1.用蘋果助手或者其他工具進入 iOS 的
/private/var/containers/Bundle/Application/yalu102/yalu102.app/
。 - 2.用文本編輯器打開
dropbear.plist
文件吠裆。 - 3.替換 127.0.0.1:22 為 22伐谈。
- 4.重啟設(shè)備,重新使用越獄工具恢復(fù)越獄硫痰。
參考:http://bbs.iosre.com/t/make-package-ssh-ios10-2/7564
或者直接去 Cydia 里安裝 dropbear 插件衩婚。
3.連接到 iOS 設(shè)備
iOS 設(shè)備安裝了 openSSH 后,在 Mac 端打開終端效斑,確保 Mac 和 iOS 設(shè)備連接到同一網(wǎng)絡(luò)非春,在終端里輸入命令:ssh root@iOSIP。iOS 設(shè)備的 ip 地址:
在終端中輸入命令:ssh root@10.5.53.182
,回車奇昙,接著輸入 ssh 的默認密碼alpine
后即可連接到 iOS 設(shè)備护侮。
4.找到需要砸殼的 app
找到 app 所在目錄,格式為/var/mobile/Containers/Data/Application/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/
储耐,可以使用同步助手羊初、itools 等工具查找。
也可以在 Cydia 里安裝 ps 命令行工具后什湘,使用ps –e
命令查找长赞,方法是 ssh 成功后,關(guān)閉所有 app闽撤,打開需要砸殼的 app得哆,輸入ps –e
命令,即可打印出所有進程哟旗,/var/mobile
開頭的那個目錄就是 app 所在的目錄贩据。
5.進行砸殼
下面的砸殼是舊版 dumpdecrypted 的方法,比較繁瑣闸餐。AloneMonkey 的 這個 https://github.com/AloneMonkey/dumpdecrypted 更加簡單饱亮。
- 把
dumpdecrypted.dylib
拷貝到/usr/lib
。 iOS 9之前是拷貝到 app 的 Document 目錄的舍沙, iOS 9 之后出現(xiàn)了權(quán)限問題近上,所以拷貝到/usr/lib
- 修改 user 為
mobile
:su mobile
- 進入到某個具有寫權(quán)限的目錄,例如
cd /var/mobile/Documents
- 使用
DYLD_INSERT_LIBRARIES
加載動態(tài)庫到 app 上场勤,格式為DYLD_INSERT_LIBRARIES='dumpdecrypted.dylib的目錄' '需要砸殼的app執(zhí)行文件的目錄'
戈锻,例如:DYLD_INSERT_LIBRARIES=/usr/lib/dumpdecrypted.dylib /var/mobile/Applications/F7753B03-3F06-4524-A735-5BF5B398C730/WeChat.app/WeChat
。這是系統(tǒng)的 dyld 提供的加載動態(tài)庫的功能和媳,可以在 dyld 源代碼中看到這部分邏輯格遭。
如果出現(xiàn)dyld: could not load inserted library 'dumpdecrypted.dylib' because no suitable image found. Did find: dumpdecrypted.dylib: required code signature missing for 'dumpdecrypted.dylib'
,需要對 dumpdecrypted.dylib 進行簽名留瞳。
在 Mac 上列出證書:security find-identity -v -p codesigning
拒迅,用列出的證書簽名:
codesign --force --verify --verbose --sign "iPhone Developer: xxx xxxx (xxxxxxxxxx)" dumpdecrypted.dylib
。把簽名后的dumpdecrypted.dylib
重新拷到 iOS 設(shè)備上她倘,重新進行砸殼璧微。
砸殼完畢后,在當(dāng)前目錄會生成一個.decrypted
后綴的文件硬梁,這就是砸殼后的文件前硫,將其拷貝到 Mac 上即可導(dǎo)入其頭文件、用反編譯工具打開分析荧止∫俚纾可以在 Mac 上使用 scp 命令拷貝越獄機上的文件:scp -P 端口號(默認22) root@iOSIP:/var/mobile/Documents/xxx.decrypted ~/Documents/xxx.decrypted
阶剑。如果拷貝的是文件夾,加上-r
參數(shù)危号。
dumpdecrypted
原理是 app 啟動后會被系統(tǒng)解密牧愁,因此可以把解密后的內(nèi)存 dump 出來。但是如果要對 app extension 進行砸殼外莲,由于 extension 是依賴于主 app 的猪半,不能獨立啟動,所以砸殼方法就失效了偷线∧ト罚可以參考這個改進版對 extension 砸殼的方法:https://github.com/CarinaTT/dumpdecrypted
使用 class-dump 導(dǎo)出 app 的頭文件
Class-dump 是一個可以導(dǎo)出 Objective-C 頭文件的工具,官網(wǎng):http://stevenygard.com声邦。
通過分析頭文件里的 API俐填,可以簡單地分析一個類的實現(xiàn),或者查找一些私有 API翔忽。
class-dump 官網(wǎng)上的版本不能導(dǎo)出用 swift 編寫的工程的頭文件,當(dāng)出現(xiàn)Error: Cannot find offset for address 0x3a546a04 in dataOffsetForAddress:
這樣的錯誤時盏檐,就說明這個 app 可能是用 swift 編寫的歇式。
建議去 github 上手動編譯最新版的 class-dump,或者使用 class-dump-z 代替胡野,下載地址:https://code.google.com/archive/p/networkpx/downloads材失。
把下載到的class-dump-z
執(zhí)行文件放到/usr/local/bin/
,賦予執(zhí)行權(quán)限chmod +x /usr/local/bin/class-dump-z
硫豆。這樣就可以在終端使用 class-dump 命令了:class-dump-z –H '需要導(dǎo)出頭文件的app目錄' –o '導(dǎo)出頭文件的存放目錄'
龙巨。
例如要 dump 系統(tǒng)自帶的計算器,導(dǎo)出它的頭文件熊响,命令如下:
class-dump-z -H /Applications/Calculator.app -o ~/Documents/headers
旨别。
拿到砸殼后的 .decrypted 文件后,直接使用class-dump-z
即可導(dǎo)出頭文件汗茄。
此時秸弛,使用之前 reveal 定位到的類名,即可找到對應(yīng)的文件洪碳,查看類里面的方法递览。
可以看到,在掃一掃界面瞳腌,微信使用了- (void)captureOutput: didOutputSampleBuffer: fromConnection:
這個方法绞铃,說明它是截取了視頻流的幀圖像,再對圖像進行二維碼分析嫂侍,而不是用AVFoundiation
提供的二維碼識別方法儿捧。
如果還想進一步查看方法的邏輯荚坞,可以使用Hopper Disassembler
對 .decrypted 文件進行反編譯。
使用使用 Hopper Disassembler 靜態(tài)分析
一個專門反編譯 OC 程序的工具纯命。官網(wǎng):http://www.hopperapp.com西剥。試用版有功能限制,30分鐘退出一次亿汞,不能保存和導(dǎo)入反編譯后的文件瞭空,不能動態(tài)調(diào)試等。
打開 Hopper Disassembler疗我,直接將 .decrypted 文件拖入咆畏,選擇對應(yīng)的 CPU 架構(gòu)類型即可,例如這個.decrypted 是從 iPad mini2 上生成的吴裤,那么就是 arm64旧找。
打開后會自動進行分析,列出方法名麦牺、字符串等信息钮蛛,但是大多數(shù)都是匯編語言。閱讀匯編語言剖膳,還需要了解對應(yīng)架構(gòu)寄存器功能的知識魏颓。
在左側(cè)可以搜索類名,方法名吱晒。
右側(cè)的 is referenced by 和 have reference to 可以看到方法之間的的交叉引用關(guān)系:
按空格鍵可以彈出方法的邏輯跳轉(zhuǎn)圖:
Hopper Disassembler 可以將匯編語言轉(zhuǎn)換為 OC 風(fēng)格的偽代碼甸饱,但是舊版的 hopper 不能對 arm64 文件使用這個功能。建議使用 armv7s 以下的 iOS 設(shè)備的原因就在這里仑濒。以下是使用 iPad2 越獄設(shè)備反編譯后叹话,生成的匯編代碼和對應(yīng)的偽代碼,由于微信的代碼比較復(fù)雜墩瞳,這里選用的是另外一個更簡單的二維碼 app 的代碼:
可以看到aptureOutput: didOutputSampleBuffer: fromConnection:
里驼壶,首先用取到的幀生成了一張圖片,再用createRotatedImage:degrees:
對圖片做了一次處理矗烛,最后用decodeImage:cgimg:
對圖片進行二維碼分析辅柴。要想查看這些方法,只需要再搜索對應(yīng)的方法名就可以了瞭吃。最新版 hopper 也可以雙擊直接跳轉(zhuǎn)碌嘀。
另外一個反編譯工具 IDA 也可以反編譯 armv7 的 app ,使用方法類似歪架,可以和 Hopper Disassembler 對照著看股冗。需要注意的是 IDA 的 Pro 版才支持 arm64 的 app,而 Pro 版不支持免費試用和蚪。
靜態(tài)分析經(jīng)驗總結(jié)
追蹤調(diào)用流程
- 對于靜態(tài)函數(shù)止状,直接用交叉引用功能
is referenced by
查看函數(shù)在哪里被引用烹棉。注意 hopper 面板里列出的引用不是完整的,可以用快捷鍵x
列出完整的引用 - 對于 OC 方法怯疤,由于 runtime 在調(diào)用時不是直接引用方法浆洗,而是引用了 selector,所以需要搜索方法名字符串和 selector集峦,然后再用
is referenced by
查找哪些地址引用了此字符串或者 selector伏社,來查找方法調(diào)用 - 通過寄存器的賦值操作回溯參數(shù)的傳遞
- 通過查找某些關(guān)鍵字符串,回溯到關(guān)鍵函數(shù)
注意塔淤,反匯編工具有時候會分析出錯誤的指令摘昌,所以有些函數(shù)體是丟失的,需要在反編譯時手動 undefined高蜂。
分析匯編代碼
- 使用 hopper 的偽代碼轉(zhuǎn)換功能聪黎,可以將 OC 方法的匯編代碼轉(zhuǎn)換為 OC 風(fēng)格的偽代碼。此功能對 arm64 的支持不是很好备恤,建議使用 armv7 或者 armv7s 的越獄機
- 在函數(shù)的開始稿饰,32 位 arm 上前四個參數(shù)存放在 r0-r3 中,其他參數(shù)存放在棧中露泊,結(jié)束后湘纵,返回值放在 r0 中;在 arm64 上滤淳,前7個參數(shù)存放在 x0–x7 中,返回值存在 x0 中
- 有些代碼是被開發(fā)者故意混效過的砌左,例如打亂執(zhí)行流程脖咐、加入冗余代碼,可以借助一些 IDA 插件處理后再分析汇歹,例如 CrowdDetox屁擅、optimice python plugin,不過只是分析 iOS 的話产弹,很少會遇到這種情況
基本的匯編知識
你并不需要花時間理解每一條匯編指令派歌,只需要梳理出關(guān)鍵點就能理清代碼的邏輯。
逆向中關(guān)鍵的指令:
-
ldr
痰哨,mov
胶果,讀取指令,從地址讀取數(shù)據(jù)到寄存器斤斧。 -
str
早抠,保存指令,保存數(shù)據(jù)到寄存器撬讽。 -
b
蕊连,跳轉(zhuǎn)指令悬垃,跳轉(zhuǎn)到某個地址。 -
cmp
甘苍,比較指令尝蠕,說明這里有分支。
32 位 arm 的調(diào)用約定:
寄存器 | 描述 |
---|---|
r0-r3 | 傳遞參數(shù)與返回值载庭。如果斷點在 OC 方法的第一行看彼,那 r0 就是 self,r1 就是 cmd昧捷。如果超過四個參數(shù)闲昭,或者一些例如結(jié)構(gòu)體的參數(shù)超過了32位 bit,那么參數(shù)將會通過棧來傳遞靡挥;返回值一般都在 r0 上 |
r4-r6, r8, r10-r11 | 沒有特殊規(guī)定序矩,通用寄存器 |
r7 | 棧幀指針寄存器(Frame Pointer),指向前一個保存的棧幀(stack frame)和鏈接寄存器(link register跋破, lr)在棧上的地址 |
r9 | 操作系統(tǒng)保留 |
r12 | IP 寄存器(intra-procedure scratch) |
r13 | SP 寄存器(stack pointer)簸淀,是棧頂指針 |
r14 | LR 寄存器(link register),存放函數(shù)返回后需要繼續(xù)執(zhí)行的指令地址 |
r15 | PC 寄存器(program counter)毒返,指向當(dāng)前指令地址 |
CPSR | 當(dāng)前程序狀態(tài)寄存器(Current Program State Register)租幕,在用戶狀態(tài)下存放像 condition 標(biāo)志中斷禁用等標(biāo)志 |
arm64 的調(diào)用約定:
arm64有 r0 - r30 是31個通用整形寄存器,PC 不能再作為寄存器直接訪問拧簸。每個寄存器可以存取一個64位大小的數(shù)劲绪。 當(dāng)使用 x0 - x30 訪問時,它就是一個64位的數(shù)盆赤。當(dāng)使用 w0 - w30 訪問時贾富,訪問的是這些寄存器的低32位。
寄存器 | 描述 |
---|---|
x0–x7 | 傳遞參數(shù)與返回值牺六。如果參數(shù)個數(shù)超過了8個颤枪,多余的參數(shù)會存在棧上;返回值一般都在 x0 上 |
x29 | 棧幀指針寄存器(Frame Pointer)淑际,指向前一個保存的棧幀(stack frame)和鏈接寄存器(link register畏纲, lr)在棧上的地址 |
x31 | SP 寄存器(stack pointer),是棧頂指針春缕;根據(jù)不同指令盗胀,也有可能是 zero register |
x30 | LR 寄存器(link register),存放函數(shù)的返回地址 |
CPSR | 當(dāng)前程序狀態(tài)寄存器(Current Program State Register)锄贼,在用戶狀態(tài)下存放像 condition 標(biāo)志中斷禁用等標(biāo)志 |
x86-64 的調(diào)用約定:
x86-64 有16個64位寄存器读整,分別是:
rax,rbx,rcx米间,rdx强品,esi,edi屈糊,rbp的榛,rsp,r8逻锐,r9夫晌,r10,r11昧诱,r12晓淀,r13,r14盏档,r15
寄存器 | 描述 |
---|---|
rax | 作為函數(shù)返回值使用 |
rsp | 棧指針寄存器凶掰,指向棧頂 |
rdi,rsi蜈亩,rdx懦窘,rcx,r8稚配,r9 | 依次用作函數(shù)參數(shù)畅涂;如果斷點在 OC 方法的第一行,那 rdi 就是 self道川,rsi 就是 cmd |
rbx午衰,rbp,r10冒萄,r11苇经,r12,r13宦言,r14根暑,r15 | 通用寄存器 |
棧幀相關(guān)的知識融师,可以參考:iOS開發(fā)同學(xué)的arm64匯編入門
匯編指令速查插件
有許多很有用的插件可以對靜態(tài)分析提供幫助。
有時候看到不了解的匯編指令沾鳄,每次都去 Google 查找施流,是一件很低效的事响疚。可以安裝插件瞪醋,直接在 hopper 和 IDA 中顯示指令的功能忿晕。
Hopper 插件:hopperref
Hopper 可以使用 Python 編寫的擴展插件。安裝插件hopperref银受,把Show Instruction Reference.py``arm.sql``x86-64.sql
拷貝到~/Library/Application Support/Hopper/Scripts/
目錄下即可践盼。之后就能在 hopper 界面的菜單欄Scripts
中找到Show Instruction Reference
選項鸦采,點擊即可輸出選中指令的詳細文檔。
mov
指令的文檔:
IDA 插件:idaref
hopperref 插件是源自 一個 IDA 的插件 idaref咕幻。
把idaref.py
拷貝到your_ida_path/ida.app/Contents/MacOS/plugins/
下渔伯,把archs
文件夾拷貝到your_ida_path/ida.app/Contents/MacOS/plugins/archs
。archs
文件夾里是匯編指令的文檔x86-64.sql``x86-64_old.sql``arm.sql``mips32.sql``xtensa.sql
肄程。
之后打開 IDA锣吼,就可以在Edit
菜單中多出了idaref
選項,選擇Start Idaref
就開啟了自動提示蓝厌,
當(dāng)選中匯編指令時玄叠,對應(yīng)的文檔就會顯示在Instruction Reference
窗口中。
IDA 插件:FRIEND
除了 idaref拓提,還有另一個插件 FRIEND 也提供了匯編指令和寄存器的文檔功能读恃。只要把鼠標(biāo)停在指令或者寄存器上就會顯示文檔懸浮窗。
需要注意的是崎苗,編譯出來的 IDA dylib 插件是對應(yīng) IDA 版本的狐粱,如果要使用不同版本的 IDA,就需要重新編譯胆数。把對應(yīng)版本的FRIEND.dylib
和FRIEND64.dylib
拷貝到your_ida_path/ida.app/Contents/MacOS/plugins/
下肌蜻,再打開 IDA 就會在Edit->Plugins
中多出FRIEND
選項。
點擊選項必尼,打開 FRIEND 的設(shè)置蒋搜。需要加載 FRIEND 提供的 XML 配置文件,對應(yīng)二進制文件的 x86_64 或者 arm 平臺判莉。例如x86_64.xml
配置中提供了x86_64 instructions
項豆挽,選中后,勾上下面的四個功能選項券盅,點擊 OK 保存帮哈。
之后,當(dāng)鼠標(biāo)停在指令或者寄存器上就會顯示文檔懸浮窗锰镀。
識別庫函數(shù)
很多時候娘侍,二進制文件中的函數(shù)都被去掉了符號,因此只能看到很多sub_100017D90
這樣的函數(shù)泳炉,難以直觀分析憾筏。而程序會使用到很多第三方庫,例如加密庫花鹅、壓縮庫氧腰、網(wǎng)絡(luò)庫,這些第三方庫一般都是開源的,可以得到函數(shù)符號古拴,如果能恢復(fù)這部分函數(shù)的符號箩帚,就能避免浪費時間在分析這些開源代碼上,也能通過分析開源庫的交叉引用斤富,追蹤程序自身的邏輯膏潮。
這部分代碼一般都是 C 和 C++ 函數(shù),OC 方法的名字都保存在 Mach-O 文件的符號表中满力,不會被去除符號焕参。如果你需要分析 C++ 程序,可以使用下面的工具進行輔助油额。
FLIRT:庫快速識別和鑒定技術(shù)
IDA 提供了FLIRT Signature
功能叠纷,F(xiàn)LIRT 全稱是庫快速識別和鑒定技術(shù),可以為帶有符號的庫文件中的函數(shù)生成簽名潦嘶,再把簽名文件導(dǎo)入到分析后的 app 中涩嚣,就會識別出匹配到的函數(shù),重命名為正確的符號掂僵。
但是生成正確的簽名并不容易航厚。用于生成簽名的庫文件,編譯時的編譯器版本锰蓬、配置和 app 中用到的庫的編譯器版本幔睬、配置需要相同。這樣才能生成相同的代碼芹扭,從而生成相同的代碼簽名麻顶。
具體的使用方法,可以在書籍IDA Pro 權(quán)威指南
中找到舱卡。
識別加密函數(shù)
類似的辅肾,有些 IDA 插件可以識別程序中用到的加密常數(shù)、加密方法和壓縮方法轮锥。例如 Find Crypt 可以尋找常用加密算法中的常數(shù)矫钓,IDA signsrch 可以尋找二進制文件所使用的加密、壓縮算法舍杜,IDA scope 可以自動識別 windows 函數(shù)和壓縮新娜、加密算法。
可以從這些關(guān)鍵函數(shù)入手蝴簇,尋找程序中的關(guān)鍵邏輯。
如何分析系統(tǒng)庫
有時候在分析某個 crash 時匆帚,或者對某個系統(tǒng)功能感興趣時熬词,會需要分析特定版本的 iOS 系統(tǒng)庫的實現(xiàn),例如UIKit.framework
Foundiation.framework
。
絕大部分時候互拾,只需要分析模擬器版本的系統(tǒng)庫就可以了歪今。因為模擬器的系統(tǒng)庫保留了所有的符號,查找交叉引用更直接颜矿。
不過有些系統(tǒng)庫只在真機上才有寄猩,或者你需要特定版本的庫用于分析 crash 時,可以從這里下載對應(yīng)的系統(tǒng)庫骑疆。
真機的系統(tǒng)庫和模擬器的有些差別田篇。系統(tǒng)庫在真機上經(jīng)過了很多編譯優(yōu)化,去除了大部分私有的函數(shù)符號箍铭,交叉引用也不像模擬器版本的那樣直接泊柬。真機上的所有系統(tǒng) framework 都被整合成了一個大文件,名為dyld_shared_cache_arm64
或者dyld_shared_cache_armv7
诈火。函數(shù)在尋址時兽赁,是基于整個dyld_shared_cache_xxx
文件進行尋址的。
當(dāng)你把真機連接到 Xcode冷守,Xcode 會把真機上的系統(tǒng)庫拷貝到~/Library/Developer/Xcode/iOS DeviceSupport
刀崖,從dyld_shared_cache_xxx
中切分出每個單獨的 framework。但是當(dāng)你反編譯這些 framework 時拍摇,會發(fā)現(xiàn)代碼里會使用很多無效地址的函數(shù)指針亮钦,難以分析。這是因為在dyld_shared_cache_xxx
中授翻,一個 framework 引用另一個 framework 中的函數(shù)時或悲,是相當(dāng)于在一個庫中直接引用的,直接跳轉(zhuǎn)到對應(yīng)的地址堪唐,而不是再用函數(shù)符號經(jīng)過 lazy binder 進行調(diào)用巡语。當(dāng) framework 從dyld_shared_cache_xxx
中切分出來后,這些函數(shù)調(diào)用的地址就會指向 framework 外淮菠,無法追蹤男公。
所以在分析真機的系統(tǒng)庫時,最好是配合模擬器版本的系統(tǒng)庫輔助分析合陵,可以看到私有的符號枢赔,也可以看到更明確的交叉引用∮抵或者用 IDA 直接分析整個 dyld_shared_cache_xxx
文件踏拜,不過這樣做需要反匯編整個文件,耗時很大低剔。
結(jié)尾
靜態(tài)分析的整個流程如上速梗,剩下的就是積累經(jīng)驗了肮塞。通過靜態(tài)分析查看一些簡單函數(shù)的實現(xiàn),在大部分情況下都足夠了姻锁。不過靜態(tài)分析的信息是有限的枕赵,有時候很難找到想要的函數(shù),這時候就需要動態(tài)分析上場了位隶。下一篇文章將講解動態(tài)分析拷窜。