Mach-O文件格式

2020-09-28  本文已影响0人  答案不止一个

Mach-O文件结构

Mach-O(Mach Object)是一种基于Mach内核的文件格式。iOS系统生成的可执行程序或者动态库文件的存储布局格式被称之为mach-o格式。Mach-O可以有一个或多个架构。多个架构的称为FAT文件。Mach-O文件结构包括

  1. Mach-O header 文件的目标基本信息,包含架构例如PPC,PPC64,IA-32或x86-64等信息
  2. Load Command 文件的逻辑结构和文件在虚拟内存中的布局。可以根据他找到相关的Section 原始数据。mach-o文件由诸多的load command组成,,每个load command所代表的是一种数据类型。每种load command都是结构体struct load_command的扩展结构体。
  3. Section 节:每个段则由多个节(Section)组成。节是内容分类的最小管理单元。每个节的描述信息是一个称之为:struct section的结构体。每个节有一个唯一的名称用来标识这个节。
image

ASLR技术

ASLR,全称是Address Spce Layout Randomization,地址空间布局随机化,是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,增加了攻击者预测目的地址的难度,防止攻击者直接定位代码位置,阻止溢出攻击。这种技术会使得每个程序或者库每次运行加载到内存中时的基地址都不是固定而是随机的,这种机制会增加黑客的破解难度。

iOS中,Mach-O文件 load Commonds 中LC_DYLD_INFO或者LC_DYLD_INFO_ONLY 就是用来记录所有需要进行地址调整的位置。这样当程序被加载到内存时,加载器就会将需要调整的地址分别进行调整处理,以便转化为真实的内存地址。这个过程称之为基地址重定向(rebase)。

Header

mach-o/loader.h 。使用otool -h -v 命令查看Mach-O文件头。

$ otool -h -v  TestMachO
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00     EXECUTE    40       5256   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE

对于FAT文件。Fat文件的header,最前面时FatHeader, 接着是各架构的arch

struct fat_header {
    unsigned long   magic;      /* FAT_MAGIC */
    unsigned long   nfat_arch;  /* number of structs that follow */
};

struct fat_arch {
    cpu_type_t  cputype;    /* cpu specifier (int) */
    cpu_subtype_t   cpusubtype; /* machine specifier (int) */
    unsigned long   offset;     /* file offset to this object file */
    unsigned long   size;       /* size of this object file */
    unsigned long   align;      /* alignment as a power of 2 */
};

单架构的 Mach-O header的定义

struct mach_header_64 {
    uint32_t    magic;      /* mach magic number identifier */
    cpu_type_t  cputype;    /* cup 架构 cpu specifier */
    cpu_subtype_t   cpusubtype; /*  machine specifier */
    // 文件类型常见有的MH_OBJECT(目标文件)、MH_EXECUTABLE(可执行二进制文件)、MH_DYLIB (动态库)。
    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 */
};
mach-o Header.png

Load Command

Load Command告诉操作系统应当如何加载文件中的数据,对系统内核加载器和动态链接器起指导作用。使用MachOView查看LoadCommand

mach-o load commands.png
常见的Segment
  1. LC_SEGMENT_64: 64 位segment 的映射。64-bit segment of this file to be mapped.

  2. LC_DYLD_INF0_0NLY:记录了有关链接的重要信息,包括在_LINKEDIT中动态链接相关信息的具体偏移和大小。ONLY表示这个加载指令是程序运行所必需的。

  3. LC_SYMTAB:为文件定义符号表和字符串表。在链接文件时被链接器使用,同时也用于调试器映射符号到源文件。符号表定义的本地符号仅用于调试,而已定义和未定义的 external符号被链接器使用。

  4. LC_DYSYMTAB:将符号表中给出符号的额外符号信息提供给动态链接器。

  5. LC_LOAD_DYLINKER:默认的加载器路径。

  6. LC.UUID:用于标识Mach-0文件的ID,也用于崩溃堆栈和符号文件的对应解析。

  7. LC_VERSION_MIN_IPHONEOS:系统要求的最低版本。

  8. LC.SOURCE.VERSION:构建二进制文件的源代码版本号。

  9. LC.MAIN:程序的入口。dykl获取该地址,然后跳转到该处执行。

  10. LC_ENCRYPTION_INFO_64:文件是否加密的标志,加密内容的偏移和大小。

  11. LC_LOADJDYLIB:依赖的动态库,包括动态库名称、当前版本号、兼容版本号。可以使用 “otool-Lxxx”命令查看。

$ otool -L TestMachO
SDSAD:
    /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1751.108.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.0.0)
    ...
  1. LC_RPATH: Runpath Search Paths, @rpath
  2. LC_FUNCTION_STARTS:函数起始地址表,使调试器和其他程序能很容易地看到一个地
    址是否在函数内。
  3. LC_DATA_IN_CODE:定义在代码段内的非指令的表。 • LC_CODE_SIGNATURE:代码签名信息。
LC_Segment_64

segment 的结构定义:

struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;        /* LoadCommand类型 */
    uint32_t    cmdsize;    /* LoadCommand结构的大小  */
    char        segname[16];    /* segment name */
    uint64_t    vmaddr;     /* 对应section的起始地址 。映射到虚拟地址的偏移 */
    uint64_t    vmsize;     /* 映射到虚拟地址的大小 */
    uint64_t    fileoff;    /* 对应于当前架构文件的偏移(注意:是当前架构文件,不是整个FAT文件) */
    uint64_t    filesize;   /* 文件的大小 */
    vm_prot_t   maxprot;    /* 段页面的最高内存保护 */
    vm_prot_t   initprot;   /* 初始内存保护。 */
    uint32_t    nsects;     /* 包含section 的个数 */
    uint32_t    flags;      /* flags 页面标志 */
};

系统将fileoff偏移处filesize大小的内容加载到虚拟内存的vmaddr处,大小为vmsize,Segment页面的权限由initprot进行初始化。它的权限可以动态改变,但是不能超过maxprot的值,例如 _TEXT初始化和最大权限都是可读/可执行/不可写。
Segment 的分类

每一个 Segment 对应着0到多个Section 的数据。LC_Senment_64 的后面位置则存储着 Section64 数据(找到对应的section 原始数据的偏移,大小等)。

Section64

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* 对应Segment 的名称 */
    uint64_t    addr;       /* 映射到虚拟地址的偏移 */
    uint64_t    size;       /* size in bytes of this section */
    uint32_t    offset;     /* file offset of this section */
    uint32_t    align;      /* 字节对其大小 (power of 2) */
    uint32_t    reloff;     /* file offset of relocation entries */
    uint32_t    nreloc;     /*  重定位入口的个数。 */
    uint32_t    flags;      /* flags (section type and attributes)*/
    uint32_t    reserved1;  /* 保留位 (for offset or index) */
    uint32_t    reserved2;  /* reserved (for count or sizeof) */
    uint32_t    reserved3;  /* reserved */
};
Segment _TEXT
Segment _DATA
用于分析Mach-O的工具
// 通过file命令查看文件信息
$ file TestMachO
SDSAD: Mach-O 64-bit executable x86_64

相关链接

上一篇下一篇

猜你喜欢

热点阅读