4逆向 - Mach-O

 

App从开发到安装到手机的过程

开发完毕 → 编译、链接、签名 → .app包

app包的内容

图片、bundle、nib、Mach-O文件(写的代码编译成了这个文件,就是iOS的可执行文件)

→ 压缩(改后缀,变成ipa文件)

上传 & 安装阶段

→ 上传到App Store → 用户安装

→ 直接通过PP助手之类的安装

逆向App的思路

点击按钮做了什么事情?

  1. 做界面分析
    1. Reveal - UI层面
    2. Cycripe - 控制器层面
  2. 代码分析
    1. 对Mach-O做静态分析(类似于打开Xcode,一行行看代码)
      1. class-dump
      2. MachOView
    2. 动态调试
      1. 对运行中的App进行代码调试
        1. LLDB
        2. debugserver
  3. 代码编写 & 注入
    1. 编写注入
    2. 重新签名

class-dump

作用:把Mach-O文件的class信息dump出来,生成对应的.h头文件

使用:

代码的编译过程

代码如何变成Mach-O的呢?

1.代码编译成汇编

2.汇编编译成机器语言

3.class-dump分析机器语言,使用符号表生成头文件


如何看.m文件中的代码呢?

1.机器语言 → 汇编语言(没问题,同一种架构平台下,汇编指令和机器码一一对应) - 反编译

2.汇编 → OC语言(不可以完全反推敲回去,汇编指令和OC代码之间为一对多)

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处理器

  1. 处理器不一样,指令集不一样。
    1. armv6
    2. armv7
    3. armv7s
    4. arm64
  2. 指令集向下兼容

好处:节省内存

方案一:多个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文件

Universal Binary(通用二进制文件)

定义:同时适用多种架构的二进制文件

我们开发的app,需要同时支持arm64和arm32,说明是一个通用二进制文件

使用Xcode控制生成的通用二进制文件

可以通过设置Architectures中的内容和Valid Architectures两个选项求交集,即为最终可支持的架构类型。

特点

把通用二进制文件拆开 - 瘦身

如果我也能这么方便的瘦身多好...

指令: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类型:

dyld在加载的时候,一旦发现Mach-O的类型是这三种类型,则会加载。

dyld是一种Mach-O,他的作用是加载其他三种类型的Mach-O


装载之后才如何一步步执行到main函数的呢?且听下回分解