4逆向 - Mach-O
Fri, May 4, 2018
App从开发到安装到手机的过程
开发完毕 → 编译、链接、签名 → .app包
app包的内容
图片、bundle、nib、Mach-O文件(写的代码编译成了这个文件,就是iOS的可执行文件)
→ 压缩(改后缀,变成ipa文件)
上传 & 安装阶段
→ 上传到App Store → 用户安装
→ 直接通过PP助手之类的安装
逆向App的思路
点击按钮做了什么事情?
- 做界面分析
- Reveal - UI层面
- Cycripe - 控制器层面
- 代码分析
- 对Mach-O做静态分析(类似于打开Xcode,一行行看代码)
- class-dump
- MachOView
- 动态调试
- 对运行中的App进行代码调试
- LLDB
- debugserver
- 对运行中的App进行代码调试
- 对Mach-O做静态分析(类似于打开Xcode,一行行看代码)
- 代码编写 & 注入
- 编写注入
- 重新签名
class-dump
作用:把Mach-O文件的class信息dump出来,生成对应的.h头文件
使用:
- class-dump复制到mac的/usr/local/bin目录,才能在terminal中使用
- 原因:指令在环境变量里配置,bin目录是变量中默认的配置路径
- 指令:class-dump -H mach-o文件路径 -o 目标文件夹路径
- 结果:所有头文件都导出来了
代码的编译过程
代码如何变成Mach-O的呢?
1.代码编译成汇编
2.汇编编译成机器语言
3.class-dump分析机器语言,使用符号表生成头文件
如何看.m文件中的代码呢?
1.机器语言 → 汇编语言(没问题,同一种架构平台下,汇编指令和机器码一一对应) - 反编译
2.汇编 → OC语言(不可以完全反推敲回去,汇编指令和OC代码之间为一对多)
- 汇编中的注释为 → ;
- 可以还原个大概:工具 - Hopper Disassmbler
Hopper Disassmbler
简介
作用:把Mach-O转换为OC或Swift伪代码
第三个按钮可以看个大概的代码实现
第二个按钮可以看到方法之间的代码跳转
使用举栗
分析下UIKit的.m文件是如何实现的
UIKit的.m文件 → 已经编译成了一个Mach-O文件,在手机端放着
路径:
动态库共享缓存
释义:UIKit.framworks这些动态库,都打包放在缓存中
路径:/System/Library/Caches/com.apple.dyld/dyld_shared_cache_armX
X: 代表Arm处理器的指令集架构
iPhone CPU 使用了ARM处理器
- 处理器不一样,指令集不一样。
- armv6
- armv7
- armv7s
- arm64
- 指令集向下兼容
好处:节省内存
方案一:多个Mach-O方案
每一个Mach-O都有自己独立的描述信息,但是这两个文件的描述信息有很多是一样的
方案二:共享缓存,用一个文件承载多个动态库,可以起到压缩的作用
加载动态库方案 - dyld程序
当想要加载某个动态库的时候,会转发给dyld,dyld会去共享缓存中拿需要的动态库
dyld这个东东已开源
抽取动态库共享缓存中的某个库
方案:dsc_extractor - dsc抽取器
UIKit Mach-O分析
分析下UIViewController的init方法
Mach-O
Mach Object的缩写,是Mac/iOS上存储程序、库的标准格式
属于Mach-O格式的文件类型
可以在xnu中查
xnu - 内核
从内核角度看源码,可以看到底层数据结构
关联概念 - FreeBSD Unix Linux XNU Darwin MacOSX
Mach-O常用类型
下面的文件都是Mach-O格式,只是文件类型不一样
可执行文件只是Mach-O的一种。
MH_OBJECT类型
目标文件 - .o文件
编译完成的目标文件
编译 → 生成.o文件 → 链接 → 可执行文件
静态库文件 - .a
静态库文件.a就是多个.o文件合并在一起
证明方法
file test.o
MH_EXECUTE类型 - 可执行文件
你看,生成的Test.app这个东东,,Linking中说明了,他是一个Executable类型的Mach-O文件
MH_DYLIB类型 - 动态库文件
.dylib
.frameword/xx
MH_DYLINKER类型 - 动态链接编辑器
/usr/lib/dyld - 上面说的从动态库共享缓存中抽取动态库的文件
MH_DSYM - 存储着二进制文件的符号信息的文件
作用:帮助分析Crash信息
Xcode可生成的Mach-O文件
- Excutable
- Dynamic Libary
- Bundle
- Static Library
- Relocatable Object File - 一些目标文件
Universal Binary(通用二进制文件)
定义:同时适用多种架构的二进制文件
我们开发的app,需要同时支持arm64和arm32,说明是一个通用二进制文件
使用Xcode控制生成的通用二进制文件
可以通过设置Architectures中的内容和Valid Architectures两个选项求交集,即为最终可支持的架构类型。
特点
- 因为要支持多架构的代码,通用二进制文件会比单独支持一个的大一些
- 由于两种架构有一些共同资源,所以并不会达到单一版本的两倍那么大
- 由于执行过程中,只调用一部分代码,运行起来不需要额外的内存
- 因为文件比原来的大,也被成为「胖纸二进制文件」Fat Binary
把通用二进制文件拆开 - 瘦身
如果我也能这么方便的瘦身多好...
指令:lipo 文件 -thin 要抽出的架构名称 -output 目标路径
Mach-O的基本结构
结构描述 - 三段组成
Header
放文件类型、目标架构类型
Load commands
描述文件在虚拟内存中有哪些逻辑结构和布局
内存是分段管理
- 数据段
- 代码段
- 栈段
...
一个Mach-O载入内存的时候,是由许多段组成的。Load Command就是用来描述将来有哪些段,每个段有多大,段的顺序是怎样的
类似是一个指针,指向了真实Data所在的地方
Data
放真实数据
查看Mach-O
otool: 查看Mach-O特定部分和段的内容
otool:苹果自带的
GUI工具: MachOView
dyld和Mach-O
Mach-O如何载入内存呢?是通过dyld -
dyld可以加载的Mach-O类型:
- 可执行文件
- DYLIB
- BUNDLE
dyld在加载的时候,一旦发现Mach-O的类型是这三种类型,则会加载。
dyld是一种Mach-O,他的作用是加载其他三种类型的Mach-O
装载之后才如何一步步执行到main函数的呢?且听下回分解