從Mach-O獲取Bitcode

當(dāng)我們設(shè)置了 Enable Bitcode=YES 立砸,進(jìn)行Archive時为朋,bitcode會被嵌入到鏈接后的Mach-O中,用于提交到App Store。從編譯日志中可以看出谈跛,Archive時多了一個編譯參數(shù) -fembed-bitcode

非Archive編譯時塑陵,Enable Bitcode 將會增加編譯參數(shù) -fembed-bitcode-marker感憾, 該參數(shù)用于在Mach-O中做標(biāo)記,但是不會真正產(chǎn)生Bitcode令花。因為本地編譯調(diào)試時并不需要bitcode吹菱,去掉這個不必要的步驟加快編譯速度。

對于靜態(tài)庫等打開了Bitcode編譯彭则,通過MachOview查看會發(fā)現(xiàn)有一個__LLVM, __bitcode 段鳍刷;而全工程編譯出來對應(yīng)的是 __LLVM, __bundle 段;可以使用 segedit 命令將指定的Section導(dǎo)出:

    segedit XXX.o -extract __LLVM __bitcode result.bc
viewBundle.png

如上圖俯抖,需要注意的是输瓜, __LLVM, __bundle導(dǎo)出的并不是直接的bitcode格式,通過MachOView查看可得知是Xar文檔格式芬萍,xar格式包含了一個xml格式的文件頭(TOC)尤揣,里面用于存放各種文件的基本屬性以及一些附加附加信息,可以通過xar命令查看并解壓

xar -d toc.xml -f bundle # 導(dǎo)出文件頭

查看xml的結(jié)構(gòu)柬祠,包括以下內(nèi)容:

  • ld 的基本參數(shù)北戏,我們鏈接時使用的是clang,實際上clang內(nèi)部調(diào)用了ld漫蛔,這里記錄的是ld的參數(shù)
    • version: bitcode bundle 的版本號
    • architecture: 目標(biāo)架構(gòu)
    • platform: 目標(biāo)平臺
    • sdkversion: sdk版本
    • dylibs: 鏈接的動態(tài)庫
    • link-options: 其他鏈接參數(shù)
  • 文件目錄
    • checksum類型
    • 創(chuàng)建時間
    • 每個文件的信息
      • 文件名嗜愈,這里并非原始文件名,而是按照鏈接時輸入的順序被重命名為數(shù)字序號
      • 基本屬性莽龟,包括checksum蠕嫁、偏移、大小等
      • 文件類型毯盈,一般是Bitcode剃毒,還有兩種特殊類型,Object以及Bundle搂赋,這里賣個關(guān)子赘阀,大家有興趣可已自行研究(想想如果一個源代碼文件是.s格式,要如何支持bitcode)
      • 編譯器類型(clang/swift)及編譯參數(shù)脑奠,這部分就是object文件中 __LLVM,__cmdline 的內(nèi)容
    • 下一個文件的信息(如有)
    • 重復(fù)

如要提取bitcode基公,需要進(jìn)一步解壓:

xar -xf result.bc

通過otool檢查二進(jìn)制文件是否開啟了bitcode:

otool -arch armv7 -l xxxx.a | grep __LLVM | wc -l 

通過判斷是否包含 __LLVM 來判斷是否支持bitcode,但是這種方式區(qū)分不了bitcode和bitcode-marker捺信,確定是否包含bitcode酌媒,還需要檢查otool輸出中__LLVM Segment 的長度欠痴,如果長度只有1個字節(jié),則并不能代表真正開啟了bitcode:

$ otool -l test_bitcode.o | grep -A 2  __LLVM | grep size      
    size 0x0000000000000b10      
    size 0x0000000000000042 
$ otool -l test_bitcode_marker.o | grep -A 2  __LLVM | grep size      
    size 0x0000000000000001      
    size 0x0000000000000001

