1. 引言
微體系結構側通道攻擊利用了處理器內部組件的競爭,從而泄漏了進程之間的信息。雖然從理論上講這類攻擊很簡單,但實際的實現方式往往很復雜,並且需要對文獻記載不充分的處理器函數和其他領域專有的知識有充分的了解。因此,進入微體系結構側通道攻擊的工作存在障礙,這阻礙了該領域的發展以及現有軟件抵御此類攻擊的能力的分析。
本文介紹 Mastik,一個用於測試微體系結構側通道攻擊的工具包。Mastik旨在提供已發布的攻擊和分析技術的實現。在撰寫本文時,Mastik尚處於 開發的早期階段。0.02版的代號 “Aye Aye Cap'n” ,目前已發布。
該版本包括在Intel x86-64體系結構上實施六種基於緩存的攻擊。其中包括對L1數據緩存的Prime + Probe攻擊,L1指令緩存上的Prime + Probe,LLC上的Prime + Probe,FLUSH + RELOAD,FLUSH + FLUSH和性能降低攻擊。
除了實施攻擊之外,Mastik還提供了一些促進攻擊的工具。這些函數包括用於處理符號代碼引用的函數,例如加載程序符號名稱或調試信息,以及簡化側通道攻擊中常用的某些系統函數的函數。0.02版中的新內容是FR-trace實體程序,它支持從命令行安裝Flush + Reload攻擊。
現在,我們用一些例子來說明Mastik的好處( 第二節 ),然后進行更詳細的介紹 接口說明( 第三節 )。
2. Mastik示例
為了展示Mastik的強大函數,我們現在展示如何在GnuPG 1.4.13上重現Flush + Reload攻擊。GnuPG 1.4.13使用平方和乘法算法,用於執行RSA解密和簽名的模冪運算步驟。Yarom和Falkner證明此實現容易受到Flush + Reload邊信道攻擊的攻擊。他們使用Flush + Reload跟蹤受害者對乘法、平方和模塊化歸約運算的使用。從跟蹤的操作中,攻擊者可以恢復與受害者的私鑰相對應 的指數位。
盡管“FLUSH + RELOAD”攻擊的核心相對簡單,但是攻擊的實現需要以固定的間隔重復探測內存。當被操作系統中斷時,它還應該能夠與受害者重新同步。此外,攻擊者需要能夠將源代碼位置轉換為內存地址。Mastik負責大部分此類操作。它隱藏了復雜性,並為用戶提供了一個用於發起攻擊的簡單接口。
Listing 1 顯示了攻擊的執行情況 (類似的實現程序在Mastik發行版的 demo/FR-gnupg-1.4.13.c 中)。Mastik使用非透明句柄類型 fr_t 抽象攻擊。攻擊句柄通過調用來實例化 fr_prepare() ( 第9行 )。11 至 18行 設置攻擊監視的內存位置。和Yarom和Falkner一樣 ,我們將攻擊設置為監視代碼中計算乘法、平方和模塊化歸約運算的位置。為了指定這些位置,我們使用對受害者源代碼行的引用( 第5行 )。sym_getsymboloffset() 使用GnuPG二進制文件中的調試信息將這些引用轉換為二進制文 件中的偏移量。我們使用 map_offset() 函數將這些偏移量映射到間諜程序的地址空間,並且 fr_monitor() 設置“FLUSH + RELOAD”攻擊以監視這些位置。
1 #define SAMPLES 100000
2 #define SLOT 2000
3 #define THRESHOLD 100
4
5 char *monitor[] = { "mpih−mul.c:85", "mpih−mul.c:271", "mpih−div.c:356" };
6 int nmonitor = sizeof (monitor)/ sizeof (monitor [0]);
7
8 int main(int ac, char **av) {
9 fr_t fr = fr_prepare ();
10
11 for ( int i = 0; i < nmonitor; i++) {
12 uint64_t offset = sym_getsymboloffset(av [1], monitor[i ]);
13 if ( offset == ~0ULL) {
14 fprintf ( stderr , "Cannot find %s in %s\n", monitor[i], av [1]);
15 exit (1);
16 }
17 fr_monitor ( fr , map_offset(av [1], offset ));
18 }
19
20 uint16_t *res = malloc(SAMPLES * nmonitor * sizeof(uint16_t));
21 bzero( res , SAMPLES * nmonitor * sizeof(uint16_t));
22 fr_probe( fr , res );
23
24 int l = fr_trace ( fr , SAMPLES, res, SLOT, THRESHOLD, 500);
25 for ( int i = 0; i < l ; i++) {
26 for ( int j = 0; j < nmonitor; j++)
27 printf ("%d ", res[ i * nmonitor + j ]);
28 putchar( ' \n' );
29 }
30 }
攻擊本身是在 24行 。fr_trace() 函數在其監視的任何內存位置中等待活動。然后,它以固定的時間間隔收集活動記錄。當檢測到足夠長時間的不活動狀態或函數空間不足以存儲結果時,收集將停止。這些活動記錄了從受監視位置讀取數據所花費的時間。較短的訪問時間表明該位置已緩存,因此處於活動狀態。
在終止之前,程序將輸出結果。程序輸出的一部分顯示在 圖1 。如圖所示,每個受監視位置都有明確的活動區域。根據這些信息,攻擊者可以重構受害者執行的操作順序並推斷出指數。
如我們所見,Mastik為攻擊提供了易於使用的接口。它隱藏了大多數攻擊實施細節,僅公開了針對特定受害者所需的那些參數。 現在,我們將描述Mastik在抽象各種攻擊時采用的一些模式。
3. API設計
設計用於側信道攻擊的API的挑戰之一是在三個沖突的目標之間取得平衡。我們希望接口簡單統一,同時,我們想發揮出每種攻擊獨特的優勢。另外,我們希望接口的實現盡可能優化,以最大程度地減少攻擊足跡。
Mastik通過為所有攻擊提供類似的接口來實現這種平衡,而無需提供基礎操作的共享實現。因此,攻擊接口具有相同的外觀,但是接口中存在針對某種特定攻擊的變化,並且在不同攻擊中用於相似目的的接口類型不具有相同的超類型。

