本文介紹Google Native Client的設計思路.

系統架構

      一個NaCl應用程序由許多可信和不可信NaCl模塊組成,每個模塊都在一個進程中單獨運行。假想一個基於NaCL實現的,用於管理和分享圖片的應用,它由兩個組件構成,一個用javascript實現的用戶界面接口,運行在web瀏覽器中,另一個是圖片處理庫,作爲一個Native Client模塊實現。在這個場景,兩者都是不可信的。瀏覽器組件通過瀏覽器執行環境進行限制,而圖片處理庫通過Native Client容器進行限制。在運行這個圖片應用前,用戶必須將Native Client作爲瀏覽器插件進行安裝,我們將它視爲一個可信的NaCl模塊。

      當用戶訪問我們這個圖片應用的web站點時,瀏覽器會加載並執行應用程序的javascript組件部分,後者會通過Native Client插件去加載圖片處理庫到Native Client容器中來。這個加載過程不會跳出提示窗口的,Native Client負責對本地應用模塊的行爲進行限制。

      每個組件都運行在它私有的地址空間內。組件間通信是通過NaCl的可靠數據包服務---模塊間通信(IMC)來進行的。NaCl爲瀏覽器和NaCl模塊之間的通信提供了兩種方式:簡單遠程過程調用(SRPC),Netscape插件應用程序接口(NPAPI),這兩者都基於IMC實現。爲了避免訪高頻率的通信帶來的消息負擔,IMC還提供了共享內存段和共享同步對象。

      NaCl模塊還可以訪問”服務運行時”,它提供了內存管理操作,創建線程和其他系統服務。這個接口和操作系統的系統調用接口類似。應用程序中可以包含多個Native Client模塊,並且可信和不可信模塊都可以使用IMC。例如,上面說的圖片應用的用戶可以選擇安裝一個可信的NaCl服務,用於圖片的本地存儲。由於它能訪問本地硬盤,因此這個存儲服務必須作爲一個本地的瀏覽器插件安裝,而不能作爲一個Native Client模塊實現。在應用程序初始化時,用戶接口會檢查這個存儲服務是否可用,若可用,則會與它建立一個IMC通信通道,並把這個通信通道的描述符傳遞給圖片處理庫,從而是兩者可以通過基於IMC的服務(SRPC,共享內存等)進行直接通信。這種情況下,NaCl模塊一般會靜態鏈接到一個負責提供過程接口來訪問存儲服務的庫,這個庫對底層的IMC層面的通信細節實現進行封裝,從而不用理會底層到底使用的是SRPC還是共享內存。存儲服務組件假定圖片處理庫是不可信的,並確保只執行符合接口契約的服務。

      類似上面存儲服務這樣的組件一般在NaCl內核以外實現,這類似操作系統的微內核模型,簡單,穩定,鬆耦合。

      下面來詳細介紹NaCl系統的各個組成部分的設計。

內外兩層沙箱

       Native Client基於x86進程內“內層沙箱“構建而成。而爲了進一步防禦攻擊,還構建了一個”外層沙箱“用於進程邊界的隔離。

      內層沙箱使用代碼靜態分析技術來在不可信的x86代碼中檢查安全隱患。以前這樣的分析受到了很多技術的挑戰,比如說可自我修改的代碼,重疊指令。爲了解決這樣的問題,在Native Client中爲代碼制定了一系列契約和結構規則,從而確保本地代碼模塊能可靠地進行分解,然後通過代碼驗證器來確保可執行文件只包含合法指令集中的指令。

      內層沙箱還利用了x86內存分段機制來限制數據和指令的內存引用。

內層沙箱用於在一個本地進程中創建一個安全的子域。在這個子域中我們可以將一個可信的運行時服務(Service Runtime)子系統和不可信模塊放置在同一個進程中。通過一個安全的跳躍/計分板機制來允許可信代碼和不可信代碼之間的控制轉移。

內層沙箱不僅將系統與本地模塊進行分離,而且還使得本地模塊與操作系統進行了分離。

