12 - Mach-O

2021-05-10  本文已影响0人  卡布奇诺_95d2

Mach-O 简介

Mach-O其实是Mach Object文件格式的缩写,是MacOSiOS上一种用于可执行文件、目标代码、动态库的文件格式。类似于Windows上的PE格式(Protable Executable),Linux上的ELF格式(Executable and Linking Format)。Mach-O作为a.out格式的替代,提供了更强的扩展性。

Mach-O 文件类型

16206262137187.jpg

属于Mach-O格式的常见文件:

Mach-O 文件结构

16204596529344.jpg

由上图可以看出,Mach-O主要分三大块:

Header

Header的数据结构如下:

/*
 * 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) */

各变量释义:

名称 定义
magic Mach-O魔数。如:
FAT:0xcafebabe
ARMv7:0xfeedface
ARM64:0xfeedfacf
cputype、cpusubtype CPU架构及子版本
filetype 文件类型,共有11种宏定义类型,如:
MH_EXECUTABLE(可执行二进制文件)
MH_OBJECT(目标文件)
MH_DYLIB(动态库)
ncmds 加载命令的数量
sizeofcmds 所有加载命令的大小
flags dyld加载需要的一些标记,有28种宏定义,具体看源码,其中MH_PIE表示启用ASLR地址空间布局随机化
reserved 64位保留字段

如何查看Mach-O文件的header信息

Mac ~/testDemo.app objdump --macho -private-header testDemo
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64   ARM64        ALL  0x00     EXECUTE    23       2888   NOUNDEFS DYLDLINK TWOLEVEL PIE
Mac ~/testDemo.app otool -h testDemo
testDemo:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           2    23       2888 0x00200085
16206293716647.jpg

Load Commands

数据结构如下:

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

Load Commands的数目和总大小已经在Header中记录,见ncmdssizeofcmds。所有加载指令都是以cmd、cmdsize开头,cmd字段表示该指令的类型,cmdsize字段以字节为单位,主要记录偏移量,以便于Load Commands指针查找下一条加载指令。32位架构的cmdsize是以4字节的倍数,64位结构的cmdsize是以8字节的倍数,不够用0填充。

Load Commands中常见的加载指令:

如何查看Mach-O文件的Load Command信息

objdump --macho --private-headers 【Mach-O路径】
16204683422789.jpg
 Mac ~/testDemo.app otool -l testDemo
16206339480322.jpg 16206336854994.jpg

Data

由多个二进制组成,逐一排列,包含__TEXT代码、__DATA代码、符号表,存储实际的代码和数据

数据结构如下:

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   /* 节名 */
    char        segname[16];    /* 所属段名 */
    uint64_t    addr;       /* 映射到虚拟地址的偏移 */
    uint64_t    size;       /* 节的大小 */
    uint32_t    offset;     /* 节在当前架构文件中的偏移 */
    uint32_t    align;      /* 节的字节对齐大小n,2^n */
    uint32_t    reloff;     /* 重定位入口的文件偏移 */
    uint32_t    nreloc;     /* 重定位入口个数 */
    uint32_t    flags;      /* 节的类型和属性*/
    uint32_t    reserved1;  /* reserved (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
    uint32_t    reserved3;  /* 保留位,以上两同理 */
};

Section节已经是最小的分类了,Data的大部分数据内容集中在__TEXT、__DATA这两段中。

__TEXT节 含义
__text 程序可执行代码区域
__stubs 间接符号存根,用于跳转到懒加载指针表
__stubs_helper 懒加载符号加载辅助函数
__cstring 只读的C字符串,包含OC的部分字符串和属性名
__DATA节 含义
__nl_symbol_ptr 非懒加载指针表,dyld加载时立即绑定值
__la_symbol_ptr 懒加载指针表,第1次调用才绑定值
__got 非懒加载全局指针表
__mod_init_func constructor函数
__cfstring OC字符串

Mach-O 学习使用过程中用到的指令

file命令

file [-bcLvz][-f <名称文件>][-m <魔法数字文件>...][文件或目录...]
//不加参数
[root@localhost ~]# file install.log
install.log: UTF-8 Unicode text

