在windows上可執(zhí)行文件的格式是exe,在Linux上ELF是可執(zhí)行文件,而在蘋果系統(tǒng)上,Mac OS X和ios系統(tǒng)上,可執(zhí)行文件的格式是Mach-O格式。官方解釋地址:https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachORuntime/index.html
1、Mach與Mach-O
這里先提醒大家一下,Mach不是Mac,Mac是蘋果電腦Macintosh的簡稱,而Mach則是一種操作系統(tǒng)內(nèi)核。Mach內(nèi)核被NeXT公司的NeXTSTEP操作系統(tǒng)使用。在Mach上,一種可執(zhí)行的文件格是就是Mach-O(Mach Object file format)。Mac OS X是Unix的“后代”,但所主要支持的可執(zhí)行文件格式是Mach-O。
iOS是從OS X演變而來,所以同樣是支持Mach-O格式的可執(zhí)行文件。
2、ios可執(zhí)行文件初探
作為ios開發(fā)者,我們比較熟悉ipa包,這種文件格式,然而,實際上這只是一個變相的zip的壓縮包。我們將其解壓之后發(fā)現(xiàn)
這其實是一個Payload的包 打開這個包 不能發(fā)現(xiàn) 這是一個test1.app的文件 也就是xcode生成的product文件
而這其實也就是一個文件夾,打開這個文件夾(顯示包含內(nèi)容)發(fā)現(xiàn)其中有一個同名的test1的可執(zhí)行文件,這就是我們最終要尋找的在ios上的可執(zhí)行文件
我們用file命令來查看這個文件的文件類型
這是一個64位的Mach-O 格式的可執(zhí)行文件
3、Mach-O文件細究
根據(jù)蘋果官方文檔提供Mach-O文件的數(shù)據(jù)主體可分為三個部分
頭部(Header)、加載命令(Load Commands)、數(shù)據(jù)(Data)
而數(shù)據(jù)部分則又被分割成了一段段的Segments。
下面我們使用otool工具來一探究竟
a) 先來查看下這個可執(zhí)行文件的頭部是怎樣的
一堆看不明白的東西,分別予以解釋
magic:0xfeedfacf 。這個東西是Mach-O的魔數(shù)。簡單介紹下什么叫做魔數(shù):很多類型的文件,其起始的幾個字節(jié)的內(nèi)容是固定的(或是有意填充,或是本就如此)。根據(jù)這幾個字節(jié)的內(nèi)容就可以確定文件類型,因此這幾個字節(jié)的內(nèi)容被稱為魔數(shù) (magic number)。
在OS X上 可執(zhí)行文件的標識有這樣幾個魔數(shù):cafebabe、feedface、feadfacf、還有就是以#!開頭的。
cputype、cpusubtype:指的是CPU類型和CPU子類型
在/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/mach里找到machine.h的定義 如下圖
這里有支持的cpu的詳細編號命名 0代表ARM的格式都將支持
caps: 額 官網(wǎng)上都沒介紹 這個待查
filetype 2:代表可執(zhí)行文件
ncmds:指的是加載命令(load commands)的數(shù)量,例子中一共22個,編號0-21
sizeofcmds:表示要load的22個commands的總的大小
flags:可用來檢驗是否開啟了PIE,如開啟了則需要移除方能正常使用MachOView才能把文件結(jié)構(gòu)檢測出來
它有這么幾種定義值:我們的文件無未定義引用?(MH_NOUNDEFS),是為動態(tài)鏈接器(MH_DYLDLINK),使用two-level名Cheng綁定(MH_TWOLEVEL)且應(yīng)被加載到隨機地址?(MH_PIE).
b)再來看加載命令(Load Commands) 截取其中一個Commands來分析
Load command 0 :command編號
cmdLC_SEGMENT_64:即是將文件中的段映射進內(nèi)存中的地址空間
segname:16字節(jié)段名稱
vmaddr:段虛擬內(nèi)存的起始地址
vmsize:段虛擬內(nèi)存的大小
fileoff:段在內(nèi)存中的偏移量
filesize:段在文件中的大小
maxprot:段頁面所需要的最高內(nèi)存保護
initprot:段頁面初始內(nèi)存保護
nsects:段中包含section的數(shù)量
flags:其他雜項標志位
c)接下來看data,注意到command和data都是以segment為大單元字節(jié),但是在data里還有section字節(jié) 所以重點介紹section的組織格式 截取一個section供分析
sectname:section的名字
segname:section歸屬為哪一個segment
addr:secction起始內(nèi)存地址
size:section的大小
offset:該section的文件偏移量
align:字節(jié)大小對齊
reloff:重定位入口的文件偏移
nreloc:需要重定位的入口數(shù)量
flags:包含了section的類型和獨自的屬性
最后兩項保留用
了解了這些 才能根據(jù)Mach-O的文件結(jié)構(gòu) 去分析類的名稱和類的方法 即是class-dump的實現(xiàn)原理,同時這也是MachOView等的分析原理
參考自:http://turingh.github.io/2016/03/07/mach-o%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F%E5%88%86%E6%9E%90/
附上otool命令大全
-f print the fat headers
-a print the archive header
-h print the mach header
-l print the load commands
-L print shared libraries used
-D print shared library id name
-t print the text section (disassemble with -v)
-p ? start dissassemble from routine name
-s print contents of section
-d print the data section
-o print the Objective-C segment
-r print the relocation entries
-S print the table of contents of a library
-T print the table of contents of a dynamic shared library
-M print the module table of a dynamic shared library
-R print the reference table of a dynamic shared library
-I print the indirect symbol table
-H print the two-level hints table
-G print the data in code table
-v print verbosely (symbolically) when possible
-V print disassembled operands symbolically
-c print argument strings of a core file
-X print no leading addresses or headers
-m don't use archive(member) syntax
-B force Thumb disassembly (ARM objects only)
-q use llvm's disassembler (the default)
-Q use otool(1)'s disassembler
-mcpu=arg use `arg' as the cpu for disassembly
-j print opcode bytes
-P print the info plist section as strings
-C print linker optimization hints