iOS逆向学习笔记之--Match-O文件结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wj610671226/article/details/82562764

iOS逆向学习笔记之–Match-O文件结构

Match-O是Mach object的缩写,是Mac/iOS上用于存储程序、库的标准格式,是苹果的可执行文件类型。

利用MachOView查看对应的可执行文件包括:

文件头 mach64 Header
加载命令 Load Commands
文本段 __TEXT
数据段 __DATA
动态库加载信息 Dynamic Loader Info
入口函数 Function Starts
符号表 Symbol Table
动态库符号表 Dynamic Symbol Table
字符串表 String Table
补充:每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间
  • Match-O的Header

    利用otool工具查看Mach-o文件的头部

    macdeiMac:haluo mac$ otool -hv EasyBike.decrypted 
    Mach header
          magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
       MH_MAGIC     ARM         V7  0x00     EXECUTE    59       6016   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
    Mach header
          magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
    MH_MAGIC_64   ARM64        ALL  0x00     EXECUTE    59       6744   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
    
    
    // 头部的结构如下
    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;           
      };
    
    • magic
    魔数,系统加载器通过改字段快速,判断该文件是用于32位or64位。
    //32位魔数
    
    #define    MH_MAGIC    0xfeedface    
    
    
    #define    MH_CIGAM    0xcefaedfe    
    
    
    //64位魔数
    
    #define   MH_MAGIC_64 0xfeedfacf 
    
    
    #define   MH_CIGAM_64 0xcffaedfe
    
    • CPU Type
    CPU类型以及子类型字段,该字段确保系统可以将适合的二进制文件在当前架构下运行。
    
    #define CPU_TYPE_ARM        ((cpu_type_t) 12)
    
    
    #define CPU_TYPE_ARM64   (CPU_TYPE_ARM | CPU_ARCH_ABI64)
    
    • CPU SubType
    CPU指定子类型,对于inter,arm,powerpc等CPU架构,其都有各个阶段和等级的CPU芯片,该字段就是详细描述其支持CPU子类型
    
    #define CPU_SUBTYPE_ARM_V7        ((cpu_subtype_t) 9)
    
    
    #define CPU_SUBTYPE_ARM64_ALL    ((cpu_subtype_t) 0)
    
    
    #define CPU_SUBTYPE_ARM64_V8     ((cpu_subtype_t) 1)
    
    
    • File Type
    说明该mach-o文件类型(可执行文件,库文件,核心转储文件,内核扩展,DYSM文件,动态库等)
    
    #define    MH_OBJECT    0x1        //.o目录文件
    
    
    #define    MH_EXECUTE    0x2     //a.out可主动执行文件
    
    
    #define    MH_DYLIB    0x6     //.dylib文件
    
    
    #define    MH_DSYM        0xa        //.dSYM文件    
    
    
    #define    MH_KEXT_BUNDLE    0xb //.kext驱动文件
    
    • Number of load Commands
      表示加载命令条数

    • Size of Load Commands
      表示加载命令大小

    • Flags

    标志位,该字段用位表示二进制文件支持的功能,主要是和系统加载,链接相关
    
    #define    MH_NOUNDEFS    0x1        // 目前没有未定义的符号,不存在链接依赖
    
    
    #define    MH_DYLDLINK    0x4        // 该文件是dyld的输入文件,无法被再次静态链接
    
    
    #define    MH_PIE 0x200000          // 加载程序在随机的地址空间,只在 MH_EXECUTE中使用
    
    
    #define    MH_TWOLEVEL    0x80      // 两级名称空间
    

这里写图片描述

  • Load Commands 加载命令

Load Commands的作用是告诉操作系统应当如何加载文件中的数据,对系统内核加载器和动态连接器起指导作用。

load command结构如下:
struct load_command {
  uint32_t cmd;        /* type of load command */
  uint32_t cmdsize;    /* total size of command in bytes */
};


// CMD字段含义:
LC_SEGMENT          将文件中(32位或64位)的段映射到进程地址空间中 // LC_SEGMENT_64
LC_SYMTAB           符号表地址
LC_DYSYMTAB         动态符号表地址
LC_DYLD_INFO_ONLY   动态链接相关信息
LC_LOAD_DYLINKER    加载一个动态链接器(动态库加载器),通常路径是“/usr/lib/dyld”
LC_LOAD_DYLIB       加载一个动态链接共享库。如“/usr/lib/libSystem.B.dylib”,这是C标准库。每个库由动态链接器加载并包含一个符号表
LC_UUID             文件的唯一标识,crash解析中也会有该值,去确定dysm文件和crash文件是匹配的
LC_VERSION_MIN_IPHONEOS 二进制文件要求的最低操作系统版本
LC_MAIN             设置程序主线程的入口地址和栈大小
LC_SOURCE_VERSION   构建该二进制文件使用的源代码版本
LC_FUNCTION_STARTS  定义一个函数起始地址表,使调试器和其他程序易于看到一个地址是否在函数内
LC_DATA_IN_CODE     定义在代码段内的非指令数据

