http://www.cocoachina.com/mac/20150122/10988.html

http://www.reinterpretcast.com/hello-world-mach-o

很多朋友都知道,在Windows上exe是可直接执行的文件扩展名,而在Linux(以及很多版本的Unix)系统上ELF是可直接执行的文件格式,那么在苹果的操作系统上又是怎样的呢?在iOS(和Mac OS X)上,主要的可执行文件格式是Mach-O格式。本文就关于iOS上的可执行文件和Mach-O格式做一个简要整理。

Mach-O格式是iOS系统上应用程序运行的基础,了解Mach-O的格式,对于调试、自动化测试、安全都有意义。在了解二进制文件的数据结构以后,一切就都显得没有秘密。

0. Mach与Mach-O

这里先提醒大家一下,Mach不是Mac,Mac是苹果电脑Macintosh的简称,而Mach则是一种操作系统内核。Mach内核被NeXT公司的NeXTSTEP操作系统使用。在Mach上,一种可执行的文件格是就是Mach-O(Mach Object file format)。1996年,乔布斯将NeXTSTEP带回苹果,成为了OS X的内核基础。所以虽然Mac OS X是Unix的“后代”,但所主要支持的可执行文件格式是Mach-O。

iOS是从OS X演变而来,所以同样是支持Mach-O格式的可执行文件。

1. iOS可执行文件初探

作为iOS客户端开发者,我们比较熟悉的一种文件是ipa包(iPhone Application)。但实际上这只是一个变相的zip压缩包,我们可以把一个ipa文件直接通过unzip命令解压。

解压之后,会有一个Payload目录,而Payload里则是一个.app文件,而这个实际上又是一个目录,或者说是一个完整的App Bundle。

在这个目录中,里面体积最大的文件通常就是和ipa包同名的一个二进制文件。找到它,我们用file命令来看一下这个文件的类型:

1
2
3
XXX: Mach-O universal binary with 2 architectures
XXX (for architecture armv7): Mach-O executable arm
XXX (for architecture armv7s): Mach-O executable arm

由此看来,这是一个支持armv7和armv7s两种处理器架构的通用程序包,里面包含的两部分都是Mach-O格式。

对于一个二进制文件来讲,每个类型都可以在文件最初几个字节来标识出来,即“魔数”。比如PNG图片的最初几个字节是\211 P N G \r \n \032 \n (89 50 4E 47 0D 0A 1A 0A)。我们再来看下这个Mach-O universal binary的:

1
0000000 ca fe ba be 00 00 00 02 00 00 00 0c 00 00 00 09

没错,开始的4个字节是cafe babe,即“Cafe baby”。了解Java或者说class文件格式的同学可能会很熟悉,这也是.class文件开头的“魔数”,但貌似是Mach-O在更早的时候就是用了它。在OS X上,可执行文件的标识有这样几个魔数(也就是文件格式):

cafebabe

feedface

feadfacf

还有一个格式,就是以#!开头的脚本

cafebabe就是跨处理器架构的通用格式,feedface和feedfacf则分别是某一处理器架构下的Mach-O格式,脚本的就很常见了,比如#!/bin/bash开头的shell脚本。

这里注意一点是,feedface和cafebabe的字节顺序不同,我们可以用lipo把上面cafebabe的文件拆出armv7架构的,看一下开头的几个字节:

0000000 ce fa ed fe 0c 00 00 00 09 00 00 00 02 00 00 00

2. Mach-O格式

接下来我们再来看看这个Mach-O格式到底是什么样的格式。我们可以通过二进制查看工具查看这个文件的数据,结果发现,不是所有数据都是相连的,而是被分成了几个段落。

在一位叫做JOE SAVAGE的老兄发布的图片上来看,Mach-O的文件数据显现出来是这个样子的:

(图形化的Mach-O文件数据)

大家可以对数据的分布感受下。

虽然被五颜六色的标记出来,可能这还不是特别直接。再来引用苹果官方文档的示意图:

(Mach-O文件格式基本结构)

从这张图上来看,Mach-O文件的数据主体可分为三大部分,分别是头部(Header)、加载命令(Load commands)、和最终的数据(Data)。

