轉自:http://bbs.pediy.com/showthread.php?t=167398php
1.爲何要進行調試與分析
研究iOS程序有不少用處,好比:
找bug或者漏洞,想知道某程序有沒有漏洞或者bug。
某程序能實現某功能,我想知道如何實現,如ios6發短信功能,還有比較時髦的iPhone5通話錄音功能。
對iOS程序進行DIY擴展功能,好比曾半仙前輩的做弊插件http://bbs.pediy.com/showthread.php?t=163435,就是一個例子。2011年的時候我也實現了相似的功能,作了一個iPhone版的「歡樂鬥地主」的記牌器功能。有朋友問到如何實現的,主要就是hook了recv函數,從封包獲得想要的信息,通過處理得知54張牌還剩什麼牌。
總的來講對iOS程序進行調試與分析的目的和調試與分析Windows平臺程序的目的是同樣的。
2. 調試與分析的準備工做
iOS的封閉性極大地增大研究它的難度。爲使系統更加安全,iOS引入了不少安全機制;如代碼加密,使得分析前先要解密代碼,SandBox(沙盒),code signing(代碼簽名),使得不能運行未簽名的代碼。這些安全機制使得研究它的難度比Linux/Windows要大。
儘管這樣,並不能阻擋咱們前進的腳步。
必要的硬件準備:一個越獄的設備,最好是iPhone
越獄是必要的,由於只有越獄才能運行非AppStore上的軟件。
必要的軟件準備:在Cydia裏安裝下面的軟件
1.OpenSSH,OpenSSH是Linux下經常使用的服務,裝上後設備可充當服務SSH服務端
2.GNU Debugger(gdb)調試工具
3.adv-cmds(ps命令)
4. darwin cc tools (otools)
5. Link Identity Editor (ldid)
PC端安裝SSH Secure Shell Client或putty等SSH客戶端軟件,以便能經過SSH創建到設備的鏈接。
Ida pro,該軟件搞過逆向的都熟悉,靜態分析功能很強大,特別是大於或等於6.2的版本,能識別Objective-C結構,能把Objective-C函數名顯示以下圖:
Hopper Disassembler,該軟件是剛出來沒多久的,我還沒用過,聽說功能也很強大。無論怎麼說ida pro是太貴了,通常我的不多買得起,這時候Hopper Disassembler是另外一個選擇。最後是class-dump-z,在沒有上面兩個工具的狀況下用它能得到符號信息。
有了軟硬件的準備,即可以開始調試了。
1.PC端運行SSH客戶端,鏈接到設備,輸入用戶名:root,默認密碼:alpine,第一次鏈接上去後建議用passwd命令改掉默認密碼,以防黑客經過局域網入侵。
2.在設備上運行想調試的程序,如iRead,運行命令ps –ax能夠看到全部運行的進程id,gdb –p pid這樣就能夠調試指定進程(gdb –p 10110)。
另外,能夠經過otool –l | grep crypt 輸出可知道已加密代碼的位置。有這些準備後,咱們能夠先運行下面命令測試一下。
SSH Secure Shell 3.2.9 (Build 283)
Copyright (c) 2000-2003 SSH Communications Security Corp - http://www.ssh.com/
This copy of SSH Secure Shell is a non-commercial version.
This version does not include PKI and PKCS #11 functionality.
MMs-iPod:~ root# passwd
Changing password for root.
New password:
Retype new password:
MMs-iPod:~ root# ps -ax
PID TTY TIME CMD
……(省略部分無關記錄)
12416 ?? 0:02.52 /var/mobile/Applications/4DFD17D1-39AC-4F10-8AB8-3A4CB99E9E77/iRead.app/iRead
……(省略部分無關記錄)
MMs-iPod:~ root# gdb -p 12416
/usr/bin/gdb: line 55: awk: command not found
warning: unrecognized host cpusubtype , defaulting to host==armv7.
GNU gdb 6.3.50-20050815 (Apple version gdb-1708 + reverse.put.as patches v0.4) (Mon Apr 16 00:53:47 UTC 2012)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "arm-apple-darwin".
/private/var/root/12416: No such file or directory
Attaching to process 12416.
Reading symbols for shared libraries . done
Reading symbols for shared libraries ............................................................................................................................................. done
Reading symbols for shared libraries + done
0x39556eb4 in mach_msg_trap ()
(gdb) quit
The program is running. Quit anyway (and detach it)? (y or n) y
Detaching from process 12416.
MMs-iPod:~ root# otool -l /var/mobile/Applications/4DFD17D1-39AC-4F10-8AB8-3A4CB99E9E77/iRead.app/iRead | grep crypt
cryptoff 8192
cryptsize 8601600
cryptid 1
3.解密代碼並用IDA和gdb分析
按上文的步驟便能經過gdb來調試iOS平臺的應用程序,可是在gdb下看代碼是很不方便的,確定沒有在咱們熟悉的ida pro上看得舒服。爲了能在ida上進行靜態分析,須要先去除DRM保護,iOS程序每每是以加密的狀態存在於正版app的ipa文件中的,只有去除掉DRM保護(俗稱ipa破解),才能用ida來進行靜態分析。也許有人會說,用如維享應用匯、91助手、PP助手等工具下載下來的ipa直接拖到ida裏就能分析,需知道這樣的ipa是通過破解了的,不是正版的ipa。
3.1DRM破解(代碼解密)基本原理
1.定位到待破解軟件在var/mobile/Applications下所在位置
2.複製軟件目錄下全部的文件到一個臨時目錄
3.用工具otool分析程序主文件,找出cryptsize和cryptid
4.運行待破解程序,用gdb附加到目標進程
5.由於程序已經運行,因此此時是解密狀態。dump當前未加密的內存
6.將dunp結果輸出到一個bin文件,退出gdb調試程序
7.將主程序文件的cryptid字段改成0,改加密狀態爲未加密
8.將dump出來的bin文件中的未加密內存的內容覆蓋到原主程序文件的相應位置
9.對主程序簽名
10.刪除一些垃圾文件
11.打包IPA
下面以京東LeBook爲例子,用gdb手工解密,具體操做以下:
先經過SSH客戶端鏈接到iOS設備上,切換到對應程序的目錄。依次輸入下面的命令:
root# cd /var/mobile/Applications/4DFD17D1-39AC-4F10-8AB8-3A4CB99E9E77/iRead.app/
root# gdb -e ./iRead
/usr/bin/gdb: line 55: awk: command not found
warning: unrecognized host cpusubtype , defaulting to host==armv7.
/usr/bin/gdb: line 136: file: command not found
/usr/bin/gdb: line 171: file: command not found
/usr/bin/gdb: line 171: awk: command not found
GNU gdb 6.3.50-20050815 (Apple version gdb-1708 + reverse.put.as patches v0.4) (Mon Apr 16 00:53:47 UTC 2012)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "arm-apple-darwin".Reading symbols for shared libraries .. done
(gdb) set sharedlibrary load-rules ".*" ".*" none
(gdb) set inferior-auto-start-dyld off
(gdb) set sharedlibrary preload-libraries off
(gdb) rb doModInitFunctions
Breakpoint 1 at 0x2fe0cece
<function, no debug info> __dyld__ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE;
(gdb) r
Starting program: /private/var/mobile/Applications/4DFD17D1-39AC-4F10-8AB8-3A4CB99E9E77/iRead.app/iRead
Breakpoint 1, 0x2fe0cece in __dyld__ZN16ImageLoaderMachO18doModInitFunctionsERKN11ImageLoader11LinkContextE ()
(gdb) dump memory dec.bin 0x3000 0x837000
該命令是把已解密的內存dump到dec.bin文件中,0x3000爲開始地址,0x837000爲結束地址,爲何是這兩個地址呢?
從上文otool的輸出知道cryptoff=8192(0x2000),cryptsize=8601600(0x834000),而當地址空間被加載的時候,開始地址是0x1000,而不是0x000。0x2000+0x1000等於0x3000,因此開始地址爲0x3000,結束地址爲開始地址加上被加密的映像大小,爲0x3000+0x834000=0x837000。
(gdb) kill
Kill the program being debugged? (y or n) y
(gdb) q
到此,咱們獲得解密後代碼,只要用它來替換掉原來被加密的代碼便可,
root#cp iRead iRead.bak
root# dd seek=8192 bs=1 conv=notrunc if=./dec.bin of=./iRead
上面這條命令在iOS設備上執行完要大大幾分鐘,用WinHex來進行替換會快不少,從文件偏移8192(0x2000)開始替換,大小爲加密塊大小0x834000字節。在可執行程序有兩個或多於兩個Fat header的狀況下,開始替換的偏移地址還要加上Architecture Offset,Architecture Offset能夠用命令「otool –f ./iRead」獲得。可參考《Hacking and Securing iOS Applications》第七章,解釋得很詳細具體。另喜歡看中文的朋友能夠參考論壇裏肖兄(Claud)的文章http://bbs.pediy.com/showthread.php?t=152843 ,只是要注意的是,要在doModInitFunctions下斷,而最好不要在程序入口點下斷。由於若是斷在程序入口點,動態庫的入口函數已被調用,有些程序會被inline hook,有可能形成dump出來的內容是錯的。
接下來把cryptid由1改成0,表示不加密。再用「ldid –S iRead」從新籤一下。而cryptid能夠這樣來定位到,從剛纔替換區域開始處往上拉,一直拉到能看到不少.dylib的路徑,放慢速度仔細看,在最後一個dylib的地方,再往上大概0x28的地方有個01,把它改成00就行,若是不是很肯定,能夠經過「otool -l iRead | grep crypt」來看cryptid是否變爲0來肯定。
至此完成代碼解密,再打包成ipa文件便完成app的破解。
另外還能夠在Cydia裏裝上Crackulous軟件來破解,Crackulous軟件是全自動化的圖形界面的破解程序。只要點幾下就能生成破解後的ipa文件,方便易用快捷。可是也有個缺點,就是有些軟件不能用它來破解。相對於第二個是全自動化的方法,第一個方法雖然比較麻煩,但便於理解過程。
3.2獲取符號信息便於調試
獲取到儘量多的符號信息對分析調試有很大的幫助,就像在茫茫大海中航行的船找到了燈塔,不至於迷失方向,不知所措。有了足夠多的符號,咱們就能夠迅速地定位目標程序的關鍵點,不會迷失在二進制的海洋中,因找不到方向而無從下手。
前文提到了,利用ida pro能夠幫助咱們獲取符號,特別是大於或等於6.2的版本,能識別Objective-C結構,能把Objective-C函數全都識別出來,以下圖,爲本人利用ida 6.4打開京東LeBook-1.0.4的截圖。
能夠從左邊Functions window看出來,沒有一個函數是以「sub_」開頭的,也就說全部的函數都識別出來了。
沒有ida pro的朋友可使用Hopper Disassembler,也能把Objective-C函數識別出來。在版權方要求刪除以前,能夠從看雪論壇下載到。聽說該軟件很好用,但因本人習慣了ida pro沒使用過它,不便進行過多的評論。
上面說到的兩個軟件都是商業軟件,不是人人都願意買或買得起,這時還能夠利用class-dump-z獲取符號信息,建議不能使用盜版、不肯意使用盜版而又沒錢買正版的朋友使用,它的命令行以下:
class-dump-z.exe -u armv7 -A -a iRead > iRead.txt
class-dump-z.exe -u armv7 -A -a iRead -H -o C:\
其中-u armv7這個參數比較有用,而class-dump和class-dump-x 不支持-u armv7參數。兩年前,ida pro尚未更新到6.2,Hopper Disassembler尚未問世,class-dump-z是惟一的選擇。本人就是使用它來獲取符號信息的。
上面的命令行生成的iRead.txt文件的部份內容以下圖:
能夠很清楚地看到類叫什麼名,是如何定義的,有哪些成員,繼承了哪些接口,還有函數的實現地址。這些信息都是對逆向頗有用的信息。
3.3用gdb進行調試
有了足夠的符號信息,經過靜態分析,咱們能夠初步猜出程序的邏輯,哪裏是關鍵點,再經過動態跟蹤來證明猜測,結合上面給出的調試的步驟,下面開始用gdb進行調試:
ps –au 注意輸出信息中目標進程的pid
gdb –p pid 從上面的輸出信息獲得進程pid
info sh 注意目標的基地址,記爲base
break *(0xAABBCC+base) 0xAABBCC爲你想下斷點的地址
display /x $r0
display / $pc | $cpsr.t
ni
在實際的環境,命令行以下:
MMs-iPod:~ root# ps -ax
PID TTY TIME CMD
……(省略部分無關記錄)
12416 ?? 0:02.52 /var/mobile/Applications/4DFD17D1-39AC-4F10-8AB8-3A4CB99E9E77/iRead.app/iRead
……(省略部分無關記錄)
MMs-iPod:~ root# gdb -p 12416
/usr/bin/gdb: line 55: awk: command not found
warning: unrecognized host cpusubtype , defaulting to host==armv7.
GNU gdb 6.3.50-20050815 (Apple version gdb-1708 + reverse.put.as patches v0.4) (Mon Apr 16 00:53:47 UTC 2012)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "arm-apple-darwin".
/private/var/root/12416: No such file or directory
Attaching to process 12416.
Reading symbols for shared libraries . done
Reading symbols for shared libraries ............................................................................................................................................. done
Reading symbols for shared libraries + done
0x39556eb4 in mach_msg_trap ()
(gdb) info sh
info sh
The DYLD shared library state has not yet been initialized.
Requested State Current State
Num Basename Type Address Reason | | Source
| | | | | | | |
1 iRead - 0x26000 exec C C /private/var/mobile/Applications/4DFD17D1-39AC-4F10-8AB8-3A4CB99E9E77/iRead.app/iRead at 0x26000 (offset 0x25000)
(objfile is) [memory object "/private/var/mobile/Applications/4DFD17D1-39AC-4F10-8AB8-3A4CB99E9E77/iRead.app/iRead" at 0x26000]
這條命令info sh很重要,特別強調一下。不少網友在QQ羣小窗問我這個問題,ida裏看到某個關鍵函數的地址爲0x1C4038,用gdb成功附加進程後,break *0x1C4038下斷點,總是斷不下來,爲何呢。出現這樣的狀況是由於斷點沒下對,程序在編譯時啓用了PIE(Position Independent Executables),該安全機制是從ios4.3開始引進的,和Windows下的ASLR相似,這時候下斷點的地址要加上基地址纔對。注意到括號裏的「(offset 0x25000)」,故這樣下斷(gdb) break *(0x25000+0x1C4038)
Breakpoint 1 at 0x1e9038
若是不願定斷點下得對不對,可用下面的gdb命令來確認,
(gdb) x/10i (0x25000+0x1C4038)+1
0x1e9039: b5 03 push {r4, r5, r6, r7, lr}
0x1e903b: af 4d add r7, sp, #12
0x1e903d: f8 04 8d 84 str.w r8, [sp, #-4]!
0x1e9041: b0 05 sub sp, #16
0x1e9043: 46 00 mov r5, r0
0x1e9045: 20 0c movs r0, #0
0x1e9047: 46 03 mov r4, r1
0x1e9049: 90 02 str r0, [sp, #12]
0x1e904b: 90 00 str r0, [sp, #8]
0x1e904d: 2a 18 cmp r2, #0
看到gdb反彙編出來的代碼和ida裏顯示的同樣,繼續運行
(gdb) c
Continuing.
程序運行後,在iOS設備上點擊一本書暢讀,斷點被觸發中斷下來,這時能夠用以下display命令顯示和ni或si一步一步跟進去分析。
Breakpoint 1, 0x001e9038 in dyld_stub_pthread_key_create ()
(gdb) display /x $r0
1: /x $r0 = 0x1fdae580
(gdb) display /i $pc | $cpsr.t
2: x/i $pc | $cpsr.t 0x1e9038: f0 b5 push {r4, r5, r6, r7, lr}
補充一點,若是以爲每次下斷的地址都要加上基地址比較麻煩,能夠像咱們在Windows下調試啓用了ASLR功能的程序那樣操做,經過把PE頭中表示ASLR的bit清零來禁用ASLR功能。對於iOS系統,mach_header是以下定義的,
struct mach_header
{
uint32_t magic;
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
uint32_t filetype;
uint32_t ncmds;
uint32_t sizeofcmds;
uint32_t flags;
};
根據頭文件裏的「#define MH_PIE 0x200000」,咱們只要把flags裏bit21清零便可。對程序進行了改動,從新簽名一下「ldid –S iRead」,再從新運行,就會發現程序基地址不會再變化了。
還能夠用removePIE(https://github.com/peterfillmore/removePIE)程序來幫咱們作相同的工做。能夠從代碼看出工做原理是徹底同樣的。
最後,如何以爲gdb界面不夠友好,用它來調試得不爽,還能夠參考論壇obaby的帖子 (http://bbs.pediy.com/showthread.php?t=138472),用ida pro來進行圖形化界面的調試。
4. 京東LeBook不嚴謹邏輯的繞過的演示
4.1背景交待
京東商城(www.360buy.com)上10元的暢讀卡,能夠下載京東LeBook軟件後,用該軟件下載1000本電子書來閱讀,可是有兩個限制,一是要聯網才能看,二是隻能在一個月內看。還有下載的電子書以加密形式存在。
通過分析發現京東LeBook驗證邏輯不是很嚴謹,既然解密pdf所須要的certificate都設計爲從網絡傳送過來了,是否是暢讀卻簡單地經過本地數據庫來標識,這使得上面兩個限制能夠去掉。經過替換sqlite3數據庫(jdreader.db)便可實現。
4.2繞過步驟
解壓附件中的injectHook1.zip(會員區有bin)到如下目錄, /Library/MobileSubstrate/DynamicLibraries,並執行命令:chmod +x injectHook1.dylib給它加上可執行屬性。該dylib對iRead進行了兩處Hook,一處是讓iRead認爲設備的udid爲「4A696E67646F6E67283336306275792E636F6D29」(下文的random就是由它計算獲得的),另外一處是把解密所須要的certificate寫到數據庫。
註冊A、B兩帳號,A帳號出10元買一個暢讀月卡,用A帳號從設備登陸而後註銷;B帳號從設備登陸而後註銷,最後用A再次登陸。
在iOS設備上下載你所想要的電子書(按照遊戲規則最多可下載1000本),在設備上點暢讀,每本書打開一次。這樣才能確保解密所須要的certificate存到數據庫裏。
結束京東LeBook程序,把它的數據庫文件複製到電腦上,路徑爲:/Library/Application Support/Root/jdreader.db,對該數據庫文件用SQLite Database Browser程序 (http://sqlitebrowser.sourceforge.net/)執行下面幾條SQL命令
update LocalBook set userid=2 where isJoyRead=1 and hasdownload=1
update LocalBook set isJoyRead=0 where isJoyRead=1 and hasdownload=1
update User set random="0001X2NNi+0gEX6kKfArTVWJCBZnWjRBeWFpWmZBZHpiYVQ=" where id=2
把數據庫文件複製回去,再次啓動京東LeBook程序,接下來
4.3 讓咱們一塊兒來見證奇蹟的時刻
能夠看到,暢讀標誌沒有了,斷網狀況點擊能順利閱讀了,兩個限制成功地去掉了。
參考文獻:
1. 《Hacking and Securing iOS Applications》
2. https://developer.apple.com/library/...reference.html
最後
感謝cd-team的各位朋友,DarkMage(dm557),Windknown,Daniel,s1mbily,Danniez等。
感謝kanxue,Albert_liuwei等各位朋友!html