MahO文件

352 阅读6分钟

可执行文件

可执行格式魔数用途
PE32/PE32+MZ可移植的可执行格式,Windows和Intel的Extensible FirmWare Interface(EFI)二进制文件的原始格式。尽管OS X 不支持这个格式,但是引导加载器支持这个格式,并且可以加载boot.efi文件
ELF\x7FELF:Lnux和大部分UNIX的原生格式.OSX 不支持ELF
脚本#!UNIX脚本和一些解释器脚本使用的格式:主要用于shell脚本,但是 也常用于其他解释器,例如Ped AWK. PHP等。内核寻找#!后而跟着的字符串,然后执行这个字符串表示的命令。文件剩下的部分通过标准输入stdin)传递给这个命令
通用二进制格式0xcafebabe(小尾顺序) 0xbebafeca(大尾顺序)包含多种架构支持的二进制格式,只在OS X上支持
Mach-O0xfeedface(32位)0xfeedfac(6is 4位)OS X的原生二进制格式

MachO概述

Mach-O其实是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式, 类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format);

常见的文件格式: 1.目标文件.o 2.库文件 .a .dylib .framework 3. 可执行文件 dyld: 动态链接器 .dsym 符号表release 版本生成项目名.app.dSYM文件夹,路径../Contents/Resources/DWARF中生成 Mach-O dSYM companion file 格式的文件,是该项目的符号表;

加载与执行

首先了解下什么是进程,进程是一个程序的执行实体.
那 App 的启动就可以分为两步:

  • 内核创建一个进程,分配空间,加载 dyld

  • dyld 加载根据mach-o的格式将可执行文件加载到内存.
    之后系统通过解析文件建立依赖初始化运行环境执行进程这几步来真正的运行App.

  • MachO 是一种文件格式

    • 包含:可执行文件、动态库、静态库、目标文件、dyld等;

    • 可执行文件:

      • 查看某个文件:file

      • 通用二进制文件 fat/universy biniary

      • lipo 命令

        • lipo -thin 拆分架构
        • lipo -create 合并架构

MachO结构

image.png

  1. Header
  2. LoadCommands
  3. Data(Segment段)
  4. Loder Info链接信息
1.Header

用于快速确认文件 cpu类型,文件类型,与LoadCommands之间紧密相连;

2.LoadCommands

用于描述每一段起始位置,指示加载器如何加载二进制文件, 与Data之间有一段空区域,可以写汇编代码;

3.Data(Segment段)
  • Section Text 代码段(由代码编译成的指令集)
  • Section Data 数据段(含堆、栈、全局和静态变量)
4.Loder Info链接信息

完整的MacO文件末端是一系列链接信息。其中包含了动态链接器用来链接可执行文件或者依赖所需的符号表、字符串表等

我们从main.c 开始分析MachO 文件

/*************************************************************************
	> File Name: main.c
	> Author:曹亮
	> Mail:caoliang2025@163.com
	> Created Time: 三 11/ 3 21:09:45 2021
 ************************************************************************/

#include<stdio.h>
static char global_name[] = "name_global";
int main( int argc, char *argv[] )
{
    char temp_str[] = "Hello World";
    int tempI = 10;
    printf("Hello World!\n");
    return 0;
}

clang编译main.c 生成a.out

file 命令查看一下文件类型 a.out ,MachO 64位 image.png 我们通过MachOView查看一下文件 image.png

  • 文件头 mach64 Header
  • 加载命令 Load Commands
  • 文本段 __TEXT
  • 数据段 __DATA
  • 动态库加载信息 Dynamic Loader Info
  • 入口函数 Function Starts
  • 符号表 Symbol Table
  • 动态库符号表 Dynamic Symbol Table
  • 字符串表 String Table

Mach Header - 可执行文件文件头

使用otool -h 工具查看一下头文件 image.png mach-o文件里面含有文件信息

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

从图中可知,通过Header可以获得CPU架构信息、文件类型等信息

Magic Number:魔数用来确定是32位还是64位 (MH_MAGIC_64,说明当前是64位)

CPU Type:CPU类型 (本例中是arm64)

CPU Subtype:CPU子类型()

File Type:用来确定文件类型(MH_EXECUTE, 可执行文件)

Number of Load Command:加载命令的数量,本例子中位23个加载命令(详解2中可对应下)

Size of Load Command:表示23个加载命令的总字节数为2888字节

Flags:表示二进制文件支持的功能,主要与系统的加载、链接有关

Mach-O文件包含非常详细的加载指令,这些指令非常清晰地指示加载器如何设置并且加载二进制数据。 使用otool -l 查看加载命令 image.png 告诉loader如何设置并加载二进制数据

命令详解

1-5、LC_SEGMENT_64(xxx) 将该段映射到进程地址空间中

6、 LC_DYLD_INFO_ONLY 加载动态链接库

7、LC_SYMTAB 载入符号表地址

8、LC_DYSYMTAB 载入动态符号表地址

9、LC_LOAD_DYLINKER 加载动态加载库

10、LC_UUID 确定文件的唯一标识(crash文件中也会有,用来检查crash文件与dysm文件是否匹配)

11、

12、LC_SOURCE_VERSION 构建该二进制文件使用的源代码版本

13、LC_MAIN 设置程序主线程的入口地址和栈大小

14、LC_ENCRYPTION_INFO_64 获取加密信息

15-19、LC_LOAD_DYLIB(xxx) 加载额外的动态库

20、LC_RPATH Dyld维护一个称为运行路径列表的路径的当前堆栈。当遇到@rpath时,它会被替换为运行路径列表中的每个路径,直到找到可加载的dylib

21、LC_FUNCTION_STARTS 定义一个函数起始地址表,使调试器和其他程序易于看到一个地址是否在函数内

22、LC_DATA_IN_CODE 定义在代码段内的非指令的表

23、LC_CODE_SIGNATURE 获取应用签名信息

说明:一、二两部分让kernel知道如何读取MachO文件,并制定MachO文件的动态链接器用来完成后续的动态库加载,然后设置好程序的入口信息;后面的部分就相当于run起来之后,为每一个映射到虚拟内存中的指令操作提供真实的物理地址支持

Symbol Pointer

image.png

Dynamic Loader Info 链接信息

代表非懒加载符号表与懒加载符号表,这两个指针表,保存着与字符串标对应的函数指针

image.png 一共分四个区:

1、Rebase Info:  pointer rebase的信息

2、Binding Info:non-lazy symbol pointer绑定需要的信息

3、Lazy Binding Info:lazy symbol pointer绑定需要的信息

4、Export Info:暴露给外部的符号的信息

该部分在hook代码中非常有用,根据mach-o的符号动态链接原理,让non-lazy/lazy symbol 指针重新指向对应的symbol stub代码位置,起到在runtime的hook