回过头来,我们再看上面那张图,也许就都明白了。黄色部分是头部、红色是加载命令、而其它部分则是被分割成Segments的数据。

3. Mach-O头部

这里,我们用otool来看下Mach-O的头部信息,得到:

1
2
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedface      12          9  0x00          2    45       4788 0x00218085

更详细的,我们可以通过otool的V参数得到翻译版:

1
2
3
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
   MH_MAGIC     ARM         V7  0x00     EXECUTE    45       4788   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE

前面几个字段的意义,上下对比就能看懂,我这里主要说下这样几个字段:

filetype,这个可以有很多类型,静态库(.a)、单个目标文件(.o)都可以通过这个类型标识来区分。

ncmdssizeofcmds,这个cmd就是加载命令,ncmds就是加载命令的个数,而sizeofcmds就是所占的大小。

flags里包含的标记很多,比如TWOLEVEL是指符号都是两级格式的,符号自身+加上自己所在的单元,PIE标识是位置无关的。

4. 加载命令

上面头部中的数据已经说明了整个Mach-O文件的基本信息,但整个Mach-O中最重要的还要数加载命令。它说明了操作系统应当如何加载文件中的数据,对系统内核加载器和动态链接器起指导作用。一来它描述了文件中数据的具体组织结构,二来它也说明了进程启动后,对应的内存空间结构是如何组织的。

我们可以用otool -l xxx来看一个Mach-O文件的加载命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Load command 0
      cmd LC_SEGMENT
  cmdsize 56
  segname __PAGEZERO
   vmaddr 0x00000000
   vmsize 0x00004000
  fileoff 0
 filesize 0
  maxprot ---
 initprot ---
   nsects 0
    flags (none)
Load command 1
      cmd LC_SEGMENT
  cmdsize 736
  segname __TEXT
   vmaddr 0x00004000
   vmsize 0x00390000
  fileoff 0
 filesize 3735552
  maxprot r-x
 initprot r-x
   nsects 10
    flags (none)
Section
  sectname __text
   segname __TEXT
      addr 0x0000b0d0
      size 0x0030e7f4

上面这段是执行结果的一部分,是加载PAGE_ZERO和TEXT两个segment的load command。PAGE_ZERO是一段“空白”数据区,这段数据没有任何读写运行权限,方便捕捉总线错误(SIGBUS)。TEXT则是主体代码段,我们注意到其中的r-x,不包含w写权限,这是为了避免代码逻辑被肆意篡改。

我再提一个加载命令,LC_MAIN。这个加载指令,会声明整个程序的入口地址,保证进程启动后能够正常的开始整个应用程序的运行。

除此之外,Mach-O里还有LC_SYMTAB、LC_LOAD_DYLIB、LC_CODE_SIGNATURE等加载命令,大家可以去官方文档查找其含义。

至于Data部分,在了解了头部和加载命令后,就没什么特别可说的了。Data是最原始的编译数据,里面包含了Objective-C的类信息、常量等。

本文是对Mach-O文件格式的一个理解小结,希望能够抛砖引玉,帮助各位朋友把握可执行文件的主题脉络,进而解决各类问题。

参考:

THE NITTY GRITTY OF “HELLO WORLD” ON OS X

OS X ABI Mach-O File Format Reference