外層沙箱是第二道防禦機制。它會對運行NaCl模塊的進程的所有系統調用,通過與一個允許的系統調用白名單進行比對來拒絕或通過此調用。目前白名單上允許的系統調用有46個。

運行時機制

      IMC允許可信和不可信模塊之間越過進程邊界發送/接收包含無類型字節數組的數據包,還可以包含用於文件共享,共享內存對象,通信通道的”NaCl資源描述符“。第一種方式是SRPC,它用於定義和使用子過程來跨模塊邊界訪問,包括從瀏覽器中的javascript調用NaCl代碼。第二種是NPAPI,它提供了與瀏覽器狀態交互的接口,包括打開URL,訪問DOM。這兩種機制都可以用來與瀏覽器進行交互,包含頁面內容修改,處理鼠標和鍵盤事件,獲取其他站點內容,因爲這些都可以通過javascript訪問。

      如上所說,運行時服務負責爲NaCl模塊之間,以及和瀏覽器之間交互提供容器。它一般會提供應用程序編程環境相適應的一系列系統服務。爲了支持malloc/free接口或其他內存分配抽象機制,它提供了sbrk()和mmap()系統調用。它提供了POSIX線程接口的一個子集,用於線程的創建和銷燬,條件變量,互斥量,信號量,線程本地存儲。它還提供了常用的POSIX文件I/O接口,用於通信通道以及基於web的只讀內容的操作。爲了防止惡意的網絡訪問,connect()和accept()這樣的系統調用不可使用。

攻擊層面

      從內而外,有以下幾個層面會受到攻擊者關注:

      1)內層沙箱:二進制驗證

      2)外層沙箱:OS系統調用截取

      3)服務運行時二進制模塊加載器

      4)服務運行時跳轉接口

      5)IMC通信接口

      6)NPAPI接口


      除了內外層沙箱,系統還包含了CPU和NaCl模塊白名單機制。

下面來看各個組件詳細的實現細節

內層沙箱

      這層僅限於機器代碼中明顯的控制流(即調用和跳轉)。其他類型的控制流(如異常)在NaCl運行時服務中處理。

      內層沙箱定義了一系列用於可靠分解的規則,一個用於監視這些規則的編譯工具鏈,以及一個確保代碼符合規則的靜態代碼分析器,通過這三者來實現代碼的可信檢查。

NaCl模塊二進制代碼規則:

C1 Once   loaded   into   the   memory,   the   binary   is  not   writable, 
enforced by OS-level protection mechanisms during execution. 
C2 The   binary   is   statically   linked   at   a  start   address   of   zero,with the first byte of text at 64K.  
C3 All   indirect control transfers use   a nacljmp   pseudo-   instruction (defined below). 
C4 The   binary   is   padded   up   to   the   nearest   page   with   at  
one hlt instruction (0xf4).   
C5 The binary contains no instructions or pseudo-instructions   overlapping a 32-byte boundary.   
C6 All   valid instruction addresses are   reachable by   a   fall-   through disassembly that starts at the load (base) address.     

C7 All direct control transfers target valid instructions.                                                                            


      使用80386段來限制在虛擬32位地址空間中的連續子段中的數據引用。同時禁止一些操作碼的使用,比如syscall和int(不可信代碼就無法直接調用系統調用),所有修改x86段狀態的指令(如lds等),ret。允許hlt指令,但它會導致模塊立即終結。處於安全考慮,還禁止所有其他的特權0級的指令,因爲在一個正確的用戶態指令流中不該使用。(其他的細節不翻譯了,太晦澀了)

      我們發現使用的驗證器能以30MB/s的速度對代碼進行檢查,這與下載數據相比非常小,因此並不會造成性能上的問題。

