昨天說 Spectre 理論上需要做一些前置動作。因為處理器的分支預測是依據之前跑過的路線去猜接下來會跑啥,所以 Spectre 的論文裡面,有提到:
if (x < array1_size)
y = array2[array1[x] * 256];
這份 code 大家應該很熟悉,跟昨天長得有 87% 像。
Spectre 攻擊其實分成兩步,第一步是先把分支預測「訓練」成我們想要的樣子,第二部才是用預測執行去做幹資料的動作。
由於真正拿來攻擊的 code 裡面的 if 是不會成立的,所以我們一開始執行的時候,會讓 if 成立。這樣執行之後,分支預測就會覺得「這個 if 都會成立」,所以之後走到這邊時,都會先假設 if 成立,故預先執行 if 內的程式。然後我們再把 code 改成跟攻擊時一樣,把 x 換掉,然後就可以進行幹資料的動作了。
進階版的話,比前述的基本版還要強一些。
基礎板裡面,必須要「剛好執行到那個分支」才能做攻擊。不過,進階版的話,則是可以透過惡搞分支預測的方式,一直執行我們想要的分支(而且不用剛好在那個分支附近!)。
在 x86 指令集中,jmp
(跳轉) 可以跳轉到:
假設受害者的機子上,剛好有一段 code,裡面是有 jmp
到某個地方的指令,然後這個指令的目標剛好不在快取中,那這時候,處理器就有可能會跑去攻擊方想要執行某段指令的地方,然後對它們做預先執行。
當需要用到快取,但那東西不在快取時,這狀況叫做 cache miss。cache miss 的狀況下,處理器差不多會花個 100 週期以上跑去記憶體找那東西。
不過,我們可以「幫忙把快取清掉」:-)
這招在 Project Zero 文中是寫 Varient 2,Spectre Paper 在第五章。
這招要搞成功的話,還是得配合基本版來做使用。因為處理器最後還是會發現預測錯誤,然後把做的事情都弄掉,但一樣還是有不會把快取等等清掉的問題。
這招就比較猛了,因為只要你有辦法知道要跳去哪裡,就可以做一點事情了。甚至還能讀 OS 核心的記憶體。雖然現在有 ASLR (把記憶體的開始位置給隨機化), 不過 Project Zero 還是想辦法從分支預測的機制配合基本款攻擊來取得核心記憶體位置了。
因為這招是能配合 vmcall
使用的,所以在 VM Guest 的狀況下,也是能夠打到 Host 去的。
下一篇會講關於 Meltdown 的部分。