了解iOS上的可执行文件和Mach-O格式的更多相关文章

  1. 细数iOS上的那些安全防护

    细数iOS上的那些安全防护  龙磊,黑雪,蒸米 @阿里巴巴移动安全 0x00 序 随着苹果对iOS系统多年的研发,iOS上的安全防护机制也是越来越多,越来越复杂.这对于刚接触iOS安全的研究人员来说非 ...

  2. 微信双开是定时炸弹?关于非越狱iOS上微信分身高危插件ImgNaix的分析

    作者:蒸米@阿里移动安全 序言 微信作为手机上的第一大应用,有着上亿的用户.并且很多人都不只拥有一个微信帐号,有的微信账号是用于商业的,有的是用于私人的.可惜的是官方版的微信并不支持多开的功能,并且频 ...

  3. ios上position:fixed失效问题

    手机端上的猫腻真是多啊~~~ 此起彼伏! 最近又遇到了 固定定位的底部导航在ios上被弹出去 此时内心1w+个草泥马奔过~~~~~~~~ 直接上解决方案: <div class="ma ...

  4. :active 为什么在ios上失效

    :active是针对鼠标,而手机上是没有鼠标,而是touchstart,所以早成了ios上不兼容 解决方法是: window.onload = function(){ document.body.ad ...

  5. 解决protobuf不能直接在IOS上使用,利用protobuf-net在IOS上通讯

    ---------------------------------------------------------------------------------------------------- ...

  6. iOS上简单推送通知(Push Notification)的实现

    iOS上简单推送通知(Push Notification)的实现 根据这篇很好的教程(http://www.raywenderlich.com/3443/apple-push-notification ...

  7. iOS上new Date出现Invalid Date的问题,

    用angular的ngModel绑定time的时候,在安卓调试没问题,没想到在iOS上出现了NaN:NaN,后台丢过来的数据大概是这样的2016-03-08 20:14 然而问题就出在这个分隔符&qu ...

  8. javascript的onbeforeunload函数在IOS上运行

    今天在做项目的时候,组长让我用iPad测试一下前面写的离线缓存,后退不刷新页面,发现在iPad上onbeforeunload函数在iPad上一带而过,不运行??? 无奈之下,发现原来在IOS上,有自己 ...

  9. ios上 更改 状态栏(UIStatusBar)

    摘要 ios上 更改状态栏(UIStatusBar)的颜色 ios UIStatusBar statusBar 状态栏 更改状态栏颜色 目录[-] IOS上 关于状态栏的相关设置(UIStatusBa ...

随机推荐

  1. Jmeter源代码学习心得

    1.TestPlan和WorkBench GUI类是直接加载的,因此左边的树形菜单开始启动Jmeter时显示也是这两个,默认写死了的!可以改源码!在MenuFactory中有相应代码. 2.其它的GU ...

  2. 1 Groovy

    1.1  什么是Groovy? groovy 是一个弱类型,动态语言,并且运行在JVM之上.它与java联系紧密.它是一个功能丰富和友好的java语言. Groovy源代码,通过Groovy编译器编译 ...

  3. SSMS 远程连接SERVER 设置 - Unable to connect to SQL Server instance remotely

    问题描述: 新装了一台SERVER,在SERVER本地打开SSMS链接sever,一且正常.但是用我自己local去链接的时候出现以下错误. A network-related or instance ...

  4. appium使用常见问题汇总--持续更新

    问题1:使用adb devices查看连接设备,提示unauthorized 解决方案:进入进程管理,关闭adb进程,然后再查看连接设备 问题2:adb connect ip提示远程计算机积极拒绝,无 ...

  5. appium手机自动化环境搭建

    在robotframework环境安装完成的基础上进行如下安装,如果没有安装rfs环境,请先参考robotframework安装文章:Robot Framework的环境搭建 文件下载地址:链接:ht ...

  6. 关系型数据库---MySQL---对中文字段排序

    1.对中文进行排序时会发生错误,原因是使用的字符集不是中文的字符集: 解决:CONVERT函数 SELECT a.id,a.`name`,a.ch_name FROM `user` a ORDER B ...

  7. javascrip学习之基础

    弹窗信息:三种消息框:警告框.确认框.提示框. window.alert("sometext"); var r=confirm("按下按钮");//提示信息,返 ...

  8. PlayMaker Action的执行顺序

    如图:默认的是从上到下 先执行Play Sound,再执行Destroy Object. 可以点击右上角的齿轮,也就是设置按钮选中Action Sequence,这样就会同时执行.

  9. rail 怎样在已有数据库上继续开发

    今天刷贴看到了这篇文章http://ruby-china.org/topics/16493,老大回复的很有意义,在这里备份一个 而要把现有的数据库纳入 Migration,一个简单方法: 创建一个空 ...

  10. Windows Server 2008下的FTP服务器设置

    今天刚在新服务器上部署好Windows Server 2008,同时安装好了SQL,基本上还算顺利.没想到在设置FTP服务器的时候遇到了麻烦.按照以往的经验,安装好Serv-U以后,同时在防火墙设置里 ...