Mach-O 介紹

一、Mach-O 相關(guān)概念簡介

1.概念描述

Mach-O 為 Mach Object 文件格式的縮寫之景,它是一種用于可執(zhí)行文件铲汪、目標(biāo)代碼熊尉、動態(tài)庫罐柳、內(nèi)核轉(zhuǎn)儲的文件格式。作為 a.out 格式的替代狰住,Mach-O 提供了更強(qiáng)的擴(kuò)展性张吉,并提升了 符號表 中信息的訪問速度。

通用二進(jìn)制
通用二進(jìn)制代碼有兩種基本類型:

  • 一種類型就是簡單提供兩種獨(dú)立的二進(jìn)制代碼催植,一個(gè)用來對應(yīng)x86架構(gòu)肮蛹,一個(gè)用來對應(yīng)PowerPC架構(gòu)。
  • 另外一種類型就是只編寫一個(gè)架構(gòu)的代碼创南,當(dāng)另外一種處理環(huán)境時(shí)讓系統(tǒng)自動調(diào)用模擬器運(yùn)行伦忠,這會導(dǎo)致運(yùn)行速度下降。

多重架構(gòu)二進(jìn)制(胖二進(jìn)制)
在 NeXTSTEP 稿辙,OPENSTEP 和 Mac OS X 中昆码,可以將多個(gè)Mach-O文件組合進(jìn)一個(gè)多重架構(gòu)二進(jìn)制(胖二進(jìn)制)文件中,以用一個(gè)單獨(dú)的二進(jìn)制文件支持多種架構(gòu)的指令集邓深。例如未桥,一個(gè)Mac OS X中的多重架構(gòu)二進(jìn)制可以包含32位和64位的PowerPC代碼,或PowerPC和x86的32位代碼芥备,甚至包含32位的PowerPC代碼冬耿,64位PowerPC代碼,32位x86代碼和64位x86代碼萌壳。

2.對 Mach-O 文件進(jìn)行操作

  • 使用 file Mach-O 命令查看 Mach-O 文件類型

    查看文件架構(gòu).png
    從上圖可以看出該可執(zhí)行文件是一個(gè)通用二進(jìn)制文件亦镶,且包含2種架構(gòu):arm_v7 和 arm64。

  • 使用 lifo -info <Mach-O> 命令查看文件架構(gòu)

    文件架構(gòu).png

  • 使用 lipo 命令拆分某種架構(gòu)

lipo <Mach-O> -thin <架構(gòu)名> -output <輸出文件路徑>
  • 使用 lipo 命令合并多種架構(gòu)
lipo -create <Mach-O1> <Mach-O2> -output <輸出文件路徑>

二袱瓮、查看可執(zhí)行文件

1.使用 otool 命令查看 Mach-O 文件

  • 查看可執(zhí)行文件的動態(tài)鏈接庫
otool -L WeChat
  • 查看頭信息
otool -h WeChat
  • 查看是否加密
otool -l WeChat | grep crypt

查看是否加密.png

cryptid 為 0 時(shí)表示無加密缤骨,即已砸殼;
cryptid 為 1 時(shí)表示有加密尺借,即未砸殼绊起。

  • 查看頭信息
otool -h DingTalk

2.使用 MachOView 軟件查看

用 MachOView 打開可執(zhí)行文件可以看到有胖二進(jìn)制文件的結(jié)構(gòu)如下圖:


胖二進(jìn)制.png

可以看到可執(zhí)行文件包括三個(gè)部分:

  • Fat Header:包含架構(gòu)數(shù)量及不同架構(gòu)指令集的簡單信息
  • Executable(ARM_V7):arm_v7 架構(gòu)對應(yīng)的指令集
  • Executable(ARM64_ALL):arm64 架構(gòu)對應(yīng)的指令集

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

Mach-O.png

Mach-O主要分為三個(gè)部分:Header燎斩、Load commandsData虱歪。

  • Header:包含字節(jié)順序、架構(gòu)類型栅表、加載指令的數(shù)量等笋鄙,使得系統(tǒng)可以快速確認(rèn)一些信息,比如當(dāng)前文件用于32位還是64位怪瓶,對應(yīng)的處理器是什么萧落、文件類型是什么。
  • Load commands:它是一張包含很多內(nèi)容的表,內(nèi)容包括區(qū)域的位置找岖、符號表陨倡、動態(tài)符號表等。每個(gè)加載指令都包含一個(gè)元信息宣增,比如指令類型玫膀、名稱、在二進(jìn)制文件中的位置等等爹脾。
  • Data:通常是對象文件中最大的部分帖旨。主要包含代碼、數(shù)據(jù)灵妨,例如符號表解阅,動態(tài)符號表等等。Data 中包含若干個(gè) segment (段)泌霍,每個(gè) segment 下又有若干個(gè) section(節(jié))货抄。