另外秒咨,Archive版本引入了 Symbol HidingDebug info Striping 機制喇辽,在鏈接時,bitcode中所有非導(dǎo)出符號均被隱藏雨席,取而代之的是 __hidden#0_ 或者 __ir_hidden#1_ 這樣的形式菩咨,debug信息也只保留了line-table,所有跟文件路徑陡厘、標(biāo)識符抽米、導(dǎo)出符號等相關(guān)的信息全部都從bitcode中移除,相當(dāng)于做了一層混淆糙置,這種情況從IR代碼中打印出的方法名全為__hidden#XXX云茸,目前在嘗試從中拿到內(nèi)存地址再到符號表中去找映射關(guān)系。

符號表其實是DWARF的集合形式谤饭,它是內(nèi)存地址與函數(shù)名标捺,文件名,行號的映射表揉抵。DWARF 全名是 Debugging with Attribute Record Formats 亡容,是一種調(diào)試信息的存放格式。

DWARF 第一版發(fā)布于 1992 年冤今,主要是為 UNIX 下的調(diào)試器提供必要的調(diào)試信息闺兢,例如內(nèi)存地址對應(yīng)的文件名以及代碼行號等信息,通常用于源碼級別調(diào)試使用戏罢。另外通過 DWARF屋谭,還能還原運行時的地址成為可讀的源碼符號(及行號)。

DWARF 調(diào)試信息簡單的來說就是在機器碼和對應(yīng)的源代碼之間建立一座橋梁帖汞,大大提高了調(diào)試程序的能力戴而。

iOS 中引入 DWARF 這種調(diào)試信息格式,其實也是順應(yīng)歷史的潮流翩蘸,因為 DWARF 已經(jīng)在類 UNIX 系統(tǒng)中逐步替換 stabs(symbol table strings),成為一種主流的調(diào)試信息格式淮逊。使用 GCC 或者 LLVM 系列編譯器都可以很方便的生成 DWARF 調(diào)試信息催首。

知道了Bitcode在Mach-O的位置以及獲取方法,再來看看Mach-O的其他內(nèi)容泄鹏,Mach-O 其實是 Mach Object 文件格式的縮寫郎任。

屬于 Mach-O 格式的常見文件

  • 目標(biāo)文件 .o
  • 庫文件
    • .a
    • .dylib
    • Framework
  • 可執(zhí)行文件
  • dyld ( 動態(tài)鏈接器 )
  • .dsym ( 符號表 )

使用 file 命令可以查看文件類型

一張網(wǎng)上到處都有的結(jié)構(gòu)圖:

mach-o.png

1. Header

描述了 Mach-O 的 CPU 架構(gòu)、文件類型以及加載命令等信息备籽。

可進(jìn)入 Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach-o/loader.h 查看頭文件

struct mach_header_64 {
    uint32_t    magic;      /* 魔數(shù) 0xfeedface舶治、0xcafebabe分井、0xfeedfacf 分別對應(yīng)64、fat霉猛、64*/
    //如0xfeedface表示32位二進(jìn)制格式尺锚,0xfeedfacf表示64位;
    cpu_type_t  cputype;    /* cpu 類型 比如 ARM */
    cpu_subtype_t   cpusubtype; /* cpu 具體類型 比如arm64 , armv7 */
    uint32_t    filetype;   /* 文件類型 例如可執(zhí)行文件 .. 具體見下面枚舉 */
    uint32_t    ncmds;      /* load commands 加載命令條數(shù) */
    uint32_t    sizeofcmds; /* load commands 加載命令大小*/
    uint32_t    flags;      /* 標(biāo)志位標(biāo)識二進(jìn)制文件支持的功能 , 主要是和系統(tǒng)加載惜浅、鏈接有關(guān)  具體見下面枚舉*/
    uint32_t    reserved;   /* reserved , 保留字段 */
};
filetype:
#define    MH_OBJECT    0x1        /* Target 文件:編譯器對源碼編譯后得到的中間結(jié)果 */
#define    MH_EXECUTE    0x2        /* 可執(zhí)行二進(jìn)制文件 */
#define    MH_FVMLIB    0x3        /* VM 共享庫文件 */
#define    MH_CORE        0x4        /* Core 文件瘫辩,一般在 App Crash 產(chǎn)生 */
#define    MH_PRELOAD    0x5        /* preloaded executable file */
#define    MH_DYLIB    0x6        /* 動態(tài)庫 */
#define    MH_DYLINKER    0x7        /* 動態(tài)連接器 /usr/lib/dyld */
#define    MH_BUNDLE    0x8        /* 非獨立的二進(jìn)制文件,往往通過 gcc-bundle 生成 */
#define    MH_DYLIB_STUB    0x9        /* 靜態(tài)鏈接文件 */
#define    MH_DSYM        0xa        /* 符號文件以及調(diào)試信息坛悉,在解析堆棧符號中常用 */
#define    MH_KEXT_BUNDLE    0xb        /* x86_64 內(nèi)核擴(kuò)展 */
flags:
#define    MH_NOUNDEFS    0x1        /* Target 文件中沒有帶未定義的符號伐厌,常為靜態(tài)二進(jìn)制文件 */
#define MH_INCRLINK 0x2     /* the object file is the output of an
                       incremental link against a base file
                       and can't be link edited again */
