理解Mach-O文件格式(1)

原文地址

寫在之前

之前工作中對Mach-O文件有一定的接觸, 原本早就想寫一篇文章分享一下舅桩,但是奈何只是不夠深入, 總怕分析的有問題誤導(dǎo)讀者。

最近又在閱讀深入解析Mac OS X 與iOS 操作系統(tǒng)担猛,借著這個機(jī)會記錄下自己的學(xué)習(xí)成果, 并結(jié)合之前的經(jīng)驗, 加上一些實例讓讀者更好的理解。
畢竟對于程序員來說 大部分人對抽象的概念的感覺就是 聽說過很多原理, 依然不知道大佬說的是什么

Mac OS 與 iOS 支持的文件類型

Unix-Like系列的操作系統(tǒng), 可以通過命令 chmod +x 給予文件可執(zhí)行權(quán)限, 但是這不代表這個文件具有可執(zhí)行權(quán)限, 實際上 Apple家的操作系統(tǒng)只支持三種文件格式丢氢。

  1. #!開頭的腳本文件
  2. 通用二進(jìn)制文件
  3. Mach-O格式文件

但是實際上 以#!開頭的腳本文件其實是shell解釋器找到后面指定的腳本解釋器來執(zhí)行的, 而通用二進(jìn)制文件其實是多個架構(gòu)的Mach-O文件的打包體傅联。
通用二進(jìn)制文件其實有個更加形象化的名字fat binary
那么操作系統(tǒng)如何知道你打開的文件是何種類型的?
其實是通過這些文件頭的固定數(shù)字來區(qū)分的, 對于這些固定數(shù)字通常叫做 Magic Number(魔數(shù)).

對于fat binary的魔數(shù)是 0xcafebabe(小端)0xbebafeca大端
對于Mach-O的魔數(shù)是 0xfeedface(32位) 0xfeedfacf(64位)

多說無益~~上代碼

我們以/usr/bin/perl為例 (這是一個fat binary)
$ file /usr/bin/perl
/usr/bin/perl: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [i386:Mach-O executable i386]
/usr/bin/perl (for architecture x86_64):    Mach-O 64-bit executable x86_64
/usr/bin/perl (for architecture i386):  Mach-O executable i386
$ otool -vh /usr/bin/perl
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL LIB64     EXECUTE    17       1800   NOUNDEFS DYLDLINK TWOLEVEL PIE

不過可能你覺得拿著系統(tǒng)的命令來看感覺不那么真實, 那么cat命令我們都用過吧疚察,來看下


/usr/include/mach-o/fat.h路徑下有關(guān)于fat binary文件的頭文件定義

struct fat_header {
    uint32_t    magic;      /* FAT_MAGIC or FAT_MAGIC_64 */
    uint32_t    nfat_arch;  /* 包含的架構(gòu)數(shù) */
};

struct fat_arch {
    cpu_type_t  cputype;    /* cpu類型 */
    cpu_subtype_t   cpusubtype; /* 機(jī)器標(biāo)示符  */
    uint32_t    offset;     /* 當(dāng)前架構(gòu)在這個文件中的便宜量 */
    uint32_t    size;       /* 當(dāng)前架構(gòu)在文件中的長度*/
    uint32_t    align;      /* 對齊方式 */
};

不知道大家還記得不記得之前使用windows的時候有System32和64之分, 那是因為在windows操作系統(tǒng)中不同架構(gòu)的可執(zhí)行文件是分開存放的蒸走。

蘋果在某次WWDC大會聲稱自己優(yōu)雅的將多個架構(gòu)合并在了一個文件中。引來果粉一陣鼓掌貌嫡。
其實fat binary文件的真正布局非常簡單比驻。

以/usr/bin/perl為例



Apple的實現(xiàn)只是將不同架構(gòu)的文件并排放在一起,然后在文件頭部添加不同架構(gòu)的描述信息, 然后再加載當(dāng)前架構(gòu)的Mach-O文件 丟棄掉其他架構(gòu)的部分即可岛抄。實在是簡單粗暴~~

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

