iOS逆向 12:Mach-O文件(上)

简介: iOS逆向 12:Mach-O文件(上)

本文主要介绍Mach-O文件格式以及通用二进制文件


Mach-O文件概述


  • Mach-O其实是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式, 类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)
  • Mach-O是一种用于可执行文件、目标代码、动态库的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性。


Mach-O文件格式


常见的Mach-O格式有以下几种


  • 1、目标文件.o
  • 2、库文件,细分主要有以下几种:
  • .a
  • .dylib
  • .framework
  • 3、可执行文件
  • 4、dyld
  • 5、.dsym


我们可以通过终端的file指令来查看文件的类型


  • $file 文件路径


1、目标文件.o


  • 1、新建一个C文件
#include <stdio.h>
int main(){
    printf("test\n");
    return 0;
}

2、通过clang命令编译C文件

image.png

  • 查看文件夹:ls
  • clang -c test.c : 将.c文件编译成.o文件(OC在后端中使用的是LLVM编译,而前端使用的工具是clang,即理解为 LLVM包含clang)。
  • file test.o :查看文件类型 - Mach-O文件、64位、x86_64架构
  • clang test.o :将.o文件编译成可执行文件
  • ./a.out :执行可执行文件


  • 3、也可以通过一行命令将c文件编译成可执行文件:clang -o test2 test.c

image.png

重复3的操作,再次生成一个可执行文件 test3。此时问题来了,这三个可执行文件(即a、test2、test3)是否是一样的?我们可以通过md5验证,如果hash值一样则说明是一样的,反之不一样

image.png

中间产物.o文件


其中.c.out文件的区别是中间多了一个.o文件。而在我们的实际开发中,其实是有多个源码的,所以最终的可执行文件是由多个源码生成的,如下所示,将两个.o文件编译成一个可执行文件


  • 通过clang生成.o文件: clang -c test1.c test.c
  • 通过clang将.o文件编译成可执行文件:clang -o demo1 test1.o test.o

image.png

  • 如果此时换一下链接顺序呢?,例如: clang -o demo2 test.o test1.o
  • 多个源码一次性生成可执行文件:clang -o demo test1.c test.c
  • 对比上述生成的三个可执行文件,是否是同一个?这里我们也通过md5生成的hash值进行对比


image.png

  • 结论:通过对比发现,如果改变了.o文件的连接顺序,那么Mach-O的也会随之发生变化
  • 这里可以通过 objdump查看Mach-o链接顺序,例如:
  • objdump --macho -d demo + objdump --macho -d demo1,这里demo和demo1的源文件链接顺序是test1 + main


image.png

objdump --macho -d demo2,demo2中源文件的连接顺序是main + test1

image.png

上述所说的链接顺序,对应到我们日常开发中是指工程中的 target -> Build Phase -> Compiles Sources,这里就对应源文件的编译顺序,如果源文件的顺序发生了变化,生成的可执行文件是不一样的


2、库文件


  • :主要是程序代码的集合,即将N个文件组织起来,是共享程序代码的一种方式
  • 库的分类
  • 开源库(公有库):源码是公开的,可以看到每个文件的实现,例如Github中的AFNetwoeking、SDWebImage等
  • 闭源库(私有库):源码未公开,是经过编译后的二进制文件,看不到具体的实现,再细分又分为静态库动态库


静态库 & 动态库


  • 静态库的存在形式主要有两种(建议用.framwork):.a + .framework
  • 1、.a是一个纯二进制文件,.a不能直接使用,至少需要.h文件配合,可能还会需要资源文件
  • 2、.framework中除了有二进制文件外,还有资源文件,且可以直接使用
  • 两者关系:.a + .h + sourceFile = .framework
  • 优势:方便共享代码,便于合理使用
  • 实现iOS的模块化,即将固定业务模块化为静态库
  • 共享代码,但不希望被看到代码的具体实现
  • 动态库的存在形式也是两种:.dylib + .framework
  • .framework为什么既是静态库又是动态库?
  • 1、系统的.framework是动态库
  • 2、自定义的.framework是静态库
  • 静态库和动态库的区别:
  • 1、静态库链接时会被完整的拷贝到项目中,如果有多个App都是用了同一个静态库,会拷贝多份,浪费内存
  • 2、动态库不会赋值,只有一份,在程序运行时动态加载到内存中,多个App共用一份,节约内存