Mastik中的每次攻擊都使用一個攻擊描述符進行封裝, 它是指向描述攻擊信息的結構體的不透明指針,以及管理該結構體和實施攻擊的一組函數。 表格1 總結實現了的攻擊、描述符類型和用於攻擊函數的前綴。將來,我們希望某些攻擊會共享描述符。例如,Evict + Reload 可以使用LLC Prime + Probe描述符( l3pp_t)。 對於每種攻擊,Mastik提供三種函數: 描述符管理,攻擊設置和攻擊。
-
描述符管理 初始化函數 XX_prepare() (XX 是攻擊前綴) 初始化描述符。對於某些描述符類型,可能需要一個為初始化例程提供參數的參數。 當前,只有LLC Prime + Probe接受參數。通過 NULL 選擇默認行為。XX_release() 函數釋放描述符及其分配的所有資源。
-
攻擊設定 每種攻擊都定義了一個攻擊空間,其中包含一組該攻擊可以操作的指針。對於大多數攻擊,操作是 監控 此指針位置的活動。指針的屬性和攻擊相關。在Flush + Reload攻擊中,一個指針是運行攻擊的進程的虛擬地址空間中的地址。對於Prime + Probe攻擊,這些指針標識目標緩存中的集合。對於每種攻擊,Mastik提供了幾種函數來管理使用描述符描述的攻擊所探測的指針集。這些函數是:
-
XX_monitor() 向描述符監視的一組指針中添加一個指針。
- XX_unmonitor() 從描述符監視的點集中刪除一個指針。
- XX_monitorall() 將所有可能的指針添加到描述符監視的指針集。僅支持L1攻擊。
- XX_unmonitorall() 從描述符監視的指針集中刪除所有點。
- XX_getmonitoredset() 返回描述符監視的指針集。
- XX_randomise() 使用非安全的偽隨機數生成器對受監視指針的集合進行重新排序。
初始化L1攻擊描述符會按隨機順序監視所有指針(緩存集)。初始化其他描述符以不監視任何指針。
-
攻擊 攻擊階段,由 XX_probe()實現, 包括探測每個監視指針以確定是否活動。典型的結果是一組定時數據,用於測量探測每個指針的時鍾周期數。結果與XX_getmonitoredset() 返回的探針的順序匹配。 兩次攻擊的結果含義不同。有關更多信息,請參見源文件。
-
攻擊變化 Prime + Probe攻擊通常受益於雙向探測。對於L1 Data和LLC Prime + Probe攻擊,Mastik提供了函數 XX_bprobe(), 該函數沿相反的方向執行探測。對於LLC攻擊,通常是計算緩存未命中的次數而不是探測緩存集的總時間。函數 l3_probecount() 和 l3_bprobecount() 執行此操作。
-
反復攻擊 通常,單次探測能提供的信息太少。函數 XX_repeatedprobe() 執行一系列的探測,如果環境支持XX_bprobe(),該函數會交替使用 XX_probe() 和 XX_bprobe(),否則,只是用XX_probe() 。該函數的 slot 參數可管理探測行為,使其在每 slot 個周期內執行一次探測。如果錯過了一個 slot,則該 slot 中各點的計時結果將設置為0。(對於probecount版本 ,其結果將設置為〜0)
-
痕跡 Flush + Reload和Flush + Flush攻擊支持重復攻擊的擴展版本。這個版本中,XX_trace() 等待受監視的緩存行中的活動。數據收集在檢測到活動時開始,並在不再檢測到活動或存儲結果的空間用盡時停止。
-
性能下降攻擊 與其他攻擊不同,性能降低攻擊不會監視受害者。相反,它針對目標受害者經常使用的緩存行,並將其從緩存中逐出。為了反映是不同的用途 ,使用函數 target 代替 monitor 發起攻擊。 例如函數pda_target() 將目標添加到性能下降攻擊的目標列表中。函數 pda_activate() 和 pda_deactivate() 開始和停止攻擊。 pda_activate() 產生執行攻擊的子進程。 pda_deactivate() 殺死子進程。
-
符號管理 Mastik提供了三種將符號轉換為文件偏移量的函數。 sym_loadersymboloffset() 在加載程序符號表中找到一個符號。 sym_debuglineoffset() 找到對應於特定源代碼行的機器代碼。在Linux中,這些功能依賴於 libbfd,libdwarf 和 libelf。 要使用,請確保 libdwarf-devel 和 binutils-devel( 或者 libdwarf-dev,binutils-dev 和 libelf-dev) 已安裝。
sym_getsymboloffset() 提供了用於將符號引用轉換為文件偏移量的通用接口。它可以識別四種輸入格式:文件偏移,虛擬地址,裝載程序符號 和行號。它可以進一步識別允許偏移的簡單算術運算。例如,輸入“ main + 0x40“ 指 main 函數開始后64字節的位置。
-
實用功能 map_offset() 將文件映射到進程的虛擬地址,並以文件中指定的偏移量返回指向數據的指針。僅映射包含指定偏移量的頁面。 unmap_offset() 刪除映射。delayloop() 執行多個周期的繁忙循環。如果XX_repeatedprobe() 沒有提供所需的功能,可以在每次探測之間使用 delayloop() 函數。delayloop() 的另一種用途是為了生成足夠的活動以避免CPU頻率縮放。根據我們的經驗, delayloop (3000000000U) 總是能達到預期的效果。但對你來說,可能會不同。