Mach-O

一年鸳、什么是Mach-O文件?

Mach-OMach Object文件格式的縮寫丸相,是mac以及iOS上可執(zhí)行文件的格式搔确。Mach-O文件對應(yīng)有多種格式:

  1. 目標文件.o
  2. 庫文件:
    .a靜態(tài)庫文件
    .dylib動態(tài)庫文件
    .framework系統(tǒng)級為動態(tài)庫文件,自己創(chuàng)建的為靜態(tài)庫文件
  3. 可執(zhí)行文件及MDW.app內(nèi)部的MDW文件(通用二進制文件)
  4. dyld動態(tài)鏈接器將依賴的動態(tài)庫加載到內(nèi)存
  5. .dsym符號表

Xcode中我們可以直接創(chuàng)建.c文件灭忠,通過終端clang命令來對.c文件進行編譯或生成可執(zhí)行文件膳算,下面看一下clang怎樣使用的。

1弛作、創(chuàng)建一個main.c文件如下:

#include <stdio.h>
int main(){
    printf("打犹榉洹:yahibo\n");
    return 0;
}

2、編譯文件

clang -c main.c

會生成main.o文件映琳,該文件即為mach-o文件机隙,通過命令file main.o查看文件信息如下:

main.o: Mach-O 64-bit object x86_64

是一個object類型的文件稱為目標文件蜘拉,并不是可執(zhí)行文件

3昔驱、生成可執(zhí)行文件

  • 命令clang main.o 會生成a.out文件疗隶,即可執(zhí)行文件,通過ls查看
  • 命令clang -o main main.o 也會生成可執(zhí)行文件main
  • 命令clang -o main main.c 直接根據(jù)源文件生成可執(zhí)行文件main
  • 命令 size -x -l -m a.out 查看文件信息不见,如下:
Segment __PAGEZERO: 0x100000000 (vmaddr 0x0 fileoff 0)
Segment __TEXT: 0x1000 (vmaddr 0x100000000 fileoff 0)
    Section __text: 0x2a (addr 0x100000f50 offset 3920)
    Section __stubs: 0x6 (addr 0x100000f7a offset 3962)
    Section __stub_helper: 0x1a (addr 0x100000f80 offset 3968)
    Section __cstring: 0x11 (addr 0x100000f9a offset 3994)
    Section __unwind_info: 0x48 (addr 0x100000fac offset 4012)
    total 0xa3
Segment __DATA: 0x1000 (vmaddr 0x100001000 fileoff 4096)
    Section __nl_symbol_ptr: 0x10 (addr 0x100001000 offset 4096)
    Section __la_symbol_ptr: 0x8 (addr 0x100001010 offset 4112)
    total 0x18
Segment __LINKEDIT: 0x1000 (vmaddr 0x100002000 fileoff 8192)
total 0x100003000

4葱跋、執(zhí)行文件

./a.out 或 ./main

輸出:

打映旨摹:yahibo

以上步驟可以用來寫c并編譯執(zhí)行。

多個文件是如何編譯的呢娱俺?

開發(fā)中根據(jù)不同功能模塊我們會分很多文件來實現(xiàn)际看,在clang中是可以對多個文件進行一次性打包,生成一個可執(zhí)行文件矢否。如下:

1仲闽、新建一個功能文件

people.c

#include <stdio.h>
void sleep(){
    printf("正在睡覺\n");
}

2、在main.c中聲明sleep方法并調(diào)用

void sleep();//聲明方法
int main(){
    printf("打咏├省:yahibo\n");
    sleep();//調(diào)用方法
    return 0;
}

3赖欣、編譯為可執(zhí)行文件

clang -o main main.c people.c

4、執(zhí)行可執(zhí)行文件

./main

運行如下:

打友槊怼:yahibo
正在睡覺

二顶吮、通用二進制文件(Universal binary)

iOS中不同手機對應(yīng)著可能不同的架構(gòu),如arm64粪薛、armv7悴了、armv7s。為了兼容不同架構(gòu)的手機违寿,蘋果推出了通用二進制文件湃交,包含了應(yīng)用程序常用的這些架構(gòu),因此通用二進制文件藤巢,比單一架構(gòu)二進制文件要大很多搞莺。

