iOS逆向學習之四(初識Mach-O)

什麼是Mach-O文件?

Mach-O是Mach object的縮寫,是Mac\iOS上用來存儲程序、庫的標準格式html

Mach-O文件類型

  • 能夠點擊下載xnu源碼,在源碼中的EXTERNAL_HEADERS/mach-o/loader.h 文件中,咱們能夠看到Mach-O格式的全部文件類型

xun是蘋果MacOS\iOS等操做系統的內核git

  • 常見的Mach-O文件類型
Mach-O類型 示例文件
MH_OBJECT 目標文件(.o)
靜態庫文件(.a)注:
MH_EXECUTE 可執行文件,存放App的全部源碼信息,在.app/xx
MH_DYLIB 動態庫文件.dylib 或者 .framework/xx
MH_DYLINKER 動態連接編輯器,也就是以前所說的/usr/lib/dyld工具
MH_DSYM 此文件中存儲這二進制文件符號信息,在開發中,咱們常用此文件來分析App的崩潰信息

Mach-O的基本結構

能夠點擊官網查看Mach-O的介紹。github

Mach-O組成

Mach-O由3個部分組成緩存

  • Header,包含文件類型、目標架構類型等等
  • Load commands,是描述文件在虛擬內存中的邏輯結構和佈局,至關於一份目錄索引
  • Raw segment data,在Load commands中所定義的Segment,在這裏都能找到原始數據。

Raw segment data存放了全部的原始數據,而Load commands至關於Raw segment data的索引目錄sass

窺探Mach-O的結構

經常使用工具

  • 命令行工具,經過file命令查看Mach-O文件的基本信息
file 文件路徑
複製代碼
  • otool,查看Mach-O特定部分和段的內容
#查看Mach-O文件的header信息
otool -h 文件路徑

#查看Mach-O文件的load commands信息
otool -l 文件路徑
複製代碼

更多使用方法,終端輸入otool -help查看bash

  • lipo,用來處理多架構Mach-O文件,經常使用命令以下
#查看架構信息
lipo -info 文件路徑

#導出某種類型的架構
lipo 文件路徑 -thin 架構類型 -output 輸出文件路徑

#合併多種架構類型
lipo 文件路徑1 文件路徑2 -output 輸出文件路徑
複製代碼

Universal Binary(通用二進制文件)

通用二進制文件就是同時適用於多種架構的二進制文件,它包含了多種不一樣架構的獨立的二進制文件,它有如下特色網絡

  • 由於須要存儲多種架構的代碼,因此通用二進制文件要比單架構二進制文件要大
  • 由於兩種種架構之間能夠共用一些資源,因此兩種架構的通用二進制文件大小不會達到單一架構版本的兩倍。
  • 運行過程當中只會調用其中的部分代碼,因此運行起來不會佔用額外的內存
  • 通用二進制文件一般也被稱爲「胖二進制文件(Fat binary)」

dyld和Mach-O

dyld是iOS中用來加載可執行文件、動態庫的工具,其實它自己也是一個Mach-O文件。架構

什麼是dyld?
  • dyld 動態加載器(又叫作動態連接編輯器)
  • dyld的源碼能夠點擊此處下載
dyld的做用。

dyld能夠用來加載如下三種類型的Mach-O文件app

  • MH_EXECUTE
  • MH_DYLIB
  • MH_BUNDLE

經過查看dyld的源碼能夠看到加載文件時的類型校驗框架

從編碼到App安裝到手機

想要了解Mach-O文件,首先要了解從編寫代碼,開發App到App打包並安裝到手機上的整個過程。

  • 首先咱們編寫完成代碼以後,會經過LLVM編譯器預處理咱們的代碼,好比將宏放在指定的位置
  • 預處理結束以後,LLVM會對代碼進行詞法分析和語法分析,生成AST。AST是抽象語法樹,主要用來進行快速遍歷,實現靜態代碼檢查的功能。
  • AST會生成IR,IR是一種更加接近機器碼的語言,經過IR能夠生成不一樣平臺的機器碼。對於iOS平臺,IR生成的可執行文件就是Mach-O.
  • 而後經過連接器將符號和地址綁定在一塊兒,而且將項目中的多個Mach-O文件合併成一個Mach-O文件。
  • 最後經過簽名等操做生成.app文件,而後對.app文件進行壓縮就生成了咱們能夠安裝的ipa包。
  • 固然,ipa包的安裝途徑有兩種:
    • 經過開發者帳號上傳到App Store,而後在App Store上下載安裝。
    • 經過PP助手、iFunBox、Xcode等工具來安裝

逆向App,咱們須要作哪些工做?

初步瞭解了什麼是Mach-O文件,以及App從開發到安裝的過程,咱們就能夠來學習如何逆向一款App

  • 界面分析 經過以前的學習,咱們已經可使用Cycript和Reveal對App的界面進行分析
  • 代碼分析 iOS開發中,全部的代碼最後都會通過編譯生成Mach-o文件,因此咱們須要對Mach-O文件進行靜態分析