1. Header

頭文件就是該可執(zhí)行文件的信息概要

頭部.png

該部分結(jié)構(gòu)可以 打開 <macho-o/loader.h> 查看

/*
 * 64位結(jié)構(gòu)頭
 */
struct mach_header_64 {
    uint32_t    magic;      // Mach-O 文件的
    cpu_type_t  cputype;    // CPU 架構(gòu)
    cpu_subtype_t   cpusubtype; // CPU 架構(gòu)子版本
    uint32_t    filetype;   // 文件類型。常見的有 MH_OBJECT(目標(biāo)文件)朱转、MH_EXECUTE(可執(zhí)行文件)蟹地、MH_DYLIB(動態(tài)庫)、MH_DYLINKER(動態(tài)鏈接器)
    uint32_t    ncmds;      // 加載指令數(shù)量
    uint32_t    sizeofcmds; // 加載指令大小
    uint32_t    flags;      // dyld 加載需要的一些標(biāo)記
    uint32_t    reserved;   // 64 位的保留字段
};

2. Load commands

Load commands 作用是讓系統(tǒng)知道如何加載文件中的信息藤为,對系統(tǒng)內(nèi)核加載器和動態(tài)鏈接器起引導(dǎo)作用怪与。

部分加載指令.png

從上圖可以看到,Load command 包含以下部分:

  • LC_SEGMENT_64:定義一個(gè)段缅疟,加載后被映射到內(nèi)存中分别,包括里面的節(jié)。相當(dāng)與一個(gè)數(shù)據(jù)索引存淫,指明了不同類型數(shù)據(jù)的地址和大小
  • LC_DYLD_INFO_ONLY:記錄了有關(guān)鏈接的重要信息耘斩,包括 __LINKEDIT 中動態(tài)鏈接相關(guān)信息的具體偏移和大小
  • LC_SYMTAB:為文件定義符號表和字符串表,在鏈接文件時(shí)被連接器使用桅咆,同時(shí)也用于調(diào)試器映射符號到源文件括授。
  • LC_DYSYMTAB:將符號表中給出符號的額外符號信息提供給動態(tài)鏈接器
  • LC_LOAD_DYLINKER:默認(rèn)的加載器路徑
  • LC_UUID:用于標(biāo)識 Mach-O 文件的 ID,也用于奔潰堆棧和符號文件的對應(yīng)解析
  • LC_VERSION_MIN_IPHONEOS:系統(tǒng)要求的最低版本
  • LC_SOURCE_VERSION:構(gòu)建二進(jìn)制文件的源代碼版本號
  • LC_MAIN:程序的入口。dyld 獲取改地址岩饼,然后跳轉(zhuǎn)到該處執(zhí)行
  • LC_ENCRYPTION_INFO_64:文件是否加密標(biāo)志刽脖,加密內(nèi)容的偏移和大小
  • LC_LOAD_DYLIB:依賴的動態(tài)庫
  • LC_RPATH:Runpath Search Paths, @rpatch 搜索的路徑
  • LC_FUNCTION_STARTS:函數(shù)起始地址表,使用調(diào)試器和其他程序能很容易看到一個(gè)地址是否在函數(shù)內(nèi)
  • LC_DATA_IN_CODE:定義在代碼段內(nèi)的非指令的表
  • LC_CODE_SIGNATURE:代碼簽名信息

3. Data

LC_SEGMENT_64 加載指令映射的就是 Data 中的數(shù)據(jù)偏移和大小忌愚,該文件組要包含四個(gè)段:

  • __PAGEZERO:空指針陷阱段,映射到虛擬內(nèi)容控件的第一頁却邓,用于捕捉對 NULL 指針的引用
  • __TEXT:代碼段/只讀數(shù)據(jù)段
  • __DATA:讀取和寫入數(shù)據(jù)的段
  • __LINKEDIT:動態(tài)鏈接器需要使用的信息硕糊,包括重定位信息、綁定信息、懶加載信息等