架構(gòu)選擇

arm.png
  • 注意以上標記的兩處取交集,來確認最終架構(gòu)
  • 1處默認架構(gòu)為arm64掂咒、armv7
  • 如果需要添加armv7s還需要在1處添加armv7s字符

通過以上配置真實編譯出來的是包含arm64才沧、armv7架構(gòu),因為工程中使用了第三方靜態(tài)庫不包含armv7s因此這里配置為標準架構(gòu)模式绍刮。

通用二進制文件在哪呢温圆?

xxx.app中的xxx黑色文件即是通用二進制文件,右鍵xxx.app顯示包內(nèi)容即可獲得孩革。

lipo命令

通過lipo命令可以查看岁歉、拆分及合并以上提出的架構(gòu),在做靜態(tài)庫時也會使用嫉戚,來合并真機下和模擬器下的靜態(tài)庫刨裆,以適應(yīng)不同的調(diào)試環(huán)境澈圈。

  • MDW.app中我獲取可執(zhí)行文件MDW

1彬檀、查看架構(gòu)信息

lipo -info MDW

打印如下:

Architectures in the fat file: MDW are: armv7 arm64

2帆啃、拆分armv7、arm64架構(gòu)

lipo MDW -thin armv7 -output MDW_armv7
lipo MDW -thin arm64 -output MDW_arm64

查看armv7信息:

lipo -info MDW_armv7

打印如下:

Non-fat file: MDW_armv7 is architecture: armv7

查看arm64信息:

lipo -info MDW_arm64

打印如下:

Non-fat file: MDW_arm64 is architecture: arm64

3窍帝、合并架構(gòu)

lipo -create MDW_armv7 MDW_arm64 -output MDW_ALL

查看合并后的信息

lipo -info MDW_ALL

打印如下:

Architectures in the fat file: MDW_ALL are: armv7 arm64

產(chǎn)生的可執(zhí)行文件如圖:

mackos.png

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

官方圖解:

structure.png

文件分為三個部分:

  • Header:包含Mach-O文件的基本信息,字節(jié)順序坤学、架構(gòu)類型疯坤、加載指令的數(shù)量等
  • Load commands:包含區(qū)域位置、符號表深浮、動態(tài)符號表压怠,加載Mach-O文件時使用這里的數(shù)據(jù)確定內(nèi)存分布
  • Data:數(shù)據(jù)段segement,包含具體代碼飞苇、常量菌瘫、類、方法等布卡,有多個segment雨让,每個segment有0到多個section,每個段有一個虛擬地址映射到進程的地址空間

直接使用MachOView打開MDW可執(zhí)行文件忿等,如下:

macho.png
  • 胖二進制文件中包含了armv7栖忠、arm64架構(gòu)
  • 通過MachOView即可查看可執(zhí)行文件的所有信息

1、Header

除了以上直接查看header贸街,還可以通過otool命令查看header信息:

otool -f MDW

打印如下:

Fat headers
fat_magic 0xcafebabe
nfat_arch 2
architecture 0
    cputype 12
    cpusubtype 9
    capabilities 0x0
    offset 16384
    size 7587424
    align 2^14 (16384)
architecture 1
    cputype 16777228
    cpusubtype 0
    capabilities 0x0
    offset 7618560
    size 8748384
    align 2^14 (16384)

otool -h MDW

打逾帜:

Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedface      12          9  0x00           2    48       5080 0x00210085
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           2    48       5752 0x00210085
  • 以上打印的兩段分別是armv7、arm64架構(gòu)下的header信息
    objc4源碼loader.h文件中有mach_header的結(jié)構(gòu)體定義薛匪,如下:
struct mach_header {
    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 */
};
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:魔數(shù)皇帮,確定是64位還是32位
  • cputype:cpu類型
  • cpusubtype:cpu子類型
  • filetype:Mach-O支持多種文件類型,使用filetype來標注具體文件類型
  • ncmds:加載命令的數(shù)量
  • sizeofcmds:命令區(qū)域(load commands)總的字節(jié)大小
  • flags:標識二進制文件所支持的功能蛋辈,主要與系統(tǒng)的加載属拾、鏈接有關(guān)

