Mach-O_introduce

Mach-O基础知识

Posted by kunnan on May 29, 2018

mach-o

image

mach-o记录编译后的可执行文件,对象代码,共享库,动态加载代码和内存转储的文件格式。

  • /usr/include/mach-o
devzkndeMacBook-Pro:mach-o devzkn$ tree -L 4
.
├── arch.h
├── arm64
│   └── reloc.h
├── compact_unwind_encoding.h
├── dyld.h
├── dyld_images.h
├── fat.h
├── getsect.h
├── i386
│   └── swap.h
├── ldsyms.h
├── loader.h
├── module.map
├── nlist.h
├── ppc
│   ├── reloc.h
│   └── swap.h
├── ranlib.h
├── reloc.h
├── stab.h
├── swap.h
└── x86_64
    └── reloc.h
  • /usr/include/mach-o/loader.h 重点看
#include <mach-o/loader.h>

Mach-O 文件包含三个区域

  • 1、Mach-O Header:包含字节顺序,magic,cpu 类型,加载指令的数量等
  • 2、Load Commands:包含很多内容的表,包括区域的位置,符号表,动态符号表等。每个加载指令包含一个元信息,比如指令类型,名称,在二进制中的位置等。
  • 3、Data:最大的部分,包含了代码,数据,比如符号表,动态符号表等。

The existing load commands are listed below:

  • LC_SEGMENT — contains different information on a certain segment: size, number of sections, offset in the file and in memory (after the load)
  • LC_SYMTAB — loads the table of symbols and strings
  • LC_DYSYMTAB — creates an import table; data on symbols is taken from the symbol table
  • LC_LOAD_DYLIB — defines the dependency from a certain third-party library

The most important segments are the following

  • __TEXT — the executed code and other read-only data
  • __DATA — data available for writing; including import tables that can be changed by the dynamic loader during lazy binding
  • __OBJC — different information of the standard library of Objective-C language of execution time
  • __IMPORT — import table only for 32-bit architecture (I managed to generate it only on Mac OS 10.5)
  • __LINKEDIT — here, the dynamic loader places its data for already loaded modules (symbol tables, string tables, etc.)

I、Header

使系统能够快速定位其运行环境以及文件类型

image

  • struct
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 */
};
  • Fat Binary Fat Header的数据结构在<mach-o/fat.h>头文件上有定义: image
#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 */
};
//说明对应Mach-O文件大小、支持的CPU架构、偏移地址
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 */
};

从上面的例子,可以看出胖文件的fat_magic是0xcafebabe;顺便说一下64位的Mach-O文件的魔数值为#define MH_MAGIC_64 0xfeedfacf

file 就是根据Magic 来判断的
file ~/tmp
/Users/devzkn/tmp: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O dynamically linked shared library arm_v7] [arm64:Mach-O 64-bit dynamically linked shared library arm64]

magic 存放的位置在/usr/share/file/magic,更多信息请看这里

  • ` otool -h tmp` print the mach header

II、 Load Commands

这些加载命令在Mach-O文件加载解析时,被内核加载器或者动态链接器调用,指导如何设置加载对应的二进制数据段;

  • Any load command starts with the following fields:
struct load_command
{
  uint32_t cmd;  //command numeric code
  uint32_t cmdsize;  //size of the current command in bytes
};
  • Load Commands 是跟在 Header 后面的加载命令区.
<!-- 所有 commands 的大小总和即为 Header->sizeofcmds 字段,共有 Header->ncmds 条加载命令。 -->
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedface      12          9  0x00           6    33       3640 0x00100085

  • otool -l: OS X/iOS发展到今天,已经有40多条加载命令,其中部分是由内核加载器直接使用,而其他则是由动态链接器处理。其中几个主要的Load Commend为LC_SEGMENT, LC_LOAD_DYLINKER, LC_UNIXTHREAD, LC_MAINLC_SEGMENT 的数据结构的更多信息请查看这里
<!-- 看加载命令区 -->
devzkndeMacBook-Pro:~ devzkn$ otool -l tmp
tmp (architecture armv7):
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedface      12          9  0x00           6    33       3640 0x00100085