下面是 64 位 segment 段的數(shù)據(jù)結(jié)構(gòu)

struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;        // 指令類型
    uint32_t    cmdsize;    // 指令大小
    char        segname[16];// 段的名字
    uint64_t    vmaddr;     // 映射到虛擬地址的偏移
    uint64_t    vmsize;     // 映射到虛擬地址的大小
    uint64_t    fileoff;    // 對應(yīng)當(dāng)前架構(gòu)文件的偏移
    uint64_t    filesize;   // 文件的大小
    vm_prot_t   maxprot;    // 段頁面的最高內(nèi)存保護(hù)
    vm_prot_t   initprot;   // 初始內(nèi)存保護(hù)
    uint32_t    nsects;     // 包含的節(jié)的個(gè)數(shù)
    uint32_t    flags;      // 段頁面的標(biāo)志
};

段中包含的節(jié)的數(shù)據(jù)結(jié)構(gòu)

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   // 節(jié)的名字
    char        segname[16];    // 所屬段的名字
    uint64_t    addr;       // 映射到虛擬地址的偏移
    uint64_t    size;       // 節(jié)的大小
    uint32_t    offset;     // 節(jié)在當(dāng)前架構(gòu)文件中的偏移
    uint32_t    align;      // 節(jié)的字節(jié)對齊大小
    uint32_t    reloff;     // 重定位入口的文件偏移
    uint32_t    nreloc;     // 重定位入口的個(gè)性
    uint32_t    flags;      // 節(jié)的類型和屬性
    uint32_t    reserved1;  // 保留位
    uint32_t    reserved2;  // 保留位
    uint32_t    reserved3;  // 保留位
};

__Text 段中包含的節(jié)

  • __text:程序可執(zhí)行的代碼區(qū)域
  • __stubs:間接符號存根简十,跳轉(zhuǎn)到懶加載指針表
  • __stub_helper:幫助解決懶加載符號加載的輔助函數(shù)
  • __objc_methname:方法名
  • __objc_classname:類名
  • __objc_methtype:方法簽名
  • cstring:只讀的 C 風(fēng)格字符串檬某,包含 OC 的部分字符串和屬性名

__Data 段中包含的節(jié)

  • __nl_symbol_ptr:非懶加載指針表,在 dylib 加載時(shí)立即綁定值
  • __la_symbol_ptr:懶加載指針表螟蝙,第一次調(diào)用是才會綁定值
  • __got:非懶加載全局指針表
  • __mod_init_func:constructor 函數(shù)
  • __mod_term_func:destructor 函數(shù)
  • __cfstring:OC 字符串
  • __objc_classlist:程序中類的列表
  • __objc_nlclslist:程序中自己實(shí)現(xiàn)了+load 方法的類
  • __objc_protolist:協(xié)議列表
  • __objc_classrefs:被引用的類列表
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恢恼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胰默,更是在濱河造成了極大的恐慌场斑,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牵署,死亡現(xiàn)場離奇詭異漏隐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)奴迅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門青责,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人取具,你說我怎么就攤上這事脖隶。” “怎么了暇检?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵产阱,是天一觀的道長。 經(jīng)常有香客問我占哟,道長心墅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任榨乎,我火速辦了婚禮怎燥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蜜暑。我一直安慰自己铐姚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布肛捍。 她就那樣靜靜地躺著隐绵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拙毫。 梳的紋絲不亂的頭發(fā)上依许,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音缀蹄,去河邊找鬼峭跳。 笑死膘婶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蛀醉。 我是一名探鬼主播悬襟,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拯刁!你這毒婦竟也來了脊岳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤垛玻,失蹤者是張志新(化名)和其女友劉穎割捅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夭谤,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棺牧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朗儒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颊乘。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖醉锄,靈堂內(nèi)的尸體忽然破棺而出乏悄,到底是詐尸還是另有隱情,我是刑警寧澤恳不,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布檩小,位于F島的核電站,受9級特大地震影響烟勋,放射性物質(zhì)發(fā)生泄漏规求。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一卵惦、第九天 我趴在偏房一處隱蔽的房頂上張望阻肿。 院中可真熱鬧,春花似錦沮尿、人聲如沸丛塌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赴邻。三九已至,卻和暖如春啡捶,著一層夾襖步出監(jiān)牢的瞬間姥敛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工瞎暑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彤敛,地道東北人忿偷。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像臊泌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子揍拆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

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