了解 Mach-O 文件

什么是 Mach-O

????Mach-O 其實(shí)是 Mach Object 文件格式的縮寫,它是一種用于可執(zhí)行文件、目標(biāo)代碼、動(dòng)態(tài)庫(kù)的文件格式,作為 a.out 格式的替代, Mach-O 提供了更強(qiáng)的擴(kuò)展性。在 OS X 中,內(nèi)核擴(kuò)展、命令行工具、應(yīng)用程序、框架和庫(kù)(共享和靜態(tài))是使用 Mach-O 文件實(shí)現(xiàn)的。可以在 Mach-O Programming Topics 查看相關(guān)介紹。

分析 Mach-O 文件的工具

  • /usr/bin/lipo
    查看二進(jìn)制文件信息,可以生成或者拆分多架構(gòu)文件
  • /usr/bin/file
    顯示文件的類型。對(duì)于多架構(gòu)文件,它會(huì)顯示構(gòu)成存檔的每個(gè)圖像的類型。
  • /usr/bin/otool
    列出了 Mach-O 文件中特定部分和段的內(nèi)容。它包括每個(gè)支持的體系結(jié)構(gòu)的符號(hào)反匯編器,并且它知道如何格式化許多常見(jiàn)節(jié)類型的內(nèi)容。
  • /usr/bin/pagestuff
    顯示關(guān)于組成圖像的每個(gè)邏輯頁(yè)面的信息,包括每個(gè)頁(yè)面中包含的部分和符號(hào)的名稱。此工具不適用于包含多個(gè)架構(gòu)的圖像的二進(jìn)制文件。
  • /usr/bin/nm
    允許查看目標(biāo)文件符號(hào)表的內(nèi)容。
  • MachoView.app
    MachoView.app

    在查看 Mach-O 文件之前,有必要了解MachOView.app 工具,MachOView工具屬于免費(fèi)開(kāi)源項(xiàng)目,源代碼可在Github-MachOView下載,可以在 Mac 中查看 Mach-O文件的詳細(xì)信息

Mach-O 文件結(jié)構(gòu)

一個(gè) Mach-O 文件包含三個(gè)主要區(qū)域(如下所示):

  • Header:指定文件的目標(biāo)架構(gòu)
  • Load commands :指定文件的邏輯結(jié)構(gòu)和文件在虛擬內(nèi)存中的布局。
  • Raw segment data:包含加載命令中定義的段的原始數(shù)據(jù)。
Apple Mach-O 文件結(jié)構(gòu)

當(dāng)然,可以通過(guò) MachOView 對(duì) App 可執(zhí)行文件進(jìn)行查看,Mach-O 文件里面的內(nèi)容如下圖所示:

Mach-O 文件結(jié)構(gòu)

Mach64 Header

每個(gè) Mach-O 文件的開(kāi)頭都有一個(gè) Header ,用于將文件標(biāo)識(shí)為 Mach-O 文件。Header 包含該二進(jìn)制文件的一般信息。字節(jié)順序、架構(gòu)類型、加載指令的數(shù)量等。使得可以快速確認(rèn)一些信息,比如當(dāng)前文件用于32位還是64位,對(duì)應(yīng)的處理器是什么、文件類型是什么

Mach64 Header 結(jié)構(gòu)

macho/loader.h 中,可以查看到 Mach-O header 信息結(jié)構(gòu)代碼如下:


struct mach_header_64 {
    uint32_t        magic;      // 64位還是32位
    cpu_type_t      cputype;  
    cpu_subtype_t   cpusubtype; // CPU 子類型,比如 armv8 CPU_SUBTYPE_ARM_64
    uint32_t        filetype;   // 文件類型 MH_EXECUTE
    uint32_t        ncmds;      // load commands 的數(shù)量
    uint32_t        sizeofcmds; // load commands 大小
    uint32_t        flags;      // 標(biāo)簽
    uint32_t        reserved;   // 保留字段
};