<!--LC_SEGMENT_64  将对应的段中的数据加载并映射到进程的内存空间去 -->
Load command 0
      cmd LC_SEGMENT
  cmdsize 668
  segname __TEXT
   vmaddr 0x00000000
   vmsize 0x00044000

   Load command 1
      cmd LC_SEGMENT
  cmdsize 1144
    segname __DATA
Load command 2
      cmd LC_SEGMENT
  cmdsize 56
  segname __LINKEDIT
   vmaddr 0x0004c000
   vmsize 0x00028000
  fileoff 311296
 filesize 154080
  maxprot 0x00000001
 initprot 0x00000001
   nsects 0
    flags 0x0
Load command 3
          cmd LC_ID_DYLIB
      cmdsize 84
         name /Library/MobileSubstrate/DynamicLibraries/.dylib (offset 24)
   time stamp 1 Thu Jan  1 08:00:01 1970
      current version 1.0.0
compatibility version 1.0.0

Load command 4
            cmd LC_DYLD_INFO_ONLY
        cmdsize 48
     rebase_off 311296

<!-- 符号表信息 -->
Load command 5
     cmd LC_SYMTAB
 cmdsize 24
  symoff 323260
   nsyms 4023
  stroff 372600
 strsize 76988
 <!-- LC_DYSYMTAB符号表  动态符号表信息
 -->
Load command 6
            cmd LC_DYSYMTAB
        cmdsize 80
      ilocalsym 0
<!-- 唯一的 UUID,标示该二进制文件,128bit -->
Load command 7
     cmd LC_UUID
 cmdsize 24
    uuid 5A3F7C11---ADC9-

<!-- 要求的最低系统版本(Xcode中的Deployment Target) -->
Load command 8
      cmd LC_VERSION_MIN_IPHONEOS
  cmdsize 16
  version 9.1
      sdk 10.3

Load command 9
      cmd LC_SOURCE_VERSION
  cmdsize 16
  version 0.0
  <!-- 加密信息   grep cryptid 可以查看是否加密。 -->
Load command 10
          cmd LC_ENCRYPTION_INFO
      cmdsize 20
     cryptoff 16384
    cryptsize 262144
      cryptid 0
<!-- 加载的动态库,包括动态库地址、名称、版本号等 load a dynamically linked shared library,load a dynamically linked shared library that is allowed to be missing -->
Load command 11
          cmd LC_LOAD_DYLIB
      cmdsize 96
         name /System/Library/PrivateFrameworks/StoreServices.framework/StoreServices (offset 24) 名称
   time stamp 2 Thu Jan  1 08:00:02 1970
      current version 1440.12.0  版本号
compatibility version 1.0.0

Load command 27
          cmd LC_RPATH
      cmdsize 32
         path @executable_path (offset 12)

         Load command 28
          cmd LC_RPATH
      cmdsize 56
         path /Library/MobileSubstrate/DynamicLibraries (offset 12)
Load command 29
          cmd LC_RPATH
      cmdsize 36
         path /var/mobile/frameworks/ (offset 12)

<!-- 函数地址起始表 -->

Load command 30
      cmd LC_FUNCTION_STARTS
  cmdsize 16
  dataoff 322000
 datasize 852

Load command 31
      cmd LC_DATA_IN_CODE
  cmdsize 16
  dataoff 322852
 datasize 408
 <!-- 代码签名信息 : code_signature-->
Load command 32
      cmd LC_CODE_SIGNATURE
  cmdsize 16
  dataoff 449600
 datasize 15776

Segment

Mach-O 文件有多个段(Segment),每个段有不同的功能。然后每个段又分为很多小的 Section。 LC_SEGMENT 意味着这部分文件需要映射到进程的地址空间去

  • section的命名规则
