前言

  1. Mach-O文件格式源码
  2. Mach-O苹果官方手册

想要程序跑起来,那么这个可执行文件的格式就需要被当前的操作系统所理解,比如:

  • Linux 操作系统下可执行文件格式是 ELF
  • Windows 的可执行文件格式是 PE32/PE32+
  • Android 的可执行文件格式是 ELF
  • OSX 和 iOS 的可执行文件格式是 Mach-O

准备工具

方式1: 终端查看

来到Mach-O文件所在位置,输入相关命令得到Mach-O文件信息。为了更直观点,推荐方式2查看Mach-O文件。

方式2: 工具查看

首先要下载一个可以查看Mach-O文件格式的工具,本来想用 MachOView ,无奈下载完后打开Mach-O文件后,会闪退,就去逆向论坛找了个大神些的替代MachOView的工具 MachOExplorer 。我这里下载使用的是MachOExplorer,下载完后,打开,点击菜单栏的 file -> Mach-O文件 (此处我用模拟器的运行,打开了Debug-iphonesimulator文件夹,找到ipa,邮件显示包内容后可以获得可执行文件)。


Mach-O文件是什么

在OSX和iOS系统下,平时接触到的可执行文件、库文件、dsym文件、动态库、动态链接器(dyld)都是这种格式。Mach-O的组成结构包括:Header (头部)、Load commands(加载命令)、Data(Data包含多个 Segment(段),Segment中包含多个 Section(节))

简单介绍dsym文件,后续开篇介绍。

  • dSYM 文件是什么: Xcode编译项目后,会有一个项目同名的 dSYM 文件,dSYM 是保存 16 进制函数地址映射信息的中转文件。
  • dSYM 文件的作用: release 模式打包或上线后,崩溃错误不直观,这时就需要分析 crash report 文件,iOS 设备中会有日志文件保存我们每个应用出错的函数内存地址,通过 Xcode 的 Organizer 可以将 iOS 设备中的 DeviceLog 导出成 crash 文件,这个时候我们就可以通过出错的函数地址去查询 dSYM 文件中程序对应的函数名和文件名。大前提是我们需要有软件版本对应的 dSYM 文件,这也是为什么我们很有必要保存每个发布版本的 Archives 文件了。

Header (头部)

header的数据结构

Mach-O的头部信息,可以使我们快速得到一些信息,比如
32位结构还是64位结构,比如文件类型架构类型等等。
让我们先来看看header的数据结构定义。

/** 32位架构对于header的定义*/
struct mach_header {uint32_t    magic;      /* mach magic number identifier */cpu_type_t  cputype;    /* cpu specifier */cpu_subtype_t   cpusubtype; /* machine specifier */uint32_t    filetype;   /* type of file */uint32_t    ncmds;      /* number of load commands */uint32_t    sizeofcmds; /* the size of all the load commands */uint32_t    flags;      /* flags */
};/** 64位架构对于header的定义*/
struct mach_header_64 {uint32_t    magic;      /* mach magic number identifier */cpu_type_t  cputype;    /* cpu specifier */cpu_subtype_t   cpusubtype; /* machine specifier */uint32_t    filetype;   /* type of file */uint32_t    ncmds;      /* number of load commands */uint32_t    sizeofcmds; /* the size of all the load commands */uint32_t    flags;      /* flags */uint32_t    reserved;   /* reserved */
};

32位和64位架构的头文件对比,多了一个reserved(保留字段)。
现在来解释说明一下这些字段都有什么意义:

  • magic : 魔数,用于快速确认该文件是64位还是32位的。若值为0xfeedfacf是64位的,值为0xfeedface则是32位的。
  • cputype :CPU架构类型。比如arm,例如x86_64
  • cpusubtype : 对应的架构具体类型。比如arm64、armv7等。
  • filetype : 文件类型。比如可执行文件(MH_EXECUTE)、库文件、Dsym文件等。
  • ncmds : 加载命令条数。
  • sizeofcmds : 所有加载命令大小。
  • flags : dyld加载时需要的标志位。
  • reserved :保留字段。
