# [P2IM: Scalable and Hardware-independent Firmware Testing via Automatic Peripheral Interface Modeling](https://www.usenix.org/conference/usenixsecurity20/presentation/feng) ## Abstract * 挑戰:嵌入式韌體的動態測試或模糊測試常受到硬體限制而失敗。 * 解決方法:P2IM框架,抽象化外圍設備、自動生成模型用於處理韌體I/O。 * 可無視外圍設備 ## Introduction * 針對的目標是**MCU**。 * 常見的模擬器無法模擬整個MCU的外圍設備,導致模糊測試失敗。 * 有些方法是將外圍設備相關轉到真實設備上去互動,但會降低執行速度和提高硬體依賴性而不實用。 * 新的方法P2IM,將Firmware binary當作輸入,用一個框架將Firmware和外圍設備包起來並同時處理,在兩者之間做模糊輸入和回傳。 * ![架構圖](https://i.imgur.com/HITb8dT.png) ## Overview * 問題:同上述,模擬器中做模糊測試的問題。 * 挑戰: * 硬體依賴性:需要真實硬體設備、硬體設備的速度比模擬慢、平行化被硬體限制。 * 多種類的外部設備:因為硬體依賴性的問題,有人提出使用全模擬的測試方式。但MCU太多種類,需要的外部設備也很多種類,不同型號、品牌可能規格都不同,造成模擬器需要特製而無法廣泛使用。 * 不同的OS、系統設計:不像常見的電腦OS,MCU使用更大型的操作集,有些甚至只有管理硬體的系統庫而非OS。 * 不兼容的模糊測試接口:因為MCU輸入通常與外部設備有關,所以有不同類型的協定。 * 雖然模糊測試本身也有挑戰,但該paper以解決MCU的模糊測試為重點。 --- * 設計了一個框架,可將模糊器當作元件裝在裡面,用可擴展、獨立在韌體上的方式去測試。 * 不模擬外部設備,不需要任何真實設備。 * 只需在模擬器需要的時候給予外部設備的輸入,即使和真實設備不同,只要模擬器可以接受,就可以避免中斷發生。 --- * 外部設備分為在晶片上的和不在晶片上的,本文只考慮在晶片上的。 * 外部設備的I/O接口分成`memory-mapped registers`、`interrupts`和`direct memory access`,本文只考慮前兩者,因為第三種難以通用化且MCU不常出現。 * 幫模擬器多定義了一個屬性叫`P2IE`(Processor-Peripheral Interface Equivalence),當模擬器模擬接口而非外部設備本身,且接口和韌體預期的接口一致時,P2IE滿足。 --- * 框架模型推倒的過程稱作`P2IM`(Processor-Peripheral Interface Modeling),包含兩個步驟: 1. 手動地為一大類的MCU架構定義抽象模型。 * 從MCU的文檔可以得到通用的patterns、conventions還有韌體和外部設備的接口等等。 2. 使用`explorative firmware executions`自動地將模型實體化。 * 給定韌體的MCU,實體化對應的抽象模型。像是暫存器映射的位置、暫存器之間的傳遞關係等等。 * 因為MCU異質性高,需要事先給定這些訊息來實現P2IE。 * 框架還包含了支援模糊器元件化的部分,可以將任意模糊器當作一個黑盒輸入到框架中。 ## 抽象模型 * 建立抽象模型是第一個步驟,也是唯一手動的步驟。 * 將外部暫存器分為四類,提供每類存取的pattern和處理的策略(模擬器要如何辨別暫存器種類、知道種類後要怎麼存取) * 抽象模型將外部中斷觸發抽象出一個特別的clannel,允許自定義中斷的策略。 * 抽象模型中包含各種存取的位置,對於相同MCU架構的設備,這些位置不需改變。 ### Register Category, Access Patterns and Handling 1. Control Registers * 通常用於控制、設定外部設備 * Access pattern: read-modify-write * Access handling: non-volatile memory word 2. Status Registers * 一組Flag用來表示外部設備的狀態 * Access pattern: 無條件讀取並評估讀取值 * Access handling: 輸出使用探索式執行找可接受的值;寫入外部設備的部分直接無視 3. Data Registers * 資料在外部設備和韌體之間傳遞的主要通道 * Access pattern: 辨識到SR特定的狀態或是直接寫入的行為 * Access handling: 輸出使用模糊測試的回傳;寫入外部設備的部分直接無視 4. Control-Status Register * CR和SR的混和,設計複雜且在MCU中不常見。 * Access pattern: 配置階段中用RMW修改CR的部分,操作階段存取SR的部分;一開始會分類成CR,發現有SR的存取後才會修正。 * Access handling: 要不對CR操作要不對SR操作,不會兩者同時。 * 這些pattern和handling是靠經驗法則得到的,但實作中發現準確度很高。 * 有時候SR和CR也會直接寫入,可能會分類錯誤成DR,但依然決定這樣辨別。 * 因為DR最多。 * SR可能會被修正回去。 * 分類錯誤的CR不會影響測試。 ### 中斷觸發 * 中斷可以視為一種給韌體的特別輸入信號。 * 原本外部設備可以發出中斷通知韌體某些事件(如數據準備就緒),然後再作相對應的操作;然而在不模擬MCU外部設備的情況下,就不會觸法中斷,導致韌體可能崩潰或停止。 * P2IM預設在經過固定的block數目後會執行中斷。 ### 不可行的外部設備輸入和誤報 * 在目前的抽象模型中,可能觸發真實設備不會到達的程式碼路徑,造成誤報。 * 但P2IM不會修剪這些路徑,而是將這件是留給測試工具(模糊器)完成。 * 因為控制輸入這個任務應該是模糊器的工作。 * 某些情況下可能需要測試這些不可到達到意外輸入。 ## 自動模型實體化 * 需要提供韌體特定的訊息,使用探索性執行。 * 和模糊測試交錯進行,模糊過程中如果遇到未建模的外部存取就實體化。 * 模型穩定、也沒有新識別的暫存器一段時間後,停止實體化並回復模糊。 * 一次測試可能會有多次的實體化 * 完成的模型可以重複用在相同的韌體 * 模型包含 * 辨識出暫存器種類和內存位置 * 每種類型暫存器的存取策略 * 中斷觸發的策略 * 不包含外部設備的的配置或內部結構等。 * ![](https://i.imgur.com/NMhjEkK.png) ### 暫存器辨識 * 同[Register Category, Access Patterns and Handling](#Register-Category-Access-Patterns-and-Handling)寫的,用大量經驗分辨暫存器種類。 * P2IM還根據是否屬於同一個外部設備去做分組。 ### 暫存器存取處理和探索性執行 * DR和CR的存取如前面寫的,直接且統一。 * SR的策略包含SR和C&SR裡面的SR,相當複雜。 * 不同的SR裡面的flag代表的意義完全不一樣,將SR1的值設定到SR2可能會導致崩潰。 * 同樣的SR在不同的執行點也會有不同的結果。 * 遇到新的SR時,P2IM暫停並開始探索性執行,開始多個thread找SR的最佳值,找到後再返回給韌體。 * 探索性執行要找2^32太花時間,所以只找32+1(每個bit分別=1和全部都0)。 * 探索性執行當回傳值被調用停止。 * 最後的回傳值,選擇沒有崩潰的值或是不因SR值崩潰的值裡面,訪問DR次數最佳的(???),都一樣就隨機找一個。 * 希望藉由SR分組,類似的SR可以用相同的策略。 ### 中斷辨識 * MCU架構支援很多中斷,但特定的設備可能只支援其中一些。 * 執行時,韌體會動態禁用和啟用中斷。 ### P2IM實作 * 用QEMU當作基本的模擬器實現框架。 * 修改和加入部分程式碼來實現 * 動態韌體檢測 * 模糊器元件化 * 探索式執行 ## 評估和結果 * 三個層面去評估 1. 不同MCU和不同OS是否滿足P2IE 2. 執行時的效能 3. 是否能完全模擬並進行模糊測試 ### MCU Peripherals & OSes * 單元測試使用到的外部設備。 * ![](https://i.imgur.com/yUDljRF.png) * 使用到的MCU OS/system libraries, (NuttX, RIOT, and Arduino) 3 target MCU SoCs (STM32 F103RB, NXP MK64FN1M0VLL12, and Atmel SAM3X8E) * 外部設備被存取的次數: * ![](https://i.imgur.com/lMi9T4y.png) * 即便是操作單純的設備存取也不一定比較少 * 每種外部設備分類暫存器的成功率: * ![](https://i.imgur.com/tRDYnYn.png) * 範圍在76%~92% * 可以發現沒有特定哪一種比較好,代表P2IM對外部設備的類型毫不在意 * 單元測試的通過率(滿足P2IE)為79%(55個成功)。 * 看起來好像不高,但在此之前是0% * 失敗的原因通常在分類暫存器錯誤或是不正確的中斷 ### End-to-end Tests * 找了10個MCU的真實設備,每個目的都不同(無人機~工業控制系統),都是發展很成熟的韌體了。 * ![](https://i.imgur.com/7qiZW07.png) * 雖然外部設備和其暫存器更多更複雜,但準確度卻比前面的測試高,可能是因為這些韌體都有遵照預想的存取模式走。 * 時間方面最差是十分鐘左右,還可以接受(一般的模糊測試可能會到數天)。 ### 分類錯誤和誤報 * 在對10個真實韌體的測試中,分類錯誤的表如下: * ![](https://i.imgur.com/3sQ9kQd.png) * 第一類錯誤是把SR分成DR,通常這不會導致崩潰,但會降低模糊測試的速度。 * 第二類錯誤是把DR分成CR,這種錯誤可能導致韌體停止。 * 這些錯誤的原因都是驅動程序沒有按照通用或是正確的方式去對暫存器進行存取。 ### 模糊測試結果 * 使用未修改的AFL當作P2IM的模糊器。 * 發現7個以前未知的錯誤。 * 使用P2IM可以讓程式碼覆蓋率提高7到30倍,雖然還是蠻低的。 * ![](https://i.imgur.com/ZioN3lq.png) * 覆蓋率低的原因可能有:程式碼有些地方根本沒用到、ALF太爛、前面提過的一些錯誤、輸入的持續時間也可能影響結果(但一般的模糊器沒考慮這件事) ## 討論 * DMA就是特別,違反通用性,所以我們不支援。 * 雖然大多針對ARM架構設計,但經過測試也可以過展到其他架構(AVR、MIPS 和 RISC-V),且可以按照類似的暫存器分類。 * 但有觀察到RISC-V有特別的中斷和暫存器種類,因此要處理這些差異才能擴展到非ARM MCU架構。 * 其實不一定要使用模糊器當作分析器,concolic等等的也可以使用。 ## 相關工作 * 動態分析: * Avatar使用混和模擬框架,用於concolic執行。 * Surrogates強化了Avatar。 * Avatar2擴展Avatar,不用使用真實設備了。 * Charm針對智慧型手機,其功能類似Avatar。 * Prospect在系統層轉發到外部設備去存取,但不適用於MCU裝置。 * 靜態分析: * FIE擴展KLEE,應用符號執行在韌體上。 * Inception使用Avatar將外部設備存取轉到真實設備上。 * FirmUSB使用符號執行,針對8051/52架構的USB控制器。 * Firmalice使用concolic執行找到韌體中的身分驗證繞過漏洞。 ## 結論 * P2IM是一種針對外部設備接口進行建模的技術。 * 第一個支援MCU外部設備的模擬,提高MCU的程式碼覆蓋率並進行動態測試。 * P2IM建構一個框架,根據輸入的韌體檔和模糊器執行。 * 測試中成功啟動79%的韌體。 * 發現了7個新錯誤。 ###### tags: `paper`