????如上面代碼所示,包含了

  • magic
    表示是 64 位還是 32 位的、
  • cputype
    CPU 類型,比如 CPU_TYPE_ARM,可以在 macho/machine.h 文件中查看其他。
  • cpusubtype
    CPU 子類型,比如 CPU_SUBTYPE_ARM64_ALL,可以在 macho/machine.h 文件中查看其他。
  • filetype
    文件類型
  • ncmds
    load commands 的數(shù)量和大小等文件信息
  • sizeofcmds
    load commands 大小
  • flags
    標(biāo)志位,標(biāo)識(shí)二進(jìn)制文件支持的功能。
  • reserved
    保留字段

Mach-O 文件類型

其中,文件類型 filetype 表示了當(dāng)前 Mach-O 屬于哪種類型。下面源代碼列舉了 filetype 的全部類型:

/*
* Constants for the filetype field of the mach_header
 */
#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 */
#define MH_FILESET 0xc  /* a file composed of other Mach-Os to
        be run in the same userspace sharing
        a single linkedit. */

其中,部分類型是我們開(kāi)發(fā)中常用的:

  • OBJECT,指的是 .o 文件或者 .a 文件;
  • EXECUTE,指的是 IPA 拆包后的可執(zhí)行文件;
  • DYLIB,指的是 .dylib 或 .framework 文件;
  • DYLINKER,指的是動(dòng)態(tài)鏈接器;
  • DSYM,指的是保存有符號(hào)信息用于分析閃退信息的文件。

Load Commands

緊隨 Header 的是一系列的 Load Commands ,負(fù)責(zé)描述文件在虛擬內(nèi)存中邏輯結(jié)構(gòu)和布局,這些加載指令清晰地告訴加載器如何處理二進(jìn)制數(shù)據(jù),有些命令是由內(nèi)核處理的,有些是由動(dòng)態(tài)鏈接器處理的。除了其他信息,Load Commands 可以指定:

  • 文件在虛擬內(nèi)存中的初始布局

  • 符號(hào)表的位置(用于動(dòng)態(tài)鏈接)

  • 程序主線程的初始執(zhí)行狀態(tài)

  • 包含主可執(zhí)行文件的導(dǎo)入符號(hào)定義的共享庫(kù)的名稱

Load Commands結(jié)構(gòu)

macho/loader.h 中,可以查看到 load_command 信息結(jié)構(gòu)代碼如下:

struct load_command {
 uint32_t cmd;  /* type of load command */
 uint32_t cmdsize; /* total size of command in bytes */
};

Load Commands 類型

下面列舉了 load commands 全部的類型:


/* Constants for the cmd field of all load commands, the type */
#define LC_SEGMENT 0x1 /* segment of this file to be mapped */
#define LC_SYMTAB 0x2 /* link-edit stab symbol table info */
#define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */
#define LC_THREAD 0x4 /* thread */
#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */
#define LC_LOADFVMLIB 0x6 /* load a specified fixed VM shared library */
#define LC_IDFVMLIB 0x7 /* fixed VM shared library identification */
#define LC_IDENT 0x8 /* object identification info (obsolete) */
#define LC_FVMFILE 0x9 /* fixed VM file inclusion (internal use) */
#define LC_PREPAGE      0xa     /* prepage command (internal use) */
#define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */
#define LC_LOAD_DYLIB 0xc /* load a dynamically linked shared library */
#define LC_ID_DYLIB 0xd /* dynamically linked shared lib ident */
#define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */
#define LC_ID_DYLINKER 0xf /* dynamic linker identification */
#define LC_PREBOUND_DYLIB 0x10 /* modules prebound for a dynamically */
    /*  linked shared library */
#define LC_ROUTINES 0x11 /* image routines */
#define LC_SUB_FRAMEWORK 0x12 /* sub framework */
#define LC_SUB_UMBRELLA 0x13 /* sub umbrella */
#define LC_SUB_CLIENT 0x14 /* sub client */
#define LC_SUB_LIBRARY  0x15 /* sub library */
#define LC_TWOLEVEL_HINTS 0x16 /* two-level namespace lookup hints */
#define LC_PREBIND_CKSUM  0x17 /* prebind checksum */

/*
 * load a dynamically linked shared library that is allowed to be missing
 * (all symbols are weak imported).
 */
#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)

#define LC_SEGMENT_64 0x19 /* 64-bit segment of this file to be
       mapped */
