Mach-O文件格式分析

最近用純Swift參照很是知名的Aspects寫了個Aspect,是基於Runtime進行方法交換,正好以前據說過能夠經過fishhook動態修改 C 語言函數,全部就研究了一下,可是要想看懂fishhook,須要先了解Mach-O,這一塊一直是個人知識盲點,這一次索性花些時間一併消化一下。蘋果源碼查看這裏html

Mach-O簡介

Mach-O,是Mach object文件格式的縮寫,是一種可執行文件、目標代碼、共享程序庫、動態加載代碼和核心dump。是a.out格式的一種替代。Mach-O提供了更強的擴展性,並提高了符號表中信息的訪問速度。ios

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

複製代碼

咱們看到Mach-O有多種文件類型,常見的格式:git

  1. 可執行文件github

  2. objcetbash

    • o 文件(目標文件)
    • .a 靜態庫文件.其實就是N個.o文件的集合
  3. DYLIB: 動態庫文件架構

    • dylib
    • framework
  4. 動態鏈接器,dynamic linkerapp

  5. DSYM:分析APP崩潰信息ide

C 文件 —> 可執行文件

很是推薦 Mach-O 文件一 ,這個也是從這篇文章拿來的。函數

  1. test.c 的 C 文件post

    int main(){
         return 0;
     }
    複製代碼
  2. 編譯一下 clang -c test.c,生成 test.o 文件

  3. 經過 file 命令查看一下 file test.o,能夠看到,test.o 爲 Mach-O 文件,object 文件 test.o: Mach-O 64-bit object x86_64

  4. 經過 clang 連接一下目標文件test.o clang test.o,text.c 就轉變成一個 a.out 的可執行文件

  5. 執行./a.out,轉換執行過程

  6. 執行clang -o test1 test.o,連接 test.0 目標文件,生成 test1 的可執行文件

  7. 執行clang -o test2 test.c,直接一次性將源文件生成 test2 的可執行文件

Screen Shot 2019-06-16 at 2.55.46 PM.png

Screen Shot 2019-06-16 at 2.56.43 PM.png

Mach-O結構

852671-9fde036a1ce9d902.jpg

經過上圖,能夠看出Mach-O主要由如下三部分組成:

  • Mach-O頭部(Mach Header):描述了Mach-O的cpu架構、文件類型以及加載命令等信息。
  • 加載命令(load command):描述了文件中數據的具體組織結構,不一樣的數據類型使用不一樣的加載命令表示。
  • Data:Data中的每一個段(segment)的數據都保存在這裏,段的概念與ELF文件中段的概念相似。每一個段都有一個或多個Section,它們存放了具體的數據與代碼,主要包含代碼、數據,例如符號表,動態符號表等等。

** 來用MachOView驗證一下該示例的MachO文件結構:**

d5833e6da709a578cfffed4352783f2a926ed76b.png

簡單瀏覽mach-o可執行文件,具體能夠分爲幾個部分:

  • 文件頭 mach64 Header
  • 加載命令 Load Commands
  • 文本段 __TEXT
  • 數據段 __TEXT
  • 動態庫加載信息 Dynamic Loader Info
  • 入口函數 Function Starts
  • 符號表 Symbol Table
  • 動態庫符號表 Dynamic Symbol Table
  • 字符串表 String Table

mach_header_64

/*
 * The 64-bit mach header appears at the very beginning of object files for
 * 64-bit architectures.
 */
struct mach_header_64 {
	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 */
	uint32_t	reserved;	/* reserved */
};

複製代碼
  • magic:魔數,系統加載器經過該字段快速,判斷該文件是用於32位or64位。
  • cputype:標識CPU的架構,好比ARM,X86,i386等等,該字段確保系統能夠將適合的二進制文件在當前架構下運行。
  • cpusubtype::具體的CPU類型,區分不一樣版本的處理器,好比arm6四、armv7。
  • filetype::說明該mach-o文件類型(可執行文件,庫文件,核心轉儲文件,內核擴展,DYSM文件,動態庫)。
  • ncmds:LoadCommands數量,每一個LoadCommands表明了一種Segment的加載方式。
  • sizeofcmds:全部Load commands的總字節大小。
  • flags:標識二進制文件支持的功能,主要與系統的加載、連接有關。
  • reserved:保留字段。

Load commands

