1、ELF(Executable Linkable Format):linux下的可执行文件格式,按照ELF格式编写的文件包括:.so、.a等。这里就不对该格式做过多介绍了。
2、PE(Portable Executable):windows下的可执行文件格式,按照PE格式编写的文件包括:.dll、.lib、.exe等。如图所示:
PE文件主要结构:DOS头+PE头+SessionTable+Session1+...+SessionN+....
DOS头:为兼容DOS系统
PE头:含文件属性、文件大小、Session数量等信息
SessionTalbe:含全部Session地址、偏移等
Session:每个文件至少包含两个Session,代码.text和数据.data。
代码装载后在内存中的结构如下:
Text段:存储了由代码编译成的指令集
Data段:含堆、栈、全局和静态变量
Bss段:未初始化全局和静态变量
3、Mach-O:IOS/MacOS下可执行文件格式,平时常见的.app或者ipa只是zip压缩包并非可执行文件,可执行文件在压缩包中。在mac下使用file命令打印任意可执行文件便可以看到如下内容:
> file /Applications/filename.app/Contents/MacOS/filename
Mach-O 64-bit executable x86_64
Mach-O结构主要包含Header、segment、section,如图所示(是不是和PE有点像):
从上图看Mach-o主要包含如下部分:
- 文件头 mach64 Header:文件类型、大小等信息
- 加载命令 Load Commands:指示加载器如何加载二进制文件
- 文本段 __TEXT:类似PE的.text段
- 数据段 __DATA:类似PE的.data段
- 动态库加载信息 Dynamic Loader Info
- 入口函数 Function Starts
- 符号表 Symbol Table
- 动态库符号表 Dynamic Symbol Table
- 字符串表 String Table
4、ADD:
一个C语言代码的编译过程:预编译---编译---汇编---链接。
而一个OC语言代码的编译过程是一样的...
C通常用gcc编译,OC通常用clang编译,而二者也其实是可以通用的,用clang来编译C代码、gcc来编译OC代码也是OK的,区别仅在优缺点上而已。
接下来详细介绍osx和ios下的文件格式。
--------------------------------------------------------------------------------------------- Header部分
struct mach_header { uint32_t magic; cpu_type_t cputype; cpu_subtype_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; }; struct mach_header_64 { uint32_t magic; cpu_type_t cputype; cpu_subtype_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; uint32_t reserved; };
用于判断程序的平台版本,也就是说判断是x86 or x64,在mach-o/loader.h中定义machheader与machheader64 结构的附近会看到两条宏,如果MHMAGIC的值是0xfeedface就说明是一个x86程序反之如果是0xfeedfacf 就是x64程序(以上理论基于osx,ios并未验证)。
#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) */
用于判断CPU的平台与版本,在mach/machine.h头文件中可以看到两组宏定义CPUTYPE???和CPUSUBTYPE???,定义着各种各样的型号。
filetype
用于判断程序的文件类型,在mach-o/loader.h中定义一组宏可以很直观的明白该位的意义。
#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 */
ncmds代表Load Command的个数,sizeofcmds代表ncmds段Load Command的总字节数。
flags与reserved
flags表示dyld加载标志位,它的标识宏在mach-o/loader.h中格式为MH_???,reserved是x64的保留位,目前看了下基本是恒为0x00000000。
- Load Command
struct load_command { uint32_t cmd; uint32_t cmdsize; };
cmd该位的值表示其类型,这点最开始本以为只是标记,对照hex查看时发现结构对不上,在头文件中好好的看了下才发现自己认知错误。mach-o/loader.h中定义loadcommand结构体的下方有着一组宏(LC???)与注解,不同类型对应不同的的结构。cmdsize该位代表所占字节数。
segment_command LC_SEGMENT segment_command_64 LC_SEGMENT_64 fvmlib_command LC_IDFVMLIB or LC_LOADFVMLIB dylib_command LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB` sub_framework_command LC_SUB_FRAMEWORK sub_client_command LC_SUB_CLIENT` sub_umbrella_command LC_SUB_UMBRELLA sub_library_command LC_SUB_LIBRARY prebound_dylib_command LC_PREBOUND_DYLIB dylinker_command LC_ID_DYLINKER, LC_LOAD_DYLINKER orLC_DYLD_ENVIRONMENT thread_command LC_THREAD or LC_UNIXTHREAD routines_command LC_ROUTINES symtab_command LC_SYMTAB dysymtab_command LC_DYSYMTAB twolevel_hints_command LC_TWOLEVEL_HINTS prebind_cksum_command LC_PREBIND_CKSUM uuid_command LC_UUID rpath_command LC_RPATH linkedit_data_command LC_CODE_SIGNATURE,LC_SEGMENT_SPLIT_INFO,LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT. encryption_info_command LC_ENCRYPTION_INFO version_min_command LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS LC_VERSION_MIN_WATCHOS dyld_info_command LC_DYLD_INFO or LC_DYLD_INFO_ONLY linker_option_command LC_LINKER_OPTION symseg_command LC_SYMSEG ident_command LC_IDENT fvmfile_command LC_FVMFILE entry_point_command LC_MAIN source_version_command LC_SOURCE_VERSION
- Data
struct section { char sectname[16]; char segname[16]; uint32_t addr; uint32_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; }; struct section_64 { char sectname[16]; char segname[16]; uint64_t addr; uint64_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; uint32_t reserved3; };
0x00 摘要
人生无根蒂,飘如陌上尘。 分散逐风转,此已非常身。
— 陶渊明 《杂诗》
mach-o
格式是OS X系统上的可执行文件格式,类似于windows的PE
与linux的ELF
,如果不彻底搞清楚mach-o
的格式与相关知识,去做其他研究,无异于建造空中阁楼。
每个Mach-O文件斗包含一个Mach-O头,然后是载入命令(Load Commands),最后是数据块(Data)。
接下来就对整个Mach-O的格式做出详细的分析。
0x01 Mach-O格式简单介绍
Mach-O文件的格式如下图所示:
又如下几个部分组成:
- Header:保存了Mach-O的一些基本信息,包括了平台、文件类型、LoadCommands的个数等等。
- LoadCommands:这一段紧跟Header,加载Mach-O文件时会使用这里的数据来确定内存的分布。
- Data:每一个segment的具体数据都保存在这里,这里包含了具体的代码、数据等等。
0x02 Headers
2.1 数据结构
Headers的定义可以在开源的内核代码中找到。
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 |
/* * 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) */ |
根据mach_header
与mach_header_64
的定义,很明显可以看出,Headers的主要作用就是帮助系统迅速的定位Mach-O文件的运行环境,文件类型。
2.2 实例
使用工具分析一个mach-o文件来具体的看一下Mach-O Headers。
通过otool可以得到Mach header的具体的情况,但是可读性略微有一点差。
1 2 3 4 5 |
➜ bin otool -h git git: Mach header magic cputype cpusubtype caps filetype ncmds sizeofcmds flags 0xfeedfacf 16777223 3 0x80 2 17 1432 0x00200085 |
还有一个工具是MachOview可以看的更清楚一点。
- MagicNumber的值为0xFEEDFACF所以该文件是一个64位平台上的文件
- CPU Type和CPU SubType也很容易理解,运行在X86_64的CPU平台上
- File Type标示了该文件是一个可执行文件,后面具体分析
- Flags标示了这个MachO文件的四个特性,后面具体分析
2.3 具体参数
2.3.1 FileType
因为Mach-O文件不仅仅用来实现可执行文件,同时还用来实现了其他内容