#define LC_ROUTINES_64 0x1a /* 64-bit image routines */
#define LC_UUID  0x1b /* the uuid */
#define LC_RPATH       (0x1c | LC_REQ_DYLD)    /* runpath additions */
#define LC_CODE_SIGNATURE 0x1d /* local of code signature */
#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */
#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */
#define LC_LAZY_LOAD_DYLIB 0x20 /* delay load of dylib until first use */
#define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */
#define LC_DYLD_INFO  0x22 /* compressed dyld information */
#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */
#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */
#define LC_VERSION_MIN_MACOSX 0x24   /* build for MacOSX min OS version */
#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */
#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */
#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat
        like environment variable */
#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
#define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */
#define LC_SOURCE_VERSION 0x2A /* source version used to build binary */
#define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */
#define LC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */
#define LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */
#define LC_LINKER_OPTIMIZATION_HINT 0x2E /* optimization hints in MH_OBJECT files */
#define LC_VERSION_MIN_TVOS 0x2F /* build for AppleTV min OS version */
#define LC_VERSION_MIN_WATCHOS 0x30 /* build for Watch min OS version */
#define LC_NOTE 0x31 /* arbitrary data included within a Mach-O file */
#define LC_BUILD_VERSION 0x32 /* build for platform min OS version */
#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) /* used with linkedit_data_command, payload is trie */
#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) /* used with linkedit_data_command */
#define LC_FILESET_ENTRY (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */

通過(guò) MachOView 來(lái)繼續(xù)查看 LoadCommands 內(nèi)容

MachOView 中查看的 LoadCommands

對(duì)應(yīng)的描述如下表格:

load commands Type 描述
LG_SEGMENT_64 將文件中(32位或64位)的段映射到進(jìn)程地址空間中
LC_DYLD_INFO_ONLY 動(dòng)態(tài)鏈接相關(guān)信息 rebase、bind
LC_SYMTAB 符號(hào)表
LC_DYSYMTAB 動(dòng)態(tài)符號(hào)表
LC_UUID 文件的 UUID
LC_VERSION_MIN_IPHONES 支持最低的操作系統(tǒng)版本
LC_SOURCE_VERSION 源代碼版本
LC_MAIN 程序入口地址和棧大小
LC_LOAD_DYLIB 依賴系統(tǒng)庫(kù)路徑
LC_RPATH 運(yùn)行時(shí)優(yōu)先搜索依賴庫(kù)的目錄路徑
LC_FUNCTION_STARTS 函數(shù)起始地址表
LC_CODE_SIGNATURE 代碼簽名

Segment_Command

LC_SEGMENT_64LC_SEGMENT 是加載的主要命令,它負(fù)責(zé)指導(dǎo)內(nèi)核來(lái)設(shè)置進(jìn)程的內(nèi)存空間,下面是

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

  • cmdsize
    代表 load command 的大小
  • segname
    段名稱
    • __PAGEZERO 作為可執(zhí)行文件的第一個(gè)段。該段位于虛擬內(nèi)存位置 0 并且沒(méi)有分配保護(hù)權(quán)限。該__PAGEZERO段是當(dāng)前體系結(jié)構(gòu)的一個(gè)完整 VM 頁(yè)面的大小。因?yàn)?__PAGEZERO 段中沒(méi)有數(shù)據(jù),所以不占用文件中的空間
    • __TEXT 對(duì)應(yīng)的就是代碼段
    • __DATA 對(duì)應(yīng)的是可讀/可寫的數(shù)據(jù)
    • __OBJC 包含由 Objective-C 語(yǔ)言運(yùn)行時(shí)支持庫(kù)使用的數(shù)據(jù)
    • __LINKEDIT 是支持dyld的,里面包含一些符號(hào)表等數(shù)據(jù)
    • __IMPORT 包含符號(hào)存根和指向未在可執(zhí)行文件中定義的符號(hào)的非惰性指針。此段僅為針對(duì) IA-32 架構(gòu)的可執(zhí)行文件生成。
  • VM Address
    段的虛擬內(nèi)存地址
  • VM Size
    段的虛擬內(nèi)存大小
  • file offset
    段在文件中偏移量
  • file size:段在文件中的大小
    將該段對(duì)應(yīng)的文件內(nèi)容加載到內(nèi)存中:從offset處加載 file size大小到虛擬內(nèi)存 vmaddr處
  • nsects
    標(biāo)示了Segment中有多少secetion