Load commands跟在mach_header以後。全部命令的總大小由mach_header中的sizeofcmds字段給出。oad commands必須有前兩個字段cmd和cmdsize。cmd字段以該命令類型的常量填充。每一個命令類型都有專門針對它的結構。cmdsize字段是特定加載命令結構的字節大小加上跟隨它的任何一部分,這是加載命令(即節結構、字符串等)的一部分。爲了前進到下一個加載命令,cmdsize能夠被添加到當前加載命令的偏移量或指針中。32位架構的cmdsize必須是4字節的倍數,對於64位架構必須是8字節的倍數(這些永遠是任何加載命令的最大對齊)。填充的字節必須爲零。目標文件中的全部表也必須遵循這些規則,以便文件能夠進行內存映射。不然,這些表的指針在某些機器上沒法正常工做或根本沒法正常工做。全部padding歸零像對象將比較逐字節。

/*
 * The segment load command indicates that a part of this file is to be
 * mapped into the task's address space. The size of this segment in memory, * vmsize, maybe equal to or larger than the amount to map from this file, * filesize. The file is mapped starting at fileoff to the beginning of * the segment in memory, vmaddr. The rest of the memory of the segment, * if any, is allocated zero fill on demand. The segment's maximum virtual
 * memory protection and initial virtual memory protection are specified
 * by the maxprot and initprot fields.  If the segment has sections then the
 * section structures directly follow the segment command and their size is
 * reflected in cmdsize.
 */
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 */
};
複製代碼
  • cmd:Load commands的類型,全部這些加載命令由系統內核加載器直接使用。常見的有下面幾種:
    • LC_SEGMENT:表示這是一個段加載命令,須要將它加載到對應的進程空間上。
    • LC_LOAD_DYLIB:這是一個須要動態加載的連接庫,它使用dylib_command結構體表示。
    • LC_MAIN:記錄了可執行文件的主函數main()的位置,它使用entry_point_command結構體表示。
    • LC_CODE_SIGNATURE:代碼簽名的加載命令,描述了Mach-O的代碼簽名信息,它屬於連接信息,使用linkedit_data_command結構體表示。
  • cmdsize:Load command的大小。
  • segname[16]:16字節的段名字。
  • vmaddr:段的虛擬內存起始地址。
  • vmsize:段的虛擬內存大小。
  • fileoff:段在文件中的偏移量。
  • filesize:段在文件中的大小。
  • maxprot:段頁面所須要的最高內存保護(4=r,2=w,1=x)。
  • initprot:段頁面初始的內存保護。
  • nsects:段中包含section的數量。
  • flags:標識符。

Section數據

部分的 Segment (主要指的 __TEXT 和 __DATA)能夠進一步分解爲 Section。之因此按照 Segment -> Section 的結構組織方式,是由於在同一個 Segment 下的 Section,能夠控制相同的權限,也能夠不徹底按照 Page 的大小進行內存對其,節省內存的空間。而 Segment 對外總體暴露,在程序載入階段映射成一個完整的虛擬內存,更好的作到內存對齊。

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[16]:好比__text、stubs,第一個是__text,就是主程序代碼。
  • segname[16]:該section所屬的segment,好比__TEXT。
  • addr:該section在內存的啓始位置。
  • size:該section的大小。
  • offset:該section的文件偏移。
  • align:字節大小對齊。
  • reloff:重定位入口的文件偏移。
  • nreloc:須要重定位的入口數量。
  • flags:包含section的type和attributes。
  • reserved1:保留字段1 (for offset or index)。
  • reserved2:保留字段2 (for count or sizeof)。
  • reserved3:保留字段3。

段的命名規則是兩個下劃線緊跟着大寫字母(如__TEXT),而section的命名則是兩個下劃線緊跟着小寫字母(__text)。

下面列出段中可能包含的section:

  • __TEXT段:
    __text, __cstring, __picsymbol_stub, __symbol_stub, __const, __litera14, __litera18;
  • __DATA段

__data, __la_symbol_ptr, __nl_symbol_ptr, __dyld, __const, __mod_init_func, __mod_term_func, __bss, __commom;

  • __IMPORT段

__jump_table, __pointers;

其中__TEXT段中的__text是實際上的代碼部分;__DATA段的__data是實際的初始數據。

關於Mach-o文件格式就講完了,若是對程序從加載到執行過程感興趣能夠看Mach-O文件格式和程序從加載到執行過程趣探 Mach-O:加載過程,講的很詳細。

MachO 文件結構詳解
Mach-O 文件一
Mach-O文件格式和程序從加載到執行過程
iOS逆向基礎Mach-O文件(1)
Mach-O 文件格式探索

相關文章
相關標籤/搜索