/* filetype 类型 */
#define  MH_OBJECT   0x1     /* relocatable object file */
#define  MH_EXECUTE  0x2     /* demand paged executable file */
#define  MH_FVMLIB   0x3     /* fixed VM shared library file */
#define  MH_CORE     0x4     /* core file */
#define  MH_PRELOAD  0x5     /* preloaded executable file */
#define  MH_DYLIB    0x6     /* dynamically bound shared library */
#define  MH_DYLINKER 0x7     /* dynamic link editor */
#define  MH_BUNDLE   0x8     /* dynamically bound bundle file */
#define  MH_DYLIB_STUB   0x9     /* shared library stub for static */
#define  MH_DSYM     0xa     /* companion file with only debug */
#define  MH_KEXT_BUNDLE  0xb     /* x86_64 kexts *//* flags : dyld加载时需要的标志位。*/
#define    MH_NOUNDEFS 0x1     // 目前没有未定义的符号,不存在链接依赖
#define    MH_DYLDLINK  0x4     // 该文件是dyld的输入文件,无法被再次静态链接
#define    MH_PIE 0x200000     // 加载程序在随机的地址空间,只在 MH_EXECUTE中使用
#define    MH_TWOLEVEL  0x80    // 二级名称空间

dyld

动态链接器,苹果的开源项目, 下载dyld ,当内核执行到 LC_DYLINK(后文讲述)的时候,链接器会启动,查找进程所依赖的动态库,并加载到内存中。

flags : MH_PIE - 随机地址空间(ASLR)

进程每一次启动,地址空间都会随机化。如果采用传统的方式,程序每启动一次,启动的虚拟内存镜像一致的话,黑客很容易就重写内存来破解程序。所以,ASLR可以有效避免黑客的攻击。

打开Xcode,来到Main函数,打断点,运行程序开启lldb调试。当到达断点位置时,在控制台输入:

/** 加载模块地址*/
image list -o -f

可以发现每次运行程序,地址都在变化。

flags : MH_TWOLEVEL - 二级名称空间

这是dyld的一个独有特性,说是符号空间中还包括所在库的信息,这样子就可以让两个不同的库导出相同的符号,与其对应的是平摊名称空间.

演示查看header结构

方式1: 通过命令行来查看Mach-O文件的header结构

/* file + 可执行文件路径 : 查看文件类型 */
zhuangyuan$ file GV_VOUCHER_CN
-> Mach-O 64-bit executable x86_64/* lipo -info + 可执行文件路径 : 查看文件架构 */
zhuangyuan$ lipo -info GV_VOUCHER_CN
-> Non-fat file: GV_VOUCHER_CN is architecture: x86_64/* otool -h + 可执行文件路径 : 查看Mach-O文件的header信息 */
zhuangyuan$ otool -h GV_VOUCHER_CN
->
Mach headermagic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags0xfeedfacf 16777223          3  0x00           2    51       6184 0x00218085/* otool -hv + 可执行文件路径 : 查看Mach-O文件的header信息的翻译*/
zhuangyuan$ otool -hv GV_VOUCHER_CN
->
Mach headermagic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00     EXECUTE    51       6184   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE

如下图:

方式2: 利用MachOExplorer工具查看Mach-O文件的header结构

比终端看的更直观看,终端的好处就是装逼还挺成功的,哈哈。


Load commands 结构 (加载指令模块)

Load commands 紧跟在header之后,说明了操作系统应当如何加载文件中的数据,对系统内核加载器和动态链接器起了指导性作用。这些加载指令告诉加载器如何处理二进制数据,有些命令是由内核处理的,有些是由动态链接器处理的。在源码中有明显的注释说明这些是动态链接器处理的。

Load commands

struct load_command {uint32_t cmd;       /* type of load command */uint32_t cmdsize;   /* total size of command in bytes */
};
  • cmd: 加载指令类型
  • cmdsize :加载指令大小

查看Mach-O文件的所有数据