通過(guò) MachOView 查看 TEXT 段 內(nèi)容

MachOView 中 LG_SEGMENT_64(_TEXT)

Raw segment data

Load Commands 之后,所有的 Mach-O 文件都包含一個(gè)或多個(gè) Segment 的數(shù)據(jù)。Segment 的每個(gè)部分都包含某種特定類型的代碼或數(shù)據(jù),每個(gè) Segment 定義了一個(gè)虛擬內(nèi)存區(qū)域,動(dòng)態(tài)鏈接器將其映射到進(jìn)程的地址空間。Segments 和 Sections 的確切數(shù)量和布局由加載命令和文件類型指定

Section_64 結(jié)構(gòu)

macho/loader.h 中,可以查看到 section_64 信息結(jié)構(gòu)代碼如下:

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(主程序),__DATA(數(shù)據(jù)段)
  • addr
    該 section在內(nèi)存的起始位置
  • size
    該 section 的大小
  • offset
    該 section 的文件偏移
  • align
    字節(jié)大小對(duì)齊
  • reloff
    重定位入口的文件偏移
  • nreloc
    需要重定位的入口數(shù)量
  • flags
    包含 section 的 type 和 attributes

Section 的類型

名稱 描述 所屬部分
__TEXT,__text 可執(zhí)行機(jī)器碼。編譯器通常只在此部分中放置可執(zhí)行代碼,而不放置任何類型的表或數(shù)據(jù)。 __TEXT
__TEXT,__cstring 常量 C 字符串。靜態(tài)鏈接器在構(gòu)建最終產(chǎn)品時(shí)合并常量 C 字符串值,刪除重復(fù)項(xiàng)。 __TEXT
__TEXT,__picsymbol_stub 與位置無(wú)關(guān)的間接符號(hào)存根 __TEXT
__TEXT,__symbol_stub 間接符號(hào)存根。 __TEXT
__TEXT,__const 初始化常量。 __TEXT
__TEXT,__literal4 4 字節(jié)字面量 __TEXT
__TEXT,__literal8 8 字節(jié)字面量 __TEXT
__DATA,__data 已初始化的可變變量 __DATA
__DATA,__la_symbol_ptr 懶加載符號(hào)指針 __DATA
__DATA,__nl_symbol_ptr 非懶加載符號(hào)指針 __DATA
__DATA,__dyld 動(dòng)態(tài)鏈接器使用的占位符部分 __DATA
__DATA,__const 初始化的可重定位常量 __DATA
__DATA,__mod_init_func 靜態(tài)構(gòu)造函數(shù) __DATA
__DATA,__mod_term_func 模塊終止功能 __DATA
__DATA,__bss 未初始化靜態(tài)變量的數(shù)據(jù) __DATA
__DATA,__common 位于全局范圍內(nèi)的變量聲明 __DATA
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載自 http://www.blogfshare.com/ioss-mach-o.html[http://www...
    廚子閱讀 415評(píng)論 0 1
  • 一、前言 本文簡(jiǎn)要解析Mach-O文件格式、結(jié)構(gòu),主要是自己認(rèn)識(shí)Mach-O文件,學(xué)習(xí)的一個(gè)過(guò)程,一些地方可能介紹...
    KinKen閱讀 1,662評(píng)論 0 4
  • Mach-o文件 Mach-O 是iOS/macOS系統(tǒng)上應(yīng)用程序的格式 通用二進(jìn)制文件(胖二進(jìn)制文件) 因?yàn)镸a...
    CharType閱讀 1,015評(píng)論 0 2
  • Mach-O文件簡(jiǎn)介 Mach-O 是Mach Object文件格式的縮寫,是運(yùn)用于mac以及iOS上;它是一種用...
    新生代農(nóng)民工No1閱讀 549評(píng)論 0 5
  • 最近嘗試做符號(hào)分析及靜態(tài)鏈接相關(guān)方面的探索,發(fā)現(xiàn)離不開(kāi)對(duì)Mach-O文件結(jié)構(gòu)的分析。所以這里整理一篇筆記,來(lái)梳理下...
    Super超人閱讀 1,985評(píng)論 0 8