Mach-O 學(xué)習(xí)小結(jié)(一)

最近學(xué)習(xí)了一下 Mach-O ,這里做個筆記記錄确虱,整理思路,加深理解。
原文出處 Valar Morghulis 的博客
本文修改了原文幾處錯誤。

概述

第一章 描述了 Mach-O 文件的基本結(jié)構(gòu)幌羞;
第二章 概述了符號,分析了符號表(symbol table)竟稳。
第三章 探尋動態(tài)鏈接属桦。
第四章 分析fishhook。
第五章 分析BeeHive他爸。
第六章 App啟動時間聂宾。

結(jié)構(gòu)分析

關(guān)于 Mach-O 的文件格式,在網(wǎng)上常痴矬裕看到如下這張圖亏吝,出自官方文檔《OS X ABI Mach-O File Format Reference》:

mach-o-structure.png

通過這張圖,可以看到盏混,從布局上,Mach-O 文件分為三個部分:Header惜论、Load Commands许赃、Data。但這張圖過于簡略馆类,信息不完善混聊,可能會讓人困惑。先簡單分析 Header 和 Load Commands 的結(jié)構(gòu)吧乾巧!

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

32位和64位分別對應(yīng)了不同的結(jié)構(gòu)句喜,但大同小異预愤,這里以64位為例。

mach-o/loader.hstruct mach_header 定義了 Header 的結(jié)構(gòu):

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

從圖中可以看出咳胃,Header的結(jié)構(gòu)是固定的植康,size固定為32 bytes(64位架構(gòu))

每個字段的意思,如下:

mach-header.png

magic展懈,系統(tǒng)內(nèi)核用來判斷是否是 mach-o 格式销睁。

// magic 
#define MH_MAGIC    0xfeedface  /* 32位大端字節(jié)序*/
#define MH_CIGAM    0xcefaedfe  /* 32位小端字節(jié)序 */
#define MH_MAGIC_64 0xfeedfacf  /* 64位大端字節(jié)序 */
#define MH_CIGAM_64 0xcffaedfe  /* 64位小端字節(jié)序 */

filetype,描述了二進(jìn)制文件的類型存崖,包括了十來個有效值冻记,常打交道的包括:

#define MH_OBJECT      0x1    // 中間目標(biāo)文件,例如.o文件
#define MH_EXECUTE     0x2    // 可執(zhí)行文件
#define MH_DYLIB       0x6    // 動態(tài)鏈接庫
#define MH_DYLINKER    0x7    // 動態(tài)鏈接器

flags 是雜項来惧,通常它包含的信息用于為動態(tài)鏈接器服務(wù)冗栗,告訴后者如何工作。

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

Load Commands 可以被看作是一個 command 列表供搀,緊貼著 Header隅居,所以它的 file offset 是固定的:0x20(上面說的header的size為32 bytes,即0x20)趁曼。一共有哪些 load commands 呢军浆?Load commands 由內(nèi)核定義,不同版本的 command 數(shù)量不同挡闰,本文所參考的內(nèi)核乒融,一共定義了 50+ load commands,它們的 type 是以LC_為前綴常量摄悯,譬如 LC_SEGMENT赞季、LC_SYMTAB 等。

每個 command 都有獨立的結(jié)構(gòu)奢驯,但所有 command 結(jié)構(gòu)的前兩個字段是固定的:

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

第一個字段指定了類型申钩,第二個字段指定了大小,確保它能被正確解析瘪阁。

這里只講其中的一個 load command 撒遣,LC_SEGMENT_64,因為它和 segment管跺、section 有關(guān)义黎;命令格式如下:

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

它描述了文件映射的兩大問題:從哪里來(fileofffilesize)豁跑、到哪里去(vmaddr廉涕、vmsize);它還告訴了內(nèi)核該區(qū)域的名字(segname,即 segment name)狐蜕,以及該區(qū)域包含了幾個 section(nsects)宠纯,以及該區(qū)域的保護(hù)級別(initprotmaxprot)层释。

  • 每一個 segment 的 VP (Virtual Page) 都根據(jù) initprot 進(jìn)行初始化婆瓜,initprot 指定了如何通過 讀/寫/可執(zhí)行 初始化頁面的保護(hù)級別;segment 的保護(hù)設(shè)置可以動態(tài)改變湃累,但是不能超過 maxprot 中指定的值(在 iOS 中勃救,+x(可執(zhí)行) 和+w(可寫) 是互斥的);initprot治力、maxprot 的值均用八進(jìn)制表示(1=r蒙秒,2=w,4=x)
  • flags 是雜項標(biāo)志位
  • vmsize 并不等于 filesize宵统,對于 4KB 大小的 VP晕讲,vmsize 是 4K 的倍數(shù);換句話說马澈,vmsize 一般大于 segment 的實際大小

對于 segment 而言瓢省,有了這些信息,其結(jié)構(gòu)其實就足夠清晰了痊班,但是如何知道其中各個 sections 的具體位置和 size 呢勤婚?

