美文网首页
09_Mach-O文件解析

09_Mach-O文件解析

作者: 伶俐ll | 来源:发表于2020-08-04 17:15 被阅读0次
    Macho-O 是什么

    Mach-O 是 Mach object 文件格式的缩写,它是一种用于记录可执行文件、对象代码、共享库、动态加载代码和内存转储的文件格式。

    大多数基于 Mach 内核的操作系统都使用 Mach-O,如NeXTSTEP、OS X 和 iOS 。

    Universal Binary

    Universal Binary:通用二进制文件,也称胖二进制文件,是将支持不同架构的Mach-O打包在一起,再在文件起始位置加上Fat Header来说明所包含的Mach-O文件支持的架构和偏移地址信息。


    Snip20200814_24.png
    • Magic Number:魔数(与UNIX的ELF文件一样),加载器通过这个魔数值来判断这是什么文件。
    • Number of Architecture: 当前的Fat Binary文件包含了多少个不同架构的Mach-O文件。

    Mach-O文件格式

    image.png

    官方描述:
    https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html

    可以在xnu源码中,查看到Mach-O格式的详细定义(https://opensource.apple.com/tarballs/xnu/

    Mach-O文件的数据主体可分为三个部分:

    • Header:结构体定义在mach-o/loader.h头文件中,存储着Mach-O的一些基本信息,包括了文件类型、平台、LoadCommands的个数等等。
    • LoadCommands:加载命令,在mach-o/loader.h中可以看到对Load Command的定义,这一段紧跟Header,加载Mach-O文件时会使用这里的数据来确定内存的分布。
    • Data:每一个segment的具体数据都保存在这里,这里包含了具体的代码、数据等等。

    下面将分别对每一部分进行介绍

    一、Header

    查看xnu源码我们可以得知,定义Header的结构体如下:

    struct mach_header {
        uint32_t magic;
        cpu_type_t cputype;
        cpu_subtype_t cpusubtype;
        uint32_t filetype;
        uint32_t ncmds;
        uint32_t sizeofcmds;
        uint32_t flags;
    };
    struct mach_header_64 {
        uint32_t magic;
        cpu_type_t cputype;
        cpu_subtype_t cpusubtype;
        uint32_t filetype;
        uint32_t ncmds;
        uint32_t sizeofcmds;
        uint32_t flags;
        uint32_t reserved;
    };
    

    1、 magic

    Mach-O的魔数,很多类型的文件,其起始的几个字节的内容是固定的,根据这几个字节的内容就可以确定文件类型,因此这几个字节的内容被称为魔数。在这里用于标示文件架构类型,也就是说判断是x86、x64、armv7、arm64。

    //fat.h
    //胖二进制文件
    #define FAT_MAGIC   0xcafebabe
    #define FAT_CIGAM   0xbebafeca
    //loader.h
    //非64bit架构文件
    #define MH_MAGIC    0xfeedface  
    #define MH_CIGAM    0xcefaedfe
    //loader.h
    //64bit架构文件
    #define MH_MAGIC_64 0xfeedfacf 
    #define MH_CIGAM_64 0xcffaedfe
    

    2、 cputype与cpusubtype

    定义了Mach-o所能支持的所有CPU类型,x86和arm的如下。

    #define CPU_ARCH_ABI64  0x01000000      /* 64 bit ABI */
    #define CPU_TYPE_X86        ((cpu_type_t) 7)
    #define CPU_TYPE_I386       CPU_TYPE_X86   
    #define CPU_TYPE_X86_64     (CPU_TYPE_X86 | CPU_ARCH_ABI64)
    #define CPU_TYPE_ARM        ((cpu_type_t) 12)
    #define CPU_TYPE_ARM64          (CPU_TYPE_ARM | CPU_ARCH_ABI64)
    

    3、ncmds和sizeofcmds

    这个cmd就是加载命令,ncmds就是加载命令的个数,而sizeofcmds就是加载命令的字节数。

    4、flags

    程序的标识位

    5、 reserved

    64bit的保留字段。

    6、filetype

    用于判断程序的文件类型,在mach-o/loader.h中定义一组宏可以很直观的明白该位的意义。

    #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 */
                    /*  linking only, no section contents */
    #define    MH_DSYM     0xa     /* companion file with only debug */
                    /*  sections */
    #define    MH_KEXT_BUNDLE  0xb     /* x86_64 kexts */
    

    其中,常见的Mach-O文件类型有:

    MH_OBJECT :
    • .o 文件(目标文件)
      zhanglingli@bogon 逆向 % file test.c 
      test.c: c program text, UTF-8 Unicode text
      zhanglingli@bogon 逆向 % clang -c test.c 
      zhanglingli@bogon 逆向 % file test.o
      test.o: Mach-O 64-bit object x86_64
      
    • .a文件(静态库文件,其实就是N个.o合并在一起)
      zhanglingli@bogon 逆向 % file libPods.a 
      libPods.a: Mach-O universal binary with 2 architectures: 
      [arm_v7:current ar archive random library] [arm64:current 
      ar archive random library]
      libPods.a (for architecture armv7): current ar archive 
      random library
      libPods.a (for architecture arm64): current ar archive 
      random library
      
    MH_EXECUTE

    可执行文件

    ```
     lingli@bogon 逆向 % clang -o test2 test.c
     lingli@bogon 逆向 % file test2
     test2: Mach-O 64-bit executable x86_64
     ```
    
    MH_DYLIB:动态库文件
    • .dylib
    zhanglingli@bogon 逆向 % cd /usr/lib           
    zhanglingli@bogon lib % file libssl.44.dylib 
    libssl.44.dylib: Mach-O 64-bit dynamically linked shared 
    library x86_64
    
    • .framework/xx
    MH_DYLINKER

    动态链接编辑器

    • /usr/lib/dyld
    zhanglingli@bogon 逆向 % file dyld 
    dyld: Mach-O universal binary with 2 architectures: 
    [arm_v7s:Mach-O dynamic linker arm_v7s] [arm64:Mach-O 
    64-bit dynamic linker arm64]
    dyld (for architecture armv7s):   Mach-O dynamic linker 
    arm_v7s
    dyld (for architecture arm64):    Mach-O 64-bit dynamic 
    linker arm64
    
    MH_DSYM

    存储着二进制文件符号信息的文件,.dSYM/Contents/Resources/DWARF/xx(常用于分析APP的崩溃信息)

    zhanglingli@bogon 逆向 % file weather                                          
    weather: Mach-O universal binary with 2 architectures: 
    [arm_v7:Mach-O dSYM companion file arm_v7] [arm64]
    weather (for architecture armv7):    Mach-O dSYM 
    companion file arm_v7
    weather (for architecture arm64):    Mach-O 64-bit dSYM 
    companion file arm64
    
    MH_CORE0

    程序Crash之后产生的Core文件

    MH_BUNDLE0

    Xcode里面可以创建bundle的Target,编译之后在bundle后缀名的文件夹下面可以找到

    在Xcode中查看target的Mach-O类型
    image.png

    二、Load commands

    在mach-o/loader.h中可以看到对Load Command的定义。

    struct load_command {
        uint32_t cmd;
        uint32_t cmdsize;
    };
    

    加载命令,上面头部中的数据已经说明了整个Mach-O文件的基本信息,但整个Mach-O中最重要的还要数加载命令。这些加载命令在Mach-O文件加载解析时,被内核加载器或者动态链接器调用,指导如何设置加载对应的二进制数据段。

    Snip20200814_28.png Snip20200814_27.png
    • VM Address: 位于虚拟内存中的地址
    • VMSize: 占用虚拟内存的大小
    • File Offset: 在Mach-o文件中的位置
    • File Size: 在Mach-o文件中占据的大小

    由上表可知

    • _PAGEZERO段的file size为0,意味着在mach-o文件中并不存在_pagezero段的内容, 在mach-o文件载入内存的时候 ,才会给mach-o文件分配 _pagezero段。
    • __TEXT段的内存地址正好是_pagezero段的内存大小,并且__TEXT段的file offset是0,说明从头部信息开始到最后一个__TEXT段结束,都属于代码段,也就是_TEXT段。
    • __TEXT段的VMSize和File Size相等,说明 __TEXT段的数据是直接载入内存的。
    • 在没有使用ASLR技术之前,可执行文件的内存地址是0x0,__TEXT段的内存地址就是VM Address,在使用了ASLR技术之后,可执行文件的内存地址是ASLR随机产生的Offset;__TEXT段的内存地址就是VM Address + Offset。
    可以使用size -l -m -x来查看Mach-O的内存分布
    zhanglingli@bogon 逆向mySelf % size -l -m -x daf 
    daf (for architecture armv7):
    Segment __PAGEZERO: 0x4000 (vmaddr 0x0 fileoff 0)
    Segment __TEXT: 0xc000 (vmaddr 0x4000 fileoff 0)
        Section __text: 0x1db4 (addr 0xbcc8 offset 31944)
        Section __picsymbolstub4: 0x3b0 (addr 0xda7c offset 39548)
        Section __stub_helper: 0x2e8 (addr 0xde2c offset 40492)
        Section __objc_methname: 0xf60 (addr 0xe114 offset 41236)
        Section __cstring: 0x419 (addr 0xf074 offset 45172)
        Section __objc_classname: 0xb8 (addr 0xf48d offset 46221)
        Section __objc_methtype: 0xab9 (addr 0xf545 offset 46405)
        total 0x4336
    Segment __DATA: 0x4000 (vmaddr 0x10000 fileoff 49152)
        Section __nl_symbol_ptr: 0x48 (addr 0x10000 offset 49152)
        Section __la_symbol_ptr: 0xec (addr 0x10048 offset 49224)
        Section __cfstring: 0x30 (addr 0x10134 offset 49460)
        Section __objc_classlist: 0xc (addr 0x10164 offset 49508)
        Section __objc_nlclslist: 0x4 (addr 0x10170 offset 49520)
        Section __objc_protolist: 0x18 (addr 0x10174 offset 49524)
        Section __objc_imageinfo: 0x8 (addr 0x1018c offset 49548)
        Section __objc_const: 0xae8 (addr 0x10194 offset 49556)
        Section __objc_selrefs: 0x94 (addr 0x10c7c offset 52348)
        Section __objc_protorefs: 0x8 (addr 0x10d10 offset 52496)
        Section __objc_classrefs: 0xc (addr 0x10d18 offset 52504)
        Section __objc_superrefs: 0x4 (addr 0x10d24 offset 52516)
        Section __objc_ivar: 0x8 (addr 0x10d28 offset 52520)
        Section __objc_data: 0xa0 (addr 0x10d30 offset 52528)
        Section __data: 0x168 (addr 0x10dd0 offset 52688)
        Section __bss: 0x140 (addr 0x10f38 offset 0)
        total 0x1078
    Segment __LINKEDIT: 0x8000 (vmaddr 0x14000 fileoff 65536)
    total 0x1c000
    

    三、Raw segment data

    在Load commands中定义的Segment的最原始的编译数据,是Mach-O文件中最大的一部分,包含了Load Command中所需的数据以及在虚存地址偏移量和大小;一般Mach-O文件有多个段(Segement),段每个段有不同的功能,一般包括:

    • __PAGEZERO: 空指针陷阱段,映射到虚拟内存空间的第一页,用于捕捉对NULL指针的引用;
    • __TEXT: 代码段,包含了函数代码以及其他只读数据(Header、LoadCommands)。该段数据的保护级别为:VM_PROT_READ(读)、VM_PROT_EXECUTE(执行),防止在内存中被修改;
    • __DATA: 数据段,包含了程序数据,全局变量,该段可写;
    • __LINKEDIT: 链接器使用的符号以及其他表。

    dyld和Mach-O

    APP的可执行文件、动态库都是由dyld负责加载的,dyld用于加载以下类型的Mach-O文件

    • MH_EXECUTE
    • MH_DYLIB
    • MH_BUNDLE

    相关文章

      网友评论

          本文标题:09_Mach-O文件解析

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