Unix標(biāo)準(zhǔn)了一個可移植的二進(jìn)制格式ELF但是蘋果并沒有實現(xiàn)它而是維護(hù)了一套NeXTSTEP的遺物 Mach-Object簡稱Mach-O别惦。
但是這并不是說蘋果不遵守POSXI規(guī)范,這個規(guī)范通常說的是源碼級別的跨平臺性夫椭,對于二進(jìn)制則不強(qiáng)制要求掸掸。

下面是一個官方提供的圖片。


Mach-0 Header

先來介紹Mach-O的Header(只介紹64位)信息蹭秋。
相關(guān)頭文件定義在/usr/include/mach-o/loader.h里面扰付。如果需要使用只需要加載<mach-O/loader.h>

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;   /* 文件類型 */
    uint32_t    ncmds;      /* load commadns的個數(shù) */
    uint32_t    sizeofcmds; /* load commands的總大小 */
    uint32_t    flags;      /* 動態(tài)連接器標(biāo)志*/
    uint32_t    reserved;   /* 保留*/
};

/* Constant for the magic field of the mach_header_64 (64-bit architectures) */
#define MH_MAGIC_64 0xfeedfacf /* 小端 */
#define MH_CIGAM_64 0xcffaedfe /* 大端 */


注: Mach-O文件不僅僅是可執(zhí)行文件, 也包括目標(biāo)文件(.o) 動態(tài)庫, Bundle插件等。
標(biāo)志位
flag 標(biāo)記了一些dyld加載 執(zhí)行 中可配置的信息感凤。
關(guān)于Mach-O文件的魔數(shù)信息悯周,有興趣的讀者可以按照之前的方式親自動手嘗試一下

Mach-O Load commands

Mach-O文件中最重要的元信息就是 load Commands,加載命令緊跟在文件頭信息之后陪竿。

//   [_mach_header_|___load_commands___||___load_commands___||____other____|]

struct load_command {
    uint32_t cmd;       /*  load command的類型 */
    uint32_t cmdsize;   /*  command 的長度 */
};

LC_SEGMENT

對于加載命令是LC_SEGMENT的命令指定了內(nèi)核如何設(shè)置新運(yùn)行的進(jìn)程的內(nèi)存空間
對應(yīng)的頭文件也在<mach-o/loader.h>

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;     /* 當(dāng)前segment加載的虛擬內(nèi)存起始地址 */
    uint64_t    vmsize;     /* 當(dāng)前segment加載的虛擬內(nèi)存地址占用的長度  */
    uint64_t    fileoff;    /* segment在文件中的偏移 */
    uint64_t    filesize;   /* segment在文件中的長度 */
    vm_prot_t   maxprot;    /* 最大的保護(hù)級別 */
    vm_prot_t   initprot;   /* 初始化的保護(hù)級別 */
    uint32_t    nsects;     /* 包含sections的個數(shù)  */
    uint32_t    flags;      /* 標(biāo)志位 */
};