a. LC_SEGMENT_64 和 LC_SEGMENT

加载的主要命令,它负责指导内核来设置进程的内存空间
这里写图片描述

snegment数据结构如下:
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 */
};

struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;        /* LC_SEGMENT_64 */
    uint32_t    cmdsize;    /* includes sizeof section_64 structs */
    char        segname[16];    /* segment name */
    uint64_t    vmaddr;        /* memory address of this segment */
    uint64_t    vmsize;        /* memory size of this segment */
    uint64_t    fileoff;    /* file offset of this segment */
    uint64_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 */
};

cmd: 就是Load Command类型,这里LC_SEGMENT_64代表将文件中64位的段映射到进程的地址空间。LC_SEGMENT_64和LC_SEGMENT的结构差别不大
cmdsize: 代表Load commands的大小
segname:    16字节的段名称
vmaddr      段的虚拟内存起始地址
vmsize         段的虚拟内存大小
fileoff     段在文件中的偏移量
filesize       段在文件中的大小
maxprot     段页面的最高内存保护
initprot    初始内存保护
nsects      segment的个数
flags       标志位

b. Segment 和 Section

这里写图片描述

“__TEXT”代表的是Segment, “__text”代表Section
__PAGEZERO:一个全用0填充的段,用于抓取空指针引用(非法内存访问)。这通常不会占用物理内存空
__TEXT: 本段只有可执行代码和其他只读数据

__text          主程序代码
__stub_helper   用于动态库链接的桩的辅助
__cstring       常量字符串符号表描述信息,通过该区信息,可以获得常量字符串符号表地址

__DATA:用于读取和写入数据的一个段

__nl_symbol_ptr     非延迟导入符号指针表。
__la_symbol_ptr     延迟导入符号指针表。

__LINKEDIT:包含给动态链接器的原始数据的段,包括符号和字符串表,压缩动态链接信息,以及动态符号表等

c. Section的数据结构

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) */
};

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];    /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint64_t    addr;        /* memory address of this section */
    uint64_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) */
    uint32_t    reserved3;    /* reserved */
};

sectname:比如__text、__stubs
segname :该section所属的segment,比如__TEXT
addr : 该section在内存的起始位置
size: 该section的大小
offset: 该section的文件偏移
align : 字节大小对齐(以多少字节对齐,一般是2的乘幂)
reloff :重定位入口的文件偏移
nreloc: 需要重定位的入口数量
flags:包含section的type和attributes
reserved: 预留的字段

d. __TEXT段包含的信息

__text              程序可执行的代码区域
__stubs             间接符号存根,跳转到懒加载指针表
__sub_helper        帮助解决懒加载符号加载的辅助函数   
__cstring           只读的C风格字符串,包含OC的部分字符串和属性名
__objc_methname     方法名
__objc_classname    类名
__objc_methtype     方法签名

这里写图片描述

e. __DATA段包含的信息

__la_symbol_ptr     非懒加载指针表,在dyld加载时会立即绑定值
__mod_init_func     初始化函数
__objc_classlist    类列表
__objc_nlclslist    程序中自己实现了+load方法的类
__objc_protolist    协议列表
__objc_classrefs    被引用的类
__objc_ivar         成员变量
__got               非懒加载全局指针表

这里写图片描述

  • 动态库链接信息 LC_DYLD_INFO_ONLY
    根据该加载命令的字段偏移,可以得到压缩动态数据信息区(动态库绑定,地址重定向等信息)
struct dyld_info_command {
 uint32_t   cmd;        /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
 uint32_t   cmdsize;        /* sizeof(struct dyld_info_command) */
 uint32_t   rebase_off;    /* file offset to rebase info  */
 uint32_t   rebase_size;    /* size of rebase info   */
 uint32_t   bind_off;    /* file offset to binding info   */
 uint32_t   bind_size;    /* size of binding info  */      
 uint32_t   weak_bind_off;    /* file offset to weak binding info   */
 uint32_t   weak_bind_size;  /* size of weak binding info  */
 uint32_t   lazy_bind_off;    /* file offset to lazy binding info */
 uint32_t   lazy_bind_size;  /* size of lazy binding infs */ 
 uint32_t   export_off;    /* file offset to lazy binding info */
 uint32_t   export_size;    /* size of lazy binding infs */
};

rebase_off  重定向数据的偏移地址
rebase_size 重定向数据的大小
bind_off    数据绑定
bind_size   数据绑定信息大小
weak_bind   弱绑定
lazy_bind   延时绑定数据
export      对外开放的函数信息

这里写图片描述

猜你喜欢

转载自blog.csdn.net/wj610671226/article/details/82562764