惡意軟體的自我保護之監控PageHeap的開啟
對於黑客來說,漏洞是最有價值的資產,所以一旦發現個漏洞並開發了針對該漏洞的攻擊技術,則黑客們就希望能夠儘可能長時間的利用該漏洞。所以對於黑客們來說,如何能夠防止他們所利用的攻擊工具不被發現就顯得非常重要了。
一般來說,黑客們會在以下4種情況下利用漏洞時會被發現:
1.和其他已經被檢測出的惡意軟體的功能或原理有重疊部分;
2.因為本身的攻擊效能不穩定而被檢測到;
3.在漏洞利用時,由於其惡意程式被安全產品模擬的虛擬執行環境所誘騙,從而被發現,例如安全產品所使用的蜜罐技術;
4.惡意軟體的開發者自己將漏洞公開了;
本文我們會把重點放在第3點上,即如何防止惡意執行程式被PageHeap監控到。
PageHeap的工作原理
PageHeap是存在於Windows的SDK/WDK中一種分析記憶體工具,其作用就是很快地檢測出記憶體堆疊的執行是否正常。
為了實現這一點,它用另一個分配器來替換記憶體堆疊。此分配器將通過VirtualAlloc分配每一次的執行記憶體,這樣每一次的記憶體消耗將至少減少一個頁面的大小(一般情況下為4Kb)。
除此之外,返回的地址也將被減去請求的大小,這樣便可導致緩衝區溢位(包括棧溢位和堆溢位)。這樣一來,就防止了許多位元組數較大應用情境。
所以,PageHeap就破壞了許多惡意軟體執行的基礎,讓大多數漏洞所依賴堆疊記憶體分配不復存在。比如為了改變程序,而新增過多的程式碼,就很容易溢位被檢測到。
對PageHeap的反檢測
一旦PageHeap開始執行,那記憶體的堆分配將會慢很多。因為,堆分配的初始目的就是儘可能快地分配各種不同大小的記憶體。
所以,如果PageHeap開始執行,會通過VirtualAlloc向核心請求每一次的分配,這樣記憶體消耗就會越來越多,最後發生記憶體不足或整體效能下降。另外,這些分配會讓記憶體分配的時間變得很長。
由於window.performance.now()計數器在大多數JavaScript引擎中都可用,因此可以用它來檢測這個分配時間。window.performance.now()計數器能實時採集、分析系統內的應用程式、服務、驅動程式等的效能資料,以此來分析系統的瓶頸、監視元件的表現,最終幫助使用者進行系統的合理調配。
因為Chrome和Firefox擁有自己的分配器,所以這種方法僅限於Windows。
以IE11和Edge為例,當我們在這兩個瀏覽器總尋找分配不同數量位元組的函式時,可以使用Uint8Array,它是一個使用ArrayBuffer建立的TypedArray。
當建立ArrayBuffer時,將直接使用我們指定的值呼叫msvcrt!malloc。我們將分別使用0x10和0x1000代表小值和大值,大值將始終觸發對VirtualAlloc的呼叫。
下面我們將試著在20ms內為小值和大值分配儘可能多的Uin8Array,程式碼如下:
function doFor(fun, time) { var i = 0; var store = new Array(); var startTime = performance.now(); do { for(var j=0; j<100; j++) store.push(fun()); i++; } while((performance.now() - startTime) < time); return i;}function allocPageBA() { return new Uint8Array(0x1000);}function allocSmallBA() { return new Uint8Array(0x10);}var bigRet = doFor(allocPageBA, ALLOC_TIME);var smallRet = doFor(allocSmallBA, ALLOC_TIME);alert(bigRet);alert(smallRet);
不過在上圖中,我們還引入了其它的不相關的分配程式碼,為了解決這個問題,我們會建立一個預先分配Array的物件,然後將物件儲存在那裡,這樣就不會生成不必要的分配程式碼了。
另外,在儲存分配的物件時,為防止垃圾回收站釋放這些不必要的程式碼,可能會發生一些錯誤:
function NoAllocStore(count) { this.count = count; this.array = new Array(count); for(var i=0; i<count; i++) { this.array[i] = 0x41414141; } this.index = 0;}NoAllocStore.prototype.store = function(obj) { if (this.index >= this.count) { alert("bad"); throw false; } this.array[this.index] = obj; this.index++;}
我們運行了好幾次以上的程式碼,並比較了兩個瀏覽器在禁用PageHeap情況下的頁面配置。
在IE11中分配如下:
更詳細地的資訊如下:
非常明顯的是,在啟用了PageHeap之後,分佈顯得更加密集了,這樣在分配時間上就更為確定,這可以歸因於頁面分配比堆分配更耗時。
所以對於黑客們來說,就需要使用系統模擬器或偵錯程式測量每次malloc呼叫後執行的總指令數(ring0和ring3)。
而在Edge上,分佈是非常相似的,但你可以注意到它們更加分離。