初探Mach-O学习小记(附源码)

1、ELF(Executable Linkable Format):linux下的可执行文件格式,按照ELF格式编写的文件包括:.so、.a等。这里就不对该格式做过多介绍了。

2、PE(Portable Executable):windows下的可执行文件格式,按照PE格式编写的文件包括:.dll、.lib、.exe等。如图所示:


PE结构.jpeg


PE文件主要结构:DOS头+PE头+SessionTable+Session1+...+SessionN+....

DOS头:为兼容DOS系统
PE头:含文件属性、文件大小、Session数量等信息
SessionTalbe:含全部Session地址、偏移等
Session:每个文件至少包含两个Session,代码.text和数据.data。

代码装载后在内存中的结构如下:


内存结构.png


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有点像):


machoview.png


从上图看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下的文件格式。

--------------------------------------------------------------------------------------------
  1. Header部分
结构体定义在mach-o/loader.h头文件中

代码:
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;
};
magic
用于判断程序的平台版本,也就是说判断是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) */
cputype与cpusubtype
用于判断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与sizeofcmds
ncmds代表Load Command的个数,sizeofcmds代表ncmds段Load Command的总字节数。

flags与reserved 
flags表示dyld加载标志位,它的标识宏在mach-o/loader.h中格式为MH_???,reserved是x64的保留位,目前看了下基本是恒为0x00000000。
  1. Load Command
在mach-o/loader.h中可以看到对Load Command的定义。
代码:
struct load_command {
    uint32_t cmd;
    uint32_t cmdsize;
};
cmd与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
在写代码对macho进行解析的时候发现,LC_SEGMENT和LC_SEGMENT_64与其他的类型并不一样,结构体内有一个nsects项,该项的值代表这个Segment段存在多少个secetion子段。
  1. Data
data段的数量就是Load Command中secetion的总数量,相应的数据与大小就是addr起始的size字节大小的数据。

代码:
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_headermach_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文件不仅仅用来实现可执行文件,同时还用来实现了其他内容

    • 0
      点赞
    • 1
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值