方式1: 终端 - 演示查看load commands结构

终端输入:

otool -lv + 文件

方式2: 工具查看

Segments

Mach-O文件有多个段(Segment),每个段有不同的功能,一般的段又会按不同的功能划分为几个区(Section)。

Segments数据结构

/*segment_command_64 数据结构,其他段的数据结构类似,感兴趣可以去阅读源码*/
struct segment_command_64 { /* for 64-bit architectures */uint32_t    cmd;        /* LC_SEGMENT_64 */uint32_t    cmdsize;    /* includes sizeof section_64 structs */char        segname[16];    /* segment name */uint64_t    vmaddr;     /* memory address of this segment */uint64_t    vmsize;     /* memory size of this segment */uint64_t    fileoff;    /* file offset of this segment */uint64_t    filesize;   /* amount to map from the file */vm_prot_t   maxprot;    /* maximum VM protection */vm_prot_t   initprot;   /* initial VM protection */uint32_t    nsects;     /* number of sections in segment */uint32_t    flags;      /* flags */
};

Segments结构字段说明

LC_SEGMENTLC_SEGMENT_64 是加载的主要命令,负责指导内核将文件中(32位或64位)的段映射到进程的地址(内存)空间中。

  1. cmd : 即 Load commands 的类型,比如LC_SEGMENT_64代表将文件中64位的段映射到进程的地址空间。还有LC_SEGMENT、LC_DYLD_INFO_ONLY等等类型。
  2. cmdsize : 代表当前 ‘load command’ 的大小
  3. VM Address :段的虚拟内存地址
  4. VM Size : 段的虚拟内存大小
  5. file offset: 段在Mach-O文件中的偏移量
  6. file size : 段在文件中的大小
  7. nsects : 标识了Segment中有多少section
  8. segname[16] : 段的名称。例如__PAGEZERO、__TEXT等等。

将段对应的文件内容加载到内存中的流程:
从file offset处 加载 file size 大小到 虚拟内存VM Address处 。如果当前段是LC_SEGMENT_64(__PAGEZERO) ,则这个段的file offset、file size 、VM Address为0,因为这个段不具备访问权限,用来处理空指针的。

