# 06.MachO文件

## 一、基础概念

* Mach-O文件其实是MachObject文件格式的缩写。是Mac以及iOS上客户性文件的格式。
* 属于MachO格式的常见文件：
  * 目标文件.o
  * 库文件
    * .a
    * .dylib
    * Framework
  * 可执行文件
  * dyld
  * .dsym
* `file`指令可以产看文件类型

## 二、我们通过一个简单的例子来看下MachO是怎么生成的

Test.c

```c
# include <stdio.h>

int main() {
    printf("Hello world");
    return 1;
}
```

Test2.c

```c
# include <stdio.h>

int main2() {
    printf("Hello money");
    return 1;
}
```

### 2.1 源文件编译为.o文件（中间对象文件）

执行命令`clang -c Test.c Test2.c`

![](/files/-MZrrBtOryHeJLdrgbsd)

看到编译出了两个对应的`.o`文件

> 可以直接 -o 一步到位

### 2.2 将.o文件生成可执行文件

执行命名`clang -o TestEx Test.o Test2.o`

生成`TestEx`的可执行文件，通过`file`指令确认一下，这里可以发现生成的就是`MachO`文件了

```
file TestEx
TestEx: Mach-O 64-bit executable x86_64
```

### 2.3 链接顺序对MachO文件的影响

我们看下当前生成TestEx文件的MD5

```
md5 TestEx
MD5 (TestEx) = 3db04e90a4e078438a03f7cb9d8bf8c8
```

再执行命名`clang -o TestEx2 Test2.o Test.o`，调整.o文件顺序。

```
➜  Create md5 TestEx2
MD5 (TestEx2) = b32bbfd76c8784f8a517249f5f7c4bec
```

#### 通过objdump查看MachO文件数据

```
➜  Create objdump --macho -d TestEx
TestEx:
(__TEXT,__text) section
_main:
100003f20:    55    pushq    %rbp
100003f21:    48 89 e5    movq    %rsp, %rbp
100003f24:    48 83 ec 10    subq    $16, %rsp
100003f28:    c7 45 fc 00 00 00 00    movl    $0, -4(%rbp)
100003f2f:    48 8d 3d 60 00 00 00    leaq    96(%rip), %rdi ## literal pool for: "Hello world"
100003f36:    b0 00    movb    $0, %al
100003f38:    e8 39 00 00 00    callq    0x100003f76 ## symbol stub for: _printf
100003f3d:    b9 01 00 00 00    movl    $1, %ecx
100003f42:    89 45 f8    movl    %eax, -8(%rbp)
100003f45:    89 c8    movl    %ecx, %eax
100003f47:    48 83 c4 10    addq    $16, %rsp
100003f4b:    5d    popq    %rbp
100003f4c:    c3    retq
100003f4d:    90    nop
100003f4e:    90    nop
100003f4f:    90    nop
_main2:
100003f50:    55    pushq    %rbp
100003f51:    48 89 e5    movq    %rsp, %rbp
100003f54:    48 83 ec 10    subq    $16, %rsp
100003f58:    48 8d 3d 43 00 00 00    leaq    67(%rip), %rdi ## literal pool for: "Hello money"
100003f5f:    b0 00    movb    $0, %al
100003f61:    e8 10 00 00 00    callq    0x100003f76 ## symbol stub for: _printf
100003f66:    b9 01 00 00 00    movl    $1, %ecx
100003f6b:    89 45 fc    movl    %eax, -4(%rbp)
100003f6e:    89 c8    movl    %ecx, %eax
100003f70:    48 83 c4 10    addq    $16, %rsp
100003f74:    5d    popq    %rbp
100003f75:    c3    retq
```

