可执行文件
| 可执行格式 | 魔数 | 用途 |
|---|---|---|
| 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-O | 0xfeedface(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结构
- Header
- LoadCommands
- Data(Segment段)
- 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位
我们通过MachOView查看一下文件
- 文件头 mach64 Header
- 加载命令 Load Commands
- 文本段 __TEXT
- 数据段 __DATA
- 动态库加载信息 Dynamic Loader Info
- 入口函数 Function Starts
- 符号表 Symbol Table
- 动态库符号表 Dynamic Symbol Table
- 字符串表 String Table
Mach Header - 可执行文件文件头
使用otool -h 工具查看一下头文件
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 查看加载命令
告诉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
Dynamic Loader Info 链接信息
代表非懒加载符号表与懒加载符号表,这两个指针表,保存着与字符串标对应的函数指针
一共分四个区:
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