由于有了LC_SEGMENT命令禽翼。對于每一個Segment,將文件中偏移量為fileOff長度為filesize的文件內(nèi)容加載到虛擬地址為vmaddr的位置族跛,長度為vmsize, 頁面的權(quán)限通過initprot來初始化(比如設(shè)定讀/寫/執(zhí)行, 段的保護(hù)級別可以動態(tài)設(shè)置最大不超過maxprot

常見的Segment有以下幾個

  1. __TEXT 代碼段
  2. __PAGEZERO 空指針陷阱
  3. __DATA 數(shù)據(jù)段
  4. __LINKEDIT 包含需要被動態(tài)鏈接器使用的信息闰挡,包括符號表、字符串表礁哄、重定位項表等长酗。
  5. __OBJC(現(xiàn)已經(jīng)被合并到__DATA部分)包含會被Objective Runtime使用到的一些數(shù)據(jù)。

當(dāng)然讀者如果有興趣查看其他所有的loadcommands可以去loader.h頭文件定義去查看桐绒,也可以實際操練一下
如 使用otool 查看某些mach-O文件的所有l(wèi)oad_commands

otool -l /bin/ls

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

對于__TEXT, __DATA下面, 又有細(xì)分的各種Section夺脾,常見的如

名稱 作用
TEXT.text 只有可執(zhí)行的機(jī)器碼
TEXT.cstring 硬編碼去重后的C字符串
TEXT.const 初始化過的常量
DATA.data 初始化過的可變的數(shù)據(jù)
DATA.bss 沒有初始化的靜態(tài)變量
DATA.common 沒有初始化過的符號聲明
DATA.objc_clasname oc類名稱
DATA.objc_classlist 類列表
DATA.objc_protocollist 協(xié)議列表

···
其他的就不一一列舉之拨,建議讀者親自動手試一試, 會發(fā)現(xiàn)很多有價值的東西

了解這些有什么用?

相信看了這些內(nèi)容, 你已經(jīng)大致知道Mach-O文件的物理布局, 那么我們知道了這個文件格式能用來做什么呢咧叭?
理解了這個可以用來做下面一些東西:

  1. 依賴解耦
  2. 元信息獲取
  3. 調(diào)試代碼
  4. CI工具插件檢測
  5. 逆向

相關(guān)一些示例放在下篇文章講解蚀乔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市菲茬,隨后出現(xiàn)的幾起案子吉挣,更是在濱河造成了極大的恐慌,老刑警劉巖婉弹,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睬魂,死亡現(xiàn)場離奇詭異,居然都是意外死亡镀赌,警方通過查閱死者的電腦和手機(jī)氯哮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佩脊,“玉大人蛙粘,你說我怎么就攤上這事⊥茫” “怎么了出牧?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長歇盼。 經(jīng)常有香客問我舔痕,道長,這世上最難降的妖魔是什么豹缀? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任伯复,我火速辦了婚禮,結(jié)果婚禮上邢笙,老公的妹妹穿的比我還像新娘啸如。我一直安慰自己,他們只是感情好氮惯,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布叮雳。 她就那樣靜靜地躺著,像睡著了一般妇汗。 火紅的嫁衣襯著肌膚如雪帘不。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天杨箭,我揣著相機(jī)與錄音寞焙,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛捣郊,可吹牛的內(nèi)容都是我干的辽狈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼模她,長吁一口氣:“原來是場噩夢啊……” “哼稻艰!你這毒婦竟也來了懂牧?” 一聲冷哼從身側(cè)響起侈净,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僧凤,沒想到半個月后畜侦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躯保,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年旋膳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片途事。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡验懊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尸变,到底是詐尸還是另有隱情义图,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布召烂,位于F島的核電站碱工,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奏夫。R本人自食惡果不足惜怕篷,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酗昼。 院中可真熱鬧廊谓,春花似錦、人聲如沸麻削。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碟婆。三九已至电抚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間竖共,已是汗流浹背蝙叛。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留公给,地道東北人借帘。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓蜘渣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肺然。 傳聞我的和親對象是個殘疾皇子蔫缸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

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

  • Mach-O 概述 和 部分命令介紹 我們知道Windows下的文件都是PE文件,同樣在OS X和iOS中可執(zhí)行文...
    青花瓷的平方閱讀 14,877評論 2 52
  • 熟悉Linux和windows開發(fā)的同學(xué)都知道际起,ELF是Linux下可執(zhí)行文件的格式拾碌,PE32/PE32+是win...
    Klaus_J閱讀 3,911評論 1 10
  • 這是Mach-O系列的第二篇,趣探 Mach-O:文件格式分析是本文的一個基礎(chǔ) 我們都知道 Mach-O是 OS ...
    Joy___閱讀 11,465評論 9 47
  • linux資料總章2.1 1.0寫的不好抱歉 但是2.0已經(jīng)改了很多 但是錯誤還是無法避免 以后資料會慢慢更新 大...
    數(shù)據(jù)革命閱讀 12,146評論 2 34
  • 本文所讀的源碼街望,可以從這里找到校翔,這是 Mach-O 系列的第一篇 我們的程序想要跑起來,肯定它的可執(zhí)行文件格式要被...
    Joy___閱讀 24,162評論 9 97