BSD 高級(jí)功能
內(nèi)存管理
虛擬內(nèi)存管理是在Mach 層進(jìn)程的,Mach 控制了分頁器,并且向用戶態(tài)導(dǎo)出了各種vm_和mach_vm_消息接口。而用戶態(tài)的開發(fā)者大部分都只知道標(biāo)準(zhǔn)的POSIX 調(diào)用,因此需要對(duì)這些Mach 調(diào)用進(jìn)行封裝。BSD也使用了屬于自己的內(nèi)存管理函數(shù)。
POSIX 內(nèi)存和頁面管理系統(tǒng)調(diào)用
POSIX 為開發(fā)人員提供了多種API,用于虛擬內(nèi)存頁面的管理和嚴(yán)格控制。函數(shù)所在的頭文件為<sys/mman.h>這些函數(shù)是對(duì)Mach VM 原語的包裝,因?yàn)镸ach VM 原語才真正負(fù)責(zé)處理Mach 虛擬內(nèi)存。這些函數(shù)執(zhí)行基本的參數(shù)肩擦,然后通過current_map( ) 獲得當(dāng)前的Mach 內(nèi)存映射,最后再調(diào)用底層的Mach 函數(shù)。
BSD 內(nèi)部的內(nèi)存函數(shù)
BSD 層要求有自己的內(nèi)存管理函數(shù),這些函數(shù)自然是架構(gòu)在Mach 之上。這些函數(shù)在XNU 的BSD 應(yīng)用廣泛,但是沒有暴露給用戶態(tài)。
- BSD的MALLOC 和 zone :BSD 的malloc 系列函數(shù)<bsd/sys/malloc.h> 頭文件中。函數(shù)名為_MALLOC、_FREE、_REALLOC、_MALLOC_ZONE、_FREE_ZONE
- mcache 和 slab 分配器:這是BSD 提供的基于緩存的非常高校的內(nèi)存分配方法,默認(rèn)是實(shí)現(xiàn)是使用任何后端 slab 分配器。使用mcache 機(jī)制的主要優(yōu)點(diǎn)是速度:內(nèi)存分配和維護(hù)是在每一個(gè) CPU 自有的緩存中進(jìn)行的,因此可以映射到CPU的物理緩存,從而極大地提升訪問速度
內(nèi)存壓力
Mach VM 層支持 VM 壓力(pressure)的概念,這個(gè)概念表示的意思是系統(tǒng)的可以RAM量低到危險(xiǎn)的程度了。VM壓力的處理放在BSD 層進(jìn)程,BSD層還提供了一個(gè)系統(tǒng)調(diào)用 vm_pressure_monitor( ),這個(gè)調(diào)用直接封裝了Mach對(duì)應(yīng)的調(diào)用。當(dāng)系統(tǒng)發(fā)送內(nèi)存壓力通知,iOS 中的 Objective-C 應(yīng)用就會(huì)響應(yīng)。Objective-C的垃圾回收機(jī)制使用了libauto,libauto調(diào)用libdispatch 創(chuàng)建一個(gè)VM 壓力分發(fā)源,還會(huì)調(diào)用應(yīng)用程序提供的didReceiveMemoryWarning回調(diào)函數(shù)。
Jestam/Memorystatus(iOS)
進(jìn)程并不是總能找到可以拋棄的內(nèi)存時(shí),這時(shí)需要采用Jestam機(jī)制。這是OS X 和 iOS 實(shí)現(xiàn)的一個(gè)低內(nèi)存清晰的處理機(jī)制。也稱為Memorystatus,這個(gè)機(jī)制有點(diǎn)類似于Linux的“Out-of-Memory”殺手,最初的目的就是殺掉消耗太多內(nèi)存的進(jìn)程。Memorystatus維護(hù)了兩個(gè)列表:
- 快照列表:保存系統(tǒng)中所有進(jìn)程的狀態(tài)以及消耗的內(nèi)存頁面數(shù)
- 優(yōu)先級(jí)列表:保存要?dú)⒌舻膫溥x進(jìn)程
進(jìn)程休眠(iOS)
在iOS 5中,Jstsam/Memorystatus 和默認(rèn)的freezer 結(jié)合在一起,實(shí)現(xiàn)了對(duì)進(jìn)程的冷凍而不是殺死。通過這種方式可以提供更好的用戶體驗(yàn),因?yàn)閿?shù)據(jù)不會(huì)丟失,而且當(dāng)內(nèi)存情況好轉(zhuǎn)時(shí)進(jìn)程可以安全恢復(fù)。進(jìn)程休眠操作是有jstsam_hibernate_top_proc 完成的,這個(gè)函數(shù)冷凍底層的任務(wù)(通過task_freeze)。冷凍操作需要遍歷任務(wù)的vm_map,然后將vm_map 傳遞給默認(rèn)的 freezer。用戶態(tài)也可以通過pid_suspend( ) 和 pid_resume( )控制進(jìn)程的休眠。iOS 還定義了 pid_hibernate,這個(gè)函數(shù)目前會(huì)忽略傳入的參數(shù),僅僅喚醒kernel_hibernation_thread(即通過kern_hibnernation_wakeup發(fā)送信號(hào))。
內(nèi)核地址空間布局隨機(jī)化(ASLR)
這項(xiàng)技術(shù)是在 Mountain Lion 引入的。現(xiàn)在已經(jīng)成為操作系統(tǒng)想要阻止黑客和惡意軟件視圖注入代碼攻擊的必備技術(shù)。防御代碼注入的主要方法是數(shù)據(jù)執(zhí)行阻止(Data Execution Prevention,DEP,在Intel 中也稱為W^X或XD,在ARM中也稱為XN),DEP能使得黑客注入代碼的企圖更加困難。
工作隊(duì)列
工作隊(duì)列(work queue)是OS X 中開發(fā)的一項(xiàng)機(jī)制,作用是為用戶通訊提供多線程并且支持?jǐn)U展到多處理器支持。工作隊(duì)列也是蘋果Grand Central Dispatch(GCD)的基礎(chǔ)機(jī)制。工作隊(duì)列是通過兩個(gè)系統(tǒng)調(diào)用提供的:
- workq_open( ):創(chuàng)建一個(gè)工作隊(duì)列,LibC 中的 pthread_workqueue_create_np 函數(shù)封裝了這個(gè)系統(tǒng)調(diào)用,而這個(gè)庫函數(shù)進(jìn)一步被GCD 和 libdispatch 中的 dispatch_get_global_queue 函數(shù)封裝了
- workq_kernturn( ):負(fù)責(zé)創(chuàng)建工作隊(duì)列以外的其他所有的事情,通過以下3個(gè)定義選項(xiàng)對(duì)工作隊(duì)列進(jìn)行控制:
- WQOPS_QUEUE_ADD:對(duì)應(yīng)的是要執(zhí)行(用GCD 的話說就是分發(fā)(dispatch))的代碼塊(block)或函數(shù)。libdiapatch 實(shí)際上對(duì)每一個(gè)隊(duì)列都創(chuàng)建了兩份副本,額外的那一份副本用于overcommit(過量使用),不過這些額外的副本沒有直接暴露給調(diào)用者。通過這種方式,應(yīng)用程序的主隊(duì)列實(shí)際上只不過是默認(rèn)隊(duì)列的引用,并且設(shè)置了overcommit。overcommit位表示這個(gè)隊(duì)列可以創(chuàng)建新的線程,通常情況下不建議使用這個(gè)策略,因?yàn)榫€程多于CPU數(shù)會(huì)降低程序的運(yùn)行速度,GCD將通過dispatch_get_global_queue 調(diào)用可以接受的一個(gè)標(biāo)志(DISPATCH_QUEUE_OVERCOMMIT)來支持overcommit,但是蘋果的文檔掩蓋了這個(gè)事實(shí),宣傳這個(gè)標(biāo)志必須為0
- WQOPS_THREAD_SETCONC:控制工作隊(duì)列的并發(fā)性,pthread_workqueue_requestconcurrencu_np( ) 調(diào)用封裝了這個(gè)選項(xiàng)
-
WQOPS_THREAD_RETURN:將線程從工作隊(duì)列分開并終止線程。pthread 的 workqueue_exit( ) 調(diào)用通過一個(gè)內(nèi)部調(diào)用_thread_workq_return 封裝了這個(gè)選項(xiàng)
工作隊(duì)列設(shè)置(由項(xiàng)的添加而出發(fā))的邏輯在XNU 中非常獨(dú)特。主要工作由 wq_runitem 完成的,wq_rnitem 調(diào)用 setup_wqthread 手工創(chuàng)建了工作隊(duì)列線程的狀態(tài),設(shè)置了每一個(gè)寄存器的值。然后喚醒線程,線程以新的身份運(yùn)行
換個(gè)角度看BSD層
sysctl
BSD 和很多其他UNIX 系統(tǒng)一樣,也提供了一個(gè)統(tǒng)一的接口用于獲取和設(shè)置內(nèi)核變量,這個(gè)接口稱為sysctl(8)。而和Linux 這樣的系統(tǒng)不同之處在于,sysctl 是訪問這些變量的唯一方法,因?yàn)槿鄙?proc這樣用戶可以見的文件系統(tǒng)。
kqueue
BSD 中引入 kqueue 的目的是為了替代伸縮性不好的poll(2)/select(2)模型。這個(gè)接口著重強(qiáng)調(diào)的方面是擴(kuò)展性,允許未來添加任意數(shù)目的事件源,而不需要對(duì)編程接口進(jìn)行修改。XNU 導(dǎo)出了兩個(gè)和kqueue相關(guān)的系統(tǒng)調(diào)用:
- kqueue(#362):負(fù)責(zé)創(chuàng)建kqueue,創(chuàng)建的keque實(shí)際上是一個(gè)文件描述符
- kevent/kevent64(#363和#369):用于設(shè)置事件過濾器以及從kqueue 中讀取事件
審計(jì)(OS X)
從內(nèi)核角度看,審計(jì)只不過是在系統(tǒng)調(diào)用的邏輯中穿插了一些宏的過程:
- AUDIT_SYSCALLL_ENTRY:調(diào)用sysent 表中的一條UNIX 系統(tǒng)調(diào)用之前調(diào)用這個(gè)宏。這個(gè)宏接受3個(gè)參數(shù):系統(tǒng)調(diào)用代碼(編號(hào))、BSD進(jìn)程以及負(fù)責(zé)這個(gè)調(diào)用的線程對(duì)象
- AUDIT_ARG:在系統(tǒng)調(diào)用的實(shí)現(xiàn)內(nèi)部調(diào)用。這個(gè)宏接受一個(gè)表示操作的參數(shù),以及其他可變參數(shù),其他參數(shù)具體取決于對(duì)應(yīng)的系統(tǒng)調(diào)用
- AUDIT_SYSCALL_EXIT:在系統(tǒng)調(diào)用的實(shí)現(xiàn)之后立即被調(diào)用。參數(shù)和ENTER的參數(shù)值,還接受一個(gè)系統(tǒng)調(diào)用的返回值
強(qiáng)制訪問控制(MAC)
強(qiáng)制訪問控制(Mandatory Access Control,MAC),這是蘋果從TrustedBSD 引入的一項(xiàng)強(qiáng)大的安全特性。用戶態(tài)的視角非常有局限性,只有內(nèi)核才能可靠地實(shí)施這種安全性。
MAC策略
從用戶態(tài)看,MAC策略只不過是一個(gè)不透明的對(duì)象。然而在內(nèi)核態(tài)中,策略是一個(gè)mac_policy_conf數(shù)據(jù)結(jié)構(gòu)。策略模塊在加載時(shí)通過mac_policy_register注冊(cè)這個(gè)數(shù)據(jù)機(jī)構(gòu),在退出時(shí)應(yīng)該通過mac_policy_unregister解除注冊(cè)這個(gè)結(jié)構(gòu)。
mac_polic_conf 數(shù)據(jù)結(jié)構(gòu)中的關(guān)鍵字段是 mpc_ops,這是一個(gè)指向mac_policy_ops 數(shù)據(jù)結(jié)構(gòu)的指針。mac_policy_ops 數(shù)據(jù)結(jié)構(gòu)是一個(gè)包含了300多個(gè)函數(shù)指針的巨大結(jié)構(gòu)體,每一個(gè)策略模塊都應(yīng)該實(shí)現(xiàn)其中的函數(shù)或留空(NULL)。這些函數(shù)指針基本上覆蓋了系統(tǒng)中的每一個(gè)操作,函數(shù)名遵循mpo_object_operation_call的命名約定,其中:
- object:表示對(duì)象類型:file(實(shí)際上是文件描述符)、port、socket、sysvsem、proc 和 vnode(文件本身)
- operation:可以是“l(fā)abel”和“check”。“l(fā)abel”操作表示標(biāo)簽相關(guān)的操作。“check”操作表示認(rèn)證一個(gè)系統(tǒng)調(diào)用或陷阱
- call:對(duì)于check,通常表示訪問檢查涉及的系統(tǒng)調(diào)用(或Mach陷阱)名稱;對(duì)于label,則表示標(biāo)簽聲明周期的一個(gè)階段,通常包括init、associate 和 destory,有時(shí)候還要其他特定的動(dòng)詞
當(dāng)XNU 調(diào)用MAC層驗(yàn)證一個(gè)操作時(shí),MAC層調(diào)用策略模塊,然后策略模塊負(fù)責(zé)進(jìn)行驗(yàn)證。所有的MAC檢查基本上都符合一個(gè)模板。例如,考慮一個(gè)非常有用的mac_vnode_check_signature操作,這個(gè)操作負(fù)責(zé)實(shí)施代碼簽名。
蘋果的策略模塊
OS X 中 MAC 的主要作用是沙盒機(jī)制,在iOS中通過MAC實(shí)現(xiàn)嚴(yán)格的代碼簽名和entitlement機(jī)制,使得蘋果可以保護(hù)自己珍貴的硬件不被可怕的第三方代碼破壞。
sandbox.kext
sandbox 內(nèi)核擴(kuò)展有時(shí)候會(huì)請(qǐng)求/usr/libexec/sandboxd的服務(wù)。這個(gè)守護(hù)程序是有l(wèi)aunchd(1)啟動(dòng)的,使用的主機(jī)特殊端口#14(通過HOST_SEATABELT_PORT定義)。sandbox.kext 實(shí)現(xiàn)了一個(gè)小型的類SCHEME方言,用于定義認(rèn)證和操作許可。這個(gè)文本格式在用戶態(tài)動(dòng)態(tài)編譯,然后提交給內(nèi)核用作之后的驗(yàn)證。驗(yàn)證則是另一個(gè)內(nèi)核擴(kuò)展AppleMatch.text的職責(zé),AppleMatch.kext 負(fù)責(zé)規(guī)則和正則表達(dá)式匹配
AppleMobileFileIntegrity.text
iOS 的安全機(jī)制比 OS X 的安全機(jī)制要嚴(yán)格得多。 OS X 中的代碼簽名是可選的,而iOS 會(huì)通過 kill -9 殺掉任何代碼簽名不正確的進(jìn)行。iOS 中的“壞警察”的職責(zé)是由AppleMobileFileIntegrity.kext(AMFI)扮演的。AMFI在用戶態(tài)有一個(gè)守護(hù)程序:/usr/libexec/amfid。這個(gè)守護(hù)程序是有l(wèi)aunchd 啟動(dòng)的,也注冊(cè)了一個(gè)主機(jī)特殊端口#18(HOSR_AMFID_PORT)。這個(gè)守護(hù)程序接收來自AMFI的消息,并且負(fù)責(zé)AMFI完成一些最好在用戶態(tài)完成的任務(wù)。
對(duì)initializeAppleMobileFileIntegrity函數(shù)調(diào)用了mac_policy_register,就像所有的策略模塊一樣。這個(gè)模塊大部分回調(diào)函數(shù)都是NULL,有用的函數(shù)包括:
- mpo_vnode_chek_exec:這個(gè)AMFI的回調(diào)函數(shù)返回1(表示允許vnode執(zhí)行),但是不會(huì)在設(shè)置代碼簽名的標(biāo)志位(CS_HARD和CS_KILL)之前返回1。這可以確保所有的進(jìn)程都必須進(jìn)行代碼簽名的檢查,而且之后如果需要代碼檢查的話也能殺掉進(jìn)程
- mpo_vnode_check_signature:這是AMFI的主邏輯,這個(gè)函數(shù)使用了amfid以其內(nèi)核內(nèi)的簽名緩存來驗(yàn)證文件的代碼簽名
- mpo_proc_check_get_task:這個(gè)函數(shù)保護(hù)task_for_pid 調(diào)用,作用是獲得任務(wù)的端口(從而能完全控制任務(wù))。這個(gè)函數(shù)檢查兩個(gè)entitlement(get-taks-allow 和task-for-pid-allow),還有一個(gè)調(diào)用用于檢查是否啟用了不受限制的調(diào)試(通過amfid),如果上訴任何返回真,那么這個(gè)函數(shù)也返回真
- mpo_proc_check_run_cs_invalid:檢查是否設(shè)置了entitlement:get-task-allow、run-invalid-allow以及run-unsigned-code,或者是否啟用了不受限制的調(diào)試。如果檢查返回真,那么cs_allow-invalid清除CS_KILL、CS_HARD和CS_VALID位,并且返回真,允許執(zhí)行未簽名的代碼
AMFI能(通過PE_parse_boot_argn)識(shí)別一些引導(dǎo)參數(shù),并且根據(jù)參數(shù)禁用一些檢查。如下表:
AMFI引導(dǎo)參數(shù) | 用途 |
---|---|
PE_i_can_has_debugger | 這個(gè)XNU中使用的全局引導(dǎo)參數(shù),表示允許附著調(diào)試器。禁用大多檢查 |
cs_debug | 禁用代碼簽名 |
cs_enfoecement_disable | 禁用代碼簽名;仍然會(huì)做檢查,但是僅此而已 |
amfi_allow_any_signature | 允許任何代碼簽名,不僅是蘋果的簽名 |
amfi_unrestrict_task_for_pif | 不論進(jìn)程是否有g(shù)et-task-allow和task_for_pid-allow entitlement,都允許task_for_pid |
amfi_get_out_of_my_way | 整個(gè)禁用AMFI。明顯是蘋果的開發(fā)者原卷了AMFI事事都要插手 |