理解Mach-O文件格式(1)
原文地址
写在之前
之前工作中对Mach-O文件有一定的接触, 原本早就想写一篇文章分享一下,但是奈何只是不够深入, 总怕分析的有问题误导读者。
最近又在阅读深入解析Mac OS X 与iOS 操作系统,借着这个机会记录下自己的学习成果, 并结合之前的经验, 加上一些实例让读者更好的理解。
毕竟对于程序员来说 大部分人对抽象的概念的感觉就是 听说过很多原理, 依然不知道大佬说的是什么
Mac OS 与 iOS 支持的文件类型
在Unix-Like
系列的操作系统, 可以通过命令 chmod +x
给予文件可执行权限, 但是这不代表这个文件具有可执行权限, 实际上 Apple家的操作系统只支持三种文件格式。
- 以
#!
开头的脚本文件 - 通用二进制文件
- Mach-O格式文件
但是实际上 以#!
开头的脚本文件其实是shell解释器找到后面指定的脚本解释器来执行的, 而通用二进制文件其实是多个架构的Mach-O文件的打包体。
通用二进制文件其实有个更加形象化的名字fat binary
那么操作系统如何知道你打开的文件是何种类型的?
其实是通过这些文件头的固定数字来区分的, 对于这些固定数字通常叫做 Magic Number
(魔数).
对于fat binary
的魔数是 0xcafebabe
(小端)0xbebafeca
大端
对于Mach-O
的魔数是 0xfeedface
(32位) 0xfeedfacf
(64位)
多说无益~~上代码
我们以/usr/bin/perl为例 (这是一个fat binary)
$ file /usr/bin/perl
/usr/bin/perl: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [i386:Mach-O executable i386]
/usr/bin/perl (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/perl (for architecture i386): Mach-O executable i386
$ otool -vh /usr/bin/perl
Mach headermagic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 X86_64 ALL LIB64 EXECUTE 17 1800 NOUNDEFS DYLDLINK TWOLEVEL PIE
不过可能你觉得拿着系统的命令来看感觉不那么真实, 那么cat命令我们都用过吧,来看下
在/usr/include/mach-o/fat.h
路径下有关于fat binary
文件的头文件定义
struct fat_header {uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */uint32_t nfat_arch; /* 包含的架构数 */
};struct fat_arch {cpu_type_t cputype; /* cpu类型 */cpu_subtype_t cpusubtype; /* 机器标示符 */uint32_t offset; /* 当前架构在这个文件中的便宜量 */uint32_t size; /* 当前架构在文件中的长度*/uint32_t align; /* 对齐方式 */
};
不知道大家还记得不记得之前使用windows的时候有System32和64之分, 那是因为在windows操作系统中不同架构的可执行文件是分开存放的。
苹果在某次WWDC大会声称自己优雅的将多个架构合并在了一个文件中。引来果粉一阵鼓掌。
其实fat binary
文件的真正布局非常简单。
以/usr/bin/perl为例
Apple的实现只是将不同架构的文件并排放在一起,然后在文件头部添加不同架构的描述信息, 然后再加载当前架构的Mach-O文件 丢弃掉其他架构的部分即可。实在是简单粗暴~~
Mach-O文件结构
Unix标准了一个可移植的二进制格式ELF
但是苹果并没有实现它而是维护了一套NeXTSTEP的遗物 Mach-Object
简称Mach-O
。
但是这并不是说苹果不遵守POSXI
规范,这个规范通常说的是源码级别的跨平台性,对于二进制则不强制要求。
下面是一个官方提供的图片。
Mach-0 Header
先来介绍Mach-O的Header(只介绍64位)信息。
相关头文件定义在/usr/include/mach-o/loader.h
里面。如果需要使用只需要加载<mach-O/loader.h>
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; /* 文件类型 */uint32_t ncmds; /* load commadns的个数 */uint32_t sizeofcmds; /* load commands的总大小 */uint32_t flags; /* 动态连接器标志*/uint32_t reserved; /* 保留*/
};/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
#define MH_MAGIC_64 0xfeedfacf /* 小端 */
#define MH_CIGAM_64 0xcffaedfe /* 大端 */
注: Mach-O文件不仅仅是可执行文件, 也包括目标文件(.o) 动态库, Bundle插件等。
标志位
flag 标记了一些dyld加载 执行 中可配置的信息。
关于Mach-O文件的魔数信息,有兴趣的读者可以按照之前的方式亲自动手尝试一下
Mach-O Load commands
Mach-O文件中最重要的元信息就是 load Commands,加载命令紧跟在文件头信息之后。
// [_mach_header_|___load_commands___||___load_commands___||____other____|]struct load_command {uint32_t cmd; /* load command的类型 */uint32_t cmdsize; /* command 的长度 */
};
LC_SEGMENT
对于加载命令是LC_SEGMENT的命令指定了内核如何设置新运行的进程的内存空间
对应的头文件也在<mach-o/loader.h>
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; /* 当前segment加载的虚拟内存起始地址 */uint64_t vmsize; /* 当前segment加载的虚拟内存地址占用的长度 */uint64_t fileoff; /* segment在文件中的偏移 */uint64_t filesize; /* segment在文件中的长度 */vm_prot_t maxprot; /* 最大的保护级别 */vm_prot_t initprot; /* 初始化的保护级别 */uint32_t nsects; /* 包含sections的个数 */uint32_t flags; /* 标志位 */
};
由于有了LC_SEGMENT命令。对于每一个Segment,将文件中偏移量为fileOff长度为filesize的文件内容加载到虚拟地址为vmaddr的位置,长度为vmsize, 页面的权限通过initprot来初始化(比如设定读/写/执行, 段的保护级别可以动态设置最大不超过maxprot
常见的Segment有以下几个
- __TEXT 代码段
- __PAGEZERO 空指针陷阱
- __DATA 数据段
- __LINKEDIT 包含需要被动态链接器使用的信息,包括符号表、字符串表、重定位项表等。
- __OBJC(现已经被合并到__DATA部分)包含会被Objective Runtime使用到的一些数据。
当然读者如果有兴趣查看其他所有的loadcommands可以去loader.h头文件定义去查看,也可以实际操练一下
如 使用otool 查看某些mach-O文件的所有load_commands
otool -l /bin/ls
section
类型声明如下
struct section_64 { /* for 64-bit architectures */char sectname[16]; /* name of this section */char segname[16]; /* segment this section goes in */uint64_t addr; /* memory address of this section */uint64_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) */uint32_t reserved3; /* reserved */
};
对于__TEXT, __DATA下面, 又有细分的各种Section,常见的如
名称 | 作用 |
---|---|
TEXT.text | 只有可执行的机器码 |
TEXT.cstring | 硬编码去重后的C字符串 |
TEXT.const | 初始化过的常量 |
DATA.data | 初始化过的可变的数据 |
DATA.bss | 没有初始化的静态变量 |
DATA.common | 没有初始化过的符号声明 |
DATA.objc_clasname | oc类名称 |
DATA.objc_classlist | 类列表 |
DATA.objc_protocollist | 协议列表 |
···
其他的就不一一列举,建议读者亲自动手试一试, 会发现很多有价值的东西
了解这些有什么用?
相信看了这些内容, 你已经大致知道Mach-O文件的物理布局, 那么我们知道了这个文件格式能用来做什么呢?
理解了这个可以用来做下面一些东西:
- 依赖解耦
- 元信息获取
- 调试代码
- CI工具插件检测
- 逆向
相关一些示例放在下篇文章讲解。
作者:南栀倾寒
链接:https://www.jianshu.com/p/d43a8279a1c2
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
理解Mach-O文件格式(1)相关推荐
- 深入理解Java Class文件格式
首先, 让我们回顾一下关于class文件格式的之前两篇博客的主要内容. 在 深入理解Java Class文件格式(一) 中, 讲解了class文件在整个java体系结构中的位置和作用, 讲解了clas ...
- 深入理解 JVM Class文件格式(九)
经过前八篇关于class文件的博客, 关于class文件格式的内容也基本上讲完了. 本文是关于class文件格式的最后一篇. 在这篇博客中, 将会讲解关于方法的几个属性. 理解这篇博客的内容, 对于理 ...
- 深入理解 JVM Class文件格式(八)
在本专栏的第一篇文章 深入理解Java虚拟机到底是什么 中, 我们主要讲解了什么是虚拟机, 这篇博客是对JVM的一个概述. 在随后的几篇文章中,一直在讲解class文件格式. 在今天这篇博客中, 将会 ...
- 深入理解 JVM Class文件格式(七)
本专栏列前面的一系列博客, 对Class文件中的一部分数据项进行了介绍. 本文将会继续介绍class文件中未讲解的信息. 先回顾一下上面一篇文章. 在上一篇博客中, 我们介绍了: this_class ...
- 深入理解 JVM Class文件格式(六)
经过前几篇文章, 终于将常量池介绍完了, 之所以花这么大的功夫介绍常量池, 是因为对于理解class文件格式,常量池是必须要了解的, 因为class文件中其他地方,大量引用了常量池中的数据项. 对于还 ...
- 深入理解 JVM Class文件格式(五)
(8) CONSTANT_Class_info 常量池中的一个CONSTANT_Class_info, 可以看做是CONSTANT_Class数据类型的一个实例. 他是对类或者接口的符号引用. 它描述 ...
- 深入理解 JVM Class文件格式(三)
** JVM常量池中各数据项类型详解 ** 关于常量池的大概内容, 已经在 深入理解 JVM Class文件格式(一) 中讲解过了, 这篇文章中还介绍了常量池中的11种数据类型. 本文的任务是详细讲解 ...
- java class教程_深入理解Java Class文件格式(七)
本专栏列前面的一系列博客, 对Class文件中的一部分数据项进行了介绍. 本文将会继续介绍class文件中未讲解的信息. 先回顾一下上面一篇文章. 在上一篇博客中, 我们介绍了: this_class ...
- java怎么编程class,深入理解Java Class文件格式(一)
Class文件在Java体系结构中的位置和作用 在上一篇博客中, 大致讲解了Java虚拟机的体系结构和执行原理. 本篇博客主要讲解能够被JVM识别, 加载并执行的class文件的格式. 对于理解JVM ...
- 深入理解 WIN32 PE 文件格式
原文地址:http://www.cppblog.com/shaoxie1986/articles/126142.html 译自:An In-Depth Look into the Win32 Port ...
最新文章
- 【廖雪峰python入门笔记】if语句
- FineUI小技巧(4)关闭窗体那些事
- (2)shiro角色资源权限
- 如何安装rpm包?掌握rpm包管理工具就够了
- Dubbo设置超时时间
- 【转】Yahoo!团队:网站性能优化的35条黄金守则
- 如何在GraphPad Prism中使用非线性回归拟合模型?
- 多用as少用强制类型转换
- 计算机网络基础常考简答题,计算机网络基础知识简答题
- js动态产生对象push进数组,发现数组所有元素(element or object)一样
- C++--第6课 - 专题一经典问题解析
- [图文详解]图像处理中的高斯模糊
- 现在单片机编程语言是c吗,单片机编程用什么语言 哪个适合新手
- 地铁票务管理系统_地铁票务管理是干什么
- SVN主干合并到分支
- 计算机网络故障的排除,计算机网络故障诊断与排除
- 简单内存泄漏检测方法 解决 Detected memory leaks! 问题
- Cheat Engine(CE)的下载和安装指南以及相关教程
- 程序员跳槽时,如何正确做好职业规划?
- 好久能旅游?新加坡,一座惬意的城市
热门文章
- Elasticsearch Java虚拟机配置详解
- 当耐克用上了AI、AR技术,你的鞋也要放飞自我了?Just Do It !
- 设置foxmail通过ccproxy代理收发邮件
- 通过CCproxy配置内网linux服务器
- 2022常见软件测试面试题
- 【HTML】-- 用户注册表单
- AdminLTE2的模态框(弹出框)
- CleanMyMac X4.12.2免费版MAC电脑系统磁盘优化工具
- vue解决mintui中使用MessageBox弹窗拦截,移动端多次点击手机的物理返回键,选择确定后页面返回不正确问题
- 百度地图的一些踩坑 marker网络图片不显示