```
➜  Create objdump --macho -d TestEx2
TestEx2:
(__TEXT,__text) section
_main2:
100003f20:    55    pushq    %rbp
100003f21:    48 89 e5    movq    %rsp, %rbp
100003f24:    48 83 ec 10    subq    $16, %rsp
100003f28:    48 8d 3d 6f 00 00 00    leaq    111(%rip), %rdi ## literal pool for: "Hello money"
100003f2f:    b0 00    movb    $0, %al
100003f31:    e8 48 00 00 00    callq    0x100003f7e ## symbol stub for: _printf
100003f36:    b9 01 00 00 00    movl    $1, %ecx
100003f3b:    89 45 fc    movl    %eax, -4(%rbp)
100003f3e:    89 c8    movl    %ecx, %eax
100003f40:    48 83 c4 10    addq    $16, %rsp
100003f44:    5d    popq    %rbp
100003f45:    c3    retq
100003f46:    90    nop
100003f47:    90    nop
100003f48:    90    nop
100003f49:    90    nop
100003f4a:    90    nop
100003f4b:    90    nop
100003f4c:    90    nop
100003f4d:    90    nop
100003f4e:    90    nop
100003f4f:    90    nop
_main:
100003f50:    55    pushq    %rbp
100003f51:    48 89 e5    movq    %rsp, %rbp
100003f54:    48 83 ec 10    subq    $16, %rsp
100003f58:    c7 45 fc 00 00 00 00    movl    $0, -4(%rbp)
100003f5f:    48 8d 3d 44 00 00 00    leaq    68(%rip), %rdi ## literal pool for: "Hello world"
100003f66:    b0 00    movb    $0, %al
100003f68:    e8 11 00 00 00    callq    0x100003f7e ## symbol stub for: _printf
100003f6d:    b9 01 00 00 00    movl    $1, %ecx
100003f72:    89 45 f8    movl    %eax, -8(%rbp)
100003f75:    89 c8    movl    %ecx, %eax
100003f77:    48 83 c4 10    addq    $16, %rsp
100003f7b:    5d    popq    %rbp
100003f7c:    c3    retq
```

可以确定生成的MachO文件不同了。

> MachO是一系列.o文件的集合

## 三：MachO文件结构

### 3.1 Header

* 包含了二进制文件的一般信息
  * 字节顺序、架构类型、加载指令的数量等
  * 使得一些信息可以快速确认
    * 比如当前文件是用于32位还是64位，对应的处理器是什么、文件类型是什么

#### Header数据结构

> 可以再Xcode中快速打开文件 `Loader.h`查看，这个文件时学习MachO非常好的一个入口！

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

`magic` 快速定位是64位还是32位 `cputype` CPU类型，比如ARM `cpusubtype` CPU具体类型，比如：arm64/armv7 `filetype` 文件类型，比如：可执行文件 `ncmds` LoadCommands条数 `sizeofcmds` LoadCommands大小 `flags` 标志位，标识二进制文件支持的功能。主要是和系统加载、链接有关。

#### 使用`otool`查看`Header`内容

```
➜  Create otool -v -h TestEx2
TestEx2:
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64   X86_64        ALL  0x00     EXECUTE    16       1368   NOUNDEFS DYLDLINK TWOLEVEL PIE
```

### 3.2 Load commands

* 内容包括区域的位置、符号表、动态符号表等

#### Rebase info offset

MachO文件在加载到内存中后（虚拟内存），会被分配一个随机的偏移值`ASLR`。 我们使用静态分析工具看到的并不是真实的内存地址。

#### 查看当前ASLR

![Img](/files/-MZrrBtR7HrH5zAsWi_q)

命令`image list -o -f | grep 当前MachO文件名`

```
(lldb) image list -o -f | grep WeChat
[  0] 0x0000000000118000 /Users/RyukieW/Library/Developer/Xcode/DerivedData/WeChat-ajurlvyhlazpsbhatmdivvridyee/Build/Products/Debug-iphoneos/WeChat.app/WeChat
```

ASLR `0x0000000000118000` （随机的每次都不一样） 真实地址 `0x10011e640`

#### 用工具查看地址

![Img](/files/-MZrrBtSY3CCjzDer_Rq) `0x0000000100006640`

#### 计算真实地址

* `ASLR` + `工具显示的地址`
* 使用编程计算器验证成功！

#### 使用`otool`查看`Load commands`内容

```
➜  Create otool -v -l TestEx2
...
```

### 3.3 Data

* 通常是文件中最大的部分
* 包含Segement的具体数据

[戴铭-Apple 操作系统可执行文件 Mach-O](https://ming1016.github.io/2020/03/29/apple-system-executable-file-macho/) [iOS逆向工程（九）：ASLR](https://www.jianshu.com/p/94f67bb84c93)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ni-xiang/06.macho-wen-jian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
