<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    快速定位windows堆溢出

    VSole2022-08-05 16:21:14

    windows堆溢出的排查的困難

    windows的堆溢出,具有一定的滯后性,溢出后有時不會立即崩潰,可能運行一段時間后再崩潰,此時查看調用堆棧,也不是溢出的第一現場,此時的調用堆棧意義也不大,看不出什么貓膩,所以堆溢出,對開發人員排查具有一定的難度。本文帶你尋找定位堆溢出的第一現場。

    頁堆結構

    1、頁堆結構

    圖1畫出了頁堆的結構,其中的地址是以x86系統中的一個典型頁堆。左側的矩形是頁堆的主題部分,右側是附屬的普通堆,創建每個頁堆時,堆管理器都會創建一個附屬的普通堆。

    頁堆上空間大多是以內存頁來組織的,第一個內存頁(起始4KB)用來偽裝普通堆的HEAP結構,大多數空間被填充為0xeeeeeeee,只有少數字段(Flags和ForceFlags)是有效的,這個內存頁的屬性是只讀的。因此可用來檢測應用程序意外寫HEAP結構的錯誤。

    第二個內存頁的開始處是一個DPH_HEAP_ROOT結構,該結構包含了DPH的各種信息和鏈表,是描述和管理頁堆的重要資料。它的第一個字段是這個結構的簽名(signature),固定為0xffeeddcc,與普通的堆結構的簽名0xeeffeeff不同。它的NormalHeap字段記錄著附屬普通堆的句柄。

    DPH_HEAP_ROOT結構之后的一段空間用來存儲堆塊節點,稱為堆塊節點池(node pool)。為了防止堆塊的管理信息被覆蓋,除了在堆塊的用戶數據區前面儲存堆塊信息,頁堆還會在節點池中為每個堆塊記錄一個DPH_HEAP_BLOCK結構,簡稱為DPH節點結構。多個節點是以鏈表的形式鏈接在一起的。

    DPH_HEAP_BLOCK結構的pNodePoolListHead字段記錄這個鏈表的開頭,pNodePoolListTail字段記錄鏈表的結尾。

    它的第一個節點描述的是DPH_HEAP_ROOT結構和節點池本身所占用的空間。節點池的典型大小是4個內存頁(16KB)減去DPH_HEAP_ROOT結構的大小。

    節點池后的一個內存頁用來存放同步用的關鍵區對象,即_RTL_CRITICAL_SECTION結構,這個結構之外的空間被填充為0,DPH_HEAP_BLOCK結構的HeapCritSect字段記錄著關鍵區對象的地址。

    2、堆塊結構

    與普通堆塊相比,頁堆的堆塊結構有很大的不同,每個堆塊至少占用兩個內存頁(1個內存頁4KB),在用于存放用戶數據的內存頁后面,堆管理器總會多分配一個內存頁,這個內存頁是專門用來檢測溢出的,我們稱其為柵欄頁(fense page)。

    柵欄頁的頁屬性被設置為不可訪問(PAGE_NOACCESS),因此,一旦用戶數據區發生溢出并觸及柵欄頁,便會引發異常,如果程序在調試,那么調試器便會立刻收到異常,使調試人員可以在第一現場發現問題,并迅速定位到導致溢出的代碼.

    數據區在第一個內存頁的結尾,第二個內存頁緊鄰在數據區的后面,圖2顯示了這樣的一個頁堆堆塊(DPH_HEAP_BLOCK)的數據布局。

    (圖2 堆塊結構 摘自《軟件調試》)

    堆溢出調查方法一:使用頁堆調查堆溢出

    頁堆的工作機制

    在堆塊周圍加一個不可訪問的保護頁,用來將各個堆塊區分開;如果保護頁被覆蓋,將盡可能在接近覆蓋問題發生的地方檢測到問題并中斷給調試器。

    頁堆有2種模式

    普通頁堆(normal page heap)和完全頁堆(full page heap),兩者主要差別在于保護方式(普通頁堆在堆前后用固定填充數據保護,完全頁堆用一個不可訪問的頁保護)。

    最常見的元數據被覆蓋問題是在使用堆時沒有考慮到邊界問題,導致的溢出問題。下面是溢出可能影響堆結構的示意圖:

    (圖3 堆溢出圖示)

    測試代碼:

    //堆溢出示例//現象:輸入字符長度小于等于10,程序正常運行;超過10個字符,程序會崩潰#include #include #include #include "dump.h"WCHAR* pszCopy = NULL;bool DupString(WCHAR* psz) {    bool bRet = false;    if (psz != NULL) {        pszCopy = (WCHAR*)HeapAlloc(GetProcessHeap(), 0, 10 * sizeof(WCHAR)); //10個寬字符內存        if (pszCopy) {            wcscpy(pszCopy, psz);                      //隱患:字符超過10個就會產生溢出            wprintf(L"Copy of string: %s", pszCopy);            HeapFree(GetProcessHeap(), 0, pszCopy);            bRet = true;        }    }    return bRet;}void __cdecl wmain(int argc, WCHAR* args[]) {    SetUnhandledExceptionFilter(ExceptionFilter);//異常回調,生成dump文件    if (2 == argc) {        wprintf(L"Press any key to start");        _getch();        DupString(args[1]);    }    else {        wprintf(L"Please enter a string");    }}
    

    step 1:啟動完全頁堆

    gflag啟用頁堆,執行命令后,其實是修改的注冊表。

    命令:gflags.exe /p /enable app.exe /full

    (圖4 對測試程序開啟頁堆)

    (圖 5 gflags啟用頁堆后,gflags修改的注冊表)

    step 2:WinDbg調試程序

    1) 確定頁堆是否啟用

    (圖 6 windbg查看頁堆是否啟動)

    2) 觸發堆溢出

    windbg打開可執行程序、輸入參數:xiaosangTestForOverflow,這個參數會拷貝到動態分配的緩沖區,用來溢出緩沖區。

    使用命令.reload /f testHeapOverflow.exe加載符號,然后在調試的程序中按下任意鍵(代碼中getchar),觸發緩沖區溢出。

    (圖 7 windbg打開程序,并傳遞參數)

    (圖 8加載符號,并運行)

    (圖 9 堆溢出報錯)

    step 3:關閉完全頁堆

    gflags -p /disable testHeapOverflow.exe

    (圖 10 關閉完全頁堆)

    堆溢出調查方法二:應用程序驗證器

    step 1:應用程序驗證器設置

    選擇File->add application,然后右擊Basics下的Heaps,選擇full,然后保存。

    設置后,實際修改是注冊表(下圖可以看到設置后注冊表的變化)。

    (圖 11 使用應用程序驗證器完全頁堆)

    (圖 12 使用應用程序驗證器完全頁堆后的注冊表變化)

    step 2:WinDbg調試結果

    運行應用程序

    (圖 13 打開測試程序,并傳遞參數)

    應用程序崩潰之后,再次輸入go,獲取校驗停止碼,顯示是13。

    (圖 14 查看校驗停止碼)

    查詢校驗停止的原因,并根據指導進行查找原因。

    (圖 15 打開堆校驗停止窗口)

    (圖 16 查看如何排查校驗停止碼13的方法)

    根據校驗停止碼的原因,在windbbg輸入!heap –p –a accessAddr。

    (圖 17 查看堆溢出地址的調用堆棧,觀看溢出內存)

    堆內存
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    內存泄漏最終會導致OOM。造成內存泄漏典型場景:1.單例模式的不正確使用單例對象在初始化后將在JVM的整個生命周期中以靜態變量的方式存在。如果單例對象持有對外部對象的引用,那么這個對象將不能被JVM正常回收2.數據庫、網絡、IO連接沒有被關閉釋放這類連接,一般會提供close方法進行顯式關閉。
    區分為兩大區:Young區和Old區,又稱新生代和老年代。在不同的JVM實現及不同的回收機制中,內存的劃分方式是不一樣的。相對于基于寄存器的運行環境來說,JVM是基于棧結構的運行環境。在活動線程中,只有位于棧頂的幀才是有效的,稱為當前棧幀。正在執行的方法稱為當前方法,棧幀是方法運行的基本結構。在執行引擎運行時,所有指令都只能針對當前棧幀進行操作。而StackOverflowError表示請求的棧溢出,導致內存耗盡,通常出現在遞歸方法中。
    無論對于Java程序員還是大數據研發人員,JVM是必須掌握的技能之一。Java提供的GC功能可以自動監測對象是否超過作用域等從而達到自動回收內存的目的,可以有效防止內存泄露,有效的使用可用內存。
    在2019年東京Pwn2Own大會上,無線路由器引入為一個新種類:NETGEAR Nighthawk R6700v3。但該路由存在一些安全隱患,其中包括一個溢出漏洞,該漏洞可能允許惡意第三方從局域網控制設備。在這篇文章中,我們將詳細討...
    新生代垃圾收集器1. Serial收集器serial收集器即串行收集器,是一個單線程收集器。能讓使用者明確指定在一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒。G1取消了內結構的新生代、老年代的物理空間劃分,將整個Java劃分為大小固定的獨立區域,后臺維護一個優先列表來跟蹤這些區域的垃圾堆積程度,每次根據允許收集的時間,優先回收垃圾最多的區域。
    利用UAF漏洞UAF漏洞(Use-After-Free)是一種內存破壞漏洞,漏洞成因是一塊內存被釋放了之后又被使用。又被使用指的是:指針存在(懸垂指針被引用)。這個引用的結果是不可預測的,因為不知道會發生什么。由于大多數的內存其實都是C++對象,所以利用的核心思路就是分配去占坑,占的坑中有自己構造的虛表。
    然而在內核態中,內存的分配策略發生了變化。并把這個slab劃分為一個個object,并將這些object組成一個單向鏈表進行管理,這里需要注意slub系統把內存塊當成object看待,而不是伙伴系統中的頁。本次選擇演示的例題是2019-SUCTF的sudrv例題,查看start.sh中的信息可以發現開啟了kaslr保護與smep保護。
    奇安信CERT致力于第一時間為企業級用戶提供安全風險通告和有效解決方案。風險通告近日,奇安信CERT監測到官
    推薦 goleak 的背景goroutine 作為 golang 并發實現的核心組成部分,非常容易上手使用,但卻很難駕馭得好。我們經常會遭遇各種形式的 goroutine 泄漏,這些泄漏的 goroutine 會一直存活直到進程終結。等性能分析工具更多是作用于監控報警/故障之后的復盤。我們需要一款能在編譯部署前識別 goroutine 泄漏的工具,從更上游把控工程質量。
    上周,該威脅組織受到韓國外交部的制裁。賠償金將由谷歌管理層自行決定,并且在 12 個月內最高不超過 100 萬美元。該計劃目前涵蓋由 SCC Premium 的虛擬機威脅檢測支持的 Compute Engine 虛擬機和計算環境。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类