前几篇文章中 :linux
shell 脚本自动重签名与代码注入shell
重签应用调试与代码修改 (Hook)windows
咱们对重签名和代码注入有了必定的了解 . 那么这个过程当中咱们反复提到一个最重要的文件 -- Mach-O
.缓存
那么说来讲去 , 这个Mach-O
究竟是个什么 . 既然它这么重要 , 那么咱们有必要去好好的了解一下它 .bash
( 对概念不太感兴趣的同窗能够直接跳到第二章节 Mach-O
的文件结构 ) 微信
Mach-O
实际上是 Mach Object
文件格式的缩写,是 mac
以及 iOS
上可执行文件的格式, 相似于 windows
上的 PE
格式 ( Portable Executable ) , linux
上的 elf
格式 ( Executable and Linking Format ) .架构
它是一种用于可执行文件、目标代码、动态库的文件格式。做为 a.out
格式的替代,Mach-O
提供了更强的扩展性。函数
可是除了可执行文件外 , 其实还有一些文件也是使用的 Mach-O
的文件格式 .工具
属于 Mach-O
格式的常见文件
- 目标文件 .o
- 库文件
- .a
- .dylib
- Framework
- 可执行文件
- dyld ( 动态连接器 )
- .dsym ( 符号表 )
Tips : 使用 file
命令能够查看文件类型
也就是说 Mach-O
并不是必定是可执行文件 , 它是一种文件格式 , 分为 Mach-O Object
目标文件 、 Mach-O ececutable
可执行文件、 Mach-O dynamically
动态库文件、 Mach-O dynamic linker
动态连接器文件、 Mach-O dSYM companion
符号表文件 , 等等 .
你们能够本身经过 vim
几个 .c
, 而后 clang
生成 .o
目标文件和可执行文件来玩一下 , 以便更好地理解这几种文件以及其编译的模式 .
那么上图中咱们还看到一个 arm64
, 这个是什么意思呢 ?
- 在 release 模式下
- 支持 iOS 11.0 系统版本如下
当知足这两个条件时 , 咱们的应用打包出来的 Mach-O ececutable
可执行文件是包含 arm64
以及 arm_v7
的架构的 , iPhone 5C
以上机型都是 64
位系统了 .
那么包含了支持多架构的 Mach-O ececutable
可执行文件被称为 : 通用二进制文件 , 即多种架构均可读取运行 .
另外 Xcode
中经过编译设置 Architectures
是能够更改所生成的 Mach-O ececutable
可执行文件的支持架构的 .
编译器在生成
Mach-O
文件会选择Architectures
以及Valid Architectures
的交集 , 所以想要支持多架构的话 , 在Valid Architectures
中继续添加就能够了 , 编译生成Mach-O
以后 , 使用file
命令能够检查下结果 .
苹果公司提出的一种程序代码。能同时适用多种架构的二进制文件
同一个程序包中同时为多种架构提供最理想的性能。
由于须要储存多种代码,通用二进制应用程序一般比单一平台二进制的程序要大。
可是因为两种架构有共通的非执行资源,因此并不会达到单一版本的两倍之多。
并且因为执行中只调用一部分代码,运行起来也不须要额外的内存。
通用二进制文件一般被称为 Universal binary
, 在 MachOView
等 中叫作 Fat binary
, 这种二进制文件是能够彻底拆分开来 , 或者从新组合的 , 那么接下来咱们来玩一下 .
10.3
.选择 Release
( 测试完毕改回来 . 不然 run
太慢 )
iOS 10.3
以上 +
release
环境下会默认包含
arm64 + armv7
的架构 , 所以咱们本身加上
armv7s
和
arm64e
.
run
起来后找到 Mach-O
文件
能够看到 , 咱们的 Fat binary
就已经生成好了 .
使用 lipo - info
命令也是能够查看支持架构的
Fat binary
lipo macho文件名称 -thin 要拆分哪一个架构 -output 拆分出来文件名
复制代码
例:
lipo 通用二进制MachO_Test -thin armv7s -output macho_armv7s
复制代码
而后咱们就看到文件夹多了一个 macho_armv7s
, 查看一下 :
另外拆分后源文件并不会改变.
Fat binary
lipo -create macho_arm64 macho_arm64e macho_armv7 macho_armv7s -output newMachO
复制代码
合并后咱们来看下新生成的 和之前的文件的哈希值 .
如出一辙的 .
Tips:
这种方式在咱们合并静态库的时候会常常用到 , 由于静态库自己就是
Mach-O
文件嘛 , 另外咱们在逆向的时候 , 有时也常常会用这种方法拆分二进制文件 , 由于咱们只须要分析单一架构便可 , 无须肥大的二进制文件.
另外稍微补充一点 , 多架构二进制文件组合成通用二进制文件时 , 代码部分是不共用的 ( 由于代码的二进制文件不一样的组合在不一样的 cpu
上可能会是不一样的意义 ) . 而公共资源文件是会共用的 .
Mach-O
的组成结构如图所示包括了
Header
包含该二进制文件的通常信息
字节顺序、架构类型、加载指令的数量等。
使得能够快速确认一些信息,好比当前文件用于 32
位仍是 64
位,对应的处理器是什么、文件类型是什么
Load commands
一张包含不少内容的表
Data
一般是对象文件中最大的部分
Segement
的具体数据咱们来找一个 Mach-O 文件 使用 MachOView 或者 otool 命令去查看一下文件结构 .
那么这个 Mach-O
到底这些部分存放的是什么内容 , 加下来咱们就来一一探索一下 .
Header
中存储的内容大体如上图所示 , 那么每一条到底对应着什么呢 ? , 咱们打开源码看一下, cmd + shift + o
, 搜索 load.h
, 找 mach_header_64
结构体.
struct mach_header_64 {
uint32_t magic; /* 魔数,快速定位64位/32位 */
cpu_type_t cputype; /* cpu 类型 好比 ARM */
cpu_subtype_t cpusubtype; /* cpu 具体类型 好比arm64 , armv7 */
uint32_t filetype; /* 文件类型 例如可执行文件 .. */
uint32_t ncmds; /* load commands 加载命令条数 */
uint32_t sizeofcmds; /* load commands 加载命令大小*/
uint32_t flags; /* 标志位标识二进制文件支持的功能 , 主要是和系统加载、连接有关*/
uint32_t reserved; /* reserved , 保留字段 */
};
复制代码
mach_header_64
相较于 mach_header
, 也就是 32
位头文件 , 只是多了一个保留字段 . mach_header
是连接器加载时最早读取的内容 , 它决定了一些基础架构 , 系统类型 , 指令条数等信息.
Load Commands
详细保存着加载指令的内容 , 告诉连接器如何去加载这个 Mach-O
文件.
经过查看内存地址咱们发现 , 在内存中 , Load Commands
是紧跟在 Mach_header
以后的 .
那么这些 Load Commands
对应了什么呢 ? 咱们以 arm64 为例.
其中 _TEXT 段和 _DATA 段 , 是咱们常常须要研究的 , MachOView
下面也有详细列出.
咱们来看看 _TEXT
段里都存放了什么 , 其实真正开始读取就是从 _TEXT
段开始读取的 .
名称 | 内容 |
---|---|
_text |
主程序代码 |
_stubs , _stub_helper |
动态连接 |
_objc_methodname |
方法名称 |
_objc_classname |
类名称 |
_objc_methtype |
方法类型 ( v@: ) |
_cstring |
静态字符串常量 |
_DATA
在内存中是紧跟在 _TEXT
段以后的.
名称 | 内容 |
---|---|
_got : Non-Lazy Symbol Pointers |
非懒加载符号表 |
_la_symbol_ptr : Lazy Symbol Pointers |
懒加载符号表 |
_objc_classlist |
类列表 |
...
以及以一些数据源 就不一一列举了 .
另外有一点值得提一下的就是系统库的方法 , 因为是公用的 , 存放在共享缓存中 , 那么咱们的 Mach-O
中调用系统方法 ,
例如 : 调用 NSLog("%@,@"haha");
这个方法的实现确定不在咱们的 Mach-O
里 , 那么它如何找到方法实现呢 ?
其实就是
dyld
在进行连接的时候 , 会将Mach-O
里调用存放在共享缓存中的方法进行符号绑定 , 而这个符号在release
的时候是会被自动去掉的. 这也是咱们常用收集bug
工具时须要恢复符号表的缘由. 而所以fishhooh
在hook
系统函数的时候名字叫reBind
的缘由 .
关于符号绑定这一点咱们在讲 fishhook
的时候会详细讲述一下 .
至此 , 整个 Mach-O
文件结构咱们已经讲述完了 . 后续在逆向的过程当中涉及到具体存储内容咱们会继续介绍 .