命名规则标识段-区的表示方法为(__SEGMENT.__section)SEGMENT所有字母大写,加两个下横线作为前缀;section为小写,同样加两个下横线作为前缀。
  • Raw segment data
    一般Mach-O文件有多个段(Segement),段每个段有不同的功能;
    1). __PAGEZERO: 空指针陷阱段,映射到虚拟内存空间的第一页,用于捕捉对NULL指针的引用;<br>
    2). __TEXT: 包含了执行代码以及其他只读数据。该段数据的保护级别为:VM_PROT_READ(读)、VM_PROT_EXECUTE(执行),防止在内存中被修改;<br>
    3). __DATA: 包含了程序数据,该段可写;<br>
    4). __OBJC: Objective-C运行时支持库;<br>
    5). __LINKEDIT: 链接器使用的符号以及其他表<br>
    一般的段又会按不同的功能划分为几个区(section)。
    
  • otool -o
devzkndeMacBook-Pro:segment_dumper devzkn$ otool -o ~/tmp
/Users/devzkn/tmp (architecture armv7):
Contents of (__DATA,__objc_classlist) section
devzkndeMacBook-Pro:knMoknKKKKKKokkn.decrypted.4.0 devzkn$ otool -o MKNooJn.dec* |grep password
            name 0x82b11e passwordTextFieldFrame
            name 0x82b135 passwordPlaceholder
                name 0x82b11e passwordTextFieldFrame
                name 0x82b135 passwordPlaceholder
                name 0x82b11e passwordTextFieldFrame
                name 0x82b135 passwordPlaceholder
LC_SEGMENT(__TEXT) Number of Sections + LC_SEGMENT(__DATA) Number of Sections = Sections Number of sections

image

LC_DYSYMTAB符号表

LC_DYSYMTAB符号表有非常大的作用,捕获到线上 Crash 或者 卡顿 堆栈的地址信息时,需要进行符号还原,进而确认卡顿、崩溃的具体位置,这个使用就要使用到LC_DYSYMTAB符号表;

  • 结构
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 */
};

<!-- 符号表在 Mach-O目标文件中的地址可以通过LC_SYMTAB加载命令指定的 symoff找到,对应的符号名称在stroff,总共有nsyms条符号信息 -->

III、 Section

一个可执行文件包含多个段, 在每一个段内有一些片段。它们包含了可执行文件的不同的部分, 包含了部分的源码及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;
};
  • sections
__TEXT,__text — the code itself

__TEXT,__cstring — constant strings (in double quotes)

__TEXT,__const — different constants

__DATA,__data — initialized variables (strings and arrays)

__DATA,__la_symbol_ptr — table of pointers to imported functions

__DATA,__bss — non-initialized static variables

__IMPORT,__jump_table — stubs for calls of imported functions

它的结构体跟随在 LC_SEGMENT 结构体之后,LC_SEGMENT 又在 Load Commands 中, segment 的数据内容是跟在 Load Commands 之后的。

作用

  • 各节的作用
Section 作用
__text:  主程序代码
__stub_helper:  用于动态链接的存根
__symbolstub1:  用于动态链接的存根
__objc_methname:  Objective-C 的方法名
__objc_classname:  Objective-C 的类名
__cstring:  硬编码的字符串
__lazy_symbol: 懒加载,延迟加载节,通过 dyld_stub_binder 辅助链接
_got: 存储引用符号的实际地址,类似于动态符号表
__nl_symbol_ptr: 非延迟加载节
__mod_init_func: 初始化的全局函数地址,在 main 之前被调用
__mod_term_func: 结束函数地址
__cfstring: Core Foundation 用到的字符串(OC字符串)
__objc_clsslist: Objective-C 的类列表
__objc_nlclslist: Objective-C 的 +load 函数列表,比 __mod_init_func 更早执行
__objc_const: Objective-C 的常量
__data: 初始化的可变的变量
__bss: 未初始化的静态变量
  • Section 是具体有用的数据存放的地方