具体段说明

  • LC_SEGMENT_64(__PAGEZERO) : 空指针陷阱段,映射到虚拟内存空间的第一页,用于捕捉对NULL指针的引用。
  • LC_SEGMENT_64(__TEXT) : 包含了 执行代码 以及 其他只读数据 。该段数据的保护级别为:VM_PROT_READ(读)、VM_PROT_EXECUTE(执行),防止内存中被修改。
  • LC_SEGMENT_64(__DATA) : 包含了程序数据,可读/可写数据存放段。
  • LC_SEGMENT_64(__LINKEDIT) : 链接器使用的符号以及其他表,链接的部分,支持dyld,包含了一些符号表等数据。
  • LC_SYMTAB : 符号表信息
  • LC_DYSYMTAB :动态符号表信息
  • LC_LOAD_DYLINKER : 加载动态链接器(/usr/lib/dyld),使用Mach-O文件的时候链接器,可以看到name为 /usr/lib/dyld 的链接器来加载Mach-O文件。
  • LC_UUID : 对每个Mach-O文件都是唯一标识,crash解析中也会有,去匹配dysm文件和crash文件。
  • LC_VERSION_MIN_IPHONEOS : 二进制文件要求的最低操作系统版本(iOS Deployment Target) ,打开xcode ,输入iOS Development Target 可以查看版本
  • LC_MAIN : 设置程序主线程的入口地址和栈大小
  • LC_ENCRYPTION_INFO : 加密信息,查看文件是否加密(在终端输入otool -l 文件名 | grep cryptid
  • LC_LOAD_DYLIB : 加载的动态库,包括动态库地址和名称,当前版本号,兼容版本号(终端输入`otool -l 文件名)
  • LC_RPATH : 环境变量路径
  • LC_FUNCTION_STARTS : 函数起始地址表
  • LC_CODE_SIGNATURE : 记录可执行文件的代码是否签名

Section

大写的表示Segment,小写的表示section。
例如 __TEXT 和 __text.

Section数据结构

struct section { /* for 32-bit architectures */char        sectname[16];   /* name of this section */char        segname[16];    /* segment this section goes in */uint32_t    addr;       /* memory address of this section */uint32_t    size;       /* size in bytes of this section */uint32_t    offset;     /* file offset of this section */uint32_t    align;      /* section alignment (power of 2) */uint32_t    reloff;     /* file offset of relocation entries */uint32_t    nreloc;     /* number of relocation entries */uint32_t    flags;      /* flags (section type and attributes)*/uint32_t    reserved1;  /* reserved (for offset or index) */uint32_t    reserved2;  /* reserved (for count or sizeof) */
};

结构字段说明

  1. sectname:比如_text、stubs
  2. segname :该section所属的segment,比如__TEXT
  3. addr : 该section在内存的起始位置
  4. size: 该section的大小
  5. offset: 该section的文件偏移
  6. align :字节大小对齐
  7. reloff :重定位入口的文件偏移
  8. nreloc: 需要重定位的入口数量
  9. flags:包含section的type和attributes

主要的节说明

  • __text:主程序代码
  • __stub_helper :用于动态链接器的存根
  • __symbolstub1 : 用于动态链接器的存根
  • __objc_methname :OC的方法名
  • __objc_classname : OC的类名
  • __cstring : 字符串

查看__text 节的全部内容(二进制)

otool -s __TEXT __text + 文件

查看__text 节的全部内容(汇编)

查看__text 节的内容前10条数据(汇编)

利用MachOExplorer工具


演示部分

Mach-O文件开始加载的地方

  1. 打开MachOExplorer查看__TEXT部分的虚拟地址0x100000000
  2. 打开xcode 来到main函数打断点 ,输入 image list -o -f 查看模块加载的地址,可以看到加载可执行文件的基地址 0x000fabcd
  3. 在控制台输入 p/x 0x000fabcd + 0x100000000 得到了一个新的地址$0
  4. 选择xcode菜单的Debug —> debug workflow —> view memory 之后再Address里面输入$0地址,回车
  5. 可以看到Mach-O文件格式的一些数据 ,所以0x100000000就是Mach-O文件加载开始的地方.

本文参考了的文章:
趣探 Mach-O:文件格式分析

感谢。

iOS逆向与安全 - 5. Mach-O文件格式相关推荐

  1. iOS逆向之深入解析MachO文件

    MachO文件简介 一.什么是MachO文件? Mach-O其实是Mach Object文件格式的缩写,它是Mac以及iOS上一种用于可执行文件.目标代码.动态库的文件格式,类似于Windows上面的 ...

  2. iOS逆向(1)——利用ipa重签名,3分钟iPhone安装多个微信

    本文要达成如图效果,在一台iPhone上安装第二个微信: 准备: Xcode 微信ipa(可通过iTool进行下载) 重签名脚本 步骤 打开Xcode,新建Single View App项目,名字可以 ...

  3. android r 编译找不到头文件_「投稿」iOS逆向——砸壳与反编译

    作者:疯狂的蛋神 近来对iOS逆向十分感兴趣,就在业余时间里自己在上网找了各种资料学习,发现许多资料对于一些细节描述的不够详细,所以也踩了很多坑,我也将自己踩的一些坑总结出来,希望对大家有所帮助. 注 ...

  4. iOS逆向之自动化重签名

    iOS逆向之自动化重签名 准备工作 非越狱的iPhone手机 用PP助手下载: 微信6.6.5(越狱应用) 步骤 新建工程"自动化签名",在工程目录下新建APP文件夹放置需要重签名 ...

  5. iOS 逆向 - 收藏集 - 掘金

    分分钟让你在 微信运动 霸占榜首 - iOS - 掘金 为了纪念我失去的已越狱iPad, 不得不写点什么. 所以...以下内容 不需要越狱. 微信运动 6万步是什么概念,我不知道,因为我没走过,不过有 ...

  6. IOS 逆向开发(一)密码学 非对称加密RSA

    IOS 逆向开发(一)密码学 RSA 1. 密码学发展简介 2. 非对称加密RSA产生过程 3. RSA 数学原理 3.1 离散对数问题 3.1.1 原根 3.2 欧拉函数Φ 3.3 欧拉定理 3.4 ...

  7. android微信逆向工程,iOS逆向 - 微信自动添加好友

    相关源码:Github地址 一.前言 本篇主要实现在微信上自动添加好友,从而熟悉 iOS 逆向分析的过程. 二. 工具 2.1 MacBook 软件 制作 Tweak 的工具 端口转发,可以让我们通过 ...

  8. iOS逆向-微信自动添加好友

    前言 上次完成了 macOS 版微信小助手,现在终于有(xian)时(de)间(huang)来说说 iOS 逆向了.本篇主要实现在微信上自动添加好友(即自动验证新的朋友申请),从而熟悉 iOS 逆向分 ...

  9. IOS逆向(1)IOS越狱

    前段时间学习完JS逆向后,准备入门IOS逆向了.打算写在博客中记录下学习的过程,也会把踩过的坑记录下来让大家一起交流学习.JS逆向相关的基础网上比比皆是,后续会分享一些关于JS逆向相关的项目,就不从基 ...

  10. 【iOS逆向与安全】iOS插件开发光速入门

    前言 经过之前的学习,相信你已经能熟练的使用Frida-trace.IDA Pro等逆向工具.不过,仅仅到这肯定是不够的.接下来,学会把你逆向的结果打包成插件并运行,那iOS逆向,你也就真正的入门了. ...

最新文章

  1. Springboot使用bean方式详解(附代码)
  2. 【学术相关】研究生、博士生全程只靠自己能否发一篇 SCI?
  3. Java黑皮书课后题第8章:8.29(相同的数组)如果两个二维数组m1和m2具有相同的内容,则它们是相同的。编写一个方法,如果m1和m2相同的话,返回true
  4. (原創) 如何讀取/寫入文字檔? (IC Design) (Verilog)
  5. UVAoj 348 - Optimal Array Multiplication Sequence
  6. Redis4.0.13 安装踩雷记录
  7. 北鼎机器人_北鼎T752已入手,我的感受
  8. 【Unity3D开发小游戏】Unity3D开发《3D迷宫》小游戏
  9. 小蜜蜂吉他谱 高八度和低八度
  10. matlab 根轨迹 系统单位阶跃响应,二阶系统单位阶跃响应MATLAB仿真设计.doc
  11. MATHTYPE安装出现问题:无法打开要写入的文件;MathType打开word时“安全警告,宏已被禁用”;mathtype与AXmath不能同时使用
  12. ZZULIOJ-1088: 手机短号 (多实例)(Java)
  13. 有时用weblogic用户启动weblogic时会报错的原因以及解决办法
  14. 在线教育软件开发的目的是什么?
  15. QT的firstblood
  16. 关于TPR,FPR,precision,accuracy,F1
  17. java整合谷歌翻译
  18. 简单的学生成绩查询web系统
  19. 基于GCCAVR的诺基亚5110液晶显示
  20. Cantor三分集为啥不能在Myeclipse上完美实现呢?

热门文章

  1. h5手机端适配字体设置
  2. Win11 22H2更新了什么?Win11 22H2更新内容大盘点
  3. 人工智能“军备竞赛”的真正危险是什么?专家警告:未来挺可怕!
  4. 一条网线怎样既接路由器又接电视?
  5. android扣扣登录界面,手机QQ
  6. java php bridge吧_使用Php-Java Bridge
  7. 魅族关掉 android beam,科普Android Beam功能介绍及红米Note2与魅族魅蓝no
  8. 【天命奇御】成就进度62/71的通关攻略(4·地图篇)
  9. 银行业务学习之道:银行帐号、卡号和客户号分析
  10. 浅谈linux的inode