聊一聊 .NET高級調試 內核模式堆泄露

来源:https://www.cnblogs.com/huangxincheng/archive/2023/12/14/17900826.html
-Advertisement-
Play Games

一:背景 1. 講故事 前幾天有位朋友找到我,說他的機器記憶體在不斷的上漲,但在任務管理器中查不出是哪個進程吃的記憶體,特別奇怪,截圖如下: 在我的分析旅程中都是用戶態模式的記憶體泄漏,像上圖中的異常徵兆已經明確告訴你了,不是用戶態程式吃的記憶體,那就是內核態程式吃的,比如: 某些驅動程式 操作系統 從概率 ...


一:背景

1. 講故事

前幾天有位朋友找到我,說他的機器記憶體在不斷的上漲,但在任務管理器中查不出是哪個進程吃的記憶體,特別奇怪,截圖如下:

在我的分析旅程中都是用戶態模式的記憶體泄漏,像上圖中的異常徵兆已經明確告訴你了,不是用戶態程式吃的記憶體,那就是內核態程式吃的,比如:

  • 某些驅動程式
  • 操作系統

從概率上來說一般都是某些第三方程式記憶體泄露導致的,這一篇我們就來聊一聊這種問題該如何解決。

二:內核模式堆泄露分析

1. 驅動程式是如何分配記憶體的

相信有很多朋友都知道,用戶態的程式是直接或者間接的調用 VirtualAlloc 方法來向操作系統要記憶體,包括 C# 的 GC 堆也是一樣,它的方法簽名如下:


LPVOID VirtualAlloc(
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);

那內核中的驅動程式是如何向操作系統要記憶體的呢?一般都是調用 ExAllocatePool2 方法來要記憶體的,簽名如下:


DECLSPEC_RESTRICT PVOID ExAllocatePool2(
  POOL_FLAGS Flags,
  SIZE_T     NumberOfBytes,
  ULONG      Tag
);

上面有兩個參數要詳細解釋一下:

  • Flags 參數

一般用的多的就是 POOL_FLAG_NON_PAGEDPOOL_FLAG_PAGED 兩種,前者表示分配的記憶體是需要永久駐留記憶體,不可以交換到硬碟的。後者分配的記憶體是可以交換到硬碟的。

  • Tag 參數

這個參數的本意就是方便日後洞察記憶體泄露的,它強行讓一塊記憶體和這個 Tag(4byte的ascii 字元串) 做了強綁定,到時候通過這個 tag 就知道是誰分配的記憶體。

2. 製造內核模式堆泄露

為了能夠讓驅動程式泄露,可以使用微軟提供的 NotMyFault 工具,這個工具利用 myfault.sys 驅動不斷的向操作系統分配記憶體。官方網址為:https://learn.microsoft.com/zh-cn/sysinternals/downloads/notmyfault

打開 myfault 工具然後輸入 40M/s 的泄露,並分配在非換頁池中,同時配置下內核態轉儲dump, 代碼和截圖參考如下:


ExAllocatePool2(POOL_FLAG_NON_PAGED,40*1024*1024,"Leak");

在泄露的過程中,通過 Process Explorer 很明顯的發現提交了 6.7G 的記憶體,其中有 4.9G 是在 NonPaged 中,即通過上圖中的 POOL_FLAG_NON_PAGED 標記分配的,截圖如下:

接下來在 MyFault 上切換到 Crash 選項卡,強行讓操作系統藍屏來生成 dump 文件。

3. dump 分析

拿到dump後,先通過 !vm 觀察下操作系統級的虛擬記憶體的分佈情況。


3: kd> !vm
...
Physical Memory:          2069421 (    8277684 Kb)
Available Pages:           445015 (    1780060 Kb)
ResAvail Pages:            707292 (    2829168 Kb)
Locked IO Pages:                0 (          0 Kb)
Free System PTEs:      4295052431 (17180209724 Kb)
...
Modified Pages:             11479 (      45916 Kb)
Modified PF Pages:          11479 (      45916 Kb)
Modified No Write Pages:        0 (          0 Kb)
NonPagedPool Usage:       1219892 (    4879568 Kb)
NonPagedPoolNx Usage:       24512 (      98048 Kb)
NonPagedPool Max:      4294967296 (17179869184 Kb)
PagedPool Usage:            32907 (     131628 Kb)
PagedPool Maximum:     4294967296 (17179869184 Kb)
...
NonPagedPool Commit:      1246469 (    4985876 Kb)
...
Sum System Commit:        1409562 (    5638248 Kb)
Total Private:             279673 (    1118692 Kb)

