只要平時對安全領域感興趣的讀者確定都聽過spectre&Meltdown側信道攻擊,今天簡單介紹一下這種攻擊的原理( https://www.bilibili.com/video/av18144159?spm_id_from=333.788.b_765f64657363.1 這裏有詳細的視頻介紹,牆裂推薦)。html
一、CPU順序執行指令windows
衆所周知,程序是由一條條指令順序排列構成的,老的cpu也是逐行執行指令;隨着cpu的技術發展(著名的摩爾定律),cpu執行指令的速度愈來愈快,和內存讀寫速度的差距愈來愈大。理論上,CPU執行一條指令耗時納秒左右,但從內存讀一次數據須要耗時約100納秒(參考這裏:https://zhuanlan.zhihu.com/p/24726196),理論上相差了百倍,業界稱爲馮諾依曼瓶頸;這個速度差別不解決,CPU執行速度再快都沒用;由此衍生出了近年來CPU的一個重要特性:亂序執行數組
二、亂序執行:以下所示,好比有個if分支。正常狀況下,若是MEM==0纔會執行if分支;但CPU因爲速度百倍於內存,等把這塊內存的數據讀出來,下面那4條指令早就執行完了,因此先不判斷if條件是否成立,CPU會先執行這4條指令;等前面的指令執行完畢,輪到執行if 的時候,纔會從內存讀mem的數據,而後判斷是否爲0. 若是是,說明if這個分支原本就該執行,因爲前面已經執行完畢,因此整個效率大大提高;退一步說,就算if條件不成立,cpu白執行了4條指令,這時只須要回退(主要是寄存器的值)便可,和之前的順序執行比也沒啥損失。這種提早預測分支執行的方法截至目前至少看起來沒啥損失,還有必定的機率提高總體效率了!這個就是業界所謂的speculative execution!瀏覽器
三、緩存cache緩存
前面說了,CPU執行指令的速度和從內存讀數據的速度差了百倍。若是每次執行指令前都要從內存讀取數據,CPU會閒死的;爲了解決這個問題,衍生出了cpu另外一個很是重要的功能:緩存;第一次從內存讀取數據後,cpu會先把這些數據存放在內部緩存。下次再須要用這些數據時,不會馬上從內存讀,而是先看看本身的緩存種是否已經有了,沒有才會繼續去內存讀取;此種思想方法也能在必定程度上提高程序的執行效率和速度,但問題也隨之而來:安全
cpu的緩存是否有該數據,直接影響了從內存讀取該數據的效率,理論上差了近百倍,這個差別是很是明顯的,這就給黑客留下了「把柄」;ide
四、meltdown和side-channel attack函數
(1)利用時間差,能夠作的攻擊有不少,先舉一個通俗易懂的栗子:暴力猜密碼學習
好比我設置了一個登錄密碼「cnblogs」,用戶輸入密碼後,後臺驗證的邏輯是逐個字符比對。好比用戶輸入djgnyd,第一個字符就不對,直接返回false;好比用戶輸入cjhtsf,第一個字符對了,再繼續比對第二個,結果發現第二個錯了,再次返回。這就給了黑客可乘之機:屢次隨機輸入密碼,利用不一樣的返回時間差猜想輸入的密碼是否正確!這就是業界俗稱的side-channel attack;測試
(2)meltdown和side-channel attack
接下來介紹本文的重點:利用spectre和meltdown讀取任意內存的數據,而不受任何權限限制;計算機的整個內存種,咱們本身程序能用的只是一部分,還有不少內存是不能用的,好比部份內核的內存、其餘進程的內存、其餘虛擬機的內存(如下簡稱victim memory),操做系統會經過各類機制確保咱們的程序沒法訪問(好比保護模式下的0~3環+CPL/DPL/RPL等機制控制、操做系統對內存rwx屬性的管理)。若是不慎讀到這些內存,windows會彈出c000005內存訪問錯誤;以下:
(2.1)假設藍色是咱們能正常使用的內存,紅色是沒法使用的victim內存;咱們在藍色區域開闢一個數組A(綠色表示),只有兩個元素A[0]和A[1]; 經過A指針訪問這兩個元素是ok的;可是要想經過A+X越界直接訪問victim內存是不行的,cpu或操做系統會直接阻止,並拋出異常或彈框報錯;
(2.2)同時繼續再在藍色區域開闢一個instrument 的數組,該數據全部數據一概不能存放cpu緩存(後面會解釋緣由);
(2.3)接下來最關鍵的點來了:if(xxx) access Instrument[A[x]]
若是直接執行A[x],因爲越界到victim內存,會被終止;但這個指令在if條件分支,cpu的亂序執行特性會先不判斷if條件是否成立,而是直接access Instrument[A[x]];此時也不會檢查A[x]是否越界,而是直接讀取該內存數據;假如讀取的數據是4,那麼Instrument[A[x]] = G,這個G會被存到CPU緩存;而後又從Instrument0開始一致讀取到7,判斷哪一個數據讀取的速度最快(上面有解釋:沒在緩存的只能從內存讀,理論速度差了近百倍),很明顯第4個單元的耗時最斷,反推出A[x]=4,致使該victim的內存數據泄露;
有讀者確定會問:就算是預測分支亂序執行,爲啥不檢查A[x]是否越界了?整個攻擊最核心的點就在這裏啊!我的猜想:這和是cpu、操做系統的分工不明確致使的;cpu提早預測分支指令執行,這時if條件是否成立還不知道了,又怎麼去判斷A[x]是否越界了? 因此cpu spectaculative設計人員把這個驗證的事情甩給了操做系統內核,但願正常執行到該命令時操做系統內核能檢查一下是否越界,若是有,再kill程序、拋出異常;但等到操做系統驗證時爲時已晚,access Instrument[A[x]] 這條指令已經被執行,對應內存的數據已經被讀取到了cpu的緩存. 就算if條件不成立,回退的是寄存器的值,cpu緩存的值仍是存在的.......
這是核心的js代碼:若是操做系統或瀏覽器沒打補丁,理論上用戶在瀏覽網頁的時候,黑客能夠經過這種方式從內存讀取全部數據,致使密碼等敏感數據泄露;
五、最後怎麼避免本身的程序亂序執行代碼了?VS裏面把這裏設置成已啓用就行;
六、簡化的代碼以下(精華都在註釋了),幾個核心的函數:
(1)_mm_clflush:清除內存某個單元在cpu中的L一、L二、L3全部的緩存
(2)__rdtscp:開始計時
(3)_mm_mfence:後面的指令只能順序執行
#include <intrin.h> #include <stdio.h> #include <Windows.h> #define SHIFT_NUMBER 0x0C #define PAGE_SIZE 4096 #define BLOCK_SIZE (1 << SHIFT_NUMBER)//0x1000,一個頁 /* (LPBYTE)_aligned_malloc(256 * BLOCK_SIZE, PAGE_SIZE)這裏申請了256個頁;用flush函數 將這256個頁在cpu中的緩存失效 詳細解釋能夠看這了:http://scc.qibebt.ac.cn/docs/optimization/VTune(TM)%20User's%20Guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/instruct32_hh/vc31.htm https://zhuanlan.zhihu.com/p/141144249 */ void CacheLineFlush(LPBYTE lpArray, UINT index) { _mm_clflush(&(lpArray[index << SHIFT_NUMBER])); } void CacheLineFlush_all(LPBYTE lpArray) { for (UINT i = 0; i < 256; i++) CacheLineFlush(lpArray, i); } /************************************************************************/ /* 統計各個塊的訪問速度,並返回最快的那個塊的索引 */ /************************************************************************/ BYTE GetAccessByte(LPBYTE lpArray) { UINT64 speed[256]; UINT64 start, min; UINT index, junk; BYTE result; //爲min賦初始值 min = 0; //測試訪問速度 for (int i = 0; i < 256; i++) { //獲取array[index]的地址;也就是頁的索引 index = i << SHIFT_NUMBER; //mfence指令用於序列化內存訪問,即讓亂序執行無效化。 //後面的指令必須在前面的內存讀寫完成後再開始發射執行。 _mm_mfence(); //記錄開始週期 start = __rdtscp(&junk); junk = *(LPDWORD)(&lpArray[index]);//記錄讀取每一個單元的時間 _mm_mfence(); speed[i] = __rdtscp(&junk) - start; //若是是初始值,或者比當前值還小 if ((min == 0) || (speed[i] < min)) { min = speed[i]; result = (BYTE)i; } } return result; } /************************************************************************/ /* 用index做爲數組索引,訪問array的某個元素 */ /************************************************************************/ BYTE AccessArray(LPBYTE lpArray, UINT index) { return lpArray[index << SHIFT_NUMBER];//左移12位,效果至關於乘以4096;那麼能夠把index當作是頁的索引,這裏訪問index指向頁開頭的第一個字節; } int main() { //假定的kernel內存 BYTE kernel[4]; //array是用戶可控制的內存 LPBYTE array; kernel[0] = 0x55; kernel[1] = 0xAA; kernel[2] = 0xF0; kernel[3] = 0x0F; /* 分配256個頁,做用至關於視頻中的instrument;爲何是256了?內存中每一個最小單元是1字節,能表示從0~255一共256個數; 後續會挨個讀取這256個頁開頭的第一個字節,若是速度快,說明cpu裏面已經有緩存了,kernel單元(也就是視頻中的victim單元) 大概是是這個數; */ array = (LPBYTE)_aligned_malloc(256 * BLOCK_SIZE, PAGE_SIZE); /* 實現原理: kernel假定是受保護的內存數據 (本demo裏可訪問)。 array爲用戶可控制的一個數組,一共分爲256個塊,每一個塊的大小爲2的整數倍: (1 << SHIFT_NUMBER)。 而後從kernel中讀取一個byte,以這個byte爲索引,去訪問array所對應的塊。 以後馬上循環讀取一遍array的各個塊,若是以前訪問成功了,那麼對應的塊應該還在緩存中,對應的訪問時間要少不少。 統計各個塊的訪問週期數,最快的塊,他的索引就是受保護的那個byte。 CacheLineFlush_all 函數用於把整個數組從緩存中清除出去,這樣不至於污染訪問速度。 AccessArray 函數用於以一個索引去訪問一個數組。 GetAccessByte 函數用於測試數組的各個塊的訪問速度,返回最快的那個塊的索引。 */ CacheLineFlush_all(array);//清空本身申請的256頁在cpu的緩存,避免影響後續的讀取計時 AccessArray(array, kernel[0]);//這裏爲了突出重點說明側信道攻擊,並無用if分支預測執行,而是直接用kernel的元素作下標訪問,讓這個數寫入cpu緩存 printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array));//看看哪一個內存單元讀取的速度最快,由此反推出kernel元素的值 CacheLineFlush_all(array); AccessArray(array, kernel[1]); printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array)); CacheLineFlush_all(array); AccessArray(array, kernel[2]); printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array)); CacheLineFlush_all(array); AccessArray(array, kernel[3]); printf("Access fastest: 0x%02X\n", (DWORD)GetAccessByte(array)); getchar(); return 0; }
參考:https://www.freebuf.com/articles/system/159811.html 一步一步理解CPU芯片漏洞:Meltdown與Spectre
https://meltdownattack.com/ 官網
https://www.bilibili.com/video/av18144159?spm_id_from=333.788.b_765f64657363.1 15分鐘讀懂英特爾熔斷幽靈漏洞-Emory
https://www.fortinet.com/blog/threat-research/into-the-implementation-of-spectre (中文翻譯:https://zhuanlan.zhihu.com/p/33635193)Spectre 攻擊詳解(詳細的demo代碼)
https://bbs.pediy.com/thread-224040.htm 簡短的demo代碼,很是適合入門學習原理
https://www.cnblogs.com/zenny-chen/archive/2013/03/28/2986527.html 與Cache相關的控制
https://bbs.pediy.com/thread-254288.htm spectre跨進程泄露敏感信息