参考链接:


验证.a 、 .dylib是否是Mach-O文件


验证.a


  • 在项目中通过find命令查找.a文件:find /usr -name "*.a"
  • 随机查看一个.a文件,是一个dynamically类型: file .a文件路径

image.png

验证.dylib


  • 在项目中通过find查找.dylib文件: find /usr -name "*.dylib"
  • 查看.dylib文件:file .dylib文件路径
    image.png

3、可执行文件


这里的可执行文件,即一般是指日常项目中,编译后生成的可执行未见,可以通过file 查看其文件类型

image.png



4、dyld


dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作。而且它是开源的,任何人可以通过苹果官网下载它的源码来阅读理解它的运作方式,了解系统加载动态库的细节。


共享缓存机制


在iOS系统中,每个程序依赖的动态库都需要通过dyld(位于/usr/lib/dyld)一个一个加载到内存,然而,很多系统库几乎是每个程序都会用到的,如果在每个程序运行的时候都重复的去加载一次,势必造成运行缓慢,为了优化启动速度和提高程序性能,共享缓存机制就应运而生。所有默认的动态链接库被合并成一个大的缓存文件,放到/System/Library/Caches/com.apple.dyld/目录下,按不同的架构保存分别保存着,


验证dyld


  • 查找Mac中的dyld

image.png


  • 进入目录:cd /usr/lib
  • 查找dyld:ls dylib
  • 查看dyld的文件类型:file dyld ,是一个动态链接器,其本身也是一个Mach-O文件

image.png


5、.dsym文件


dsym介绍


Xcode编译项目后,我们会看到一个同名的 dSYM 文件,dSYM 是保存 16 进制函数地址映射信息的中转文件,我们调试的 symbols 都会包含在这个文件中,并且每次编译项目的时候都会生成一个新的 dSYM 文件,位于/Users/<用户名>/Library/Developer/Xcode/Archives目录下,所以对于每一个发布版本我们都很有必要保存对应的 Archives 文件。


当我们软件 release 模式打包或上线后,不会像我们在 Xcode 中那样直观的看到用崩溃的错误,这个时候我们就需要分析 crash report 文件了,iOS设备中会有日志文件保存我们每个应用出错的函数内存地址,通过 Xcode 的 Organizer 可以将 iOS 设备中的 DeviceLog 导出成 crash 文件,这个时候我们就可以通过出错的函数地址去查询 dSYM 文件中程序对应的函数名和文件名。大前提是我们需要有软件版本对应的 dSYM 文件,这也是为什么我们很有必要保存每个发布版本的 Archives 文件了。


验证.dsym文件


  • 程序真机+release编译时,有一个.dsym文件

image.png

.dsym也是一个mach-O文件,是一个符号表,主要用于出现崩溃后可以通过这个文件去符号,方便排查问题

image.png


通用二进制文件


mac系统所支持的cpu及硬件平台发生了很大的变化,为了解决软件在多个硬件平台上的兼容性问题,苹果开发了一个通用的二进制文件格式(Universal Binary),又称胖二进制(Fat Binary)


  • 苹果公司提出的一种程序代码。能同时适用多种架构的二进制文件
  • 同一个程序包中同时为多种架构提供最理想的性能。
  • 因为需要储存多种代码,通用二进制应用程序通常比单一平台二进制的程序要大。
  • 但是 由于两种架构有共通的非执行资源(代码以外的),所以并不会达到单一版本的两倍之多。
  • 而且由于执行中只调用一部分代码,运行起来也不需要额外的内存。


演示


  • 在日常开发的项目中,可以通过Build Setting - Mach-O type,可以指定Mach-O文件的类型,如下所示

image.png

一般我们通过真机生成的可执行文件,其架构是arm64,是一个单一架构

  • iOS 11以上的系统都只支持 64位架构

    image.png

