Mach-O 文件格式解析
Contents
- Mach-O 文件格式解析
- 一、Fat Header
- 二、Mach Header
- 三、LoadCommands
- 3.1 LC_SEGMENT
- 3.2 LC_LOAD_DYLIB
- 3.3 LC_MAIN
- 3.4 LC_ENCRYPTION_INFO
- 3.5 LC_CODE_SIGNATURE
- 3.6 LC_ID_DYLIB
- 3.7 LC_UUID
- 3.8 LC_DYLD_INFO_ONLY
- 3.9 LC_SYMTAB
- 3.10 LC_DYSYMTAB
- 3.11 LC_RPATH
- 3.12 LC_VERSION_MIN_IPHONEOS
- 3.13 LC_LOAD_DYLINKER
- 四、Symbol Table & String Table
- 4.1 n_strx
- 4.2 n_type
- 4.3 n_sect
- 4.4 n_desc
- 4.5 n_value
- 文档历史
Mach-O 文件格式是苹果的 iOS 和 macOS 使用的一套可执行文件的文件格式,类似在 Windows 平台使用 PE 文件格式,Linux 平台使用 ELF 文件格式。Mach-O 文件格式主要的分布是 Mach Header、LoadCommand、Segment、Seciton,如下图所示(该图片来源苹果官网):
当一个文件同时支持多个 CPU 平台,比如同时支持 ARMV7、ARM64,就相当是两个 Mach-O 文件,编译器会编译两个 Mach-O 文件,然后合成一个 Fat 文件,如下图所示:
操作系统运行的时候会根据自身平台运行相应的 Mach-O 文件。macOS 系统自带一个 file 命令可以查看可执行文件支持的平台,查看 python 支持的平台
1 2 3 4 5 |
$ file /usr/bin/python /usr/bin/python: Mach-O universal binary with 2 architectures /usr/bin/python (for architecture i386): Mach-O executable i386 /usr/bin/python (for architecture x86_64): Mach-O 64-bit executable x86_64 |
Xcode 在编译 iOS 程序可以选择同时支持 ARMV7 和 ARM64,编译 macOS 程序也可以选择同时支持 x86 和 x86_64,但是如果一个程序需要同时支持 iOS 和 macOS 的时候,Xcode 不能自动生成,可以使用 lipo 命令手动对文件进行合并。
1 2 |
lipo -create test_iPhone test_macOS -output test_all |
由于每个 CPU 平台都是单独的一个 Mach-O 文件,然后合成的 Fat 文件,所以体积会变大,比如某个程序我们只需要支持 ARM64,就可以把其他平台给移除掉,这样就能起到 “瘦身” 的作用,使用 lipo 命令移除其他平台。
1 2 |
lipo -thin arm64 ~/debugserver -output ~/debugserver |
一、Fat Header
Xcode 自带一个 otool 工具,可以查看 Fat 头信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ otool -f /usr/bin/file Fat headers fat_magic 0xcafebabe nfat_arch 2 architecture 0 cputype 7 cpusubtype 3 capabilities 0x0 offset 4096 size 105632 align 2^12 (4096) architecture 1 cputype 16777223 cpusubtype 3 capabilities 0x80 offset 110592 size 101392 align 2^12 (4096) |
1.1 fat_header
1 2 3 4 5 6 7 8 |
#define FAT_MAGIC 0xcafebabe #define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */ struct fat_header { uint32_t magic; /* FAT_MAGIC */ uint32_t nfat_arch; /* number of structs that follow */ }; |
1.1.1 magic
magic 是一个标记,表示是 Fat 的文件类型, 是固定的 0xcafebabe 或者是 0xbebafeca。
1.1.2 nfat_arch
nfat_arch 表示包含了多个少 Mach-O 文件。
1.2 fat_arch
fat_header 之后,紧接着就是 fat_arch
1 2 3 4 5 6 7 8 |
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 */ }; |
1.2.1 cputype
cputype 表示支持的 CPU 类型,一般有 ARMV7, ARM64, X86, X86_64 这几种类型。
1 2 3 4 5 6 7 |
#define CPU_TYPE_X86 ((cpu_type_t) 7) #define CPU_TYPE_I386 CPU_TYPE_X86 #define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) #define CPU_TYPE_ARM ((cpu_type_t) 12) #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) ...... |
1.2.2 cpusubtype
cpusubtype 表示子 CPU 类型,一般有以下几种类型
1 2 3 4 5 6 7 8 |
#define CPU_SUBTYPE_MASK 0xff000000 /* mask for feature flags */ #define CPU_SUBTYPE_LIB64 0x80000000 /* 64 bit libraries */ #define CPU_SUBTYPE_X86_ALL ((cpu_subtype_t)3) #define CPU_SUBTYPE_X86_64_ALL ((cpu_subtype_t)3) #define CPU_SUBTYPE_X86_ARCH1 ((cpu_subtype_t)4) #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) /* Haswell feature subset */ ...... |
1.2.3 offset
offset 表示是 当前架构的 Mach-O 文件的数据相对于文件开头的偏移位置,比如 16384,就表示是比 0x4000 开始的。
1.2.4 sizeof
sizeof 表示数据的大小。
1.2.5 align
表示数据的内存对齐边界。
二、Mach Header
使用 otool 查看 Mach Header
1 2 3 4 5 6 |
otool -h /usr/bin/file /usr/bin/file: Mach header magic cputype cpusubtype caps filetype ncmds sizeofcmds flags 0xfeedfacf 16777223 3 0x80 2 17 1744 0x00200085 |
2.1 mach_header
1 2 3 4 5 6 7 8 9 10 |
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 */ }; |
2.1.1 magic
magic 和 fat_hader 里的 magic 类似,也是一个标记,32 位的值是 MH_MAGIC,64位的值是 MH_CIGAM_64。
1 2 3 4 5 6 |
#define MH_MAGIC 0xfeedface /* the mach magic number */ #define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ #define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ #define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */ |
2.1.2 cputype
mach_header 里的 cputype 与 fat_arch 里的 cputype 的含义完全一样。
2.1.3 cpusubtype
mach_header 里的 cpusubtype 与 fat_arch 里的 cpusubtype 的含义完全一样。
2.1.4 filetype
filetype 表示 Mach-O 的具体文件类型,如果是可执行文件就是 MH_EXECUTE,如果是动态库就是 MH_DYLIB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#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 */ |
2.1.5 ncmds
ncmds 表示 Mach-O 文件中 load command (加载命令)的个数。
2.1.6 sizeofcmds
sizeofcmds 表示load command (加载命令) 占用的字节总大小。
2.1.7 flags
flags 表示文件的标志信息,取值如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* Constants for the flags field of the mach_header */ #define MH_NOUNDEFS 0x1 /* the object file has no undefinedreferences */ #define MH_INCRLINK 0x2 /* the object file is the output of an incremental link against a base file and can't be link edited again */ #define MH_DYLDLINK 0x4 /* the object file is input for the dynamic linker and can't be staticly link edited again */ #define MH_BINDATLOAD 0x8 /* the object file's undefined references are bound by the dynamic linker when loaded. */ #define MH_PREBOUND 0x10 /* the file has its dynamic undefined references prebound. */ #define MH_SPLIT_SEGS 0x20 /* the file has its read-only and read-write segments split */ #define MH_LAZY_INIT 0x40 /* the shared library init routine is |
2.2 mach_header_64
mach_header64 与 mach_header 的结构定义差不多,只是多了一个 reserved 字段,目前为系统保留字段。
1 2 3 4 5 6 7 8 9 10 11 |
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 */ }; |
三、LoadCommands
mach_header 之后是 LoadCommands(加载命令),使用 otool 查看 LoadCommands
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
$ otool -l /usr/bin/file /usr/bin/file: Load command 0 cmd LC_SEGMENT_64 cmdsize 72 segname __PAGEZERO vmaddr 0x0000000000000000 vmsize 0x0000000100000000 fileoff 0 filesize 0 maxprot 0x00000000 initprot 0x00000000 nsects 0 flags 0x0 Load command 1 cmd LC_SEGMENT_64 cmdsize 552 segname __TEXT vmaddr 0x0000000100000000 vmsize 0x0000000000014000 fileoff 0 filesize 81920 maxprot 0x00000007 initprot 0x00000005 nsects 6 flags 0x0 ...... 略 |
load_command 结构信息如下:
1 2 3 4 5 |
struct load_command { uint32_t cmd; /* type of load command */ uint32_t cmdsize; /* total size of command in bytes */ }; |
cmd 表示 LoadCommand (加载命令)的类型,类型的不同结构体也会有所不同,有如下一些类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/* Constants for the cmd field of all load commands, the type */ #define LC_SEGMENT 0x1 /* segment of this file to be mapped */ #define LC_SYMTAB 0x2 /* link-edit stab symbol table info */ #define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */ #define LC_THREAD 0x4 /* thread */ #define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */ #define LC_LOADFVMLIB 0x6 /* load a specified fixed VM shared library */ #define LC_IDFVMLIB 0x7 /* fixed VM shared library identification */ #define LC_IDENT 0x8 /* object identification info (obsolete) */ #define LC_FVMFILE 0x9 /* fixed VM file inclusion (internal use) */ #define LC_PREPAGE 0xa /* prepage command (internal use) */ #define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */ #define LC_LOAD_DYLIB 0xc /* load a dynamically linked shared library */ #define LC_ID_DYLIB 0xd /* dynamically linked shared lib ident */ #define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */ #define LC_ID_DYLINKER 0xf /* dynamic linker identification */ #define LC_PREBOUND_DYLIB 0x10 /* modules prebound for a dynamically */ /* linked shared library */ #define LC_ROUTINES 0x11 /* image routines */ #define LC_SUB_FRAMEWORK 0x12 /* sub framework */ #define LC_SUB_UMBRELLA 0x13 /* sub umbrella */ #define LC_SUB_CLIENT 0x14 /* sub client */ #define LC_SUB_LIBRARY 0x15 /* sub library */ #define LC_TWOLEVEL_HINTS 0x16 /* two-level namespace lookup hints */ #define LC_PREBIND_CKSUM 0x17 /* prebind checksum */ /* * load a dynamically linked shared library that is allowed to be missing * (all symbols are weak imported). */ #define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) #define LC_SEGMENT_64 0x19 /* 64-bit segment of this file to be mapped */ #define LC_ROUTINES_64 0x1a /* 64-bit image routines */ #define LC_UUID 0x1b /* the uuid */ #define LC_RPATH (0x1c | LC_REQ_DYLD) /* runpath additions */ #define LC_CODE_SIGNATURE 0x1d /* local of code signature */ #define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */ #define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */ #define LC_LAZY_LOAD_DYLIB 0x20 /* delay load of dylib until first use */ #define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */ #define LC_DYLD_INFO 0x22 /* compressed dyld information */ #define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */ #define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */ #define LC_VERSION_MIN_MACOSX 0x24 /* build for MacOSX min OS version */ #define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */ #define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */ #define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat like environment variable */ #define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */ #define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */ #define LC_SOURCE_VERSION 0x2A /* source version used to build binary */ #define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */ #define LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */ #define LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */ #define LC_LINKER_OPTIMIZATION_HINT 0x2E /* optimization hints in MH_OBJECT files */ #ifndef __OPEN_SOURCE__ #define LC_VERSION_MIN_TVOS 0x2F /* build for AppleTV min OS version */ #endif /* __OPEN_SOURCE__ */ #define LC_VERSION_MIN_WATCHOS 0x30 /* build for Watch min OS version */ |
3.1 LC_SEGMENT
一个程序一般会分为多个段,不同类型的数据放入不同的段中,代码是放入 __TEXT 段中,段结构信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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 */ }; |
3.1.1 segname
segname 表示是段的名称,最长 16 字节大小。常见的段名称有 __PAGEZERO、__LINKEDIT、__TEXT、__DATA。
3.1.1.1 __PAGEZERO
__PAGEZERO 是在可执行文件有的,动态库里没有。这个段开始地址为0(NULL指针指向的位置),是一个不可读、不可写、不可执行的空间,能够在空指针访问时抛出异常。这个段的大小,32位上是 0x4000,64位上是 4G。如图所示:
3.1.1.2 __TEXT
__TEXT 是代码段,里面主要是存放代码的,该段是可读可执行,但是不可写。
3.1.1.3 __DATA
__DATA 是数据段,里面主要是存放数据,该段是可读可写,但不可执行。
3.1.1.4 __LINKEDIT
__LINKEDIT 段用于存放签名信息,该段是只可读,不可写不可执行。
3.1.2 vmaddr
vmaddr 表示段要加载的虚拟内存地址。
3.1.3 vmsize
vmsize 表示段所占的虚拟内存的大小。
3.1.4 fileoff
fileoff 表示段数据所有的文件中的偏移地址。
3.1.5 filesize
filesize 表示段数据的大小。
3.1.6 maxprot
maxprot 表示页面所需要的最高内存保护。
3.1.7 initprot
initprot 表示页面初始的内存保护。
3.1.8 nsects
nsects 表示该段包含了多少个节区。
3.1.9 flags
flags 表示段的标志信息。
3.1.10 Section Header
节头的信息结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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) */ }; |
当一个段包含多个节区,节区头信息会以数组的形式存储在段加载命令后面,有些段的节区是 0,就那不会有 Section Header,比如 __TEXT 段包含了 10 个节区,节区头信息的存储如下:
3.1.10.1 sectname
sectname 表示节区的名称,最长 16 字节大小。
3.1.10.2 segname
segname 表示节区所在的段名。
3.1.10.3 addr
addr 表示节区所在的内存地址。
3.1.10.4 size
size 表示节区所在的大小。
3.1.10.5 offset
offset 表示节区所在文件偏移。
3.1.10.6 align
align 表示节区的内存对齐边界。
3.1.10.7 reloff
reloff 表示重定位信息的文件偏移。
3.1.10.8 nreloc
nreloc 表示重定位条目的个数。
3.1.10.9 flags
flags 表示节区的标志属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#define SG_HIGHVM 0x1 /* the file contents for this segment is for the high part of the VM space, the low part is zero filled (for stacks in core files) */ #define SG_FVMLIB 0x2 /* this segment is the VM that is allocated by a fixed VM library, for overlap checking in the link editor */ #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. */ |
标志属性如果是 SG_PROTECTED_VERSION_1,表示该段是经过加密的。
3.1.10.10 reserved1
3.1.10.11 reserved2
3.2 LC_LOAD_DYLIB
LC_LOAD_DYLIB 和 LC_ID_DYLIB、LC_LOAD_WEAK_DYLIB、LC_REEXPORT_DYLIB 这三种类型使用的结构信息是一样的
1 2 3 4 5 6 7 |
struct dylib_command { uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB */ uint32_t cmdsize; /* includes pathname string */ struct dylib dylib; /* the library identification */ }; |
dylib_command 除了 cmd 和 cmdsize,只有一个 dylib 结构
1 2 3 4 5 6 7 |
struct dylib { union lc_str name; /* library's path name */ uint32_t timestamp; /* library's build time stamp */ uint32_t current_version; /* library's current version number */ uint32_t compatibility_version; /* library's compatibility vers number*/ }; |
3.2.1 name
name 表示动态库的完整路径,它是一个 lc_str 联合结构
1 2 3 4 5 |
union lc_str { uint32_t offset; /* offset to the string */ char *ptr; /* pointer to the string */ }; |
offset 表示字符串从 dylib 结构体开始计算的偏移位置,ptr 表示是动态库路径字符串指针,比如 dylib 结构体大小为 52,lcstr 里的 offset 为 24,52 - 24 = 28,相当于从 24 开始偏移 28 位,这 28 位就是动态库路径字符串的大小,/usr/lib/libsqlite3.dylib 字符串的实际大小是 25,加上 \0 是 26,因为是 4 字节对齐,所以 lcstr 占用的空间是 28 位,如下图所示。
3.2.2 timestamp
timestamp 表示动态库构建时的时间戳。
3.2.3 current_version
current_version 表示当前版本号。
3.2.4 compatibility_version
compatibility_version 表示兼容的版本号。
3.3 LC_MAIN
1 2 3 4 5 6 7 |
struct entry_point_command { uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */ uint32_t cmdsize; /* 24 */ uint64_t entryoff; /* file (__TEXT) offset of main() */ uint64_t stacksize;/* if not zero, initial stack size */ }; |
3.3.1 entryoff
entryoff 表示 main() 函数的文件偏移,下图的 entryoff 为 103107, 十六进制为 0x192c3,
用 IDA 查看文件,将基址设为 0 ,然后跳转到 0x192c3 代码查看,果然是 main 函数的地址,如下图:
3.3.2 stacksize
stacksize 表示初始的堆栈大小。
3.4 LC_ENCRYPTION_INFO
1 2 3 4 5 6 7 8 9 |
struct encryption_info_command { uint32_t cmd; /* LC_ENCRYPTION_INFO */ uint32_t cmdsize; /* sizeof(struct encryption_info_command) */ uint32_t cryptoff; /* file offset of encrypted range */ uint32_t cryptsize; /* file size of encrypted range */ uint32_t cryptid; /* which enryption system, 0 means not-encrypted yet */ }; |
3.4.1 cryptoff
cryptoff 表示加密的文件编移。
3.4.2 cryptsize
cryptsize 表示加密的大小
3.4.3 cryptid
cryptid 表示加密的类型,如果没有加密,cryptid 为 0。比如我们自己用 Xcode 编译的程序 cryptid 都是 0,如果是从 appstore 下载的应用, cryptid 为 1。
3.5 LC_CODE_SIGNATURE
1 2 3 4 5 6 7 8 9 10 |
struct linkedit_data_command { uint32_t cmd; /* LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT. */ uint32_t cmdsize; /* sizeof(struct linkedit_data_command) */ uint32_t dataoff; /* file offset of data in __LINKEDIT segment */ uint32_t datasize; /* file size of data in __LINKEDIT segment */ }; |
3.1.5.1 dataoff
dataoff 表示 __LINKEDIT 段的在文件里的偏移位置。
3.1.5.2 datasize
datasize 表示数据的大小。
3.6 LC_ID_DYLIB
LC_ID_DYLIB 是动态库的标识,只有动态库才有这个 LoadCommand,结构和 LC_LOAD_DYLIB 的结构是一样的,名称就是动态库自己的名称。比如 AdSupport 模块的 LC_ID_DYLIB 信息的名称是 /System/Library/Frameworks/AdSupport.framework/AdSupport,如下图:
如果是我们自己写的 framework 动态库,LC_ID_DYLIB 信息的名称一般是 @rpath/test.framework/test。
3.7 LC_UUID
随机生成的 UUID。
1 2 3 4 5 6 |
struct uuid_command { uint32_t cmd; /* LC_UUID */ uint32_t cmdsize; /* sizeof(struct uuid_command) */ uint8_t uuid[16]; /* the 128-bit uuid */ }; |
3.8 LC_DYLD_INFO_ONLY
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct dyld_info_command { uint32_t cmd; /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */ uint32_t cmdsize; /* sizeof(struct dyld_info_command) */ uint32_t rebase_off; /* file offset to rebase info */ uint32_t rebase_size; /* size of rebase info */ uint32_t bind_off; /* file offset to binding info */ uint32_t bind_size; /* size of binding info */ uint32_t weak_bind_off; /* file offset to weak binding info */ uint32_t weak_bind_size; /* size of weak binding info */ uint32_t lazy_bind_off; /* file offset to lazy binding info */ uint32_t lazy_bind_size; /* size of lazy binding infs */ uint32_t export_off; /* file offset to lazy binding info */ uint32_t export_size; /* size of lazy binding infs */ }; |
3.9 LC_SYMTAB
LC_SYMTAB 是符号表和字符串表的偏移信息,结构如下:
1 2 3 4 5 6 7 8 9 |
struct symtab_command { uint32_t cmd; /* LC_SYMTAB */ uint32_t cmdsize; /* sizeof(struct symtab_command) */ uint32_t symoff; /* symbol table offset */ uint32_t nsyms; /* number of symbol table entries */ uint32_t stroff; /* string table offset */ uint32_t strsize; /* string table size in bytes */ }; |
3.9.1 symoff
symoff 表示符号表的偏移。
3.9.2 nsyms
nsyms 表示符号表条目的个数。
3.9.3 stroff
stroff 表示字符串表在文件中的偏移。
3.9.4 strsize
stroff 表示字符串表的大小。
3.10 LC_DYSYMTAB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
struct dysymtab_command { uint32_t cmd; /* LC_DYSYMTAB */ uint32_t cmdsize; /* sizeof(struct dysymtab_command) */ uint32_t ilocalsym; /* index to local symbols */ uint32_t nlocalsym; /* number of local symbols */ uint32_t iextdefsym;/* index to externally defined symbols */ uint32_t nextdefsym;/* number of externally defined symbols */ uint32_t iundefsym; /* index to undefined symbols */ uint32_t nundefsym; /* number of undefined symbols */ uint32_t tocoff; /* file offset to table of contents */ uint32_t ntoc; /* number of entries in table of contents */ uint32_t modtaboff; /* file offset to module table */ uint32_t nmodtab; /* number of module table entries */ uint32_t extrefsymoff; /* offset to referenced symbol table */ uint32_t nextrefsyms; /* number of referenced symbol table entries */ uint32_t indirectsymoff; /* file offset to the indirect symbol table */ uint32_t nindirectsyms; /* number of indirect symbol table entries */ uint32_t extreloff; /* offset to external relocation entries */ uint32_t nextrel; /* number of external relocation entries */ uint32_t locreloff; /* offset to local relocation entries */ uint32_t nlocrel; /* number of local relocation entries */ }; |
3.11 LC_RPATH
LC_RPATH 表示程序运行时的查找路径,比如加载 xxx.dylib,会去什么目录去找这个文件。结构信息如下:
1 2 3 4 5 6 |
struct rpath_command { uint32_t cmd; /* LC_RPATH */ uint32_t cmdsize; /* includes string */ union lc_str path; /* path to add to run path */ }; |
lc_str 表示是路径名称,结构信息和 3.2.1 name 的说明是一样的。Xcode 的编译选项添加 LD_RUNPATH_SEARCH_PATHS 的路径则实际在可执行文件里添加了 LC_RPATH,如下图:
3.12 LC_VERSION_MIN_IPHONEOS
支持最低的 iOS 版本号
1 2 3 4 5 6 7 8 9 10 11 12 |
struct version_min_command { uint32_t cmd; /* LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS LC_VERSION_MIN_WATCHOS */ #ifndef __OPEN_SOURCE__ /* or LC_VERSION_MIN_TVOS */ #endif /* __OPEN_SOURCE__ */ uint32_t cmdsize; /* sizeof(struct min_version_command) */ uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ }; |
3.13 LC_LOAD_DYLINKER
四、Symbol Table & String Table
Symbol 表的头信息是在 LoadCommand 里的 LC_SYMTAB,其中 symoff 表示符号表的偏移,如下图
符号表的结构是一个连续的列表,其中的每一项都是一个 struct nlist
1 2 3 4 5 6 7 8 9 10 |
struct nlist { union { uint32_t n_strx; } n_un; uint8_t n_type; uint8_t n_sect; int16_t n_desc; uint32_t n_value; }; |
上图的符号表地址为 205592,十六进制是 0x32318,来看一下符号表的信息,如下图:
4.1 n_strx
n_strx 表示符号名在字符串表中的偏移量,相当于的函数名称。从上图看第一个符号表的 n_strx 的值是 2,跳转到字符串表地址 246052,十六进制为 0x3c124。
4.2 n_type
n_type 表示类型。
4.3 n_sect
n_sect 表示是节的索引。
4.4 n_desc
n_desc 表示描述信息。
4.5 n_value
n_value 表示函数对应的地址。上面的符号表有一个函数名是 [NSString base64StringFromData2:length:]
n_value 是 44592,对应十六进制 0xAE30,用 IDA 打开文件,将基址设置为 0x4000,跳转到 0xAE30 地址,可以看到果然是 [NSString base64StringFromData2:length:]
如下图:
文档历史
exchen,2017-11-10 编写