靜態分析的工具備MachOView、class-dump、Hopper Disassembler、ida等等,後面會一一學習

  • 動態調試 除了靜態分析,咱們還須要運行目標App,對App進行動態調試

動態調試的工具備debugserver、LLDB等等

  • 代碼編寫、注入 進行完界面分析、代碼分析和動態調試以後,咱們能夠在特定位置注入咱們本身寫的代碼,必要時能夠從新簽名而且打包ipa

調試工具

calss-dump

class-dump的做用就是把Mach-O文件的class信息給導出來,生成對應的.h頭文件

  • 能夠點擊官網下載class-dump工具包
  • 下載完成以後將其中的class-dump可執行文件複製到Mac上的/usr/local/bin目錄中,這樣在終端就能識別class-dump命令了

在Mac中,終端執行的全部指令都會去/usr/bin目錄和/usr/local/bin目錄下尋找

  • class-dump的經常使用命令以下
# -H表示須要生成頭文件 -o用於指定頭文件的存放目錄
class-dump -H Mach-O文件路徑 -o 頭文件存放目錄
複製代碼

Hopper Disassmbler

Hopper Disassmbler能夠將Mach-O文件的機器語言反編譯成彙編代碼、OC僞代碼或者是Swift僞代碼。最常使用的快捷鍵有

#找出哪裏引用了這個方法
Shift + Option + X
複製代碼

下載地址:pan.baidu.com/s/1yP_VcBlQ…


靜態庫和動態庫

在iOS開發中,有不少功能都是現成可用的,不關你的App在用,其它的App也在用,好比UIKit框架、GUI框架、I/O、網絡等等。這些庫都是經過連接器連接到Mach-O文件中的。

靜態庫

靜態庫是編譯時連接的庫,須要鏈接進入Mach-O文件中,若是須要更新就必須從新編譯一次,沒法作到動態加載和更新

動態庫

動態庫是運行時連接的庫。

Mach-O是文件編譯以後的產物,因此動態庫並無參與Mach-O文件的編譯和連接。因此Mach-O文件中沒有包含動態庫的符號定義,也就是說這些符號會直接顯示未定義,可是他們的名字和對應庫的路徑會被記錄下來。在運行時經過dlopen和dlsym導入動態庫時,會根據記錄的路徑找到對應的庫,再經過記錄的名字符號找到綁定的地址。

動態庫共享緩存(dyld shared cache)

從iOS3.1開始,爲了提升性能,絕大部分的系統動態庫文件都打包存放到了一個緩存文件中(dyld shared cache),緩存路徑是/System/Library/Caches/com.apple.dyld/dyld_shared_cache_armX

dyld_shared_cache_armX裏面的X表明ARM處理器指令集的架構

ARM指令集

ARM指令集(CPU指令的集合)有如下幾種

ARM指令集 支持的設備
armv6 iPhone、iPhone3G
iPod Touch、iPod Touch2
armv7 iPhone3GS、iPhone四、iPhone4S
iPad、iPad二、iPad3(The New iPad)、iPad mini
iPod、Touch3G、iPod Touch四、iPod Touch5
armv7s iPhone五、iPhone5C、iPad4
arm64 iPhone5S、iPhone六、iPhone6 Plus、iPhone6S、iPhone6S Plus
iPhoneSE、iPhone七、iPhone7Plus、iPhone八、iPhone8 Plus、iPhoneX
iPad五、iPad Air、iPad Air二、iPad Pro、iPad Pro2
iPad mini with Retina display、iPad mini三、iPad mini4
iPod Touch6

以上全部的指令集都是向下兼容的 爲何要使用動態庫共享緩存呢?最大的好處就是節省內存。

從動態庫共享緩存抽取動態庫

因爲動態庫共享緩存太大,若是想獲取其中某個動態庫,例如UIKit,就須要從動態庫共享緩存中抽取對應的動態庫

  • 使用dyld源碼中提供的方式來進行抽取,工具在源碼中的launch-cache/dsc_extractor.cpp文件中

    • 首先須要去掉源碼中的#if 0判斷
    • 而後使用以下命令編譯dsc_extractor.cpp文件
    clang++ -o dsc_extractor dsc_extractor.cpp
    複製代碼

    此處是將dsc_extractor.cpp編譯生成可執行文件dsc_extractor

    • 進入執行文件dsc_extractor所在目錄。經過如下的命令來抽取動態庫
    ./dsc_extractor 動態庫共享緩存文件的路徑 用於存放抽取結果的目錄
    複製代碼

    建議抽取armv7s架構的動態庫,arm64抽取時會報以上錯誤,緣由是dsc_extractor.bundle不能在Xcode10以後使用

    • 抽取完成以後,使用Hopper Disassmbler打開想要逆向的動態庫,就能夠看到動態庫中的源碼信息。

相關文章
相關標籤/搜索