此时将案例最低版本改为10.3,查看可执行文件,发现是2种架构:armv7 + arm64

image.png

同时也可以在Build Setting - Architectures 中设置设置编译的架构


  • 环境变量 $(ARCHS_STANDARD):包含arm64、armv7
    image.png

还有一种架构:armv7s,生成iphone5、iphone5c可以可用的架构,并在工程中添加

image.png


查看此时的可执行文件,支持3个架构

image.png


ARM架构


ARM架构过去称作进阶精简指令集机器(Advanced RISC Machine,更早称作:Acorn RISC Machine),是一个32位精简指令集(RISC)处理器架构,ARM处理器非常适用于移动通讯领域,符合其主要设计目标为低耗电的特性。


ARM和Intel处理器的第一个区别是,前者使用精简指令集(RISC),而后者使用复杂指令集(CISC)。


ARM处理器指令集:是指计算机ARM操作指令系统。


  • armv6、armv7、armv7s、arm64、arm64e都是arm处理器的指令集,所有指令集原则上都是向下兼容
  • 苹果A7处理器支持两个不同的指令集:
  • 32位ARM指令集(armv6|armv7|armv7s
  • 64位ARM指令集(arm64)。
  • i386|x86_64Mac处理器的指令集。


iOS设备支持的指令集


image.png

参考链接:iOS 指令集架构 armv6、armv7、armv7s、arm64、arm64e、x86_64、i386


通用二进制文件源码


  • 通过CMD+shift+O搜索 fat.h
  • 找到其中通用二进制文件的头部结构fat_header如下所示

struct fat_header {
    uint32_t    magic;      /* magic字段被定义为常量FAT_MAGIC,表示这是一个胖二进制 */
    uint32_t    nfat_arch;  /* 表示有多少个Mach-O文件 */
};
  • 每个胖二进制都用fat_arch结构表示,在fat_header之后,紧接着一个或多个连续的fat_arch结构体。
struct fat_arch {
    cpu_type_t  cputype;    /* cpu类型 */
    cpu_subtype_t   cpusubtype; /* CPU的子类型 */
    uint32_t    offset;     /* 指定了当前CPU架构数据相对于当前文件开头的偏移值 */
    uint32_t    size;       /* 数据的大小 */
    uint32_t    align;      /* 数据的内存对齐边界,取值必须是2的次方,它确保了当前CPU架构的目标文件在加载到内存中时,数据是经过内存优化对齐的 */
};


终端命令


  • 1、可以通过otool来查看fat_header信息:otool -f 可执行文件
  • 2、可以通过lipo命令拆分、合并胖二进制文件。常用命令如下
  • $lipo -info MachO文件:使用lifo -info 可以查看MachO文件包含的架构
  • $lipo MachO文件 –thin 架构 –output 输出文件路径:使用lifo –thin 拆分某种架构
  • $lipo -create MachO1 MachO2 -output 输出文件路径: 使用lipo -create 合并多种架构


lipo演示


  • 通过Hopper打开 可执行文件,此时可以看到是一个 FAT archive(胖二进制文件,表示支持多种架构),选择 aarch64架构

image.png查看二进制文件中包含的架构:lipo -info 12-macho

image.png


  • 拆分:lipo 12-macho -thin armv7 -output macho_armv7,如果拆分没有的架构,会报错
  • 查看拆分后的可执行文件类型:file macho_armv7

image.png


  • 合并:lipo -create macho_armv7 macho_arm64 -output macho_v7_64
  • 查看合并后的可执行文件类型:file macho_v7_64

image.png


总结


  • Mach-O其实是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式。是一种用于可执行文件、目标代码、动态库的文件格式。且Mach-O提供了更强的扩展性
  • 常见的Mach-O格式:.o、库文件(.a、.dylib、.framework)、可执行文件dyld.dsym
  • .a + .h + sourceFile = .framwork
  • 动态.framework:系统Framework库
  • 静态.framwork:自定义的Framework库
  • 查看文件类型命令:file 文件路径
  • 查看Mach-O源文件的链接顺序:objdump --macho -d 可执行文件
  • dyld(the dynamic link editor)是苹果的动态链接器,mac中路径为/usr/lib
  • dSYM保存 16 进制函数地址映射信息的中转文件,位于/Users/<用户名>/Library/Developer/Xcode/Archives目录。可以用于通过出错的函数地址去查询 dSYM 文件中程序对应的函数名和文件名
  • 通用二进制文件(Universal Binary,也称为胖二进制(Fat Binary))。主要适用于解决多个平台的兼容性问题
  • 通过otool来查看fat_header信息:otool -f 可执行文件
  • lipo命令拆分、合并胖二进制文件
  • $lipo -info MachO文件:使用lifo -info 可以查看MachO文件包含的架构
  • $lipo MachO文件 –thin 架构 –output 输出文件路径:使用lifo –thin 拆分某种架构
  • $lipo -create MachO1 MachO2 -output 输出文件路径: 使用lipo -create 合并多种架构


相关文章
|
6月前
|
移动开发 前端开发 数据安全/隐私保护
iOS发布证书.p12文件无密码解决办法及导出带密码的新.p12文件方法
iOS发布证书.p12文件无密码解决办法及导出带密码的新.p12文件方法
200 0
|
6月前
|
Linux 数据安全/隐私保护 iOS开发
如何使用 Xcode 打包导出 IPA 文件并进行 iOS 应用内测,无需支付苹果开发者账号费用?
如何使用 Xcode 打包导出 IPA 文件并进行 iOS 应用内测,无需支付苹果开发者账号费用?
|
6月前
|
Web App开发 Go iOS开发
【IOS】教你如何在手机端轻松安装 ipa 文件 -(安装器已失效 21.10)|社区征文
【IOS】教你如何在手机端轻松安装 ipa 文件 -(安装器已失效 21.10)|社区征文
|
6月前
|
Web App开发 Go iOS开发
【IOS】教你如何在手机端轻松安装 ipa 文件 -(安装器已失效 21.10)
【IOS】教你如何在手机端轻松安装 ipa 文件 -(安装器已失效 21.10)
|
6月前
|
iOS开发 开发者
【教程】uni-app iOS 打包解决 profile 文件与私钥证书不匹配问题
【教程】uni-app iOS 打包解决 profile 文件与私钥证书不匹配问题
|
6月前
|
移动开发 监控 小程序
mPaaS常见问题之uniapp ios端云打包的配置config文件如何解决
mPaaS(移动平台即服务,Mobile Platform as a Service)是阿里巴巴集团提供的一套移动开发解决方案,它包含了一系列移动开发、测试、监控和运营的工具和服务。以下是mPaaS常见问题的汇总,旨在帮助开发者和企业用户解决在使用mPaaS产品过程中遇到的各种挑战
184 0
|
6月前
|
缓存 小程序 Android开发
mPaaS问题之iOS调用插件的时候提示没有配置mpaas. Config文件如何解决
mPaaS配置是指在mPaaS平台上对移动应用进行的各项设置,以支持应用的定制化和优化运行;本合集将提供mPaaS配置的操作指南和最佳实践,助力开发者高效管理和调整移动应用的设置。
135 1
|
6月前
|
iOS开发
ipa文件安装到ios系统
ipa文件安装到ios系统
74 0
|
6月前
|
网络安全 开发工具 数据安全/隐私保护
如何把 ipa 文件 (iOS 安装包) 安装到 iPhone 手机上? 附方法汇总
如何把 ipa 文件 (iOS 安装包) 安装到 iPhone 手机上? 附方法汇总
|
11月前
|
开发工具 数据安全/隐私保护 iOS开发
windows电脑创建ios证书和证书profile文件
windows电脑并没有mac的钥匙串工具去创建ios打包证书,也没有mac的xcode工具去上架ios应用,那么假如只有windows电脑能否使用uniapp开发和打包ios的app呢?是可以的,没有mac电脑,我们可以使用香蕉云编去创建ios证书。
212 0
windows电脑创建ios证书和证书profile文件