内容简介:在发布了《答案是“可以”,但是操作起来很复杂。 我们从头开始一步一步看看是如何进行取证的,第一步先安装PowerShell的
在发布了《 从PowerShell进程转储中提取活动历史记录 》这篇文章后,我收到了一个有趣的问题:“如果没有捕获到原始文件,是否可以提取到已执行的脚本内容呢(从磁盘里)?”
答案是“可以”,但是操作起来很复杂。 我们从头开始一步一步看看是如何进行取证的,第一步先安装PowerShell的 WinDbg模块 ,我们需要它来做一些自动化的工作。
首先搭建我们的取证实验环境,在 C:temp
之类的目录下创建如下简单的脚本:
打开PowerShell会话,运行这个脚本。然后在任务管理器中Dump Powershell 内存:
现在,使用WinDbg模块连接到Dump下来的文件:
Connect-DbgSession -ArgumentList '-z "C:UsersleeAppDataLocalTemppowershell.DMP"'
开始取证
在开始我们的取证之前,让我们脑暴一下。我们的目的是提取Powershell会话中运行的脚本对象内容(如果存在的话),那么问题就是,如何找到这个脚本对象呢?
首先,我们使用SOS的 Dump Object
命令来转储进程内存中的所有对象内容。然后,我们用 !DumpHeap
命令开始查找所有对象实例(eg:这里我们甚至没有用 -Type
过滤指令)。这一步和接下来的操作将花费很长时间,所以建议在睡觉之前跑这个命令 : D
$allReferences = dbg !dumpheap -short
一旦我们导出了所有对象引用,便可使用 !do
(Dump Object)命令将它们全部可视化。转储对象的输出中不包括被转储对象的地址,因此我们用 Add-Member
来跟踪它。
$allObjects = $allReferences | Foreach-Object { $object = dbg "!do $_"; Add-Member -InputObject $object Address $_ -PassThru -Force }
(第二天)确实是一大堆数据呢!这个进程大概有100万个被SOS识别出来的对象实例。他们中有SOS可以可视化的GUID吗? 我们来看看:
看起来我们运气还不错。在上百万个对象中,我们设法将范围缩小到PowerShell内存中的7个 System.String
对象,这些对象以某种方式引用了GUID。如果我们认为信息可能一直在 System.String
中,我们可以使用 $allReferences = dbg !dumpheap –type System.String –short
代替一开始的 $allObjects
来加快查询。 问题又来了,我们怎么知道谁包含了这些GUID呢?
为了找到答案,我们使用SOS的 !gcroot
命令。 这个命令通常用于诊断托管内存的泄漏问题。例如:“为什么CLR保留了字符串的一千万个实例?”。对于任何给定对象, !gcroot
命令告诉您哪个对象引用了它,这样递归一直到对象树的根节点。 让我们探讨一下这些根节点。
好了,所以最后一个元素( 数组中的第6个 )并不是最终的根节点。它不会再被引用,很快就会被垃圾收集器清理干净。
第5个是以一个对象数组为根节点的,其中一个元素是 ConcurrentDictionary
,它包含一个 ScriptBlock
, ScriptBlock
中包含 CompiledScriptBlockData
, CompiledScriptBlockData
包含了PowerShell AST中的节点,最后PowerShell AST引用了 CommandAst AST
,最终引用了这个GUID。
看起来还不错,那其他的呢,我们看看实例中的第4项:
有意思! 这个是以相同的根对象数组(0000026e101e9a40),相同的 ConcurrentDictionary
(0000026e003bc440)开头的,但这次最后是一个简单配对的元组(包含我们要找的String和另一个String)。 让我们深入了解那个元组及其包含的字符串:
这个元组有两个元素。 第一个元素看起来是执行脚本的路径,第二个元素看起来是该脚本中的内容。 让我们看看PowerShell Source怎么定义这些数据结构的。 搜索一下 ConcurrentDictionary
, 在第三页我们可以看到:
有一个名为 CompiledScriptBlock
的类。 它包含一个名为 s_cachedScripts
的静态(进程范围)缓存。 这是一个将一对字符串映射到 ScriptBlock
实例的字典。 如果您阅读了源代码,您可以确切地看到Tuple的内容(一个脚本路径到ScriptBlock缓存内容的映射):
这个数据结构就是我们最终要关心的。出于性能原因,PowerShell维护一个内部脚本块缓存,这样每次看到脚本时都不需要重新编译脚本块。 该缓存是路径和脚本内容的关键。 存储在缓存中的东西是 ScriptBlock
类的一个实例,它包含了已编译脚本的AST。
所以现在我们知道了它的存在,就可以自动化地并有目的性地去提取这些东西! 现在我们正式开始写提取脚本,我们要做的就是:
!dumpheap !dumpheap
现在我们可以深入其中的一个节点。 它有一个m_key,我们可以研究一下。
差不多了!让我们从结果中提取出两个东西,然后获得一个漂亮的PowerShell对象:
这是一个漫长的过程,但是我们从一开始的假设到最后提取出脚本内容,都完整的过了一遍。现在,即使原始脚本文件已经被删除,你也能够从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 m_Item2 | ConvertFrom-String | % P7 $string2 = dbg !do $item2 | Select-String 'String:s+(.*)' | % { $_.Matches.Groups[1].Value } [PSCustomObject] @{ Path = $string1; Content = $string2 } } }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
见微知著-WEB用户体验解构
李清 / 机械工业出版社 / 2010-4 / 36.00元
本书用解构分析的方法,系统全面地介绍了Web页面设计的相关知识和要素。 本书从整体到局部地对网站的元素进行解构,包括网站整体布局、整体配色方案,到网站各个功能区域,如登录区、内容区、广告区等,最后到按钮、反馈、验证码、字体、文字语气等多个细节元素。本书通过解构这些元素来讲述如何对用户体验设计进行优化,如何进行搜索引擎优化。 本书适用于网站交互设计师、视觉设计师、产品经理、网站设计人员、......一起来看看 《见微知著-WEB用户体验解构》 这本书的介绍吧!