美文网首页iOS程序员
iOS逆向基础Mach-O文件(1)

iOS逆向基础Mach-O文件(1)

作者: iOS雯Ping | 来源:发表于2018-04-25 22:16 被阅读198次

    随着iOS职位的火热,越来越多的人都想成为一名优秀的iOS开发工程师,那么在竞争激烈的时代,应该如何成为一名iOS开发工程师呢?今天让大家了解一下iOS逆向基础Mach-O文件


    作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这是一个我的iOS交流群:687528266,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!


    在学习iOS逆向的过程中,发现在解密可执行文件dumpdecrypted砸壳原理时需要用到Mach-O相关知识,在动态库注入过程中也需要理解Mach-O可执行文件的文件结构,那么有必要系统学习记录Mach-O文件的组成结构。


     Mach-O,是Mach object文件格式的缩写,是一种可执行文件、目标代码、共享程序库、动态加载代码和核心dump。是a.out格式的一种替代。Mach-O提供更多的可扩展性和更快的符号表信息存取。Mach-O应用在基于Mach核心的系统上,目前NeXTSTEP、Darwin、Mac OS X(iPhone)都是使用这种可执行文件格式。熟悉Mach-O文件格式,有助于了解苹果底层软件运行机制,更好的掌握dyld加载Mach-O的步骤,为自己动手开发Mach-O相关的加解密工具注入工具打下基础。

    Mach-O 没有类似于 XML、YAML、JSON 等诸如此类的特殊格式,它只是一个二进制字节流,被划分为了有意义的数据块。这些块包含元信息,比如,字节顺序、cpu 类型、块的大小,等等,简单期间这里我们仅讨论单架构模型。

    典型的 Mach-O 文件包含三个区域:

    1.头部 (Header): Mach-O文件的架构 比如Mac的 PPC, PPC64, IA-32, x86-64,ios的arm系列.

    2.加载命令(Load commands)描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令表示。

    3.*原始段数据(Raw segment data):可以拥有多个段(segment),每个段可以拥有零个或多个区域(section).每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。

    注:Mach-O 没有类似于 XML、YAML、JSON 等诸如此类的特殊格式,它只是一个二进制字节流,被划分为了有意义的数据块。这些块包含元信息,比如,字节顺序、cpu 类型、块的大小,等等,简单期间这里我们仅讨论单架构模型。

    一.Mach-O头部

    与Mach-O文件格式有关的结构体,都可以直接或间接的在”mach-o/loader.h“文件中找到。

    针对32位与64位架构的cpu,分别使用了mach_header与mach_header_64结构体来描述Mach-O头部。

    mach_header结构体的定义如下:

    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 */

    };

    magic字段就是我们常说的魔数(例如通常判断png文件格式,还有快速求平方根的0x5f3759df),加载器通过这个魔数值来判断这是什么样的文件,胖二进制文件的魔数值是0xcafebabe;

    cputype:与cpusubtype字段与fat_header结构体中的含义完全相同。

    filetype:字段表示Mach-O的具体文件类型。这里主要关注MH_EXECUTE、MH_DYLIB与MH_DYLIB这3个文件格式。

    nfat_arch字段是指当前的胖二进制文件包含了多少个不同架构的Mach-O文件;

    fat_header后会跟着fat_arch,有多少个不同架构的Mach-O文件,就有多少个

    fat_arch,用于说明对应Mach-O文件大小、支持的CPU架构、偏移地址等;

    sizeofcmds字段指明了Mach-O文件加载命令(load commands)所占的总字节大小。

    flags字段表示文件标志,它是一个含有一组位标志的整数,指明了Mach-O文件的一些标志信息。

    我们通过xrun新建一个helloworld文件,构建成可执行文件,通过Mac系统的otool工具(或者MachOView)即可查看mach_header信息,如下图:

    二.Load commands 结构

    Load commands紧跟在头部之后,这些加载指令清晰地告诉加载器如何处理二进制数据,有些命令是由内核处理的,有些是由动态链接器处理的。在源码中有明显的注释来说明这些是动态连接器处理的。

    struct load_command {

       uint32_t cmd;        /* type of load command */

       uint32_t cmdsize;    /* total size of command in bytes */};

    struct segment_command { /* for 32-bit architectures */

       uint32_t    cmd;        /* LC_SEGMENT */

       uint32_t    cmdsize;    /* includes sizeof section structs */

       char        segname[16];    /* segment name */

       uint32_t    vmaddr;        /* memory address of this segment */

       uint32_t    vmsize;        /* memory size of this segment */

       uint32_t    fileoff;    /* file offset of this segment */

       uint32_t    filesize;    /* amount to map from the file */

       vm_prot_t    maxprot;    /* maximum VM protection */

       vm_prot_t    initprot;    /* initial VM protection */

       uint32_t    nsects;        /* number of sections in segment */

       uint32_t    flags;        /* flags */

    };

    1.cmd 是load command的类型,本文中值=1就是LC_SEGMENT,,LC_SEGMENT的含义是(将文件中的段映射到进程地址空间)

    structload_command{

    uint32_tcmd;/* type of load command */

    uint32_tcmdsize;/* total size of command in bytes */};

    2.cmdsize 代表load command的大小(0×58个字节)。

    3.segname 16字节的段名字,当前是__PAGEZERO。

    4.vmaddr 段的虚拟内存起始地址

    5.vmsize 段的虚拟内存大小

    6.fileoff 段在文件中的偏移量

    7.filesize 段在文件中的大小

    8.maxprot 段页面所需要的最高内存保护(4=r,2=w,1=x)

    9.initprot 段页面初始的内存保护

    10.nsects 段中包含section的数量

    11.flags 其他杂项标志位

    otool工具即可查看Load command信息,如下图:

    三.Segment & Section

    这里有个命名的问题,如下图所示,__TEXT代表的是Segment,小写的__text代表 Section

    Section的数据结构

    当一个段包含多个节区时,节区信息会以数组形式紧随着存储在段加载命令后面。节区使用结构体section表示(64位使用section_64表示),定义如下:

    struct section { /* for 32-bit architectures */

       char        sectname[16];    /* name of this section */

       char        segname[16];    /* segment this section goes in */

       uint32_t    addr;        /* memory address of this section */

       uint32_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) */

    };

    sectname:比如_text、stubs 第一个是__text ,就是主程序代码

    segname :该section所属的 segment名,第一个是__TEXT

    addr : 该section在内存的起始位置,0xa588

    size: 该section的大小,0x84a

    offset: 该section的文件偏移

    align :字节大小对齐  4

    reloff :重定位入口的文件偏移  0

    nreloc: 需要重定位的入口数量   0

    flags:包含section的type和attributes,S_REGULAR—This section has no particular type. The standard tools create a __TEXT,__text section of this type.

    iOS逆向基础Mach-O文件(1)https://www.jianshu.com/p/9ea897aeea7c

    iOS逆向基础砸壳原理(2)https://www.jianshu.com/p/f79567470330

    iOS逆向基础动态库注入实现过程(3)https://www.jianshu.com/p/98a226226c70

    iOS逆向基础Hopper+LLDB调试(4)https://www.jianshu.com/p/ab66c140662b


    作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这是一个我的iOS交流群:687528266,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!

    写有不足的地方,可以联系晓雯的微信。请各位大佬多多指教!!!

    相关文章

      网友评论

        本文标题:iOS逆向基础Mach-O文件(1)

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