對于 LC_SEGMENT_64 而言,如果其nsects字段大于 0涤伐,其命令后面還會緊接著掛載nsects個描述 section 的信息馒胆,這些信息是結(jié)構(gòu)體section_64的列表,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 */
};

結(jié)構(gòu)體section_64可以看做 section header凝果,它描述了對應(yīng) section 的具體位置祝迂,以及要被映射的目標(biāo)虛擬地址。

回頭再看segment_command_64cmdsize字段器净,它的數(shù)值是segment_command_64的 size 大小型雳,加上緊接在 command 后面的所有section_64結(jié)構(gòu)體的大小。

舉個例子山害,如果 segment 含有 2 個 section纠俭,那么對應(yīng)的 segment_command_64 的 cmdsize 值為:

72(segment_command_64本身大小) + 2 * 80(section_64的大欣嘶拧) = 232 bytes
eg

這里應(yīng)該明白 segment 和 section :Mach-O 本沒有 segment柑晒,有了 LC_SEGMENT_64,于是有了 segment眷射。
segment 是一段連續(xù)的內(nèi)存地址,一般來說擁有相同的讀、寫妖碉、執(zhí)行權(quán)限涌庭,segment都是頁對齊的。section 不是頁對齊的欧宜。

更多Load Commond作用及相關(guān)定義坐榆,可以參考 聊聊 Mach-O 文件格式

Data的結(jié)構(gòu)

和 Header冗茸、Load Commands 不同席镀,Mach-O 對 Data 區(qū)域沒有任何公共的結(jié)構(gòu)上的定義。它里面盛裝的字節(jié)本來沒有意義夏漱,有了 LC_SEGMENT_64 以及其他的 load commands豪诲,一切才開始有了意義。

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

結(jié)合上面的內(nèi)容挂绰,通過一個具體的 case屎篱,綜述一下 Mach-O 的結(jié)構(gòu)。寫一個簡單的 C 文件如下:

#include <stdio.h>

int main(void) {
    printf("Hello, world!\n");
    return 0;
}

執(zhí)行g(shù)cc main.c葵蒂,得到可執(zhí)行文件 a.out交播,使用 MachOView 工具查看,得到如下結(jié)構(gòu):

simple-mach-o-view-demo.png

注意左右標(biāo)紅的部分践付,可以得到的信息:

  • 一共包括三個 segment:__TEXT秦士、__DATA、__LINKEDIT
  • segment 的內(nèi)容范圍并非一定在 Data 區(qū)內(nèi)(譬如 __TEXT segment)
  • 并非每一個 segment 都由 section 組成(譬如 __LINKEDIT segment)

為啥 __TEXT 的地址范圍從 0 開始而非從 _text 這個 section 開始呢永高?《OS X ABI Mach-O File Format Reference》是這么說的:

The header and load commands are considered part of the first segment of the file for paging purposes. In an executable file, this generally means that the headers and load commands live at the start of the __TEXT segment because that is the first segment that contains data.

一個典型的 Mach-O 結(jié)構(gòu)圖的更清晰描述應(yīng)該是這個樣子:

mach-o-structure-mine.png

總結(jié)

這篇文章主要說明了 Mach-O 文件的結(jié)構(gòu)隧土,以及三種結(jié)構(gòu)體mach_header_64segment_command_64乏梁、section_64的結(jié)構(gòu)及其意義:

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

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

//解釋 section
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 */
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末次洼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子遇骑,更是在濱河造成了極大的恐慌卖毁,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件落萎,死亡現(xiàn)場離奇詭異亥啦,居然都是意外死亡,警方通過查閱死者的電腦和手機练链,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門翔脱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人媒鼓,你說我怎么就攤上這事届吁〈硌” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵疚沐,是天一觀的道長暂氯。 經(jīng)常有香客問我,道長亮蛔,這世上最難降的妖魔是什么痴施? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮究流,結(jié)果婚禮上辣吃,老公的妹妹穿的比我還像新娘。我一直安慰自己芬探,他們只是感情好神得,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著灯节,像睡著了一般循头。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上炎疆,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天卡骂,我揣著相機與錄音,去河邊找鬼形入。 笑死全跨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亿遂。 我是一名探鬼主播浓若,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛇数!你這毒婦竟也來了挪钓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤耳舅,失蹤者是張志新(化名)和其女友劉穎碌上,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浦徊,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡馏予,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盔性。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霞丧。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖冕香,靈堂內(nèi)的尸體忽然破棺而出蛹尝,到底是詐尸還是另有隱情后豫,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布箩言,位于F島的核電站硬贯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏陨收。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一鸵赖、第九天 我趴在偏房一處隱蔽的房頂上張望务漩。 院中可真熱鬧,春花似錦它褪、人聲如沸饵骨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽居触。三九已至,卻和暖如春老赤,著一層夾襖步出監(jiān)牢的瞬間轮洋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工抬旺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留弊予,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓开财,卻偏偏與公主長得像汉柒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子责鳍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350