2、Load commands

Header之后是load commands段為加載命令段冷溶,在header結(jié)構(gòu)體中有對加載命令段相關(guān)信息的描述渐白,用于解析加載命令。在objc4源碼loader.h中逞频,有對loadcommand的定義:

struct load_command {
    uint32_t cmd;       /* type of load command */
    uint32_t cmdsize;   /* total size of command in bytes */
};
  • cmd:命令類型纯衍,針對不同架構(gòu)有不同的結(jié)構(gòu)(32位、64位)
  • cmdsize:命令所占字節(jié)大忻缯汀(32位size必須為4字節(jié)的倍數(shù)襟诸,64位size必須為8字節(jié)的倍數(shù))
    在文件中有兩個結(jié)構(gòu)體segment_commandsegment_command_64針對不同架構(gòu)的結(jié)構(gòu)體瓦堵,內(nèi)部設(shè)置字段相同。以segment_command_64為例:
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 */
};
  • cmd:加載命令類型
  • LC_SEGMENT:表示這好似一個段加載命令歌亲,需要將它加載到對應(yīng)的進程空間上
  • LC_LOAD_DYLIB:這是一個需要動態(tài)加載的鏈接庫菇用,它使用dylib_command結(jié)構(gòu)體表示
  • LC_MAIN:記錄了可執(zhí)行文件的主函數(shù)main()的位置,它使用entry_point_command結(jié)構(gòu)體表示
  • LC_CODE_SIGNATURE:代碼簽名的加載命令陷揪,描述了Mach-O的代碼簽名信息惋鸥,它屬于鏈接信息,使用linkedit_data_command結(jié)構(gòu)體表示
  • cmdsize:加載命令所占內(nèi)存大小
  • segname:存放16字節(jié)大小的段名字悍缠,當前是__PAGEZERO卦绣。
  • vmaddr:段的虛擬內(nèi)存起始地址
  • vmsize:段的虛擬內(nèi)存大小
  • fileoff:段在文件中偏移量
  • filesize:段在文件大小
  • maxprot:段頁面所需要的最高內(nèi)存保護(4=r,2=w,1=x)
  • initprot:段頁面初始的內(nèi)存保護
  • nsects:段中包含section的數(shù)量
  • flags:其他雜項標志位
    在看MachOView中的loadcommands字段:
loadcommands.png

以上為是應(yīng)用程序所有加載命令,通過上面流程能夠看到對系統(tǒng)庫的加載順序飞蚓。對比項目中引入的庫文件滤港,順序是一致的,如下圖:

xcode.png

以上加載命令含義如下:

  • LC_SEGMENT_64:將文件中的段映射到進程地址空間中
  • LC_DYLD_INFO_ONLY:動態(tài)鏈接相關(guān)信息
  • LC_SYMTAB:符號表信息趴拧,位置溅漾、偏移、數(shù)據(jù)個數(shù)八堡,供dyld使用
  • LC_DYSYMTAB:動態(tài)符號表信息樟凄,供dyld使用
  • LC_LOAD_DYLINKER:鏈接器信息,記錄使用那些鏈接器完成內(nèi)核后序的加載工作
  • LC_UUID:Mach-O文件的唯一標識
  • LC_VERSION_MIN_MACOSX:支持最低操作系統(tǒng)版本
  • LC_SOURCE_VERSION:源代碼的版本號
  • LC_MAIN:設(shè)置主線程的入口即棧大小
  • LC_LOAD_DYLIB:依賴庫信息兄渺,dyld通過該命令去加載依賴庫
  • LC_FUNCTION_STARTS:函數(shù)的起始地址表
  • LC_CODE_SIGNATURE:代碼簽名

3缝龄、Data

Data區(qū)域由Segment段和Section節(jié)組成:

segment.png
  • segment主要有__TEXT__DATA組成
  • __text:是主程序代碼
  • __stubs、__stub_helper:是動態(tài)鏈接的樁
  • __cstring:程序中c語言字符串
  • __const:常量

