iOS逆向学习之四(初识Mach-O)

什么是Mach-O文件?

Mach-O是Mach object的缩写,是Mac\iOS上用来存储程序、库的标准格式html

Mach-O文件类型

  • 能够点击下载xnu源码,在源码中的EXTERNAL_HEADERS/mach-o/loader.h 文件中,咱们能够看到Mach-O格式的全部文件类型

xun是苹果MacOS\iOS等操做系统的内核git

  • 常见的Mach-O文件类型
Mach-O类型 示例文件
MH_OBJECT 目标文件(.o)
静态库文件(.a)注:
MH_EXECUTE 可执行文件,存放App的全部源码信息,在.app/xx
MH_DYLIB 动态库文件.dylib 或者 .framework/xx
MH_DYLINKER 动态连接编辑器,也就是以前所说的/usr/lib/dyld工具
MH_DSYM 此文件中存储这二进制文件符号信息,在开发中,咱们常用此文件来分析App的崩溃信息

Mach-O的基本结构

能够点击官网查看Mach-O的介绍。github

Mach-O组成

Mach-O由3个部分组成缓存

  • Header,包含文件类型、目标架构类型等等
  • Load commands,是描述文件在虚拟内存中的逻辑结构和布局,至关于一份目录索引
  • Raw segment data,在Load commands中所定义的Segment,在这里都能找到原始数据。

Raw segment data存放了全部的原始数据,而Load commands至关于Raw segment data的索引目录sass

窥探Mach-O的结构

经常使用工具

  • 命令行工具,经过file命令查看Mach-O文件的基本信息
file 文件路径
复制代码
  • otool,查看Mach-O特定部分和段的内容
#查看Mach-O文件的header信息
otool -h 文件路径

#查看Mach-O文件的load commands信息
otool -l 文件路径
复制代码

更多使用方法,终端输入otool -help查看bash

  • lipo,用来处理多架构Mach-O文件,经常使用命令以下
#查看架构信息
lipo -info 文件路径

#导出某种类型的架构
lipo 文件路径 -thin 架构类型 -output 输出文件路径

#合并多种架构类型
lipo 文件路径1 文件路径2 -output 输出文件路径
复制代码

Universal Binary(通用二进制文件)

通用二进制文件就是同时适用于多种架构的二进制文件,它包含了多种不一样架构的独立的二进制文件,它有如下特色网络

  • 由于须要存储多种架构的代码,因此通用二进制文件要比单架构二进制文件要大
  • 由于两种种架构之间能够共用一些资源,因此两种架构的通用二进制文件大小不会达到单一架构版本的两倍。
  • 运行过程当中只会调用其中的部分代码,因此运行起来不会占用额外的内存
  • 通用二进制文件一般也被称为“胖二进制文件(Fat binary)”

dyld和Mach-O

dyld是iOS中用来加载可执行文件、动态库的工具,其实它自己也是一个Mach-O文件。架构

什么是dyld?
  • dyld 动态加载器(又叫作动态连接编辑器)
  • dyld的源码能够点击此处下载
dyld的做用。

dyld能够用来加载如下三种类型的Mach-O文件app

  • MH_EXECUTE
  • MH_DYLIB
  • MH_BUNDLE

经过查看dyld的源码能够看到加载文件时的类型校验框架

从编码到App安装到手机

想要了解Mach-O文件,首先要了解从编写代码,开发App到App打包并安装到手机上的整个过程。

  • 首先咱们编写完成代码以后,会经过LLVM编译器预处理咱们的代码,好比将宏放在指定的位置
  • 预处理结束以后,LLVM会对代码进行词法分析和语法分析,生成AST。AST是抽象语法树,主要用来进行快速遍历,实现静态代码检查的功能。
  • AST会生成IR,IR是一种更加接近机器码的语言,经过IR能够生成不一样平台的机器码。对于iOS平台,IR生成的可执行文件就是Mach-O.
  • 而后经过连接器将符号和地址绑定在一块儿,而且将项目中的多个Mach-O文件合并成一个Mach-O文件。
  • 最后经过签名等操做生成.app文件,而后对.app文件进行压缩就生成了咱们能够安装的ipa包。
  • 固然,ipa包的安装途径有两种:
    • 经过开发者帐号上传到App Store,而后在App Store上下载安装。
    • 经过PP助手、iFunBox、Xcode等工具来安装

逆向App,咱们须要作哪些工做?

初步了解了什么是Mach-O文件,以及App从开发到安装的过程,咱们就能够来学习如何逆向一款App

  • 界面分析 经过以前的学习,咱们已经可使用Cycript和Reveal对App的界面进行分析
  • 代码分析 iOS开发中,全部的代码最后都会通过编译生成Mach-o文件,因此咱们须要对Mach-O文件进行静态分析

