iOS逆向工程

weixin_33936401發表於2016-08-31

2657976-febb32a7879d5971.png

Class Dump

安裝

class-dump是可以把OC執行時的宣告的資訊匯出來的工具,可以把未經加密的app的標頭檔案匯出來。下載原始碼解壓後,將class-dump複製到/usr/bin/class-dump,同時開啟Terminal,賦予其執行許可權:

sudo chmod 777 /usr/bin/class-dump

說明:class-dump的物件必須是未經加密的可執行檔案,從Appstore下載的都是經過加密的,需要脫殼,最好是從越獄渠道下載app來進行dump

使用

class-dump -H xxx.app -o test


Theos

安裝
  1. 安裝Command Line Tools
  2. 安裝MacPorts軟體,從官網根據Mac本身的版本下載
  3. 安裝完MacPorts後開啟終端輸入 sudo port -v self update 更新MacPorts到最新版本
  4. 更新完MacPorts後安裝DPKG檔案,在終端輸入sudo port -f install dpkg
  5. 配置環境變數,在~./bash_profile中加上這兩句:
  export THEOS=/opt/theos
  export PATH=/opt/theos/bin/:$PATH
  1. /opt/theos路徑下載安裝 sudo git clone --recursive https://github.com/theos/theos.git sudo git clone -b stableversion https://github.com/haorenqq/theos/ $THEOS
  2. 下載ldid工具到THEOS的bin目錄下
  • 在終端執行如下語句:
    git clone git://git.saurik.com/ldid.gitcd ldidgit submodule update --init./make.shcp -f ./ldid $THEOS/bin/ldid
  • 下載附件解壓,將附件中的openssl 資料夾放到 ldid 資料夾下
  • 將附件的make.sh放入ldid資料夾下替換同名檔案
  • 開啟附件裡面的Specifications資料夾,裡面應該有8個檔案
  • iPhoneOS開頭的四個檔案放到下面的目錄下(如果沒有,請自己建立一個)
    /應用程式/Xcode/Content/Developer/Platforms/IphoneOS.platform/Developer/Library/Xcode/Specifications
  • iPhone Simulator 開頭的另外四個檔案放入下面的目錄下(如果沒有,請同樣建立一個)。
    /應用程式/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications
  • 之後在下面的目錄下建立usr資料夾,usr資料夾下再建立一個名為bin的資料夾
    /應用程式/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/
    之後的目錄應該是
    /應用程式/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin

iOS OpenDev

安裝

下載地址
安裝失敗看這裡

如果還是遇到安裝失敗,但是在/opt下面還是建立了三個目錄:

  • iosopendev
  • iosopendevsetup
  • iosopendevuninstall

iosopendevsetup/bin裡面已經有一個shell指令碼:iod-setup。安裝的過程就是執行iod-setup。
直接執行:sudo ./iod-setup base
發現總是在下載某個東西時失敗,sublime iod-setup來定位,發現有三個downloadGithubTarball的地方,直接註釋掉,然後手動去下載這三個東西,並拷貝到iosopendev目錄。手動下載:
https://github.com/kokoabim/iOSOpenDev
https://github.com/kokoabim/iOSOpenDev-Xcode-Templates
https://github.com/kokoabim/iOSOpenDev-Framework-Header-Files
拷貝:

sudo cp -r iosopendev-master/* /opt/iosopendev/
(iosopendev裡,sudo mkdir templates)
sudo cp -r iosopendev-xcode-templates-master/* /opt/iosopendev/templates
(iosopendev裡,sudo mkdir frameworks)
sudo cp -r iosopendev-framework-header-files-master/* /opt/iosopendev/frameworks

再次安裝:
sudo ./iod-setup base
指定最新xcode sdk:
sudo ./iod-setup sdk -sdk iphoneos
成功,表現為:

  1. ~/library/developer/xcode 裡面會多出Templates/iosopendev
  2. sublime ~/.bash_profile會看到:
export iOSOpenDevPath=/opt/iOSOpenDev
export iOSOpenDevDevice=
export PATH=/opt/iOSOpenDev/bin:$PATH
  1. 啟動xcode,新建工程,多出一個“iOSOpenDev”的模板。


    2657976-4a23e1664201a87a.png

在終端執行~/.bash_profile, ~/.bash_login or ~/.profile
這3個檔案你在你電腦中看是否能找到,我是找到~/.bash_profile這個檔案,然後設定下面的環境變數

export iOSOpenDevPath=/opt/iOSOpenDev
export iOSOpenDevDevice=
 非固定值,使用者需要先在已經越獄的iphone上點選設定-網路-檢視當前網路的IP地址,填入
export PATH=/opt/iOSOpenDev:$PATH
export PATH=/opt/local/bin:$PATH
export PATH=/opt/local/sbin:$PATH

在裝置上安裝OpenSSH並且在裝置上新增SSH簽名:

  • 在Cydia裡搜尋OpenSSH安裝,建議更改SSH的預設密碼,預設密碼是:alpine。更改方法是在Mac上登陸裝置的SSH.命令ssh root@<裝置IP>,登陸後輸入命令passwd root,輸入新密碼。

接下來給SSH新增簽名:

  • iOSOpenDev/bin/資料夾下執行:./iosod sshkey -h <裝置IP>中間問你是否繼續,當然yes,詢問密碼預設是alpine。如果期間失敗了重新試下,可能是裝置螢幕關閉會斷網,這樣就不用密碼也可以登陸裝置SSH了。
使用

使用Logos tweak工程模板建立工程,接著在Build Settings中iOSOpenDevDevice設定為自己裝置的IP地址,裝置與你的Mac要在一個區域網內。

為了方便的除錯,還要在工程裡設定一些引數。這裡介紹下這些引數的意義,首先開啟程式設定TARGETS裡的工程:

  1. iOSOpenDevCopyOnBuild 布林值YES/NO 預設是NO:是否把生成的可執行檔案拷貝到/var/root/iOSOpenDevBuilds/[project name]/[executable name]路徑下,是為了方便那些遠端SSH控制的程式,可能暫時用不到
  2. iOSOpenDevDevice 設定你裝置的IP
  3. iOSOpenDevInstallOnProfiling 布林值 預設為YES,是否在build for profiling的時候直接遠端安裝到裝置上
  4. iOSOpenDevPath 不要修改此項,是iOSOpenDev的安裝路徑
  5. iOSOpenDevRespringOnInstall 布林值 預設為YES,是否在安裝後重啟SpringBoard

由於我們需要引用標頭檔案,所以還需要在Build Settings中將Header Search Paths中加一行/opt/theos/include

TARGETS > Build Phases > Link Binary With Libraries新增檔案/opt/iOSOpenDev/lib/libsubstrate.dylib

下面我們實現hook SpringBoard,當SPringBoard啟動的時候,我們就彈出一個Alert。

在.xm中加以下程式碼:

#import <SpringBoard/SpringBoard.h>
%hook SpringBoard
- (void)applicationDidFinishLaunching:(id)application {
  %orig;  
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Welcome" message:@"Welcome to your iPhone!" delegate:nil cancelButtonTitle:@"Thanks"  otherButtonTitles:nil]; 
  [alert show]; 
  [alert release];
}
%end

由於用到了UIAlertView屬於UIKit的東西,所以我們要新增連結庫UIKit.framework。

編譯的時候不能像正常的App點Run,點選Product->Build For->Build For Profiling或者Command+Shift+i,配置好裝置IP,程式會自動安裝到裝置裡,SpringBoard會重啟,重啟後應該就能看到Alert了。

  • 如果遇到CodeSign error: code signing is required for product type ‘Dynamic Library’ in SDK ‘iOS xx’ 錯誤
    看提示是跟簽名有關,找到工程的Code Signing Identity 專案,選擇Don’t Code Sign,用Xcode開啟
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Specifications/iPhoneOSProductTypes.xcspec
    檔案,看到一項CODE_SIGNING_ALLOWED,在 com.apple.package-type.static-library.static 項中是NO,而在 com.apple.package-type.static-library.dynamic 項中是YES。修改成NO,再次重啟,編譯成功。

Reveal

安裝

下載地址

破解
  1. 輸入rm ~/Library/Preferences/com.ittybittyapps.Reveal.plist
  2. 重啟電腦

這並不是完全破解,而是一直迴圈30天試用

整合

把Reveal整合到專案裡有兩種方式:

  1. 新增Reveal的framework到工程裡(不推薦)
  • 啟動Reveal --> Help --> Show Reveal Library in Finder,拖動新增Reveal.framework到工程中。選中 Copy items into destination group's folder (if needed)以及當前的targets
  • 確保Summary--> Linked Frameworks and Libraries目錄下,包含以下三個framework:Reveal.FrameworkCFNetwork.frameworkQuartzCore.frameworkCoreGraphics.framework
  • TARGETS中設定命令TARGETS --> Settings --> Other Linker Flags -->新增命令 -ObjC
  • 執行程式,切換到Reveal,左上部分下拉框選擇當前執行的程式就可以操作了
  1. 用LLDB命令(推薦)
  • 開啟終端,在終端裡輸入touch ~/.lldbinit命令來進行建立
  • 然後在輸入open ~/.lldbinit命令,以文字編輯器開啟此檔案
  • 把如下字元複製到文字編輯器裡:
command alias zxp_reveal_load_sim expr (Class)NSClassFromString(@"IBARevealLoader") == nil ? (void *)dlopen("/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib", 0x2) : ((void*)0)
command alias zxp_reveal_start expr (void)[(NSNotificationCenter*)[NSNotificationCenter defaultCenter] postNotificationName:@"IBARevealRequestStart" object:nil];

command alias 其格式為:command alias 別名 LLDB命令,因此如上zxp_reveal_start和zxp_reveal_load_sim就是我設定的別名,也可以替換成自己喜歡的名字

  • 然後執行專案,暫停app,這時候輸入zxp_reveal_load_sim,然後再輸入zxp_reveal_start,即可在Reveal裡審查你的UI元素了
    2657976-d2a6a2224b39749f.png
  • 兩條命令順利的話,控制檯則會列印INFO: Reveal Server started (Protocol Version 25).這條資訊,如果最後未能正常輸出,那就要好好檢查一下第一條命令裡的dlopen裡包含的路徑是否正確了

MesaSQLite

安裝

下載地址


OpenSSH

越獄安裝源:Cydia/Telesphoreo (http://apt.saurik.com)
SSH服務,就能使用ssh和scp命令來實現遠端登入和遠端複製檔案功能
ssh root@192.168.2.50
scp xxx.deb root@192.168.2.50:/var/root
OpenSSH預設密碼是alphine,安裝後建議立即修改登入密碼,需要修改的賬號有2個:root和mobile
ssh root@192.168.2.50
passwd


syslogd

越獄安裝源:syslogd (http://apt.saurik.com)
syslogd是用於記錄類UNIX中系統日誌 的守護程式,我們可以通過/var/log/syslog/看到對應的log輸出

安裝完成後重啟手機
執行tail命令(需要安裝Core Utilities源),終端上執行的Log就會列印實時出來了
tail -f /var/log/syslog

例如在hook程式碼中列印NSLog

%hook SBScreenShotter

-(void)saveScreenshot:(BOOL)screenshot {
    %orig;
    NSLog(@"hello: saveScreenshot: is called");
}

%end

Filter設定成com.apple.springboard,安裝並執行裝置,ssh連上終端後輸入下面命令,在截圖之後就能看見對應的日誌了
grep hello /var/log/syslog


GDB

越獄安裝源:radare (http://cydia.radare.org/)
全名The GNU Project Debugger,類UNIX作業系統中最強大的動態除錯工具之一
gdbinit是GDB的初始化甲苯,在GDB啟動過程中執行,對GDB有一定優化作用。下載之後,解壓得到gdbinit複製到iOS的/var/root目錄下,並重新命名為.gdbinit即可:
scp /Users/Downloads/Gdbinit-master/gdbinit root@192.168.2.50:/var/root/.gdbinit

除錯

通過ssh方式登入遠端手機
ssh root@192.168.2.50
使用gdb開啟除錯
gdb
gdb注入程式
attach PID或者attach ProcessName
例如attach SpringBoard,命令完成後,程式停止,等待我們輸入進一步命令。

注意:所有GDB命令涉及的正確地址必須來自本機二進位制檔案,可以通過dyld_decache獲得,從SDK中的到的二進位制檔案與本機通常不同

常用GDB命令:

  • b break 設定斷點

    • b function 在函式起始位置設定斷點
      例如:b object_getClass

    • b *address 在記憶體地址設定斷點
      例如:b *(0x59000 + 0x00009218)
      同一程式在每次啟動時在記憶體中的位置都會發生一定偏移

      某行程式碼地址 = ASLR造成的偏移 + 本行程式碼在檔案中的地址

      ASLR可以通過info shinfo share檢視,offset標註了此二進位制檔案在記憶體中的ASLR偏移。
      目的碼在檔案中的地址可以通過IDA得到。

    • info b 檢視斷點資訊

    • disable breakpointNumber 禁用某個斷點

    • enable breakpointNumber 啟用某個斷點

    • delete breakpointNumber 刪除某個斷點

  • ni nexti 執行下一條指令,不進入函式體

  • si stepi 執行下一條指令,進入函式體

  • p print 列印

    • p $r0 列印暫存器r0中的值
  • pt ptype 列印某處的資料型別(無法列印Object-C型別)

  • po print-object 列印Object-C型別

  • x examine 列印指標指向的資料的值

  • set 設定某個值

    • set $r0 = 0x1

** IDA宜靜,GDB宜動 **


LLDB

因為Apple已經棄gdb投lldb,所以很多gdb的指令已經不再適用,我們需要配置lldb來進行除錯

  1. 配置debugserver
  • 在iOS中安裝debugserver
    debugserver執行在iOS上,顧名思義,它作為服務端,實際執行LLDB(作為客戶端)傳過來的命令,再把執行結果反饋給LLDB,顯示給使用者,即所謂的“遠端除錯”。在預設情況下,iOS上並沒有安裝debugserver,只有在裝置連線過一次Xcode,並在Window→Devices選單中新增此裝置後,debugserver才會被Xcode安裝到iOS的/Developer/usr/bin/目錄下。
  • 拷貝debugserver到本機
    scp root@iOSIP:/Developer/usr/bin/debugserver ~/debugserver
  • 給debugserver新增task_for_pid許可權,下載ent.xml到OSX的/Users/目錄,然後執行:
    /opt/theos/bin/ldid -Sent.xml debugserver
    注意,此處-S選項與ent.xml之間是沒有空格的
    正常情況下,上面這條命令會在5秒內執行完畢。如果ldid卡住了,執行超時,就換一種方案:下載ent.plist/Users/,然後執行:
    codesign -s - --entitlements ent.plist -f debugserver
  1. 將經過處理的debugserver拷回iOS
    scp ~/debugserver root@iOSIP:/usr/bin/debugserver
    ssh root@iOSIP
    root# chmod +x /usr/bin/debugserver
    這裡之所以把處理過的debugserver存放在iOS的/usr/bin/下,而沒有覆蓋/Developer/usr/bin/下的原版debugserver,一是因為原版debugserver是不可寫的,無法覆蓋;二是因為/usr/bin/下的命令無須輸入全路徑就可以執行,即在任意目錄下執行debugserver都可啟動處理過的debugserver。
  2. 在iOS上用debugserver來attach程式
    debugserver *:1234 -a "SpringBoard"
    成功後會顯示:
    2657976-13ae4c676af24791.png
  3. 在OSX上用lldb遠端除錯
    首先在Terminal中執行lldb,然後輸入以下命令:
    process connect connect://iOSIP:1234
    注意,這條命令執行耗時比較長
    執行成功後會顯示:
    2657976-2225a1259d10b20a.png
  4. 獲取ASLR的offset
    image list -o -f
    2657976-63a9540fcf10bb91.png

    其中第一列[X]是image的序號,不用管;第二列是ASLR的offset(也就是對應image的虛擬記憶體slide);第三列是image的全路徑和slide之後的基地址,也不用管~所以第二列就是我們需要的資訊。

更多LLDB指令,參考GDB和LLDB指令對照表


Cycript

越獄安裝源:Cydia/Telesphoreo (http://apt.saurik.com/)
可以寫App的指令碼語言,測試函式功能

ssh root@192.168.2.50
cycript
出現 cy#提示符,說明成功啟動Cycript
ctrl+D 退出Cycript

程式注入方式呼叫Cycript測試函式:

  • 找到程式名或PID,例如如下SpringBoard程式PID是597
    ps ax | grep SpringBoard
    2657976-3a4d51efa9ae44e3.png
  • 接下來輸入cycript -p 597cycript -p SpringBoard,把Cycript鉤到SpringBoard上,這時Cycript就已經執行在SpringBoard程式裡了。
  • 開始測試,cy語句不以引號作為結尾,所以每次只能輸入一條語句
cy# var alertView = [[UIAlertView alloc] initWithTitle:@"iOSRE" message:@"test" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]
cy# [alertView show]
cy# [alertView release]

執行上面語句後,SpringBoard就會彈出對話方塊了

  • 測試截圖程式碼
cycript -p SpringBoard
var shotter = [SBScreenShotter sharedInstance]
[shotter saveScreenshot:YES]
  • 彩色閃屏程式碼
cycript -p SpringBoard
var flash = [SBScreenFlash mainScreenFlasher]
var color = [UIColor magentaColor]
[flash flashColor:color withCompletion:nil]

ARM彙編

OC/C/C++ 操作物件:變數
ARM彙編 操作物件:暫存器、記憶體、棧
暫存器是CPU的變數,記憶體是更多的變數,效能差於暫存器,前兩者是全域性變數,棧是本地變數

ARM指令集

  • 資料操作指令
    op{cond}{s} Rd, Rn, Op2
  • 記憶體操作指令
    op{cond}{type} Rd, {Rn, Op2}
    基礎指令:
    • LDR:將資料從記憶體讀出,存到暫存器
    • STR:將資料從暫存器讀出,存到記憶體
  • 分支指令
    • 無條件分支
    • 條件分支

ARM呼叫規則

  • 前言 prologs
    1. LR入棧
    2. R7入棧
    3. R7 = SP
    4. 將需保留的暫存器原始值入棧
    5. 為本地變數開闢空間
  • 後記 epilogs
    1. 釋放本地變數佔用空間
    2. 將需保留的暫存器原始值出棧
    3. R7出棧
    4. LR出棧,PC = LR

函式前4個引數放在R0~R3中,其他引數放在棧中,返回值放在R0中

暫存器用途:

  • R0~R3:傳參和返回值
  • R7:指標,指向母函式與被呼叫子函式在棧中的交界
  • R13:SP暫存器
  • R14:LR暫存器,儲存返回地址
  • R15:PC暫存器

一些例子

相關文章