Mach-o浅说

318 阅读3分钟

Mach-O是一种mac上可执行文件的格式

iOS在加载app的时候,本质其实是通过dyld加载app中的MachO文件,MachO属于一种文件格式,其中包含了可执行文件、静态库、动态库、dyld等;其中包含的可执行文件是集合了多种架构的,例如包含了 armv7、arm64等

Mach-O文件的主组成部分:

  1. Header: 保存Mach-O的一些基本信息,包括平台、文件类型、指令数、指令总大小,dyld标记Flags等。
  2. Load Commands: 紧跟Header,加载Mach-O文件时会使用这部分数据确定内存分布,对系统内核加载器和动态连接器起指导作用。
  3. Text: 存放代码。
  4. Data:存放数据。例如:数据、字符串常量、类、方法 iShot2022-04-20_16.00.35.png

1、DATA区域(Text + data)

Segment & Section:Mach-o 文件有多个Segment(段)

每个Segment有不同的功能

每个Segment还细分为多个Section

描述
TEXT.text机器码
TEXT.cstring硬编码的字符串
TEXT.const初始化过的常量
DATA.data初始化过的可变的(静态/全局)数据
DATA.const未有初始化过的常量
DATA.bss(静态/全局)未初始化 的变量
DATA.common没有初始化过的符号声明

iShot2022-01-18 15.05.11.png

2、 常用概念:

  • VM Address : Virtual Memory Address, 段的虚拟内存地址,在内存中的位置

  • VM Size : Virtual Memory Size, 段的虚拟内存大小, 占用多少内存

iShot2022-04-20_14.44.07.png

  • File Offset : 段在文件中的偏移量

  • File Size : 段在文件中的大小 iShot2022-04-20_14.43.10.png

每个SECTION的基地址都等于上个SECTION的基地址+上个SECTION的Size

或者__PAGEZERO的基地址+SECTION的Offset iShot2022-04-20_11.45.48.png

__PAGEZERO的基地址 = 0000000000000000 Size = 0000000100000000

0000000000000000 + 0000000100000000= 0000000100000000 iShot2022-04-20_14.58.55.png

那么__TEXT的基地址就是 = 0000000100000000 iShot2022-04-20_15.06.04.png

同理,后面的 __DATA_CONST__DATA__LINKEDIT 的基地址都是 上个SECTION的基地址+上个SECTION的Size

3、 ASLR

Address Space Layout Random ,地址空间布局随机化。在iOS系统中打开一个App的时候是会将App的二进制数据从硬盘copy到内存里,那么这时候二进制数据就会对应一个内存地址,由于考虑安全因素的问题,内存的地址都是由虚拟缓存地址替代,而且地址的起始位置都是动态的,每次启动的时候都会不一样,这个技术就是ASLR。大部分主流操作系统都实现了ASLR。

讲人话就是,mach-o在载入内存的时候,前面会加一个随机大小的内存 iShot2022-04-20_15.53.20.png

所以在计算section的基地址的时候,需要额外加上ASLR

section基地址 = ASLR + pagezero + section的offset

4、结合lldb工具看一下具体数据都放在mach-o中的哪些位置:

【工具放后面】

测试代码:

int a = 10;
int b;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        char *d = "CCCT";
        NSLog(@"end");
    }
    return 0;
}

image-20211228161130633.png image-20211228161411641.png image-20211228161705270.png


LLDB工具:

下载地址:pan.baidu.com/s/1u9NCQofG… 提取码3sms

安装方法:

  1. 把插件放在项目根目录

  2. 在项目根目录新建 lldbinit 文件

  3. 在文件中写入内容:

    plugin load 插件路径
    
  4. 在项目的Edit Scheme -> LLDB init File 填入lldbinit文件路径

  5. 运行项目,断点后输入命令

    cat address 地址