#define MH_DYLDLINK 0x4     /* the object file is input for the
                       dynamic linker and can't be staticly
                       link edited again */
#define MH_BINDATLOAD   0x8     /* the object file's undefined
                       references are bound by the dynamic
                       linker when loaded. */
#define MH_PREBOUND 0x10        /* the file has its dynamic undefined
                       references prebound. */
#define MH_SPLIT_SEGS    0x20  /* Target 文件中的只讀 Segment 和可讀寫 Segment 分開  */
#define MH_TWOLEVEL    0x80        /* 該 Image 使用二級命名空間(two name space binding)綁定方案 */
#define MH_FORCE_FLAT    0x100 /* 使用扁平命名空間(flat name space binding)綁定(與 MH_TWOLEVEL 互斥) */
#define MH_WEAK_DEFINES    0x8000 /* 二進(jìn)制文件使用了弱符號 */
#define MH_BINDS_TO_WEAK 0x10000 /* 二進(jìn)制文件鏈接了弱符號 */
#define MH_ALLOW_STACK_EXECUTION 0x20000/* 允許 Stack 可執(zhí)行 */
#define    MH_PIE 0x200000  /* 對可執(zhí)行的文件類型啟用地址空間 layout 隨機化 */
#define MH_NO_HEAP_EXECUTION 0x1000000 /* 將 Heap 標(biāo)記為不可執(zhí)行,可防止 heap spray 攻擊 */

2. Load Commands

描述了文件中數(shù)據(jù)的具體組織結(jié)構(gòu)裸影,不同的數(shù)據(jù)類型使用不同的加載命令表示挣轨。

Load Commands 詳細(xì)保存著加載指令的內(nèi)容 , 告訴鏈接器如何去加載這個 Mach-O 文件.

通過查看內(nèi)存地址我們發(fā)現(xiàn) , 在內(nèi)存中 , Load Commands 是緊跟在 Mach_header 之后的 .

名稱 含義
LC_SEGMENT_64 將文件中(32位或64位)的段映射到進(jìn)程地址空間中
LC_DYLD_INFO_ONLY 動態(tài)鏈接相關(guān)信息
LC_SYMTAB 符號地址
LC_DYSYMTAB 動態(tài)符號表地址
LC_LOAD_DYLINKER 加載方式,dyld
LC_UUID 文件的UUID
LC_VERSION_MIN_MACOSX 支持的最低操作系統(tǒng)版本
LC_SOURCE_VERSION 源代碼版本
LC_MAIN 設(shè)置程序主線程的入口地址和棧大小
LC_LOAD_DYLIB 依賴庫的路徑轩猩,包含三方庫
LC_FUNCTION_STARTS 函數(shù)起始地址表
LC_CODE_SIGNATURE 代碼簽名

