格式
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 */
// 識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,每個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)常遇見的Mach-O文件類型:
MH_OBJECT,這種類型的文件有目標(biāo)文件(.o)、靜態(tài)庫文件(.a) (靜態(tài)庫文件就是N個.o文件合并在一起的)
MH_EXECUTE八孝,可執(zhí)行文件董朝,例如上面說的Super文件
MH_DYLIB,動態(tài)庫文件干跛,包括.dylib子姜、.framework
MH_DYLINKER,動態(tài)鏈接編輯器楼入,例如:位于手機(jī)這里的Device/usr/lib/的dyld程序
MH_DSYM哥捕,存儲二進(jìn)制符號信息的文件,dsym文件常用于分析APP的崩潰信息
loadCommands
用來描述文件在虛擬地址中的布局結(jié)構(gòu),就是存儲著各段數(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)核處理的扭弧,有些是由動態(tài)鏈接器處理的。在源碼中有明顯的注釋來說明這些是動態(tài)連接器處理的记舆。
根據(jù)cmd字段的類型不同鸽捻,使用了不同的函數(shù)來加載.看一看在內(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ù)處理 (開啟一個MACH線程御蒲,但是不分配棧空間)
LC-UNIXTHREAD 在內(nèi)核中由load-unixthread 函數(shù)處理 (開啟一個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碧磅、定義一個mach_header碘箍,將原來的mach_header寫到新定義的mach_header中;
3鲸郊、 在新定義的mach_header中依需將ncmds加1丰榴,sizeofcmds加上要注入的dylib庫的大小秆撮;
4四濒、將新定義并修改好的mach_header從Mach-O最開始部分覆蓋原文件的mach_header;
5职辨、指針跳過sizeofcmds大小內(nèi)存盗蟆;
6、定義一個dylib結(jié)構(gòu)體并賦值舒裤,即注入的 dylib 信息喳资;
7、回退(新mach_header中sizeofcmds已包含要注入dylib的大刑诠)并覆蓋仆邓、寫入 path 信息鲜滩。
命令 | 數(shù)據(jù)結(jié)構(gòu) | 用途 |
---|---|---|
LC_UUID | uuid_command(page 20) | 指定圖像或其對應(yīng)的dSYM文件的128位UUID |
LC_SEGMENT | segment_command | 加載此文件時,定義映射到進(jìn)程地址空間中所需的文件段宏赘。而且每個段中包含了所有的節(jié) |
LC_SYMTAB | symtab_command | 指定了文件的符號表绒北。靜態(tài)鏈接器和動態(tài)連接器連接文件的時候都需要用到這些信息,還可以通過調(diào)試器將符號映射到生成符號的原始源代碼文件察署。 |
LC_DYSYMTAB | dysymtab_command | 指定了動態(tài)連接器用到的附帶符號表信息 |
LC_THREAD LC_UNIXTHREAD | thread_command | 對于可執(zhí)行文件闷游,LC_UNIXTHREAD命令定義了進(jìn)程主線程的線程狀態(tài)。LC_THREAD和LC_UNIXTHREAD一樣贴汪,但是LC_THREAD不會引起內(nèi)核分配堆棧 |
LC_LOAD_DYLIB | dylib_command | 定義此文件鏈接的動態(tài)共享庫的名稱脐往。 |
LC_ID_DYLIB | dylib_command | 定義了動態(tài)共享庫安裝名稱 |
LC_PREBOUND_DYLIB | prebound_dylib_command | 對于此可執(zhí)行文件鏈接預(yù)綁定的共享庫,指定使用的共享庫中的模塊扳埂。 |
LC_LOAD_DYLINKER | dylinker_command | 指定內(nèi)核執(zhí)行加載文件所需的動態(tài)連接器 |
LC_ID_DYLINKER | dylinker_command | 標(biāo)志這個文件可以作為動態(tài)連接器 |
LC_ROUTINES | routines_command | 包含共享庫初始化例行程序的地址(由鏈接器的-init選項(xiàng)指定)业簿。 |
LC_ROUTINES_64 | routines_command_64 | 包含共享庫64位初始化例行程序的地址(由鏈接器的-init選項(xiàng)指定)。 |
LC_TWOLEVEL_HINTS | twolevel_hints_command | 包含兩級命名空間查詢提示表阳懂。 |
LC_SUB_FRAMEWORK | sub_framework_command | 將此文件標(biāo)識為傘形框架的子框架的實(shí)現(xiàn)梅尤。傘形框架的名稱存儲在字符串參數(shù)中。(傘形框架可以包含多個子框架岩调,蘋果不推薦這樣使用) |
LC_SUB_UMBRELLA | sub_umbrella_command | 指定此文件作為傘框架的子傘 |
LC_SUB_LIBRARY | sub_library_command | 標(biāo)志這個文件可以作為傘框架的一個字庫的實(shí)現(xiàn)巷燥。請注意,Apple尚未為子庫定義受支持的位置号枕。 |
LC_SUB_CLIENT | sub_client_command | 子框架可以明確地允許另一個框架或包鏈接到它缰揪,方法是包含一個LC_SUB_CLIENT load命令,該命令包含框架的名稱或包的客戶端名稱葱淳。 |
1钝腺、(__TEXT,__text)
這里存放的是匯編后的代碼,當(dāng)我們進(jìn)行編譯時赞厕,每個.m文件會經(jīng)過預(yù)編譯->編譯->匯編形成.o文件艳狐,稱之為目標(biāo)文件。匯編后坑傅,所有的代碼會形成匯編指令存儲在.o文件的(__TEXT,__text)區(qū)((__DATA,__data)也是類似)僵驰。鏈接后,所有的.o文件會合并成一個文件唁毒,所有.o文件的(__TEXT,__text)數(shù)據(jù)都會按鏈接順序存放到應(yīng)用文件的(__TEXT,__text)中。
2星爪、(__DATA,__data)
存儲數(shù)據(jù)的section浆西,static在進(jìn)行非零賦值后會存儲在這里,如果static 變量沒有賦值或者賦值為0顽腾,那么它會存儲在(__DATA,__bss)中近零。
3诺核、Symbol Table
符號表,這個是重點(diǎn)中的重點(diǎn)久信,符號表是將地址和符號聯(lián)系起來的橋梁窖杀。符號表并不能直接存儲符號,而是存儲符號位于字符串表的位置裙士。
4入客、String Table
字符串表所有的變量名、函數(shù)名等腿椎,都以字符串的形式存儲在字符串表中桌硫。
5、動態(tài)符號表
動態(tài)符號表存儲的是動態(tài)庫函數(shù)位于符號表的偏移信息啃炸。(__DATA,__la_symbol_ptr) section 可以從動態(tài)符號表中獲取到該section位于符號表的索引數(shù)組铆隘。