內(nèi)核擴展模塊
類似用戶態(tài)的共享庫或動態(tài)鏈接庫陡鹃,內(nèi)核擴展模塊是內(nèi)核使用的模塊冀痕,可以根據(jù)需要動態(tài)插入或移除,而且這個需求通常來自用戶態(tài)。OS X 和 iOS 中的XNU都利用了模塊技術(shù)來加載各種設(shè)備的驅(qū)動程序甩挫,因此在一個完全自包含的子系統(tǒng)中增強內(nèi)核的功能。
擴展內(nèi)核的功能
在用戶態(tài)有動態(tài)鏈接庫(Windows)和共享目標(biāo)(UNIX)機制椿每,因此在內(nèi)核態(tài)也有內(nèi)核模塊的機制:在XNU中伊者,內(nèi)核模塊稱為內(nèi)核擴展,簡稱為kext间护。和XUN核心一樣亦渗,kext也是XNU的基礎(chǔ)構(gòu)建塊。事實上汁尺,通過模塊插入的內(nèi)核代碼往往比內(nèi)核核心本身的代碼還要多法精。內(nèi)核擴展運行在內(nèi)核態(tài),因此能夠完全訪問內(nèi)核空間。開發(fā)者可以使用內(nèi)核導(dǎo)出的所有函數(shù)搂蜓,設(shè)置可以使用內(nèi)核定義為私有的函數(shù)狼荞。內(nèi)核對全局變量和數(shù)據(jù)結(jié)構(gòu)都可以訪問甚至修改,因此內(nèi)核擴展成為各種內(nèi)核級開發(fā)的首選方案帮碰。在內(nèi)核態(tài)還可以執(zhí)行性能剖析粘秆、系統(tǒng)調(diào)用掛鉤以及其他功能。在iOS中收毫,用戶態(tài)和內(nèi)核態(tài)都是固化的,企圖阻止任何形式的修改殷勘。因此盡管各種i-設(shè)備上也廣泛使用了內(nèi)核擴展模塊提供的功能此再,但是蘋果在為每一類設(shè)備構(gòu)建iOS時,都將這些內(nèi)核擴展固化到kernelcache中了(不過這些內(nèi)核擴展確實是在運行時從kernelcache中動態(tài)加載的)玲销。
模塊化結(jié)構(gòu)的安全設(shè)計
- 代碼簽名:代碼簽名是首選的方法输拇,如今已被大多數(shù)系統(tǒng)采納為標(biāo)準(zhǔn)。Windows是一個典型實例贤斜,只允許加載具有合法數(shù)字簽名的驅(qū)動程序策吠。在控制權(quán)轉(zhuǎn)交給模塊入口點之前,內(nèi)核會驗證代碼簽名瘩绒,代碼簽名保存在附加的證書中猴抹。證書必須通過私鑰簽名,內(nèi)核已知公鑰锁荔,內(nèi)核也可以通過一個信任鏈獲得這樣一個密鑰蟀给。蘋果在iOS 中到處都使用了代碼簽名,不過只對自己的代碼進行了簽名阳堕。
- 預(yù)鏈接:預(yù)鏈接(pre-link)是蘋果在OS X 和 iOS 中使用的方法跋理。引導(dǎo)加載器不按照先加載內(nèi)核,再以一定順序加載kext的方式進行加載恬总,而是加載一個kernelcache文件前普。這個文件包含了內(nèi)核,并且預(yù)鏈接了經(jīng)過選擇的擴展壹堰。結(jié)果和通過內(nèi)核動態(tài)加載的這些擴展是一樣的拭卿。預(yù)鏈接的兩點優(yōu)勢:
- 加載的熟讀快得多,因為動態(tài)加載的過程需要在運行時解析內(nèi)核和模塊中的符號缀旁,而預(yù)鏈接只需要進行一次這種解析操作记劈,要加載的內(nèi)核已經(jīng)完成了模塊的加載工作,所有的鏈接地址都已經(jīng)完成了解析
- kernelcache可以添加簽名并巍,設(shè)置還可以加密(iOS就加密了)目木。一旦加載了kernelcache,就可以禁止所有kext加載。這樣可以阻斷代碼進入iOS內(nèi)核的合法通道
內(nèi)核擴展(kext)
內(nèi)核擴展沒有鏈到kernelcache時是單獨存在的刽射,可以在/System/Library/Extensions 目錄中找到這些kext军拟。這里大部分kext都是設(shè)備驅(qū)動程序。內(nèi)核擴展可以互相依賴誓禁。每一個kext都有一個加載索引字段(Index)和一個“引用”字段(Refs)懈息。后者表示這個kext依賴的kext個數(shù),而前者表示這個kext所在列表中的索引摹恰,依賴其他kext的kext會使用到這個列表辫继。每一條kext記錄后面尖括號中的值表示當(dāng)前kext依賴的kext索引。
kext結(jié)構(gòu)
kext實際上是bundle俗慈,因此遵循通用的bundle布局:一個kext目錄只包含一個子目錄Contents/姑宽,Contents/子目錄中的文件如下表:
文件/子目錄 | 內(nèi)容 |
---|---|
CodeDiretory | kext的代碼目錄文件 |
CodeRequirements | kext的代碼需求設(shè)置 |
CodeResources | 代碼資源XML文件,包含kext中文件的散列和規(guī)則 |
CoderSignature | kext的代碼簽名:通常包含蘋果的數(shù)字證書 |
Info.plist | bundle清單屬性列表 |
MacOS | 這個目錄下包含了實際的kext為禁止代碼闺阱,二進制代碼文件的類型為BUNDLE(Mach-O類型8)或KEXTABUNDLE(因故64位炮车,Mach-O類型11) |
_CodeSignature | 包含Code文件的目錄,這些文件實際上是Code文件的符號連接 |
version.plist | 表示kext版本信息是屬性列表 |
kext安全需求
由于kext包含了要加載到內(nèi)核內(nèi)存中的代碼酣溃,所以必須有額外的安全措施保障不能隨意加載任何代碼瘦穆,因此也不會不小心加載潛在的惡意代碼。因此對kext有以下要求:
- kext 的所有者uid必須為root赊豌,gid必須為wheel扛或。
- kext 中目錄的權(quán)限最多755,即rwxrwxr-x
- kext 中任何文件的權(quán)限最多644(rw-r--r--)
內(nèi)核擴展的相關(guān)操作
kext相關(guān)的命令如下:
命令 | 用戶 |
---|---|
kextd | 在用戶空間動態(tài)加載kext |
kextfind | 通過各種各樣的屬性和標(biāo)準(zhǔn)查詢kext碘饼。模擬kextd的操作告喊,因為這條命令查找動態(tài)加載的kext |
kextlibs | 解析kext的依賴性 |
kextload | 一個簡單的kext加載器 |
kextunload | 一個簡單的kext卸載器 |
textutil | 改進版本的kextload,通過了更多的選項 |
kernelcache
在OS X 中派昧,kernelcache提供完整的內(nèi)核黔姜,針對操作系統(tǒng)運行的特定平臺進行優(yōu)化,預(yù)加載所有必要的驅(qū)動蒂萎,從而加快引導(dǎo)進程秆吵。在iOS 中,kernelcache 只包含內(nèi)核要加載的kext五慈,不含其它任何kext纳寂。這種機制是的iOS內(nèi)核更加安全且不容易篡改。在OS X 和 iOS 兩個平臺上的kernelcache都采用了相同的結(jié)果泻拦,實現(xiàn)稍有區(qū)別:
操作系統(tǒng)|內(nèi)容
OS X | Mach-O 二進制格式毙芜,可能是胖二進制格式,在偏移量384位置處帶有complzss
iOS | IMG3 加密個數(shù)的kernelcache争拐,和 OS X 一樣采用了complzss 壓縮
multi-kext
kernelcache 只是OS X 和 iOS中使用的兩種預(yù)鏈接形式之一腋粥。還有一種形式稱為multi-kext存檔,檢查mkext。這種文件只不過是兩個或多個kext的存檔隘冲,類似于kernelcache闹瞧,但是不包含內(nèi)核本身。file命令和其他命令都不失敗mkext文件展辞,但是我們可以通過這個二進制文件第一行中的“MKXTMOSX”簽名簡單地識別出這類文件奥邮。
從程序員的視角看kext
從程序員的角度看,kext只不過是一個內(nèi)核態(tài)的目標(biāo)文件罗珍,在內(nèi)核態(tài)鏈接洽腺,而不是用戶態(tài)鏈接的用戶態(tài)庫。創(chuàng)建內(nèi)核擴展本身的步驟如下:
(1)啟動Xcode覆旱,然后在System Pkug-ins 面板中選中Generic Kernel Extension
(2)Xcode 自動定義了kext入口點和退出點
(3)在Xcode的plist編輯器中直接編輯Info.plist文件
(4)編譯代碼已脓,可以在GUI中編譯,如果更喜歡用CLI的話通殃,可以通過xcodebuild(1)命令編譯
kext的內(nèi)核支持
kext是XNU中獨特的組件,因為kext表示的組件既不屬于Mach也不屬于BSD厕宗。此外画舌,盡管內(nèi)核中的大部分代碼都是用C語言編寫的,而XNU中管理kext部分的代碼是用C++編寫的已慢。I/O Kit是在kext支持的基礎(chǔ)上創(chuàng)建的曲聂,也是用C++編寫的。
Mach 的 kmod 支持
XNU 的 Mach 層被擴贊為支持內(nèi)核模塊(kernel module)佑惠。盡管Mach 層并不知道kext的存在朋腋,但是支持kmod對象,即內(nèi)核模塊對象膜楷。libKern
盡管kmod_info_t 仍然是kext的基本機構(gòu)旭咽,但是大部分kext處理邏輯都轉(zhuǎn)移到libkern目錄中,并且用C++重寫了赌厅。kext 的維護邏輯現(xiàn)在在likern/c++/OSKext.cpp 文件中穷绵,并且通過I/O Kit 框架暴露給用戶態(tài)。kext加載的幕后原理
加載kext的最初請求來自于用戶態(tài)特愿,但是實際的kext加載需要涉及內(nèi)核的內(nèi)存操作仲墨,因此必須在內(nèi)核態(tài)中完成。為了連接用戶態(tài)和內(nèi)核態(tài)揍障,kext的加載機制使用了Mach 消息目养。所有的kext操作都封裝為序列化的XML格式,并放在Mach kext_request 消息的 oll_descriptors 數(shù)據(jù)中毒嫡。這些消息屬于host_ptiv 子系統(tǒng)的一部分癌蚁,自然需要訪問主機的特權(quán)端口。Mach 消息最終會涉及mach_msg_trap,從用戶態(tài)轉(zhuǎn)移到內(nèi)核態(tài)匈勋。