3. Data

每一個段(Segment)的數(shù)據(jù)都保存在其中刃唐,段的概念和 ELF 文件中段的概念類似,都擁有一個或多個 Section 界轩,用來存放數(shù)據(jù)和代碼画饥。

Segment:
#define    SEG_PAGEZERO    "__PAGEZERO" /* 當(dāng)時 MH_EXECUTE 文件時,捕獲到空指針 */
#define    SEG_TEXT    "__TEXT" /* 代碼/只讀數(shù)據(jù)段 */
#define    SEG_DATA    "__DATA" /* 數(shù)據(jù)段 */
#define SECT_DATA   "__data"    /* the real initialized data section */
                    /* no padding, no bss overlap */
#define SECT_BSS    "__bss"     /* the real uninitialized data section*/
                    /* no padding */
#define SECT_COMMON "__common"  /* the section common symbols are */
                    /* allocated in by the link editor */
#define    SEG_OBJC    "__OBJC" /* Objective-C runtime 段 */
#define SECT_OBJC_SYMBOLS "__symbol_table"  /* symbol table */
#define SECT_OBJC_MODULES "__module_info"   /* module information */
#define SECT_OBJC_STRINGS "__selector_strs" /* string table */
#define SECT_OBJC_REFS "__selector_refs"    /* string table */

#define SEG_ICON     "__ICON"   /* the icon segment */
#define SECT_ICON_HEADER "__header" /* the icon headers */
#define SECT_ICON_TIFF   "__tiff"   /* the icons in tiff format */
#define    SEG_LINKEDIT    "__LINKEDIT" /* 包含需要被動態(tài)鏈接器使用的符號和其他表浊猾,包括符號表抖甘、字符串表等 */
Segment數(shù)據(jù)結(jié)構(gòu):
struct segment_command_64 { 
    uint32_t    cmd;        /* LC_SEGMENT_64 */
    uint32_t    cmdsize;    /* section_64 結(jié)構(gòu)體所需要的空間 */
    char        segname[16];    /* segment 名字,上述宏中的定義 */
    uint64_t    vmaddr;        /* 所描述段的虛擬內(nèi)存地址 */
    uint64_t    vmsize;        /* 為當(dāng)前段分配的虛擬內(nèi)存大小 */
    uint64_t    fileoff;    /* 當(dāng)前段在文件中的偏移量 */
    uint64_t    filesize;    /* 當(dāng)前段在文件中占用的字節(jié) */
    vm_prot_t    maxprot;    /* 段所在頁所需要的最高內(nèi)存保護(hù)葫慎,用八進(jìn)制表示 */
    vm_prot_t    initprot;    /* 段所在頁原始內(nèi)存保護(hù) */
    uint32_t    nsects;        /* 段中 Section 數(shù)量 */
    uint32_t    flags;        /* 標(biāo)識符 */
};
Section數(shù)據(jù)結(jié)構(gòu):
struct section_64 { 
    char        sectname[16];    /* Section 名字 */
    char        segname[16];    /* Section 所在的 Segment 名稱 */
    uint64_t    addr;        /* Section 所在的內(nèi)存地址 */
    uint64_t    size;        /* Section 的大小 */
    uint32_t    offset;        /* Section 所在的文件偏移 */
    uint32_t    align;        /* Section 的內(nèi)存對齊邊界 (2 的次冪) */
    uint32_t    reloff;        /* 重定位信息的文件偏移 */
    uint32_t    nreloc;        /* 重定位條目的數(shù)目 */
    uint32_t    flags;        /* 標(biāo)志屬性 */
    uint32_t    reserved1;    /* 保留字段1 (for offset or index) */
    uint32_t    reserved2;    /* 保留字段2 (for count or sizeof) */
    uint32_t    reserved3;    /* 保留字段3 */
};

