從PowerShell記憶體轉儲中提取執行的指令碼內容
上次釋出 從PowerShell流程轉儲中提取活動歷史記錄 之後,我又想到了一個有趣的問題:“是否可以提取已執行的指令碼(來自磁碟)的內容,即使這些檔案未被捕獲?”,我得到答案是“是”,但是它也很複雜。這將需要大量的WinDbg自動化工作,因此第一步, 安裝WinDbg模組 。
我們先建立一個簡單的指令碼。
開啟PowerShell會話,執行指令碼,然後建立轉儲檔案。
現在,使用WinDbg模組連線到轉儲檔案:
Connect-DbgSession -ArgumentList '-z "C:\Users\lee\AppData\Local\Temp\powershell.DMP"'
開始
我們想要提取表示在該會話中執行的指令碼的物件(如果存在)。但我們如何找到這些呢?
首先,讓我們使用SOS的“轉儲物件”命令來轉儲它知道的關於程序中每個物件的所有內容。因此,我們將從 !DumpHeap
命令開始查詢所有物件例項(即:我們甚至不使用-Type過濾器)。但還有其他方法可以做到這一點,但這一步和下一步將需要很長時間。
$allReferences = dbg !dumpheap -short
一旦我們擁有所有物件引用,讓我們使用 !do
(轉儲物件命令)讓SOS將它們全部視覺化。轉儲物件的輸出不包括被轉儲物件的地址,因此我們也將使用Add-Member來跟蹤它。
$ allObjects = $ allReferences | Foreach-Object {$ object = dbg“!do $ ”; Add-Member -InputObject $ object Address $ -PassThru -Force}
SOS在此流程例項中知道大約有一百萬個物件。但是他們中的任何一個GUID都會被SOS視覺化嗎?
看起來我們很幸運!在這些百萬個物件中,我們設法將其縮小到PowerShell記憶體中的7個System.String物件,這些物件以某種方式引用了GUID。如果我們認為資訊可能一直在System.String中,我們可以使用 “$allReferences = dbg !dumpheap –type System.String –short”
使我們的初始 “$ allObjects”
查詢更快。但是我們如何弄清楚這些GUID的是什麼?
為了找到 答案 ,我們將使用SOS的 !gcroot
命令。這通常用於診斷託管記憶體洩漏 , !gcroot
命令會告訴您引用它的物件以及引用該物件的物件 – 一直到達物件的根目錄。讓我們探討一下這些根源。
第5項根植於物件陣列(System.Object[]),其中一個元素是 ConcurrentDictionary
,它包含一個 ScriptBlock
,它又包含 CompiledScriptBlockData
,其中包含PowerShell AST中的節點,在引用這個GUID的命令AST中觸底。
這是我例項中的第4項:
這是有趣的!這個開頭是相同的根物件陣列(0000026e101e9a40),相同的ConcurrentDictionary(0000026e003bc440),但這次最後是一個包含我們的字串和另一個字串的元組(兩個專案的簡單配對)。讓我們深入瞭解那個元組及其包含的字串。
所以這個元組有兩個元素。第一個元素看起來是執行指令碼的路徑,第二個元素看起來是該指令碼中的內容。讓我們看看 PowerShell Source 對這些資料結構。我將 搜尋ConcurrentDictionary 以檢視我能找到的內容。在第三頁,我們可以看到我們正在檢視的內容:
有一個名為 CompiledScriptBlock
的類。它包含一個名為 “s_cachedScripts”
的靜態(程序範圍)快取。這是一個將一對字串對映到ScriptBlock例項的字典。如果您閱讀了原始碼,您可以確切地看到Tuple的內容 – 指令碼路徑到ScriptBlock快取時包含的內容的對映:
這個資料結構是我們最終討論的內容。出於效能原因,PowerShell維護一個內部指令碼塊快取,這樣每次看到指令碼時都不需要重新編譯指令碼塊。該快取是路徑和指令碼內容的關鍵。儲存在快取中的東西是ScriptBlock類的一個例項,它包含(除此之外)編譯的指令碼的AST。
所以現在我們知道這個東西存在了,我們可以在自動化中更智慧,並提取這些東西!現在我們需要一個真正的指令碼,這就是我們要做的:
1. 使用 !dumpheap 查詢此Tuple類的例項。dumpheap命令執行子字串搜尋,因此我們將使用正則表示式進行一些後處理。
2. 這給了我們實際想要研究的元組類的MT。
3. 使用該MT作為過濾器再次執行!dumpheap
現在我們可以探索其中一個節點。它有一個 m_key
,我們可以深入研究。
差不多了!讓我們從那些結果鍵中提取出兩個專案,然後生成一個漂亮的PowerShell物件:
這是一個將所有這些打包成函式的指令碼.
function Get-ScriptBlockCache { $nodeType = dbg !dumpheap -type ConcurrentDictionary | Select-String 'ConcurrentDictionary.Node.Tuple.String.String.]]$' $nodeMT = $nodeType | ConvertFrom-String | Foreach-Object P1 $nodeAddresses = dbg !dumpheap -mt $nodeMT -short $keys = $nodeAddresses | % { dbg !do $_ } | Select-String m_key $keyAddresses = $keys | ConvertFrom-String | Foreach-Object P7 foreach($keyAddress in $keyAddresses) { $keyObject = dbg !do $keyAddress $item1 = $keyObject | Select-String m_Item1 | ConvertFrom-String | % P7 $string1 = dbg !do $item1 | Select-String 'String:\s+(.)' | % { $_.Matches.Groups[1].Value } $item2 = $keyObject | Select-String mItem2 | ConvertFrom-String | % P7 $string2 = dbg !do $item2 | Select-String 'String:\s+(.*)' | % { $.Matches.Groups[1].Value } [PSCustomObject] @{ Path = $string1; Content = $string2 } } }
*參考來源: leeholmes ,周大濤編譯,轉載請註明來自FreeBuf.COM