静态分析的工具备MachOView、class-dump、Hopper Disassembler、ida等等,后面会一一学习

  • 动态调试 除了静态分析,咱们还须要运行目标App,对App进行动态调试

动态调试的工具备debugserver、LLDB等等

  • 代码编写、注入 进行完界面分析、代码分析和动态调试以后,咱们能够在特定位置注入咱们本身写的代码,必要时能够从新签名而且打包ipa

调试工具

calss-dump

class-dump的做用就是把Mach-O文件的class信息给导出来,生成对应的.h头文件

  • 能够点击官网下载class-dump工具包
  • 下载完成以后将其中的class-dump可执行文件复制到Mac上的/usr/local/bin目录中,这样在终端就能识别class-dump命令了

在Mac中,终端执行的全部指令都会去/usr/bin目录和/usr/local/bin目录下寻找

  • class-dump的经常使用命令以下
# -H表示须要生成头文件 -o用于指定头文件的存放目录
class-dump -H Mach-O文件路径 -o 头文件存放目录
复制代码

Hopper Disassmbler

Hopper Disassmbler能够将Mach-O文件的机器语言反编译成汇编代码、OC伪代码或者是Swift伪代码。最常使用的快捷键有

#找出哪里引用了这个方法
Shift + Option + X
复制代码

下载地址:pan.baidu.com/s/1yP_VcBlQ…


静态库和动态库

在iOS开发中,有不少功能都是现成可用的,不关你的App在用,其它的App也在用,好比UIKit框架、GUI框架、I/O、网络等等。这些库都是经过连接器连接到Mach-O文件中的。

静态库

静态库是编译时连接的库,须要链接进入Mach-O文件中,若是须要更新就必须从新编译一次,没法作到动态加载和更新

动态库

动态库是运行时连接的库。

Mach-O是文件编译以后的产物,因此动态库并无参与Mach-O文件的编译和连接。因此Mach-O文件中没有包含动态库的符号定义,也就是说这些符号会直接显示未定义,可是他们的名字和对应库的路径会被记录下来。在运行时经过dlopen和dlsym导入动态库时,会根据记录的路径找到对应的库,再经过记录的名字符号找到绑定的地址。

动态库共享缓存(dyld shared cache)

从iOS3.1开始,为了提升性能,绝大部分的系统动态库文件都打包存放到了一个缓存文件中(dyld shared cache),缓存路径是/System/Library/Caches/com.apple.dyld/dyld_shared_cache_armX

dyld_shared_cache_armX里面的X表明ARM处理器指令集的架构

ARM指令集

ARM指令集(CPU指令的集合)有如下几种

ARM指令集 支持的设备
armv6 iPhone、iPhone3G
iPod Touch、iPod Touch2
armv7 iPhone3GS、iPhone四、iPhone4S
iPad、iPad二、iPad3(The New iPad)、iPad mini
iPod、Touch3G、iPod Touch四、iPod Touch5
armv7s iPhone五、iPhone5C、iPad4
arm64 iPhone5S、iPhone六、iPhone6 Plus、iPhone6S、iPhone6S Plus
iPhoneSE、iPhone七、iPhone7Plus、iPhone八、iPhone8 Plus、iPhoneX
iPad五、iPad Air、iPad Air二、iPad Pro、iPad Pro2
iPad mini with Retina display、iPad mini三、iPad mini4
iPod Touch6

以上全部的指令集都是向下兼容的 为何要使用动态库共享缓存呢?最大的好处就是节省内存。

从动态库共享缓存抽取动态库

因为动态库共享缓存太大,若是想获取其中某个动态库,例如UIKit,就须要从动态库共享缓存中抽取对应的动态库

  • 使用dyld源码中提供的方式来进行抽取,工具在源码中的launch-cache/dsc_extractor.cpp文件中

    • 首先须要去掉源码中的#if 0判断
    • 而后使用以下命令编译dsc_extractor.cpp文件
    clang++ -o dsc_extractor dsc_extractor.cpp
    复制代码

    此处是将dsc_extractor.cpp编译生成可执行文件dsc_extractor

    • 进入执行文件dsc_extractor所在目录。经过如下的命令来抽取动态库
    ./dsc_extractor 动态库共享缓存文件的路径 用于存放抽取结果的目录
    复制代码

    建议抽取armv7s架构的动态库,arm64抽取时会报以上错误,缘由是dsc_extractor.bundle不能在Xcode10以后使用

    • 抽取完成以后,使用Hopper Disassmbler打开想要逆向的动态库,就能够看到动态库中的源码信息。

相关文章
相关标签/搜索