組成
Mach-O通常有三部分組成
頭部 (Header): Mach-O文件的架構(gòu) 比如Mac的 PPC, PPC64, IA-32, x86-64,ios的arm系列.
加載命令(Load commands): .
原始段數(shù)據(jù)(Raw segment data)*:可以擁有多個(gè)段(segment)顾翼,每個(gè)段可以擁有零個(gè)或多個(gè)區(qū)域(section)柠衅。每一個(gè)段(segment)都擁有一段虛擬地址映射到進(jìn)程的地址空間。
官方給的圖如下:
準(zhǔn)備工作
我們創(chuàng)建一個(gè)工程,mach-o
#include <stdio.h>
int main(int argc, const char * argv[]) {
printf("Hello, World!\n");
return 0;
}
打開(kāi)終端cd 到該工程的main所在目錄執(zhí)行下列命令gcc main.m -g -o test.out 會(huì)生成 test.out 文件赵誓。我們就分析這個(gè)文件
Mach-O 分析
通常一個(gè)iOS App應(yīng)用會(huì)安裝在/var/mobile/Applications,系統(tǒng)的原生App會(huì)安裝在/Applications目錄下柿赊,大部分情況下俩功,xxx.app/xxx文件并不是Mach-O格式文件,由于現(xiàn)在需要支持不同CPU架構(gòu)的iOS設(shè)備碰声,所以我們編譯打包出來(lái)的執(zhí)行文件是一個(gè)Universal Binary格式文件(通用二進(jìn)制文件诡蜓,也稱(chēng)胖二進(jìn)制文件),實(shí)際上Universal Binary只不過(guò)將支持不同架構(gòu)的Mach-O打包在一起胰挑,再在文件起始位置加上Fat Header來(lái)說(shuō)明所包含的Mach-O文件支持的架構(gòu)和偏移地址信息蔓罚。
Universal Binary
我們可以通過(guò)otool 工具查看頭
otool - f test (上面的test.out 通過(guò)該命令是沒(méi)有輸出結(jié)果的,因?yàn)槭莕o fat文件瞻颂,這里的test必須是 多個(gè)結(jié)構(gòu)體在一起的才可以)
Fat headers
fat_magic 0xcafebabe
nfat_arch 2
architecture 0
cputype 12
cpusubtype 9
capabilities 0x0
offset 16384
size 37933760
align 2^14 (16384)
architecture 1
cputype 16777228
cpusubtype 0
capabilities 0x0
offset 37961728
size 43561328
align 2^14 (16384)
胖二進(jìn)制文件定義在 /usr/include/mach-o/fat.h豺谈,
#define FAT_MAGIC 0xcafebabe
#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */
struct fat_header {
uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */
uint32_t nfat_arch; /* number of structs that follow */
};
struct fat_arch {
cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* machine specifier (int) */
uint32_t offset; /* file offset to this object file */
uint32_t size; /* size of this object file */
uint32_t align; /* alignment as a power of 2 */
};
/*
* The support for the 64-bit fat file format described here is a work in
* progress and not yet fully supported in all the Apple Developer Tools.
*
* When a slice is greater than 4mb or an offset to a slice is greater than 4mb
* then the 64-bit fat file format is used.
*/
#define FAT_MAGIC_64 0xcafebabf
#define FAT_CIGAM_64 0xbfbafeca /* NXSwapLong(FAT_MAGIC_64) */
struct fat_arch_64 {
cpu_type_t cputype; /* cpu specifier (int) */
cpu_subtype_t cpusubtype; /* machine specifier (int) */
uint64_t offset; /* file offset to this object file */
uint64_t size; /* size of this object file */
uint32_t align; /* alignment as a power of 2 */
uint32_t reserved; /* reserved */
};
magic字段就是我們常說(shuō)的魔數(shù)(就是文件結(jié)構(gòu)),加載器通過(guò)這個(gè)魔數(shù)值來(lái)判斷這是什么樣的文件蘸朋。在32 位和64 位 上magic 不一樣核无。
32 位是0xcafebabe ,64位是0xcafebabfnfat_arch字段是指當(dāng)前的胖二進(jìn)制文件包含了多少個(gè)不同架構(gòu)的Mach-O文件藕坯;
fat_header后會(huì)跟著fat_arch团南,有多少個(gè)不同架構(gòu)的Mach-O文件,就有多少個(gè)fat_arch炼彪,用于說(shuō)明對(duì)應(yīng)Mach-O文件大小吐根、支持的CPU架構(gòu)、偏移地址等辐马;
mach-o header
mach-o 的頭定義在在 /usr/include/mach-o/loader.h拷橘,
/*
* The 32-bit mach header appears at the very beginning of the object file for
* 32-bit architectures.
*/
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.
*/
struct mach_header_64 {
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 */
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 /* NXSwapInt(MH_MAGIC_64) */
我們可以通過(guò)otool -h test.out查看 mach-o header信息
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedfacf 16777223 3 0x80 2 15 1280 0x00200085
分析頭信息
1.magic,可以看到文件中的內(nèi)容最開(kāi)始部分喜爷,是以 cafe babe開(kāi)頭的對(duì)于一個(gè) 二進(jìn)制文件 來(lái)講冗疮,每個(gè)類(lèi)型都可以在文件最初幾個(gè)字節(jié)來(lái)標(biāo)識(shí)出來(lái),即“魔數(shù)”檩帐。不同類(lèi)型的 二進(jìn)制文件术幔,都有自己獨(dú)特的"魔數(shù)"。OS X上湃密,可執(zhí)行文件的標(biāo)識(shí)有這樣幾個(gè)魔數(shù)(不同的魔數(shù)代表不同的可執(zhí)行文件類(lèi)型)是mach-o文件的魔數(shù)诅挑,0xfeedface代表的是32位四敞,0xfeedfacf代表64位,cafebabe是跨處理器架構(gòu)的通用格式拔妥。
2.cputype和cupsubtype代表的是cpu的類(lèi)型和其子類(lèi)型忿危。
3.接著是filetype,2没龙,代表可執(zhí)行的文件
* Constants for the filetype field of the mach_header
*/
#define MH_OBJECT 0x1 /* relocatable object file */
#define MH_EXECUTE 0x2 /* demand paged executable file */
#define MH_FVMLIB 0x3 /* fixed VM shared library file */
#define MH_CORE 0x4 /* core file */
#define MH_PRELOAD 0x5 /* preloaded executable file */
#define MH_DYLIB 0x6 /* dynamically bound shared library */
#define MH_DYLINKER 0x7 /* dynamic link editor */
#define MH_BUNDLE 0x8 /* dynamically bound bundle file */
#define MH_DYLIB_STUB 0x9 /* shared library stub for static */
/* linking only, no section contents */
#define MH_DSYM 0xa /* companion file with only debug */
/* sections */
#define MH_KEXT_BUNDLE 0xb /* x86_64 kexts */
4.ncmds 指的是加載命令(load commands)的數(shù)量铺厨,例子中一共15個(gè),編號(hào)0-14
5.sizeofcmds 表示load commands 的bytes兜畸, load commands區(qū)域是緊接著header區(qū)域的努释。
6.最后個(gè)flags,例子中是0x00218085咬摇,可以按文檔分析之。
這個(gè)就是標(biāo)志位煞躬。
#define MH_PIE 0x200000 /* When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. */
#define MH_BINDS_TO_WEAK 0x10000 /* the final linked image uses weak symbols */
#define MH_WEAK_DEFINES 0x8000 /* the final linked image contains external weak symbols */
#define MH_TWOLEVEL 0x80 /* the image is using two-level name space bindings */
#define MH_DYLDLINK 0x4 /* the object file is input for the dynamic linker and can't be staticly link edited again */
#define MH_NOUNDEFS 0x1 /* the object file has no undefined references */
這里我們可以借助也UE程序MachOView,MachOView是Mac上查看Mach-O結(jié)構(gòu)的工具查看結(jié)構(gòu)
Load commands
load commmand直接跟在 header 部分的后面肛鹏,結(jié)構(gòu)定義在文件/usr/include/mach-o/loader.h中。
結(jié)構(gòu)定義如下
/*
* The load commands directly follow the mach_header. The total size of all
* of the commands is given by the sizeofcmds field in the mach_header. All
* load commands must have as their first two fields cmd and cmdsize. The cmd
* field is filled in with a constant for that command type. Each command type
* has a structure specifically for it. The cmdsize field is the size in bytes
* of the particular load command structure plus anything that follows it that
* is a part of the load command (i.e. section structures, strings, etc.). To
* advance to the next load command the cmdsize can be added to the offset or
* pointer of the current load command. The cmdsize for 32-bit architectures
* MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple
* of 8 bytes (these are forever the maximum alignment of any load commands).
* The padded bytes must be zero. All tables in the object file must also
* follow these rules so the file can be memory mapped. Otherwise the pointers
* to these tables will not work well or at all on some machines. With all
* padding zeroed like objects will compare byte for byte.
*/
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
這里的注釋很清楚
1.load commands 在mach_header 后面恩沛。
2.sizeofcmds 指定了load commands 的大小
3.每個(gè)command 有cmd 和cmdsize 組成在扰。
4.cmd 代表類(lèi)型。每種類(lèi)型都有常量標(biāo)示
5.cmdsize 是大小
6.cmdSize 32位必須是4字節(jié)的倍數(shù)雷客。 64位就是b字節(jié)的倍數(shù)
這些加載命令在Mach-O文件加載解析時(shí)芒珠,被內(nèi)核加載器或者動(dòng)態(tài)鏈接器調(diào)用,指導(dǎo)如何設(shè)置加載對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)段搅裙。
cmd 類(lèi)型很多都在 /usr/include/mach-o/loader.h中可以自己查看皱卓。
具體可以通過(guò)下面命令查看(test文件名)
otool -v -l test.out | open -f
段數(shù)據(jù)(Segments)
Segments包含了很多segment,每一個(gè)segment定義了一些Mach-O文件的數(shù)據(jù)部逮、地址和內(nèi)存保護(hù)屬性娜汁,這些數(shù)據(jù)在動(dòng)態(tài)鏈接器加載程序時(shí)被映射到了虛擬內(nèi)存中。每個(gè)段都有不同的功能兄朋。
一般包含下列功能
- 1). __PAGEZERO: 空指針陷阱段掐禁,映射到虛擬內(nèi)存空間的第一頁(yè),用于捕捉對(duì)NULL指針的引用颅和;
- 2). __TEXT: 包含了執(zhí)行代碼以及其他只讀數(shù)據(jù)傅事。 為了讓內(nèi)核將它 直接從可執(zhí)行文件映射到共享內(nèi)存, 靜態(tài)連接器設(shè)置該段的虛擬內(nèi)存權(quán)限為不允許寫(xiě)峡扩。當(dāng)這個(gè)段被映射到內(nèi)存后蹭越,可以被所有進(jìn)程共享。(這主要用在frameworks, bundles和共享庫(kù)等程序中有额,也可以為同一個(gè)可執(zhí)行文件的多個(gè)進(jìn)程拷貝使用)
- 3). __DATA: 包含了程序數(shù)據(jù)般又,該段可寫(xiě)彼绷;
- 4). __LINKEDIT: 含有為動(dòng)態(tài)鏈接庫(kù)使用的原始數(shù)據(jù),比如符號(hào)茴迁,字符串寄悯,重定位表?xiàng)l目等等。
一般的段又會(huì)按不同的功能劃分為幾個(gè)區(qū)(section)堕义,即段所有字母大小猜旬,加兩個(gè)下橫線(xiàn)作為前綴,而區(qū)則為小寫(xiě)倦卖,同樣加兩個(gè)下橫線(xiàn)作為前綴
下面列出段中可能包含的section:
__TEXT段:
__text, __cstring, __picsymbol_stub, __symbol_stub, __const, __litera14, __litera18;
__DATA段:
__data, __la_symbol_ptr, __nl_symbol_ptr, __dyld, __const, __mod_init_func, __mod_term_func, __bss, __commom;
__IMPORT段
__jump_table, __pointers;
其中__TEXT段中的__text是實(shí)際上的代碼部分洒擦;__DATA段的__data是實(shí)際的初始數(shù)據(jù)
可以通過(guò)otool –s查看某segment的某個(gè)section。(test是文件名)
otool -s __TEXT __text test.out
或者
otool -t test.out
想查看匯編
otool -t -v test.out
segment_command
接下來(lái)我們看 數(shù)據(jù)段 segment_command
/*
* The segment load command indicates that a part of this file is to be
* mapped into the task's address space. The size of this segment in memory,
* vmsize, maybe equal to or larger than the amount to map from this file,
* filesize. The file is mapped starting at fileoff to the beginning of
* the segment in memory, vmaddr. The rest of the memory of the segment,
* if any, is allocated zero fill on demand. The segment's maximum virtual
* memory protection and initial virtual memory protection are specified
* by the maxprot and initprot fields. If the segment has sections then the
* section structures directly follow the segment command and their size is
* reflected in cmdsize.
*/
struct segment_command { /* for 32-bit architectures */
uint32_t cmd; /* LC_SEGMENT */
uint32_t cmdsize; /* includes sizeof section structs */
char segname[16]; /* segment name */
uint32_t vmaddr; /* memory address of this segment */
uint32_t vmsize; /* memory size of this segment */
uint32_t fileoff; /* file offset of this segment */
uint32_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 */
};
/*
* The 64-bit segment load command indicates that a part of this file is to be
* mapped into a 64-bit task's address space. If the 64-bit segment has
* sections then section_64 structures directly follow the 64-bit segment
* command and their size is reflected in cmdsize.
*/
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 */
};
這里我們看看結(jié)構(gòu)體的每個(gè)成員變量代表什么
1.cmd 32位是個(gè)常量 LC_SEGMENT 64 常量LC_SEGMENT_64
2.cmdsize 該區(qū)域大小
3.segname[16] 名字
4.vmaddr segment 的內(nèi)存地址怕膛。
5.vmsize segment的內(nèi)存大小
6.fileoff 文件偏移量
- filesize 在文件中的大小
8.maxprot 頁(yè)面所需要的最高內(nèi)存保護(hù)(4=r,2=w,1=x)
9.initprot 頁(yè)面初始的內(nèi)存保護(hù)
10.nsects sections的多少
11.flags 指示器熟嫩。標(biāo)記該segment 如何操作
#define SG_HIGHVM 0x1 /* the file contents for this segment is forthe high part of the VM space, the low part is zero filled (for stacks in core files) */ 不足的部分用0 充滿(mǎn)
#define SG_FVMLIB 0x2 /* this segment is the VM that is allocated by a fixed VM library, for overlap checking in the link editor */ 這部分重新連接vm library
#define SG_NORELOC 0x4 /* this segment has nothing that was relocated in it and nothing relocated to it, that is it maybe safely replaced without relocation*/
#define SG_PROTECTED_VERSION_1 0x8 /* This segment is protected. If the=segment starts at file offset 0, the
first page of the segment is not
protected. All other pages of the
segment are protected. */
下面是section 部分
/*
* A segment is made up of zero or more sections. Non-MH_OBJECT files have
* all of their segments with the proper sections in each, and padded to the
* specified segment alignment when produced by the link editor. The first
* segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header
* and load commands of the object file before its first section. The zero
* fill sections are always last in their segment (in all formats). This
* allows the zeroed segment padding to be mapped into memory where zero fill
* sections might be. The gigabyte zero fill sections, those with the section
* type S_GB_ZEROFILL, can only be in a segment with sections of this type.
* These segments are then placed after all other segments.
*
* The MH_OBJECT format has all of its sections in one segment for
* compactness. There is no padding to a specified segment boundary and the
* mach_header and load commands are not part of the segment.
*
* Sections with the same section name, sectname, going into the same segment,
* segname, are combined by the link editor. The resulting section is aligned
* to the maximum alignment of the combined sections and is the new section's
* alignment. The combined sections are aligned to their original alignment in
* the combined section. Any padded bytes to get the specified alignment are
* zeroed.
*
* The format of the relocation entries referenced by the reloff and nreloc
* fields of the section structure for mach object files is described in the
* header file <reloc.h>.
*/
struct section { /* for 32-bit architectures */
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uint32_t addr; /* memory address of this section */
uint32_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) */
};
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 */
};
結(jié)構(gòu)體定義
1.sectname 第一個(gè)是__text ,就是主程序代碼
2.segname 該section所屬的 segment名,第一個(gè)是__TEXT
3.addr 該section在內(nèi)存的啟始位置
4.size 該section的大小
5.offset 該section的文件偏移
6.align 字節(jié)大小對(duì)齊 褐捻,
- reloff 重定位入口的文件偏移掸茅,
8.nreloc 需要重定位的入口數(shù)量,
9.flags 包含section的type和attributes
這里還有點(diǎn)東西需要看看
/* The currently known segment names and the section names in those segments */
#define SEG_PAGEZERO "__PAGEZERO" /* the pagezero segment which has no */
/* protections and catches NULL */
/* references for MH_EXECUTE files */
#define SEG_TEXT "__TEXT" /* the tradition UNIX text segment */
#define SECT_TEXT "__text" /* the real text part of the text */
/* section no headers, and no padding */
#define SECT_FVMLIB_INIT0 "__fvmlib_init0" /* the fvmlib initialization */
/* section */
#define SECT_FVMLIB_INIT1 "__fvmlib_init1" /* the section following the */
/* fvmlib initialization */
/* section */
#define SEG_DATA "__DATA" /* the tradition UNIX data segment */
#define SECT_DATA "__data" /* the real initialized data section */
/* no padding, no bss overlap */
#define SECT_BSS "__bss" /* the real uninitialized data section*/
/* no padding */
#define SECT_COMMON "__common" /* the section common symbols are */
/* allocated in by the link editor */
#define SEG_OBJC "__OBJC" /* objective-C runtime segment */
#define SECT_OBJC_SYMBOLS "__symbol_table" /* symbol table */
#define SECT_OBJC_MODULES "__module_info" /* module information */
#define SECT_OBJC_STRINGS "__selector_strs" /* string table */
#define SECT_OBJC_REFS "__selector_refs" /* string table */
#define SEG_ICON "__ICON" /* the icon segment */
#define SECT_ICON_HEADER "__header" /* the icon headers */
#define SECT_ICON_TIFF "__tiff" /* the icons in tiff format */
#define SEG_LINKEDIT "__LINKEDIT" /* the segment containing all structs */
/* created and maintained by the link */
/* editor. Created with -seglinkedit */
/* option to ld(1) for MH_EXECUTE and */
/* FVMLIB file types only */
#define SEG_UNIXSTACK "__UNIXSTACK" /* the unix stack segment */
#define SEG_IMPORT "__IMPORT" /* the segment for the self (dyld) */
/* modifing code stubs that has read, */
/* write and execute permissions */
看到這里據(jù)我估計(jì)讀者也很懵柠逞,其實(shí)我也很懵現(xiàn)在∶潦ǎ現(xiàn)在需要把上面零散的至少匯總起來(lái)了。
匯總
這里我寫(xiě)的demo名字叫mach-o-parse板壮。只解析出來(lái)了部分功能逗鸣。不過(guò)這讓我徹底搞懂了這個(gè)mach-o 的結(jié)構(gòu)
源代碼
#import "ViewController.h"
@interface ViewController ()
@end
typedef struct mach_new_header {
uint32_t magic; /* mach magic number identifier */
uint32_t cputype; /* cpu specifier */
uint16_t cpusubtype; /* machine specifier */
uint8_t zhanwei;
uint8_t caps;
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 */
uint32_t reserved;
} Mach_O_Header;
void printHeader(Mach_O_Header header){
NSLog(@"header magic 0x%0x",header.magic);
NSLog(@"header cputype %d",header.cputype);
NSLog(@"header cpusubtype %d",header.cpusubtype);
NSLog(@"header caps 0x%x",header.caps);
NSLog(@"header filetype %d",header.filetype);
NSLog(@"header ncmds %d",header.ncmds);
NSLog(@"header sizeofcmds %d",header.sizeofcmds);
NSLog(@"header flags 0x%0x",header.flags);
}
typedef struct {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
}Mach_O_load_command;
void printLoadCommand(Mach_O_load_command loadCommand){
NSLog(@"loadCommand cmd 0x%0x",loadCommand.cmd);
NSLog(@"loadCommand cmdsize %0d",loadCommand.cmdsize);
}
typedef struct { /* for 64-bit architectures */
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 */
uint32_t maxprot; /* maximum VM protection */
uint32_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
}Mach_O_segment_command;
void printSegment_command(Mach_O_segment_command segmentCommand){
NSLog(@"segmentCommand segname %s",segmentCommand.segname);
NSLog(@"segmentCommand vmaddr 0x%llx",segmentCommand.vmaddr);
NSLog(@"segmentCommand vmsize %llu",segmentCommand.vmsize);
NSLog(@"segmentCommand fileoff 0x%llx",segmentCommand.fileoff);
NSLog(@"segmentCommand filesize %llu",segmentCommand.filesize);
NSLog(@"segmentCommand maxprot 0x%x",segmentCommand.maxprot);
NSLog(@"segmentCommand initprot 0x%x",segmentCommand.initprot);
NSLog(@"segmentCommand nsects %d",segmentCommand.nsects);
NSLog(@"segmentCommand flags 0x%x",segmentCommand.flags);
}
typedef struct { /* 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 */
}Mach_O_section;
void printSection_command(Mach_O_section sectionCommand){
NSLog(@"sectionCommand secname %s",sectionCommand.sectname);
NSLog(@"sectionCommand segname %s",sectionCommand.segname);
NSLog(@"sectionCommand addr 0x%llx",sectionCommand.addr);
NSLog(@"sectionCommand size %llu",sectionCommand.size);
NSLog(@"sectionCommand offset %d",sectionCommand.offset);
NSLog(@"sectionCommand align %d",sectionCommand.align);
NSLog(@"sectionCommand reloff %d",sectionCommand.reloff);
NSLog(@"sectionCommand nreloc %d",sectionCommand.nreloc);
NSLog(@"sectionCommand flags %d",sectionCommand.flags);
}
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSData *mach = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"out"]];
static int location =0;
Mach_O_Header header ;
[mach getBytes:&header range:NSMakeRange(0, sizeof(Mach_O_Header))];
location =sizeof(Mach_O_Header);
printHeader(header);
for (int i=0; i<header.ncmds; i++) {
Mach_O_load_command loadCommand;
Mach_O_segment_command segment;
[mach getBytes:&loadCommand range:NSMakeRange(location, sizeof(Mach_O_load_command))];
location+=sizeof(Mach_O_load_command);
printLoadCommand(loadCommand);
NSUInteger length = loadCommand.cmdsize;
///減去loadCommand 的長(zhǎng)度
length-=8;
///獲取Mach_O_segment_command 的數(shù)據(jù)
[mach getBytes:&segment range:NSMakeRange(location, sizeof(Mach_O_segment_command))];
location+=sizeof(Mach_O_segment_command);
printSegment_command(segment);
length-=sizeof(Mach_O_segment_command);
if (length>0) {
Mach_O_section section ;
[mach getBytes:§ion range:NSMakeRange(location, sizeof(Mach_O_section))];
location+=sizeof(Mach_O_section);
printSection_command(section);
length-=sizeof(Mach_O_section);
}
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
測(cè)試結(jié)果截取了一部分
2018-04-19 17:43:34.811420+0800 Mach-o-parse[58332:11931902] 64 8 8696
2018-04-19 17:43:34.811548+0800 Mach-o-parse[58332:11931902] header magic 0xfeedfacf
2018-04-19 17:43:34.811656+0800 Mach-o-parse[58332:11931902] header cputype 16777223
2018-04-19 17:43:34.811752+0800 Mach-o-parse[58332:11931902] header cpusubtype 3
2018-04-19 17:43:34.811886+0800 Mach-o-parse[58332:11931902] header caps 0x80
2018-04-19 17:43:34.811982+0800 Mach-o-parse[58332:11931902] header filetype 2
2018-04-19 17:43:34.812071+0800 Mach-o-parse[58332:11931902] header ncmds 15
2018-04-19 17:43:34.812128+0800 Mach-o-parse[58332:11931902] header sizeofcmds 1280
2018-04-19 17:43:34.812171+0800 Mach-o-parse[58332:11931902] header flags 0x200085
2018-04-19 17:43:34.812228+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x19
2018-04-19 17:43:34.812310+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 72
2018-04-19 17:43:44.179138+0800 Mach-o-parse[58332:11931902] segmentCommand segname __PAGEZERO
2018-04-19 17:43:44.179337+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x0
2018-04-19 17:43:44.179672+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 4294967296
2018-04-19 17:43:44.180009+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x0
2018-04-19 17:43:44.180212+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 0
2018-04-19 17:43:44.180323+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0x0
2018-04-19 17:43:44.180489+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x0
2018-04-19 17:43:44.180610+0800 Mach-o-parse[58332:11931902] segmentCommand nsects 0
2018-04-19 17:43:44.180709+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.180803+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x19
2018-04-19 17:43:44.181155+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 472
2018-04-19 17:43:44.181445+0800 Mach-o-parse[58332:11931902] segmentCommand segname __TEXT
2018-04-19 17:43:44.181628+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x100000000
2018-04-19 17:43:44.181898+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 4096
2018-04-19 17:43:44.182053+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x0
2018-04-19 17:43:44.182541+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 4096
2018-04-19 17:43:44.183243+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0x7
2018-04-19 17:43:44.183394+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x5
2018-04-19 17:43:44.184954+0800 Mach-o-parse[58332:11931902] segmentCommand nsects 5
2018-04-19 17:43:44.185290+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.185609+0800 Mach-o-parse[58332:11931902] sectionCommand secname __text
2018-04-19 17:43:44.185923+0800 Mach-o-parse[58332:11931902] sectionCommand segname __TEXT
2018-04-19 17:43:44.187436+0800 Mach-o-parse[58332:11931902] sectionCommand addr 0x100000f50
2018-04-19 17:43:44.187659+0800 Mach-o-parse[58332:11931902] sectionCommand size 52
2018-04-19 17:43:44.187749+0800 Mach-o-parse[58332:11931902] sectionCommand offset 3920
2018-04-19 17:43:44.187854+0800 Mach-o-parse[58332:11931902] sectionCommand align 4
2018-04-19 17:43:44.188435+0800 Mach-o-parse[58332:11931902] sectionCommand reloff 0
2018-04-19 17:43:44.207834+0800 Mach-o-parse[58332:11931902] sectionCommand nreloc 0
2018-04-19 17:43:44.208121+0800 Mach-o-parse[58332:11931902] sectionCommand flags -2147482624
2018-04-19 17:43:44.208212+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x74735f5f
2018-04-19 17:43:44.208903+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 7561845
2018-04-19 17:43:44.209102+0800 Mach-o-parse[58332:11931902] segmentCommand segname
2018-04-19 17:43:44.209186+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x0
2018-04-19 17:43:44.209262+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 4294971268
2018-04-19 17:43:44.209351+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x6
2018-04-19 17:43:44.209532+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 4294971268
2018-04-19 17:43:44.209662+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0x0
2018-04-19 17:43:44.210690+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x0
2018-04-19 17:43:44.211320+0800 Mach-o-parse[58332:11931902] segmentCommand nsects -2147482616
2018-04-19 17:43:44.214560+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.214699+0800 Mach-o-parse[58332:11931902] sectionCommand secname \^F
2018-04-19 17:43:44.214795+0800 Mach-o-parse[58332:11931902] sectionCommand segname elper
2018-04-19 17:43:44.214890+0800 Mach-o-parse[58332:11931902] sectionCommand addr 0x0
2018-04-19 17:43:44.214979+0800 Mach-o-parse[58332:11931902] sectionCommand size 4294971276
2018-04-19 17:43:44.215061+0800 Mach-o-parse[58332:11931902] sectionCommand offset 26
2018-04-19 17:43:44.215160+0800 Mach-o-parse[58332:11931902] sectionCommand align 0
2018-04-19 17:43:44.215322+0800 Mach-o-parse[58332:11931902] sectionCommand reloff 3980
2018-04-19 17:43:44.215412+0800 Mach-o-parse[58332:11931902] sectionCommand nreloc 2
2018-04-19 17:43:44.215526+0800 Mach-o-parse[58332:11931902] sectionCommand flags 0
2018-04-19 17:43:44.215612+0800 Mach-o-parse[58332:11931902] loadCommand cmd 0x0
2018-04-19 17:43:44.251678+0800 Mach-o-parse[58332:11931902] loadCommand cmdsize 0
2018-04-19 17:43:44.252740+0800 Mach-o-parse[58332:11931902] segmentCommand segname __cstring
2018-04-19 17:43:44.253046+0800 Mach-o-parse[58332:11931902] segmentCommand vmaddr 0x545845545f5f
2018-04-19 17:43:44.253541+0800 Mach-o-parse[58332:11931902] segmentCommand vmsize 0
2018-04-19 17:43:44.254023+0800 Mach-o-parse[58332:11931902] segmentCommand fileoff 0x100000fa6
2018-04-19 17:43:44.254421+0800 Mach-o-parse[58332:11931902] segmentCommand filesize 15
2018-04-19 17:43:44.254554+0800 Mach-o-parse[58332:11931902] segmentCommand maxprot 0xfa6
2018-04-19 17:43:44.255075+0800 Mach-o-parse[58332:11931902] segmentCommand initprot 0x0
2018-04-19 17:43:44.255257+0800 Mach-o-parse[58332:11931902] segmentCommand nsects 0
2018-04-19 17:43:44.255360+0800 Mach-o-parse[58332:11931902] segmentCommand flags 0x0
2018-04-19 17:43:44.255604+0800 Mach-o-parse[58332:11931902] sectionCommand secname \^B
2018-04-19 17:43:44.255800+0800 Mach-o-parse[58332:11931902] sectionCommand segname __unwind_info
2018-04-19 17:43:44.255909+0800 Mach-o-parse[58332:11931902] sectionCommand addr 0x545845545f5f
2018-04-19 17:43:44.256104+0800 Mach-o-parse[58332:11931902] sectionCommand size 0
2018-04-19 17:43:44.256408+0800 Mach-o-parse[58332:11931902] sectionCommand offset 4024
2018-04-19 17:43:44.256492+0800 Mach-o-parse[58332:11931902] sectionCommand align 1
2018-04-19 17:43:44.256590+0800 Mach-o-parse[58332:11931902] sectionCommand reloff 72
2018-04-19 17:43:44.256680+0800 Mach-o-parse[58332:11931902] sectionCommand nreloc 0
2018-04-19 17:43:44.256771+0800 Mach-o-parse[58332:11931902] sectionCommand flags 4024
....
從測(cè)試代碼終于看出了mach-o 的結(jié)構(gòu)
從測(cè)試代碼看不出 loadCommand 與section 是不連續(xù)的
我們通過(guò)machoView 看出來(lái)的
截圖如下
我們從圖1 看loadCommand 的最后地址是0x00000f1c。
從圖2 看section的首地址是0x00000F50绰精。
從圖3 中我們發(fā)現(xiàn)0x00000F50又出現(xiàn)了撒璧。這個(gè)地址就是指向的需要連接的section地址。
到此為止茬底,mach-o 的具體結(jié)構(gòu)我們弄懂了沪悲。
這里還有個(gè)知識(shí)點(diǎn)就是maxprot 和initprot ≮灞恚看圖就明白了殿如。
今天終于把mach-o 文件分析完畢了。
注意 segment 有很多種最爬。所有的定義都在 loader.h 文件中涉馁。是根據(jù)loadCommand 中的cmd 參數(shù)變化更改下面的segment 的。