美文网首页IOS个人开发
Mach-O文件初了解

Mach-O文件初了解

作者: Claire_wu | 来源:发表于2019-03-25 15:05 被阅读62次

    1 什么是Mach-O

    Mach-O其实是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式, 类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)。
    如果我们想对 Mach -O 文件有所了解,可以将我们打包好的 ipa 文件后缀改成 .zip,然后解压生成 Payload 文件,在其中就可以找到 exec 文件。或者找一个动态库的 framework 在其中也可以找到 exec 文件。

    常见的MachO文件:

    • 目标文件:.o
    • 库文件:.a .dylib Framework
    • 可执行文件:dyld .dsym

    如何查看文件格式

    file [文件路径/文件名]
    
    image.png

    2 MachOView

    官网直接下载的MachOView.app打开后不停闪退,因此最好是直接去下载源码然后编译出来使用。
    MachOView GitHub地址
    下载后记得修改源码:

    image.png

    3 MachO文件结构

    使用MachOView打开两个Xcode编译后生成的app可执行文件,文件结构分别如下:


    x86_64.png armv7 and arm64.png

    根据图可以看出,同时支持多种指令集的MachO文件会针对每个指令集生成一个Executable,每一个Executable里的文件结构又是一样的。下面我们针对Executable文件来分析,先看一张官网的MachO文件结构图,可看出MachO文件主要由三部分组成:

    • Header
    • Load Commons
    • Data


      image.png

    3.1 Mach Header

    选取了Mach64 Header来分析,图中未红字表明的flags表示的是标示位,主要与系统的加载、链接相关:


    image.png

    3.2 Load Commands

    Load commands是一张包含很多内容的表,内容包括区域的位置、符号表、动态符号表等。这里就是二进制文件加载进内存要执行的一些指令。这里的指令主要在负责我们 APP 对应进程的创建和基本设置(分配虚拟内存,创建主线程,处理代码签名/加密的工作),然后对动态链接库(.dylib 系统库和我们自己创建的动态库)进行库加载和符号解析的工作。

    字段名称及意义分别如下:

    名称 含义
    LC_SEGMENT_64 将文件中(32位或64位)的段映射到进程地址空间中
    LC_DYLD_INFO_ONLY 动态链接相关信息
    LC_SYMTAB 符号地址
    LC_DYSYMTAB 动态符号表地址
    LC_LOAD_DYLINKER 使用谁加载,我们使用dyld
    LC_UUID 文件的UUID
    LC_VERSION_MIN_MACOSX 支持最低的操作系统版本
    LC_SOURCE_VERSION 源代码版本
    LC_MAIN 设置程序主线程的入口地址和栈大小
    LC_LOAD_DYLIB 依赖库的路径,包含三方库
    LC_FUNCTION_STARTS 函数起始地址表
    LC_CODE_SIGNATURE 代码签名

    首先看下Load Commands目录结构:

    image.png
    从上图可知 Load Commands 主要包含了有多个 Segment 段,每个中又包含了多个 Section 段。每一部分都是系统执行指令。其中 LC_SEGMENT 包含空指针陷阱
    __TEXT段主要包含程序代码和只读的常量,这个段的内容如果是系统动态库的内容那么所有进程公用
    __DATA 段主要包含全局变量和静态变量,这个段的内容每个进程单独进行维护
    __LINKEDIT 主要包含链接器使用的符号和其他的表(比如函数名称、地址等) 这个段的内容也是可以多进程公用的。

    此外还需介绍下和 SEGMENT 并列的一些比较重要的指令:

    • LC_LOAD_DYLINKER 该字段标明我们的MachO是被谁加载进去的。
      可以理解为LC_LOAD_DYLINKER指向的地址是微信APP加载小程序的引擎,而我们的MachO是小程序。在上图中可以看到我们的Demo1的LC_LOAD_DYLINKER指向的地址就是dylddyld确实是用来加载我们app的。

    • LC_LOAD_DYLIB
      该字段标记了所有动态库的地址,只有在LC_LOAD_DYLIB中有标记,我们MachO外部的动态库(如:Framework)才能被dyld正确的引用,否则dyld不会主动加载。(因此很多通过这个知识点来做代码注入

    • LC_MAIN 是在所有的库都加载完成后,有其中的指令启动程序的主线程。我们的程序也是在这个函数之后才开始执行 main() 函数的。

    • LC_CODE_SIGNATURE 我想每个 iOSer 都知道代码签名的机制,其实代码签名的校验也是在这个指令下进行。实际上指令会把整个文件进行 hash 化处理并签名,在运行时去验证签名的正确性。

    3.3 Data

    Data 通常是对象文件中最大的部分,包含Segement的具体数据,如静态C字符串,带参数/不带参数的OC方法,带参数/不带参数的C函数。

    可以看到,全局静态C字符,方法里面的字符串都被保存在data段的cstring里了,哪怕是%d,%s等等这样的参数类型字符串也被保存在内,但所有同样的字符串只会被保存一次:


    cstring.png

    同样所有的OC方法都被保存在methname里了:


    methname.png

    4 结语

    暂时通过MachOView初步窥探了一下MachO文件的结构,理解MachO文件结构是对后续学习MachO文件加载、dyld等建立知识基础。好多知识对于自己目前的知识水平感觉真的是看着像都懂了,但一问细节却又什么都不懂,书读百遍其义自见吧。
    参考文章:
    iOS逆向(5)-不知MachO怎敢说自己懂DYLD
    理解 Mach-O 并提高程序启动速度

    相关文章

      网友评论

        本文标题:Mach-O文件初了解

        本文链接:https://www.haomeiwen.com/subject/cecvvqtx.html