前幾篇文章中 :linux
shell 腳本自動重簽名與代碼注入shell
重籤應用調試與代碼修改 (Hook)windows
咱們對重簽名和代碼注入有了必定的瞭解 . 那麼這個過程當中咱們反覆提到一個最重要的文件 -- Mach-O
.緩存
那麼說來講去 , 這個Mach-O
究竟是個什麼 . 既然它這麼重要 , 那麼咱們有必要去好好的瞭解一下它 .bash
( 對概念不太感興趣的同窗能夠直接跳到第二章節 Mach-O
的文件結構 ) 微信
Mach-O
實際上是 Mach Object
文件格式的縮寫,是 mac
以及 iOS
上可執行文件的格式, 相似於 windows
上的 PE
格式 ( Portable Executable ) , linux
上的 elf
格式 ( Executable and Linking Format ) .架構
它是一種用於可執行文件、目標代碼、動態庫的文件格式。做爲 a.out
格式的替代,Mach-O
提供了更強的擴展性。函數
可是除了可執行文件外 , 其實還有一些文件也是使用的 Mach-O
的文件格式 .工具
屬於 Mach-O
格式的常見文件
- 目標文件 .o
- 庫文件
- .a
- .dylib
- Framework
- 可執行文件
- dyld ( 動態連接器 )
- .dsym ( 符號表 )
Tips : 使用 file
命令能夠查看文件類型
也就是說 Mach-O
並不是必定是可執行文件 , 它是一種文件格式 , 分爲 Mach-O Object
目標文件 、 Mach-O ececutable
可執行文件、 Mach-O dynamically
動態庫文件、 Mach-O dynamic linker
動態連接器文件、 Mach-O dSYM companion
符號表文件 , 等等 .
你們能夠本身經過 vim
幾個 .c
, 而後 clang
生成 .o
目標文件和可執行文件來玩一下 , 以便更好地理解這幾種文件以及其編譯的模式 .
那麼上圖中咱們還看到一個 arm64
, 這個是什麼意思呢 ?
- 在 release 模式下
- 支持 iOS 11.0 系統版本如下
當知足這兩個條件時 , 咱們的應用打包出來的 Mach-O ececutable
可執行文件是包含 arm64
以及 arm_v7
的架構的 , iPhone 5C
以上機型都是 64
位系統了 .
那麼包含了支持多架構的 Mach-O ececutable
可執行文件被稱爲 : 通用二進制文件 , 即多種架構均可讀取運行 .
另外 Xcode
中經過編譯設置 Architectures
是能夠更改所生成的 Mach-O ececutable
可執行文件的支持架構的 .
編譯器在生成
Mach-O
文件會選擇Architectures
以及Valid Architectures
的交集 , 所以想要支持多架構的話 , 在Valid Architectures
中繼續添加就能夠了 , 編譯生成Mach-O
以後 , 使用file
命令能夠檢查下結果 .
蘋果公司提出的一種程序代碼。能同時適用多種架構的二進制文件
同一個程序包中同時爲多種架構提供最理想的性能。
由於須要儲存多種代碼,通用二進制應用程序一般比單一平臺二進制的程序要大。
可是因爲兩種架構有共通的非執行資源,因此並不會達到單一版本的兩倍之多。
並且因爲執行中只調用一部分代碼,運行起來也不須要額外的內存。
通用二進制文件一般被稱爲 Universal binary
, 在 MachOView
等 中叫作 Fat binary
, 這種二進制文件是能夠徹底拆分開來 , 或者從新組合的 , 那麼接下來咱們來玩一下 .
10.3
.選擇 Release
( 測試完畢改回來 . 不然 run
太慢 )
iOS 10.3
以上 +
release
環境下會默認包含
arm64 + armv7
的架構 , 所以咱們本身加上
armv7s
和
arm64e
.
run
起來後找到 Mach-O
文件
能夠看到 , 咱們的 Fat binary
就已經生成好了 .
使用 lipo - info
命令也是能夠查看支持架構的
Fat binary
lipo macho文件名稱 -thin 要拆分哪一個架構 -output 拆分出來文件名
複製代碼
例:
lipo 通用二進制MachO_Test -thin armv7s -output macho_armv7s
複製代碼
而後咱們就看到文件夾多了一個 macho_armv7s
, 查看一下 :
另外拆分後源文件並不會改變.
Fat binary
lipo -create macho_arm64 macho_arm64e macho_armv7 macho_armv7s -output newMachO
複製代碼
合併後咱們來看下新生成的 和之前的文件的哈希值 .
如出一轍的 .
Tips:
這種方式在咱們合併靜態庫的時候會常常用到 , 由於靜態庫自己就是
Mach-O
文件嘛 , 另外咱們在逆向的時候 , 有時也常常會用這種方法拆分二進制文件 , 由於咱們只須要分析單一架構便可 , 無須肥大的二進制文件.
另外稍微補充一點 , 多架構二進制文件組合成通用二進制文件時 , 代碼部分是不共用的 ( 由於代碼的二進制文件不一樣的組合在不一樣的 cpu
上可能會是不一樣的意義 ) . 而公共資源文件是會共用的 .
Mach-O
的組成結構如圖所示包括了
Header
包含該二進制文件的通常信息
字節順序、架構類型、加載指令的數量等。
使得能夠快速確認一些信息,好比當前文件用於 32
位仍是 64
位,對應的處理器是什麼、文件類型是什麼
Load commands
一張包含不少內容的表
Data
一般是對象文件中最大的部分
Segement
的具體數據咱們來找一個 Mach-O 文件 使用 MachOView 或者 otool 命令去查看一下文件結構 .
那麼這個 Mach-O
到底這些部分存放的是什麼內容 , 加下來咱們就來一一探索一下 .
Header
中存儲的內容大體如上圖所示 , 那麼每一條到底對應着什麼呢 ? , 咱們打開源碼看一下, cmd + shift + o
, 搜索 load.h
, 找 mach_header_64
結構體.
struct mach_header_64 {
uint32_t magic; /* 魔數,快速定位64位/32位 */
cpu_type_t cputype; /* cpu 類型 好比 ARM */
cpu_subtype_t cpusubtype; /* cpu 具體類型 好比arm64 , armv7 */
uint32_t filetype; /* 文件類型 例如可執行文件 .. */
uint32_t ncmds; /* load commands 加載命令條數 */
uint32_t sizeofcmds; /* load commands 加載命令大小*/
uint32_t flags; /* 標誌位標識二進制文件支持的功能 , 主要是和系統加載、連接有關*/
uint32_t reserved; /* reserved , 保留字段 */
};
複製代碼
mach_header_64
相較於 mach_header
, 也就是 32
位頭文件 , 只是多了一個保留字段 . mach_header
是連接器加載時最早讀取的內容 , 它決定了一些基礎架構 , 系統類型 , 指令條數等信息.
Load Commands
詳細保存着加載指令的內容 , 告訴連接器如何去加載這個 Mach-O
文件.
經過查看內存地址咱們發現 , 在內存中 , Load Commands
是緊跟在 Mach_header
以後的 .
那麼這些 Load Commands
對應了什麼呢 ? 咱們以 arm64 爲例.
其中 _TEXT 段和 _DATA 段 , 是咱們常常須要研究的 , MachOView
下面也有詳細列出.
咱們來看看 _TEXT
段裏都存放了什麼 , 其實真正開始讀取就是從 _TEXT
段開始讀取的 .
名稱 | 內容 |
---|---|
_text |
主程序代碼 |
_stubs , _stub_helper |
動態連接 |
_objc_methodname |
方法名稱 |
_objc_classname |
類名稱 |
_objc_methtype |
方法類型 ( v@: ) |
_cstring |
靜態字符串常量 |
_DATA
在內存中是緊跟在 _TEXT
段以後的.
名稱 | 內容 |
---|---|
_got : Non-Lazy Symbol Pointers |
非懶加載符號表 |
_la_symbol_ptr : Lazy Symbol Pointers |
懶加載符號表 |
_objc_classlist |
類列表 |
...
以及以一些數據源 就不一一列舉了 .
另外有一點值得提一下的就是系統庫的方法 , 因爲是公用的 , 存放在共享緩存中 , 那麼咱們的 Mach-O
中調用系統方法 ,
例如 : 調用 NSLog("%@,@"haha");
這個方法的實現確定不在咱們的 Mach-O
裏 , 那麼它如何找到方法實現呢 ?
其實就是
dyld
在進行連接的時候 , 會將Mach-O
裏調用存放在共享緩存中的方法進行符號綁定 , 而這個符號在release
的時候是會被自動去掉的. 這也是咱們常用收集bug
工具時須要恢復符號表的緣由. 而所以fishhooh
在hook
系統函數的時候名字叫reBind
的緣由 .
關於符號綁定這一點咱們在講 fishhook
的時候會詳細講述一下 .
至此 , 整個 Mach-O
文件結構咱們已經講述完了 . 後續在逆向的過程當中涉及到具體存儲內容咱們會繼續介紹 .