趣探 Mach-O:文件格式分析

本文所讀的源碼,可以從這里找到,這是 Mach-O 系列的第一篇

我們的程序想要跑起來,肯定它的可執(zhí)行文件格式要被操作系統(tǒng)所理解,比如 ELFLinux下可執(zhí)行文件的格式,PE32/PE32+windows的可執(zhí)行文件的格式,那么對于OS XiOS 來說 Mach-O 是其可執(zhí)行文件的格式。

我們平時(shí)了解到的可執(zhí)行文件、庫文件、Dsym文件、動(dòng)態(tài)庫、動(dòng)態(tài)連接器都是這種格式的。Mach-O 的組成結(jié)構(gòu)如下圖所示包括了HeaderLoad commandsData(包含Segement的具體數(shù)據(jù))

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

Mach-O的頭部,使得可以快速確認(rèn)一些信息,比如當(dāng)前文件用于32位還是64位,對應(yīng)的處理器是什么、文件類型是什么

可以拿下面的代碼做一個(gè)例子

#include <stdio.h>

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    return 0;
}

在終端執(zhí)行以下命令,可以生成一個(gè)可執(zhí)行文件a.out

192:Test Joy$ gcc -g main.c

我們可以使用MachOView(是一個(gè)查看MachO 格式文件信息的開源工具)來查看
.out文件的具體格式如何

看到這里肯定有點(diǎn)懵比,不知道這是什么東西,下面看一下 header的數(shù)據(jù)結(jié)構(gòu)

32位結(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 */
};

64位架構(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 */
};

32位和64位架構(gòu)的頭文件,沒有太大的區(qū)別,只是64位多了一個(gè)保留字段罷了

  • magic:魔數(shù),用于快速確認(rèn)該文件用于64位還是32位
  • cputype:CPU類型,比如 arm
  • cpusubtype:對應(yīng)的具體類型,比如arm64、armv7
  • filetype:文件類型,比如可執(zhí)行文件、庫文件、Dsym文件,demo中是2 MH_EXECUTE,代表可執(zhí)行文件
 * 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 */
#define MH_DSYM     0xa     /* companion file with only debug */
#define MH_KEXT_BUNDLE  0xb     /* x86_64 kexts */
  • ncmds :加載命令條數(shù)
  • sizeofcmds:所有加載命令的大小
  • reserved:保留字段
  • flags:標(biāo)志位,剛才demo中顯示的都在這里了,其余的有興趣可以閱讀 mach o源碼
#define MH_NOUNDEFS 0x1     // 目前沒有未定義的符號,不存在鏈接依賴
#define    MH_DYLDLINK  0x4     // 該文件是dyld的輸入文件,無法被再次靜態(tài)鏈接
#define MH_PIE 0x200000     // 加載程序在隨機(jī)的地址空間,只在 MH_EXECUTE中使用
#define    MH_TWOLEVEL  0x80    // 兩級名稱空間

隨機(jī)地址空間

進(jìn)程每一次啟動(dòng),地址空間都會(huì)簡單地隨機(jī)化。

對于大多數(shù)應(yīng)用程序來說,地址空間隨機(jī)化是一個(gè)和他們完全不相關(guān)的實(shí)現(xiàn)細(xì)節(jié),但是對于黑客來說,它具有重大的意義。

如果采用傳統(tǒng)的方式,程序的每一次啟動(dòng)的虛擬內(nèi)存鏡像都是一致的,黑客很容易采取重寫內(nèi)存的方式來破解程序。采用ASLR可以有效的避免黑客攻擊。

dyld

動(dòng)態(tài)鏈接器,他是蘋果開源的一個(gè)項(xiàng)目,可以在這里下載,當(dāng)內(nèi)核執(zhí)行LC_DYLINK(后面會(huì)說到)時(shí),連接器會(huì)啟動(dòng),查找進(jìn)程所依賴的動(dòng)態(tài)庫,并加載到內(nèi)存中。

二級名稱空間

這是dyld的一個(gè)獨(dú)有特性,說是符號空間中還包括所在庫的信息,這樣子就可以讓兩個(gè)不同的庫導(dǎo)出相同的符號,與其對應(yīng)的是平坦名稱空間

Load commands 結(jié)構(gòu)

Load commands緊跟在頭部之后,這些加載指令清晰地告訴加載器如何處理二進(jìn)制數(shù)據(jù),有些命令是由內(nèi)核處理的,有些是由動(dòng)態(tài)鏈接器處理的。在源碼中有明顯的注釋來說明這些是動(dòng)態(tài)連接器處理的。

