參與一宗記憶體洩漏案件調查,過程學到新工具與新技巧,特筆記備忘。

問題網站平日之正常記憶體使用量依尖峰離峰維持在 40% ~ 10% 週期性起伏,但在某次 Windows Update 後,記憶體在兩三天內一路上升到 90% 以上,最後網站因記憶體不足當機。查看工作管理員,記憶體用近 16G,IIS W3WP 程序只佔 3G 多,而工作管理員上所有程序記憶體用量加總不到 10G。嘗試 IISRESET,記憶體只減少不到 4G。由這些線索,確認記憶體被不明來源佔用。

處理這種狀況,我的第一步是使用 RAMMap 分析記憶體是否被 Mapped File 等工作管理員看不到的項目佔用。參考:Windows記憶體都用到哪裡去了?

註:本文的圖片以示意為主,非案發現場照片。

RAMMap 查詢結果出現我沒遇過的狀況,Paged Pool,用量高達 5G,恰巧就是短少的部分。依據上回研究心得,Paged Pool 為可以移至磁碟的核心集區記憶體(Kernal Pool Memory),與 OS 核心程式、驅動程式有關;而 Total 5G、Active 也接近 5G,代表 5G 幾乎都在使用中必須佔用實體記憶體。由此推估可能有 OS 核心程式配置了記憶體卻沒釋放,屬於典型的記憶體洩漏(Memory Leak)。

但要怎麼查出是 Paged Pool 被哪些程式吃掉?我學到新工具 - PoolMon

PoolMon 是 WDK (Windows Driver Kits) 附屬工具程式,可用於觀察驅動程式的記憶體(Paged Kernel Pool、Non-Paged Kernel Pool)使用狀況,方便抓出 Memory Leak 問題。要取得 poolmon.exe 需先安裝 WDK,我們需要 C:\Program Files (x86)\Windows Kits\版本\Tools\x64\poolmon.exeC:\Program Files (x86)\Windows Kits\版本\Debuggers\x64\triage\pooltag.txt。pooltag.txt 負責將 PoolTag 對映到所屬程式名稱及用途,使用時跟 poolmon.exe 放在同一目錄,加上 /g 參數即可參照。

使用指令 poolmon /p /p /t /b /g 進行查詢,/p /p 限定查詢 Paged Pool,/t 顯示總計,/b 依耗用記憶體由大至小排序,/g 表參考同目錄下的 pooltag.txt 顯示名稱。poolmon 顯示範例如下:

而在本次案件中,排第一的 PoolTag 是 Toke,Mapped Driver 則為 nt!se - Token Objects,使用了 4,280,876,528 Bytes,高達 4G,明顯是兇手。其餘關鍵數字如下:數字意義

  • Allocs 75435505 (7517) 總共配置了 754 萬個,測量期間增加 7517 個
  • Frees 72221064 (7245) 總共釋放了 722 萬個,測量期間釋放 7245 個
  • Diff 3214441 配置但沒釋放的數量

由此可知,配置多於釋放,短時間內就動用了七千五百多個,長期累積了320 萬個 Token 沒釋放掉,算是已鎖定 Memory Leak 爆炸現場。

那要怎麼查出誰用了那麼多 Token Objects?幸運地找到一篇類似情境的文章,一樣是被 Token Objects 耗光記憶體,我從中學到不少追查此種問題的技能。

首先,Kernel Memory 使用量常與 Handle 有關,所謂 Handle 是指向特定資源的參考指標,資源通常是記憶體、檔案或是 Pipe。因此,當遇到 Paged Pool 問題,Handle 便是重要的健康指標,Handle 數愈多,代表耗用的記憶體愈多,通常就是問題所在。隨手可得的工作管理員,其實已經提供一些我沒注意過的重要資訊。

總 Handle 數:

要查詢各程序使用的 Handle 數,可選取欄位:

用 Handle 排序便能快速找出誰用掉最多 Handle,通常就是吃掉 Paged Pool 記憶體的元凶。
(在我們的案例中是 lsass.exe,它居然用了 330 萬個 Handle,而且不斷持續增加。)

另外,工作管理員就有 Paged Pool 大小。(數字跟 RAMMap 有點差距,但判斷問題關鍵)

下個問題是,lsass.exe 的 330 萬個 Handle 用到哪裡去了?

本案件的第三個工具登場 - Sysinternals 魔法工具箱之 Handle !

handle -p lsass -s 可統計 lsass 使用的 Handles 數,-p lsass 鎖定程序,-s 統計各種類數量,其中 Token 數 330 萬與工作管理員看到的 Handle 數量、PoolMon 看到的數字相近:

若要查詢 lsass 用了哪些 Handle,指令為 handle -p lsass -a,加 -a 會列出所有資源 Handle,否則只會現檔案相關。再深入追查會是另一個議題,就此打住。

簡單整理這次學到的知識及分析技巧:

  1. 用 RAMMap 鎖定 Page Pool 異常飆高造成記憶體不足
  2. 用 WDK PoolMon.exe 找出佔用大量 Paged Pool 的來源,並可由配置、釋放數值偵測記憶體洩漏
  3. Paged Pool 主要由 OS 核心程式或驅動程式使用,記憶體用量常與 Handle 數正相關
  4. 工作管理員可查看總使用 Handes 數及各程序使用 Handles 數
  5. SysInternals Handle 工具可查詢程式使用 Handle 的狀況

Experience of using RAMMap, PoolMon, Handle and Task Manager to analyze paged pool memory leaking.


Comments

# by Will

為什麼 lsass 本機安全認證子系統服務(Local Security Authority Subsystem Service) 會吃這麼多記憶體?是一直有人在嘗試登入嗎?

# by Jeffrey

to Will, 可能原因蠻多,但因偏向 OS 底層運作難度頗高,還在調查中。文章有篇相似案例連結,也是 lsass 爆記憶體,是某個資安軟體的 Driver Bug 造成。

Post a comment