外層沙箱

      若內存沙箱被攻破,不可信代碼就可以對運行時服務進行訪問,但此時外層沙箱可以對進程邊界的系統調用進行控制來阻止大部分危險行爲。Linux上的實現使用了ptrace接口,在windows上將會採用訪問控制列表(目前只有Linux上的實現,Mac和Windows還在開發)。

      Linux上的實現將NaCl容器作爲一個子進程啓動,然後使用ptrace對進程發出的所有系統調用進行檢查。外層沙箱維護有一個允許的系統調用白名單,試圖執行任何白名單以外的系統調用都會導致NaCl模塊立即結束。

      但目前使用ptrace的 這個實現增加了每個系統調用的負擔,因爲即使它在白名單中,也會進行兩次上下文切換以及一次查表,從而確定此調用是否被允許。因此對系統性能會有一定影 響,也就要求開發者在使用系統調用方面謹慎,並儘量減少模塊間通信頻度,可以考慮使用共享的內存和同步對象,這對於高訪問頻率,高帶寬通信的應用獲得最優 性能非常重要。

異常

      不允許使用硬件異常(段錯誤,浮點數異常)和外部中斷。爲了隔離異常,每個NaCl模塊都在一個單獨進程中運行,無法使用異常處理來從硬件異常中恢復過來,而是這個進程會被關閉。雖然不支持硬件異常,但確實支持c++異常,但不支持Windows結構化異常處理(WSEH).

服務運行時

      服務運行時是一個本地可執行代碼,它被一個NPAPI插件調用,這個插件也用來支持服務運行時和瀏覽器之間的交互。它實現了dynamic enforcement機制,這個機制維護了內層沙箱的完整性,並提供了一個資源抽象層,從而使得NaCl應用程序與主機資源以及操作系統接口分離開。它包含了可信的代碼和數據,但與包含的NaCl模塊共享同一個進程,且後者可以通過一個受控的接口去訪問服務運行時。服務運行時通過x86的內存分段和分頁機制相結合的方式來阻止不可信代碼的非法內存訪問行爲。

      當一個NaCl模塊被加載進來,它就被置於一個服務運行時地址空間中段分隔的256M區域內。NaCl模塊地址空間(NaCl”用戶”空間)的前64Kb被服務運行時保留,用於初始化。前4kb受讀寫保護,用於檢查空指針。剩下的60Kb包含了實現“跳躍”調用門和”記分板“返回門的可信代碼段。不可信的NaCl模塊緊隨64kb後的區域加載進來。

(筆記:在每個native plug-in process的內部都有一段代碼叫Service Runtime,用來模擬傳統的system call,plug-in的system call會被重定向到這個進程內的Service Runtime,即從untrust code切換到trust code。在進入Service Runtime之後,系統恢復我們熟悉的flat memory addressing,來訪問操作系統的資源。當然,只有少數安全的system call纔會被Service Runtime處理。first level sandbox中的static analysis會保證unstruct native code不能隨意進入Service Runtime.)

通信

      IMC是NaCl模塊之間通信的基礎。它基於NaCl socket而實現,提供了一個類似Unix domain socket的雙向,可靠,有序的數據包服務。當一個不可信NaCl模塊被創建時(通過DOM和javascript來創建),它會接收到第一個NaCl socket。Javascript使用這個socket向NaCl模塊發送消息,也可以將其共享給其他NaCl模塊。JavaScript還可以將NaCl socket共享爲NaCl 描述符,從而選擇將模塊連接到其他服務上去。NaCl描述符還可以用來創建共享的內存段。

      通過使用NaCl消息,NaCl的SRPC完全在不可信代碼中實現。使用SRPC可以定義JavaScript和NaCl模塊之間,或兩個NaCl模塊之間的過程接口,它支持一些基本類型(int,float,char),數組以及NaCl描述符。像XDR或Protocol Buffers這些外部數據表示層也可以很輕鬆地置於NaCl消息或SRPC之上

      NPAPI的實現也基於IMC,並支持常用NPAPI接口的一個子集。目前支持的操作包括對瀏覽器中腳本對象的屬性和方法的讀,修改和調用,簡單光柵圖形的繪製,提供createArray方法創建數組以及像文件描述符一樣打開和使用URL。後續的改進還在考慮中。

http://www.cnblogs.com/phinecos/archive/2008/12/12/1353816.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章