這里列舉幾個(gè)看上去比較熟悉的....

// 將文件的32位或64位的段映射到進(jìn)程地址空間
#define LC_SEGMENT  0x1 
#define LC_SEGMENT_64   0x19    

// 唯一的 UUID,標(biāo)示二進(jìn)制文件
#define    LC_UUID      0x1b    /* the uuid */

// 剛才提到的,啟動(dòng)動(dòng)態(tài)加載連接器
#define    LC_LOAD_DYLINKER 0xe /* load a dynamic linker */

// 代碼簽名和加密
#define    LC_CODE_SIGNATURE 0x1d   /* local of code signature */
#define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */

load command的結(jié)構(gòu)如下

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

通過 MachOView來繼續(xù)查看剛才Demo中的Load commands的一些細(xì)節(jié),LC_SEGMENT_64LC_SEGMENT是加載的主要命令,它負(fù)責(zé)指導(dǎo)內(nèi)核來設(shè)置進(jìn)程的內(nèi)存空間

  • cmd:就是Load commands的類型,這里LC_SEGMENT_64代表將文件中64位的段映射到進(jìn)程的地址空間。LC_SEGMENT_64LC_SEGMENT的結(jié)構(gòu)差別不大,下面只列舉一個(gè),有興趣可以閱讀源碼
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 */
};

  • cmdsize:代表load command的大小
  • VM Address :段的虛擬內(nèi)存地址
  • VM Size : 段的虛擬內(nèi)存大小
  • file offset:段在文件中偏移量
  • file size:段在文件中的大小

將該段對應(yīng)的文件內(nèi)容加載到內(nèi)存中:從offset處加載 file size大小到虛擬內(nèi)存 vmaddr處,由于這里在內(nèi)存地址空間中是_PAGEZERO段(這個(gè)段不具有訪問權(quán)限,用來處理空指針)所以都是零

還有圖片中的其他段,比如_TEXT對應(yīng)的就是代碼段,_DATA對應(yīng)的是可讀/可寫的數(shù)據(jù),_LINKEDIT是支持dyld的,里面包含一些符號表等數(shù)據(jù)

  • nsects:標(biāo)示了Segment中有多少secetion
  • segment name:段的名稱,當(dāng)前是__PAGEZERO

Segment & Section

這里有個(gè)命名的問題,如下圖所示,__TEXT代表的是Segment,小寫的__text代表 Section

Section的數(shù)據(jù)結(jié)構(gòu)

struct section { /* for 32-bit architectures */
    char        sectname[16];   /* name of this section */
    char        segname[16];    /* segment this section goes in */
    uint32_t    addr;       /* memory address of this section */
    uint32_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) */
};
  • sectname:比如_textstubs
  • segname :section所屬的segment,比如__TEXT
  • addr :section在內(nèi)存的起始位置
  • size:section的大小
  • offset:section的文件偏移
  • align :字節(jié)大小對齊
  • reloff :重定位入口的文件偏移
  • nreloc: 需要重定位的入口數(shù)量
  • flags:包含sectiontypeattributes

發(fā)現(xiàn)很多底層知識都是以 Mach-O為基礎(chǔ)的,所以最近打算花時(shí)間結(jié)合Mach-O做一些相對深入的總結(jié),比如符號解析、bitcode、逆向工程等,加油吧

參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,084評論 2 375

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

  • 熟悉Linux和windows開發(fā)的同學(xué)都知道,ELF是Linux下可執(zhí)行文件的格式,PE32/PE32+是win...
    Klaus_J閱讀 3,992評論 1 10
  • 13. Hook原理介紹 13.1 Objective-C消息傳遞(Messaging) 對于C/C++這類靜態(tài)語...
    Flonger閱讀 1,429評論 0 3
  • 上一篇博客介紹了mach_header相關(guān)內(nèi)容,Mach-O文件介紹之mach_header。這篇博客主要介紹Ma...
    Tomychen閱讀 2,395評論 0 7
  • 原文地址 寫在之前 之前工作中對Mach-O文件有一定的接觸, 原本早就想寫一篇文章分享一下,但是奈何只是不夠深入...
    南梔傾寒閱讀 4,829評論 3 22
  • 13.1 Objective-C消息傳遞(Messaging) 對于C/C++這類靜態(tài)語言,調(diào)用一個(gè)方法其實(shí)就是跳...
    泰克2008閱讀 2,072評論 1 6