//-b:列出辨识结果时,不显示文件名称
[root@localhost ~]# file -b install.log
UTF-8 Unicode text

//-i:输出mime类型的字符串
[root@localhost ~]# file -i install.log
install.log: text/plain; charset=utf-8

//-F:使用指定分隔符号替换输出文件名后的默认的":"分隔符
[root@localhost ~]# file -F "--" install.log
install.log-- ASCII text
[root@localhost ~]# file -F "++" install.log
install.log++ ASCII text

otool命令

otool [-arch arch_type] [-fahlLDtdorSTMRIHGvVcXmqQjCP] [-mcpu=arg] [--version] <object file> ...
-f print the fat headers
-a print the archive header
-h print the mach header
-l print the load commands
-L print shared libraries used
-D print shared library id name
-t print the text section (disassemble with -v)
-x print all text sections (disassemble with -v)
-p <routine name>  start dissassemble from routine name
-s <segname> <sectname> print contents of section
-d print the data section
-o print the Objective-C segment
-r print the relocation entries
-S print the table of contents of a library (obsolete)
-T print the table of contents of a dynamic shared library (obsolete)
-M print the module table of a dynamic shared library (obsolete)
-R print the reference table of a dynamic shared library (obsolete)
-I print the indirect symbol table
-H print the two-level hints table (obsolete)
-G print the data in code table
-v print verbosely (symbolically) when possible
-V print disassembled operands symbolically
-c print argument strings of a core file
-X print no leading addresses or headers
-m don't use archive(member) syntax
-B force Thumb disassembly (ARM objects only)
-q use llvm's disassembler (the default)
-Q use otool(1)'s disassembler
-mcpu=arg use `arg' as the cpu for disassembly
-j print opcode bytes
-P print the info plist section as strings
-C print linker optimization hints
--version print the version of /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool

1、查看App所使用的动态库

Mac ~/testDemo.app otool -L testDemo
testDemo:
    /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1774.101.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
    /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1774.101.0)
    /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 4006.0.0)

2、查看ipa是否已经砸壳

Mac ~/testDemo.app otool -l testDemo| grep crypt
     cryptoff 16384
    cryptsize 16384
      cryptid 0

其中:cryptid为0表示已经砸壳。cryptid为1表示未砸壳。

3、 查看Mach-O头部信息

Mac ~/testDemo.app otool -h testDemo
testDemo:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           2    23       2888 0x00200085

通用二进制文件

通用二进制(Universal binary)是苹果公司提出的一种程序代码,使程序能以本地程序的形式运行在使用PowerPC或者英特尔微处理器(x86)的麦金塔电脑上,在同一个程序包中同时为两种架构提供最理想的性能。硬件方面,苹果电脑公司已经将其产品线上的所有麦金塔电脑在2006年内转为英特尔处理器,相对应的软件方面,苹果最早是在2005年世界开发者大会(WWDC)上就发布了通用二进制的内容来适应这种转换。 当程序在操作系统中运行后,将自动检测通用二进制代码,根据使引用的架构自动选择合适的代码来执行,实现无损的本地程序运行速度

lipo -info 【MachO文件】

//示例
lipo -info WeChat
Architectures in the fat file: WeChat are: armv7 armv7s arm64
lipo [MachO文件] -thin 架构 -output 输出文件路径

//示例
lipo WeChat -thin arm64 -output WeChat_arm64
lipo -info WeChat_arm64
Architectures in the fat file: WeChat_arm64 are: arm64

lipo WeChat -thin armv7 -output WeChat_armv7
lipo -info WeChat_armv7
Architectures in the fat file: WeChat_armv7 are: armv7
lipo -create MachO1 MachO2 -output 输出文件路径

//示例
lipo -create WeChat_arm64 WeChat_armv7 -output WeChat_64_v7

lipo -info WeChat_64_v7
Architectures in the fat file: WeChat_64_v7 are: armv7 arm64

总结

上一篇 下一篇

猜你喜欢

热点阅读