部分的 Segment (主要指的 __TEXT__DATA)可以進(jìn)一步分解為 Section衔彻。之所以按照 Segment -> Section 的結(jié)構(gòu)組織方式,是因為在同一個 Segment 下的 Section偷办,可以控制相同的權(quán)限艰额,也可以不完全按照 Page 的大小進(jìn)行內(nèi)存對其,節(jié)省內(nèi)存的空間椒涯。而 Segment 對外整體暴露柄沮,在程序載入階段映射成一個完整的虛擬內(nèi)存,更好的做到內(nèi)存對齊废岂。

下面列舉一些常見的 Section祖搓。

Section 用途
__TEXT.__text 主程序代碼
__TEXT.__cstring C 語言字符串
__TEXT.__const const 關(guān)鍵字修飾的常量
__TEXT.__stubs 用于 Stub 的占位代碼,很多地方稱之為樁代碼湖苞。
__TEXT.__stubs_helper 當(dāng) Stub 無法找到真正的符號地址后的最終指向
__TEXT.__objc_methname Objective-C 方法名稱
__TEXT.__objc_methtype Objective-C 方法類型
__TEXT.__objc_classname Objective-C 類名稱
__DATA.__data 初始化過的可變數(shù)據(jù)
__DATA.__la_symbol_ptr lazy binding 的指針表拯欧,表中的指針一開始都指向 __stub_helper
__DATA.nl_symbol_ptr 非 lazy binding 的指針表,每個表項中的指針都指向一個在裝載過程中财骨,被動態(tài)鏈機器搜索完成的符號
__DATA.__const 沒有初始化過的常量
__DATA.__cfstring 程序中使用的 Core Foundation 字符串(CFStringRefs
__DATA.__bss BSS镐作,存放為初始化的全局變量藏姐,即常說的靜態(tài)內(nèi)存分配
__DATA.__common 沒有初始化過的符號聲明
__DATA.__objc_classlist Objective-C 類列表
__DATA.__objc_protolist Objective-C 原型
__DATA.__objc_imginfo Objective-C 鏡像信息
__DATA.__objc_selfrefs Objective-C self 引用
__DATA.__objc_protorefs Objective-C 原型引用
__DATA.__objc_superrefs Objective-C 超類引用




ASLR

Bitcode的工作流程及安全性評估

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市该贾,隨后出現(xiàn)的幾起案子羔杨,更是在濱河造成了極大的恐慌,老刑警劉巖靶庙,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件问畅,死亡現(xiàn)場離奇詭異,居然都是意外死亡六荒,警方通過查閱死者的電腦和手機护姆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掏击,“玉大人卵皂,你說我怎么就攤上這事⊙馔ぃ” “怎么了灯变?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捅膘。 經(jīng)常有香客問我添祸,道長,這世上最難降的妖魔是什么寻仗? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任刃泌,我火速辦了婚禮,結(jié)果婚禮上署尤,老公的妹妹穿的比我還像新娘耙替。我一直安慰自己,他們只是感情好曹体,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布俗扇。 她就那樣靜靜地躺著,像睡著了一般箕别。 火紅的嫁衣襯著肌膚如雪铜幽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天究孕,我揣著相機與錄音啥酱,去河邊找鬼。 笑死厨诸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的禾酱。 我是一名探鬼主播微酬,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绘趋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了颗管?” 一聲冷哼從身側(cè)響起陷遮,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎垦江,沒想到半個月后帽馋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡比吭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年绽族,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衩藤。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吧慢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赏表,到底是詐尸還是另有隱情检诗,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布瓢剿,位于F島的核電站逢慌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏间狂。R本人自食惡果不足惜攻泼,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望前标。 院中可真熱鬧坠韩,春花似錦、人聲如沸炼列。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俭尖。三九已至氢惋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稽犁,已是汗流浹背焰望。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留已亥,地道東北人熊赖。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像虑椎,于是被迫代替她去往敵國和親震鹉。 傳聞我的和親對象是個殘疾皇子俱笛,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

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