格式

1. header
header 包含了,cpu類型,加載command的數(shù)量,文件類型等信息
/*
* The 32-bit mach header appears at the very beginning of the object file for
* 32-bit architectures.
32位 架構(gòu)數(shù)據(jù)結(jié)構(gòu)類型
*/
struct mach_header {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cpu specifier */
cpu_subtype_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
};
/* Constant for the magic field of the mach_header (32-bit architectures) */
#define MH_MAGIC 0xfeedface /* the mach magic number */
#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */
/*
* The 64-bit mach header appears at the very beginning of object files for
* 64-bit architectures.
64位架構(gòu)數(shù)據(jù)結(jié)構(gòu)類型
*/
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier */
// 識(shí)CPU的架構(gòu) arm x86, i386
cpu_type_t cputype; /* cpu specifier */
// 體的CPU類型,區(qū)分不同版本的處理器
cpu_subtype_t cpusubtype; /* machine specifier */
// 文件類型
uint32_t filetype; /* type of file */
//加載了多少command,每個(gè)LoadCommands代表了一種Segment的加載方式
uint32_t ncmds; /* number of load commands */
//LoadCommand的大小,主要用于劃分Mach-O文件的‘區(qū)域’
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
uint32_t reserved; /* reserved */
};
/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /*
經(jīng)常遇見(jiàn)的Mach-O文件類型:
MH_OBJECT,這種類型的文件有目標(biāo)文件(.o)、靜態(tài)庫(kù)文件(.a) (靜態(tài)庫(kù)文件就是N個(gè).o文件合并在一起的)
MH_EXECUTE,可執(zhí)行文件,例如上面說(shuō)的Super文件
MH_DYLIB,動(dòng)態(tài)庫(kù)文件,包括.dylib、.framework
MH_DYLINKER,動(dòng)態(tài)鏈接編輯器,例如:位于手機(jī)這里的Device/usr/lib/的dyld程序
MH_DSYM,存儲(chǔ)二進(jìn)制符號(hào)信息的文件,dsym文件常用于分析APP的崩潰信息
loadCommands
用來(lái)描述文件在虛擬地址中的布局結(jié)構(gòu),就是存儲(chǔ)著各段數(shù)據(jù)的大小,分段,地址等信息
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
cmd
這些加載指令清晰地告訴加載器如何處理二進(jìn)制數(shù)據(jù),有些命令是由內(nèi)核處理的,有些是由動(dòng)態(tài)鏈接器處理的。在源碼中有明顯的注釋來(lái)說(shuō)明這些是動(dòng)態(tài)連接器處理的。
根據(jù)cmd字段的類型不同,使用了不同的函數(shù)來(lái)加載.看一看在內(nèi)核代碼中不同的command類型都有哪些作用。
LC-SEGMENT;LC-SEGMENT-64 在內(nèi)核中由load-segment 函數(shù)處理(將segment中的數(shù)據(jù)加載并映射到進(jìn)程的內(nèi)存空間去)
LC-LOAD-DYLINKER 在內(nèi)核中由load-dylinker 函數(shù)處理(調(diào)用/usr/lib/dyld程序)
LC-UUID 在內(nèi)核中由load-uuid 函數(shù)處理 (加載128-bit的唯一ID)
LC-THREAD 在內(nèi)核中由load-thread 函數(shù)處理 (開(kāi)啟一個(gè)MACH線程,但是不分配??臻g)
LC-UNIXTHREAD 在內(nèi)核中由load-unixthread 函數(shù)處理 (開(kāi)啟一個(gè)UNIX posix線程)
LC-CODE-SIGNATURE 在內(nèi)核中由load-code-signature 函數(shù)處理 (進(jìn)行數(shù)字簽名)
LC-ENCRYPTION-INFO 在內(nèi)核中由 set-code-unprotect 函數(shù)處理 (加密二進(jìn)制文件)
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* includes sizeof section_64 structs */
char segname[16]; /* segment name */
uint64_t vmaddr; /* memory address of this segment */
uint64_t vmsize; /* memory size of this segment */
uint64_t fileoff; /* file offset of this segment */
uint64_t filesize; /* amount to map from the file */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};
struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uint64_t addr; /* memory address of this section */
uint64_t size; /* size in bytes of this section */
uint32_t offset; /* file offset of this section */
uint32_t align; /* section alignment (power of 2) */
uint32_t reloff; /* file offset of relocation entries */
uint32_t nreloc; /* number of relocation entries */
uint32_t flags; /* flags (section type and attributes)*/
uint32_t reserved1; /* reserved (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
section段
存放著各段的原始數(shù)據(jù),就是Load commands區(qū)域描述的地址所指向的數(shù)據(jù)
注入dylib整體思路
1、讀取Mach-O文件信息到內(nèi)存中;
2、定義一個(gè)mach_header,將原來(lái)的mach_header寫(xiě)到新定義的mach_header中;
3、 在新定義的mach_header中依需將ncmds加1,sizeofcmds加上要注入的dylib庫(kù)的大?。?/p>
4、將新定義并修改好的mach_header從Mach-O最開(kāi)始部分覆蓋原文件的mach_header;
5、指針跳過(guò)sizeofcmds大小內(nèi)存;
6、定義一個(gè)dylib結(jié)構(gòu)體并賦值,即注入的 dylib 信息;
7、回退(新mach_header中sizeofcmds已包含要注入dylib的大?。┎⒏采w、寫(xiě)入 path 信息。
| 命令 | 數(shù)據(jù)結(jié)構(gòu) | 用途 |
|---|---|---|
| LC_UUID | uuid_command(page 20) | 指定圖像或其對(duì)應(yīng)的dSYM文件的128位UUID |
| LC_SEGMENT | segment_command | 加載此文件時(shí),定義映射到進(jìn)程地址空間中所需的文件段。而且每個(gè)段中包含了所有的節(jié) |
| LC_SYMTAB | symtab_command | 指定了文件的符號(hào)表。靜態(tài)鏈接器和動(dòng)態(tài)連接器連接文件的時(shí)候都需要用到這些信息,還可以通過(guò)調(diào)試器將符號(hào)映射到生成符號(hào)的原始源代碼文件。 |
| LC_DYSYMTAB | dysymtab_command | 指定了動(dòng)態(tài)連接器用到的附帶符號(hào)表信息 |
| LC_THREAD LC_UNIXTHREAD | thread_command | 對(duì)于可執(zhí)行文件,LC_UNIXTHREAD命令定義了進(jìn)程主線程的線程狀態(tài)。LC_THREAD和LC_UNIXTHREAD一樣,但是LC_THREAD不會(huì)引起內(nèi)核分配堆棧 |
| LC_LOAD_DYLIB | dylib_command | 定義此文件鏈接的動(dòng)態(tài)共享庫(kù)的名稱。 |
| LC_ID_DYLIB | dylib_command | 定義了動(dòng)態(tài)共享庫(kù)安裝名稱 |
| LC_PREBOUND_DYLIB | prebound_dylib_command | 對(duì)于此可執(zhí)行文件鏈接預(yù)綁定的共享庫(kù),指定使用的共享庫(kù)中的模塊。 |
| LC_LOAD_DYLINKER | dylinker_command | 指定內(nèi)核執(zhí)行加載文件所需的動(dòng)態(tài)連接器 |
| LC_ID_DYLINKER | dylinker_command | 標(biāo)志這個(gè)文件可以作為動(dòng)態(tài)連接器 |
| LC_ROUTINES | routines_command | 包含共享庫(kù)初始化例行程序的地址(由鏈接器的-init選項(xiàng)指定)。 |
| LC_ROUTINES_64 | routines_command_64 | 包含共享庫(kù)64位初始化例行程序的地址(由鏈接器的-init選項(xiàng)指定)。 |
| LC_TWOLEVEL_HINTS | twolevel_hints_command | 包含兩級(jí)命名空間查詢提示表。 |
| LC_SUB_FRAMEWORK | sub_framework_command | 將此文件標(biāo)識(shí)為傘形框架的子框架的實(shí)現(xiàn)。傘形框架的名稱存儲(chǔ)在字符串參數(shù)中。(傘形框架可以包含多個(gè)子框架,蘋(píng)果不推薦這樣使用) |
| LC_SUB_UMBRELLA | sub_umbrella_command | 指定此文件作為傘框架的子傘 |
| LC_SUB_LIBRARY | sub_library_command | 標(biāo)志這個(gè)文件可以作為傘框架的一個(gè)字庫(kù)的實(shí)現(xiàn)。請(qǐng)注意,Apple尚未為子庫(kù)定義受支持的位置。 |
| LC_SUB_CLIENT | sub_client_command | 子框架可以明確地允許另一個(gè)框架或包鏈接到它,方法是包含一個(gè)LC_SUB_CLIENT load命令,該命令包含框架的名稱或包的客戶端名稱。 |
1、(__TEXT,__text)
這里存放的是匯編后的代碼,當(dāng)我們進(jìn)行編譯時(shí),每個(gè).m文件會(huì)經(jīng)過(guò)預(yù)編譯->編譯->匯編形成.o文件,稱之為目標(biāo)文件。匯編后,所有的代碼會(huì)形成匯編指令存儲(chǔ)在.o文件的(__TEXT,__text)區(qū)((__DATA,__data)也是類似)。鏈接后,所有的.o文件會(huì)合并成一個(gè)文件,所有.o文件的(__TEXT,__text)數(shù)據(jù)都會(huì)按鏈接順序存放到應(yīng)用文件的(__TEXT,__text)中。
2、(__DATA,__data)
存儲(chǔ)數(shù)據(jù)的section,static在進(jìn)行非零賦值后會(huì)存儲(chǔ)在這里,如果static 變量沒(méi)有賦值或者賦值為0,那么它會(huì)存儲(chǔ)在(__DATA,__bss)中。
3、Symbol Table
符號(hào)表,這個(gè)是重點(diǎn)中的重點(diǎn),符號(hào)表是將地址和符號(hào)聯(lián)系起來(lái)的橋梁。符號(hào)表并不能直接存儲(chǔ)符號(hào),而是存儲(chǔ)符號(hào)位于字符串表的位置。
4、String Table
字符串表所有的變量名、函數(shù)名等,都以字符串的形式存儲(chǔ)在字符串表中。
5、動(dòng)態(tài)符號(hào)表
動(dòng)態(tài)符號(hào)表存儲(chǔ)的是動(dòng)態(tài)庫(kù)函數(shù)位于符號(hào)表的偏移信息。(__DATA,__la_symbol_ptr) section 可以從動(dòng)態(tài)符號(hào)表中獲取到該section位于符號(hào)表的索引數(shù)組。