Section含義:

  • Section64(__TEXT,__objc_methname):OC類名
  • Section64(__DATA,__objc_classlist):OC類列表
  • Section64(__DATA,__objc_protollist):OC原型列表
  • Section64(__DATA,__objc_imageinfo):OC鏡像信息
  • Section64(__DATA,__objc_selfrefs):OC類自引用
  • Section64(__DATA,__objc_superrefs):OC類超類的引用
  • Section64(__DATA,__ivar):OC類成員變量

等等挂谍,都是通過section來對OC中的具體類別做加載的叔壤。segment段分32位和64位,字段相同口叙,以64為例如下:

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 ,就是主程序代碼
  • segname:section所屬的segment名炼绘,第一個是__TEXT
  • addr:當前section在內(nèi)存中的起始位置
  • size:當前section所占內(nèi)存大小
  • offset:當前section的文件偏移
  • align:字節(jié)大小對齊
  • reloff:重定位入口的文件偏移,0
  • nreloc:需要重定位的入口數(shù)量妄田,0
  • flags:包含sectiontypeattributes
  • reserved1俺亮、reserved2預(yù)留字段

四、總結(jié)

注意在Load Commands的最后一條的地址偏移疟呐,再看Data段的起始地址偏移:

loadcommands.jpg
data.jpg
  • Load Commands最后一條指令的地址偏移為:0000311C
  • Data段的起始地址為:00004000

中間預(yù)留了一段空間脚曾,該處即是后面代碼注入的地方,此處不是專門預(yù)留出來進行注入的启具,而是考慮內(nèi)存對稱提高代碼執(zhí)行效率本讥。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拷沸,更是在濱河造成了極大的恐慌色查,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撞芍,死亡現(xiàn)場離奇詭異秧了,居然都是意外死亡,警方通過查閱死者的電腦和手機勤庐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門示惊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來好港,“玉大人愉镰,你說我怎么就攤上這事【冢” “怎么了丈探?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拔莱。 經(jīng)常有香客問我碗降,道長,這世上最難降的妖魔是什么塘秦? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任讼渊,我火速辦了婚禮,結(jié)果婚禮上尊剔,老公的妹妹穿的比我還像新娘爪幻。我一直安慰自己,他們只是感情好须误,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布挨稿。 她就那樣靜靜地躺著,像睡著了一般京痢。 火紅的嫁衣襯著肌膚如雪奶甘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天祭椰,我揣著相機與錄音臭家,去河邊找鬼。 笑死方淤,一個胖子當著我的面吹牛钉赁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臣淤,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼橄霉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起姓蜂,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤按厘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钱慢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逮京,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年束莫,在試婚紗的時候發(fā)現(xiàn)自己被綠了懒棉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡览绿,死狀恐怖策严,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饿敲,我是刑警寧澤妻导,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站怀各,受9級特大地震影響倔韭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瓢对,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一寿酌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硕蛹,春花似錦醇疼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至壶栋,卻和暖如春辰如,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贵试。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工琉兜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毙玻。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓豌蟋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桑滩。 傳聞我的和親對象是個殘疾皇子梧疲,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 這篇是對 iOS 應(yīng)用啟動時,main 函數(shù)執(zhí)行前發(fā)生的事的一點總結(jié),限于水平幌氮,如有錯誤請指正~ FAT 二進制 ...
    kikido閱讀 731評論 0 2
  • 組成 Mach-O通常有三部分組成頭部 (Header): Mach-O文件的架構(gòu) 比如Mac的 PPC, PPC...
    充滿活力的早晨閱讀 1,562評論 0 5
  • 探究App的啟動過程缭受,有助于我們優(yōu)化App的啟動時間,從main函數(shù)之前和main函數(shù)之后兩個階段進行分析一下该互。 ...
    沉江小魚閱讀 1,454評論 1 6
  • 13. Hook原理介紹 13.1 Objective-C消息傳遞(Messaging) 對于C/C++這類靜態(tài)語...
    Flonger閱讀 1,402評論 0 3
  • 首先看下效果圖和整個項目的結(jié)構(gòu) 很簡單的一個項目米者,這也是我在簡書寫的第一個項目,雖然簡單宇智,但是我覺得很漂亮 現(xiàn)在開...
    Big_Sweet閱讀 423評論 2 1