MachO文件结构详解
MachO概念
在可安装的每一个.app包中,都有一个与app同名
的可执行文件
。如图:
// 给MachO文件上执行权限
// 拿到MachO文件的路径
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
//上可执行权限
chmod +x "$TARGET_APP_PATH/$APP_BINARY"
.MachO其实是Mach Object文件格式的缩写,是Mac以及iOS上可执行文件的格式,类似Windows上的PE格式(Portable Executable),linux上的ELF格式(Executable and Linking Format)
苹果官方文档中列举了MachO的文件类型,如图:
MachO实际上是
通用的二进制文件(Universal binary)
----苹果公司提出的一种程序代码,能同时适用多种架构的二进制文件。同一程序包中同时为多种架构提供最理想的性能。需要存储多种代码,通用二进制文件通常比单一平台二进制的程序要大,但因为有共通的非执行资源,所以不会达到单一版本的几倍之多。而且执行时只调用一部分代码,运行起来也不需要额外的内存。
lipo命令
1.使用lipo -info查看MachO文件包含的架构 armv7 armv7s arm64
lipo -info MachO文件
2.使用lipo -thin拆分某种架构
lipo MachO文件 -thin 架构 -output 输出文件路径
3.使用 lipo -create合并多种架构
lipo -create MachO1 MachO2 -output 输出文件路径
MachO内部结构
- Header (头部)
包含该二进制文件的一般信息。字节顺序、架构类型、加载指令的数量等。可以快速确认一些信息,如当前文件使用32位还是64位,对应的处理器是什么、文件类型是什么等
//32位架构
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位架构
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 */
};
magic
:魔数,用于快速确认该文件用于64位还是32位
cputype
:CPU类型,比如 arm
cpusubtype
:对应的具体类型,比如arm64、armv7
filetype
:文件类型,比如可执行文件、库文件、Dsym文件
MH_EXECUTE
,代表可执行文件
ncmds
:加载命令条数
sizeofcmds
:所有加载命令的大小
reserved
:保留字段
flags
:标志位
- LoadCommands (加载命令)
用于告诉loader如何设置并加载二进制数据
// 将文件的32位或64位的段映射到进程地址空间
#define LC_SEGMENT 0x1
#define LC_SEGMENT_64 0x19
// 唯一的 UUID,标示二进制文件
#define LC_UUID 0x1b /* the uuid */
// 启动动态加载连接器
#define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */
// 代码签名和加密
#define LC_CODE_SIGNATURE 0x1d /* local of code signature */
#define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */
Data (数据段 segment)
存放数据:代码、字符常量、类、方法等
可以拥有多个segment,每个segment可以有零到多个section。每个段都有一段虚拟地址映射到进程的地址空间
Loader Info (链接信息)
一个完整的用户级MachO文件的末端是一系列链接信息。其中包含了动态加载器用来链接可执行文件或者依赖所需使用的符号表、字符串表等
以上部分参考文章:
https://juejin.im/post/5c67e7efe51d45164c75993b
苹果签名双向验证原理
代码签名
是指对可执行文件或脚本进行数字签名。用来确认软件在签名后未被修改或损害的措施。和数字签名原理一样,只不过签名的数据是代码而已。
简单的代码签名
在iOS出来以前,以前的主流操作系统(Mac/Windows)软件随便从哪里下载都能运行,系统安全存在隐患,盗版软件,病毒入侵,静默安装等等,那么苹果希望解决这样的问题,要保证每一个安装到iOS上的APP都是经过苹果官方允许的,怎样保证呢?
就是通过代码签名
。如果要实现验证
,其实最简单的方式就是通过苹果官方生成非对称加密的一对公私钥。在iOS的系统中内置一个公钥,私钥由苹果后台保存,我们传APP到AppStore时,苹果后台用私钥对APP进行签名,iOS系统下载这个APP后,用公钥验证这个签名
,若签名正确,这个APP肯定是有苹果后台认证的,并且没有被修改过,也就达到了苹果的需求:保证安装的每一个APP都是经过苹果官方允许的。
除上传到AppStore外,苹果还有那些需求?
- 安装包不需要上传到Ap pStore,可以直接安装到手机上
- 苹果为了保证系统的安全性,又必须对安装的APP有绝对的控制权
- 经过苹果允许才可以安装
- 不能滥用导致非开发APP也能被安装
为了实现这些需求,iOS签名的复杂度也开始增加,即双层签名
双层签名过程如下:(开发者认证的安全性)
⚠️思考:如果单纯的只是如上验证,所有的手机都可以安装APP?--------由此描述文件
应运而生.