Section 作用
TEXT.text 只有可执行的机器码
TEXT.cstring 去重后的C字符串(不含中文字符)
TEXT.const 初始化过的常量
TEXT.stubs 符号桩。本质上是一小段会直接跳入lazybinding的表对应项指针指向的地址的代码。
TEXT.stub_helper 辅助函数。上述提到的lazybinding的表中对应项的指针在没有找到真正的符号地址的时候,都指向这。
TEXT.unwind_info 用于存储处理异常情况信息
TEXT.eh_frame 调试辅助信息
DATA.data 初始化过的可变的(静态/全局)数据
DATA.nl_symbol_ptr 非lazy-binding的指针表,每个表项中的指针都指向一个在装载过程中,被动态链机器搜索完成的符号
DATA.la_symbol_ptr lazy-binding的指针表,每个表项中的指针一开始指向stub_helper
DATA.const 没有初始化过的常量
DATA.mod_init_func 初始化函数,在main之前调用
DATA.mod_term_func 终止函数,在main返回之后调用
DATA.bss 没有初始化的(静态/全局)变量
DATA.common 没有初始化过的符号声明
TEXT.ustring utf-8编码后的中文字符串
TEXT.objc_methname OC方法名(不含c方法,所有的方法都在TEXT.text中)
DATA.cfstring 用OC方法创建的字符串,对TEXT段中字符串的引用
DATA.__objc _classlist节 这个节列出了所有的class(metaclass自身也是一种class)。
DATA.__objc _catlist 代表的就是程序里面有哪些Category
DATA.__objc_protolist 代表的就是程序里面有哪些Protocol
DATA.__objc_classrefs 该节是为了标记这个类究竟有没有被引用
DATA.__objc_selrefs 告诉你究竟有哪些SEL对应的字符串被引用了
DATA.__objc_superrefs 在编译期指定方法对应的current_class,以方便后续的superclass方法列表查找
DATA.__objc_const 存放的是一些需要在类加载过程中用到的readonly data

otool -t print the text section (disassemble with -v)

  • otool -tv tmp

  • otool -sv __TEXT __cstring tmp

code_signature

里面包含了程序代码的签名,这个签名的作用就是保证签名后 .app 里的文件,包括资源文件,Mach-O 文件都不能够更改。

<!-- 代码签名信息 : code_signature-->
Load command 32
      cmd LC_CODE_SIGNATURE
  cmdsize 16
  dataoff 449600
 datasize 15776

IV、 Sequence of Steps in Executing an Image

  • flow_of_process_execution

image Flow of the various process execution functions in OS X 引用自《Mac OS X and iOS Internals》P519

  • parse_machfile()
(1):解析线程状态,UUID和代码签名。相关命令为LC_UNIXTHREAD、LC_MAIN、LC_UUID、LC_CODE_SIGNATURE 
(2):解析代码段Segment。相关命令为LC_SEGMENT、LC_SEGMENT_64;
(3):解析动态链接库、加密信息。相关命令为:LC_ENCRYPTION_INFO、LC_ENCRYPTION_INFO_64、LC_LOAD_DYLINKER
  • load 函数是如何被调用的

image

dyld 是Apple 的动态链接器;在 xnu 内核为程序启动做好准备后,就会将 PC 控制权交给 dyld 负责剩下的工作 (dyld 是运行在 用户态的, 这里由 内核态 切到了用户态).

V 、app 的启动过程

  • 1、内核(OS Kernel)创建一个进程,分配虚拟的进程空间等等,加载动态链接器。
  • 2、通过动态链接器加载主二进制程序引用的库、绑定符号。
  • 3、启动程序
  • mach-o_execution:

image

  • Darwin体系
    1)Darwin是一种类似unix的操作系统,他的核心是XNU。
    2)XNU是一种混合式内核。结合了mach与BSD两种内核。
    3)Mach 是微内核实现。
    4)BSD 实现在Mach的上层,这一层提供的API 支持了POSIX标准模型。在XNU中主要实现了一些高级的API与模块。
    

image

VI 、iOS安全机制

  • 代码签名:
  • 强制访问控制(Mandatory Access Control):系统检测安全属性以便确定一个用户是否有权访问该文件
  • sandbox: 沙盒在启动的时候可以设置运行的程序是否可以访问网络、文件、目录

image

See Also

/Users/devzkn/bin/knpost Mach-O_introduce Mach-O基础知识 -t c Mach-O
#原来""的参数,需要自己加上""

转载请注明: > Mach-O_introduce