********** Sum of individual system commit + Process commit exceeds overall commit by 1952 Kb ? ********
Committed pages:          1688747 (    6754988 Kb)
Commit limit:             4166573 (   16666292 Kb)

從卦中的 NonPagedPool Usage 指標可以看到,當前的 非換頁池 占用了 4.8G 記憶體,總計 121w 的記憶體頁。

接下來就是要深挖下 非換頁池 ,看看到底都是什麼 Tag 分配的,可以使用 !poolused 2 命令。


3: kd> !poolused 2
....
 Sorting by NonPaged Pool Consumed

               NonPaged                  Paged
 Tag     Allocs         Used     Allocs         Used

 Leak       119   4991221760          0            0	UNKNOWN pooltag 'Leak', please update pooltag.txt
 ConT       238     14499840          0            0	UNKNOWN pooltag 'ConT', please update pooltag.txt
 KETR     16410      8117664          0            0	UNKNOWN pooltag 'KETR', please update pooltag.txt
 EtwB       196      7565568          2       131072	Etw Buffer , Binary: nt!etw
 2872         6      5660864          0            0	UNKNOWN pooltag '2872', please update pooltag.txt
 287R      1026      4183040          0            0	UNKNOWN pooltag '287R', please update pooltag.txt
 File      9734      3877408          0            0	File objects 
 Thre      1257      3217920          0            0	Thread objects , Binary: nt!ps
 EtwR     12141      2672640          0            0	Etw KM RegEntry , Binary: nt!etw
...

從卦中數據看,有一個神秘的 Tag=Leak 的記憶體分配,它分配了 119 次,總大小 4.99G。 哈哈,其實就是剛纔通過 MyFault 做的 40M/s 的記憶體分配。

接下來的問題是:這個 Leak 是哪一個驅動程式所為呢?最簡單的辦法就是在各個驅動的記憶體空間中做記憶體搜索,看看誰裡面有 Leak 的asc硬編碼,對吧,有了這個思路,先用 lm 看看裡面都有哪些 sys 。


3: kd> lm
start             end                 module name
ffffc25c`891b0000 ffffc25c`89480000   win32kbase   (deferred)             
ffffc25c`8a190000 ffffc25c`8a545000   win32kfull   (deferred)     
...                  
fffff807`22600000 fffff807`23646000   nt         (pdb symbols) 
fffff807`23c00000 fffff807`23d16000   clipsp     (deferred)             
fffff807`47f30000 fffff807`47f4b000   monitor    (deferred)             
fffff807`47f50000 fffff807`47f59000   myfault    (deferred)             
...          

Unloaded modules:
fffff807`3c6e0000 fffff807`3c6ec000   360Sensor64.sys
fffff807`31550000 fffff807`31560000   dump_storport.sys
fffff807`315a0000 fffff807`315d3000   dump_storahci.sys
fffff807`31000000 fffff807`3101e000   dump_dumpfve.sys
fffff807`26b80000 fffff807`26bac000   luafv.sys
fffff807`26b20000 fffff807`26b30000   dump_storport.sys
fffff807`26b70000 fffff807`26ba3000   dump_storahci.sys
fffff807`26bd0000 fffff807`26bee000   dump_dumpfve.sys
fffff807`28130000 fffff807`2814c000   dam.sys 
fffff807`24200000 fffff807`2420a000   360elam64.sys
fffff807`25230000 fffff807`25241000   hwpolicy.sys

接下來就是寫腳本在每個 sys 的 start ~ end 區間做 s 搜索,這個腳本我就不放了,非常簡單,最終就在 myfault.sys 中成功找到了 Leak 硬編碼,參考如下:


3: kd> lmvm myfault
Browse full module list
start             end                 module name
fffff807`47f50000 fffff807`47f59000   myfault    (deferred)             
    Image path: \??\C:\Windows\system32\drivers\myfault.sys
    Image name: myfault.sys
    Browse all global symbols  functions  data
    Timestamp:        Fri Sep 30 00:17:31 2022 (6335C51B)
    CheckSum:         00010CED
    ImageSize:        00009000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
    Information from resource tables:
    
3: kd> ? fffff807`47f59000 - fffff807`47f50000
Evaluate expression: 36864 = 00000000`00009000

3: kd> s -a fffff807`47f50000 L?0x9000  "Leak"
fffff807`47f51559  4c 65 61 6b 0f 42 c1 41-8d 49 fd 8b d0 ff 15 0c  Leak.B.A.I......
fffff807`47f515c7  4c 65 61 6b 0f 42 c1 33-c9 8b d0 ff 15 a0 1a 00  Leak.B.3........

三: 總結

在過往的dump分析中都是用戶態程式的泄露,內核態模式堆的的泄露還是第一次分析,不是朋友提供的這次機會,真的就沒緣分啦!在這次dump分析過程中,也讓大家看到了 windbg 是多麼的強大!

圖片名稱
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目錄一、背景介紹1.1 爬取目標1.2 演示視頻1.3 軟體說明二、代碼講解2.1 爬蟲採集模塊2.2 軟體界面模塊2.3 日誌模塊三、獲取源碼及軟體 一、背景介紹 1.1 爬取目標 您好!我是@馬哥python說 ,一名10年程式猿。 我用python開發了一個爬蟲採集軟體,可自動按關鍵詞抓取小紅 ...
  • 性能優化是一場永無止境的旅程。到家門店系統,作為到家核心基礎服務之一,門店C端介面有著調用量高,性能要求高的特點。C端服務經過演進,核心介面先查詢本地緩存,如果本地緩存沒有命中,再查詢Redis。本地緩存命中率99%,服務性能比較平穩。 ...
  • 本文主要是個人的學習筆記總結,數據預處理的基本思路和方法,包括一些方法的使用示例和參數解釋,具體的數據預處理案例case詳見其他文章。如有錯誤之處還請指正! 目錄數據的質量評定數據處理步驟缺失值的處理標記缺失值刪除缺失值填充缺失值重覆值處理異常值的處理數據集合併pandas.DataFrame.co ...
  • Qt 是一個跨平臺C++圖形界面開發庫,利用Qt可以快速開發跨平臺窗體應用程式,在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實現圖形化開發極大的方便了開發效率,本章將重點介紹`Slider`滑塊條組件的常用方法及靈活運用。當涉及到C++ Qt開發中的`Slider`滑塊條組件時,你可能會... ...
  • 題目:輸入兩個正整數 m 和 n,求其最大公約數和最小公倍數。 求出最大公約數就行,最小公倍數用m*n除以最大公約數就行 package myself; import java.util.Scanner; /** * @Auther QY * @Date 2023/12/11 */ public c ...
  • 但在微服務架構中,每個微服務通常有多個實例,每個實例具有不同的位置,而且實例會動態變化,比如在負載發生變化時服務會進行擴容或縮容,或者某個實例所在的VM/Container故障後發生遷移,都會導致服務實例地址的變化。因此使用微服務架構開發的應用,必須通過服務註冊和發現技術解決此問題。 1. 簡單概述 ...
  • 1.1系統架構的演變 隨著互聯網的發展,網站應用的規模不斷擴大,常規的應用架構已無法應對,分散式服務架構以及微服務架構勢在必行,亟需一個治理系統確保架構有條不紊的演進。 1.1.1單體應用架構 Web應用程式發展的早期,大部分web工程(包含前端頁面,web層代碼,service層代碼,dao層代碼 ...
  • Quartz.NET 是一個用於在 .NET 應用程式中實現作業調度和定時任務的開源框架。它允許你在應用程式中定義和調度作業,支持複雜的調度需求,例如定時、重覆、錯過執行、依賴性等。下麵,我將通過一個簡單的實例來詳細描述 Quartz.NET 的功能、使用方法,並提供源代碼。 在這個示例中,我將使用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...