Python-和-PowerShell-协作教程-全-
Python 和 PowerShell 协作教程(全)
一、面向调查人员的 PowerShell 简介
PowerShell 提供了一个强大的获取引擎,可以从实时系统、服务器、外围设备、移动设备和数据驱动的应用程序(如 Active Directory)中获取大量信息。
由于微软决定开放 PowerShell 并提供从其他非微软平台(如 Mac 和 Linux)获取信息的能力,可以访问的信息范围实际上是无限的(通过适当的凭证)。再加上大量的内置和第三方 CmdLets(发音为“command let”),可以对其进行过滤、排序和整合,您就拥有了最终的获取引擎。
通过添加从 PowerShell 到 Python 的桥梁,我们现在可以利用丰富的逻辑机器学习和对 PowerShell 获取的原始信息的深度分析。图 1-1 描绘了我们将在本书中集成的核心组件。其结果将是为现场调查和事故响应应用开发新的创新方法的工作台。
图 1-1
PowerShell 和 Python
PowerShell 的一点历史
PowerShell 是一个微软框架,包括一个命令外壳和一个脚本语言。PowerShell 传统上由系统管理员、IT 团队、事件响应小组和法医调查人员使用,以获取有关他们管理的基础架构的操作信息。如图 1-2 所示,在过去十年中发生了显著的变化。
图 1-2
PowerShell 发展
PowerShell 如今使用情况如何?
PowerShell 通常用于自动化管理任务和检查运行的桌面、服务器和移动设备的细节。它用于检查使用通用对象模型(COM)和 Windows 管理界面(WMI)的本地和远程系统。今天,它可以用来检查和管理使用公共信息模型(CIM)的远程 Linux、Mac 和网络设备。
你是如何用 PowerShell 做实验的?
PowerShell 通常已经安装在现代的 Windows 桌面和服务器平台上。如果没有,可以直接打开自己喜欢的浏览器,搜索“Windows 管理框架 5”,然后下载安装 PowerShell。PowerShell 和 PowerShell ISE(集成脚本环境)是免费的。
我更喜欢使用 PowerShell ISE,因为它提供了:
-
有助于发现和试验 CmdLets 的集成环境
-
编写、测试和调试脚本的能力
-
轻松访问上下文相关的帮助
-
自动完成命令,加速开发和学习
导航 PowerShell ISE
安装 PowerShell ISE 后,您可以在 Windows 平台上通过单击开始菜单(Windows 8-10 的左下角)启动它,然后搜索 PowerShell ISE 并单击如图 1-3 所示的应用程序。
图 1-3
在 Windows 10 上启动 PowerShell
注意
您可以使用用户权限运行 PowerShell 和 PowerShell ISE 然而,要访问所需的许多丰富的采集功能,需要以管理员的身份运行 PowerShell。还有一句警告。以管理员或用户身份运行并执行 CmdLets 可能会损坏您的系统或删除重要文件!小心行事!
我通常将它添加到我的 Windows 任务栏中,以便于访问,如图 1-4 所示。PowerShell 和 PowerShell ISE 我都加了。高亮框中右边的图标是 ISE,左边的是 PowerShell。通过右键单击 PowerShell ISE 图标,然后再次右键单击 Windows PowerShell ISE 选项,您可以选择以管理员身份运行 PowerShell ISE。通过这样做,您将能够执行最广泛的 PowerShell CmdLets 和脚本。
图 1-4
Windows 任务栏以管理员身份启动 PowerShell ISE
启动后,ISE 有三个主窗口,如图 1-5 所示。请注意,默认情况下不显示脚本窗格,但可以从工具栏中选择查看。我已经注释了应用程序的三个主要部分:
图 1-5
PowerShell ISE 界面
-
脚本面板:该面板提供了使用附带的 PowerShell 脚本语言创建合并了多个命令的 PowerShell 脚本的能力。请注意,这不是我们在开发 PowerShell 脚本时通常开始的地方。相反,我们首先在直接命令输入面板中进行实验;一旦我们完善了我们的方法,我们就可以创建脚本。
-
直接命令输入面板:此面板用于执行 PowerShell CmdLets。这里输入的命令比祖先的 Windows 命令行或 DOS 命令强大得多。此外,这些命令的格式和结构有很大不同,并且遵循一些严格的规则。我将在下一节解释动词-名词的格式和结构,并提供更多的细节和一些例子。
-
命令帮助面板:该面板提供关于我们可用的每个 CmdLet 的详细帮助和信息。但是,我很少使用这个区域,而是使用 Get-Help CmdLet 请求直接帮助,以获得有关感兴趣的 CmdLet 的信息,了解它们如何操作,获得它们的使用示例,以及获得所有可用选项的详细信息。
PowerShell CmdLets
在我们直接进入 PowerShell CmdLets 之前,有一些警告:
-
实际上有成千上万种可能的 CmdLets。
-
如果你考虑所有可能的变化,有成千上万种可能的选择。
-
每天都有新的 cmdlet、变体和对现有 cmdlet 的更新被创建。
-
每个 CmdLet 都包含详细的帮助和示例。
每天更新 CmdLet 帮助很重要,这样可以确保您能够访问有关您正在使用或计划使用的 CmdLet 的最新信息。
什么是 CmdLet?
CmdLet 通常是一个轻量级WindowsPowerShell脚本,它执行特定的功能。我在这里通常陈述的原因是,一些 CmdLet 非常广泛,并且由于能够创建您自己的 CmdLet,它们的复杂性和对系统资源的使用可以基于开发人员的目标而变化。
然后, CmdLet 是用户给操作系统或应用程序执行服务的特定命令,例如“显示所有当前运行的进程”或“显示所有当前停止的服务”
所有 CmdLets 都表示为一个动词-名词对,并且有一个可以使用动词-名词对Get-Help <CmdLet name>
访问的帮助文件。所以没错,就算是帮助也只是另一个 CmdLet。更新帮助对于保持帮助与当前所有当前安装的 cmdlet 相关联以及为每天创建和更新的新 cmdlet 安装帮助至关重要。正如您可能猜到的,这只是另一个 CmdLet,这是您应该使用的第一个 CmdLet。具体来说:
Update-Help
您可以从直接命令输入面板执行此 CmdLet,如图 1-6 所示。将为所有已安装的模块更新帮助文件。我们将在以后的章节中讨论模块,但是现在这将更新所有的标准 PowerShell 模块。其他模块,如 Active Directory、VMWare、SharePoint 和数百个其他模块,允许购买大量设备和服务。
图 1-6
更新-帮助 CmdLet 执行
一些关键 CmdLets 的介绍
您可能会问的第一个问题是,“有哪些 CmdLets 可用?”或者更具体地说,“有哪些针对特定信息的 CmdLets?”本节将向您介绍几个关键的 cmdlet:Get-Help
、Get-Process
和Get-Member
。
获得帮助
假设我们对获取当前正在运行的服务的信息感兴趣。为了找到与此主题相关的 CmdLets,我将输入:
Get-Help services
注意,我没有请求关于特定 CmdLet 的信息,而是请求帮助系统向我提供关于可能与服务相关的任何 CmdLet 的信息。图 1-7 显示了一个简化的输出。
图 1-7
搜索与服务相关的 CmdLets
请注意,根据您使用的 PowerShell 版本、帮助文件的当前版本以及安装的 CmdLets,您的列表可能会有所不同。
下一步是选择一个或多个 cmdlet 并获取这些 cmdlet 的帮助。浏览这个简短的列表,Get-Service 听起来很有前途,所以我将通过键入以下命令来请求关于这个特定 CmdLet 的帮助:
Get-Help Get-Service
图 1-8 显示了简化的输出。请注意,有多个选项与 Get-Help CmdLet 的执行相关。对于这个例子,我使用了最简单的形式。但是,我也可以选择使用其他形式的 CmdLet,例如:
图 1-8
Get-Help Get-Service 缩写输出
Get-Help Get-Service -Detailed
或者
Get-Help Get-Service -Examples
检查输出时,我们注意到呈现给我们的每个命令的详细语法。此 CmdLet 允许我们获取关于本地或远程计算机上的服务的信息。选项-ComputerName
允许我们指定多台计算机,每台计算机之间用逗号分隔。通过使用:
Get-Help Get-Service -Examples
帮助系统将提供大量示例来演示 CmdLet 的使用(图 1-9 )。
图 1-9
获取示例帮助
获取流程
另一个有用的 CmdLet 是 Get-Process;与 Get-Service 非常相似,它返回关于在本地或远程计算机上运行的进程的信息。使用 Get-Help 更深入地观察 Get-Process(参见图 1-10 ,我们首先注意到 Get-Process 的六种不同的基本变体。从技术上讲,这些被称为参数集,允许我们以六种不同的方式运行 Get-Process CmdLet。
图 1-10
获取帮助获取过程
检查第一组参数(见图 1-11 ,我们发现所有参数都是可选的。这由每个参数周围的方括号表示。
图 1-11
获取流程
这允许我们简单地键入命令,而不包括任何额外的参数,如图 1-12 所示,带有缩写输出。
图 1-12
不带附加参数的 Get-Process
如果我只想获得与 Google Chrome 浏览器相关流程的信息,该怎么办?在图 1-13 中,我列出了我们需要利用的具体-Name
参数,以实现这一点。
图 1-13
Get-Process -Name 参数
您会注意到-Name
参数是可选的;但是,如果指定了它,您必须指定一个字符串来指示您必须提供的特定数据类型(其内容将是流程的名称)。您还会注意到单词 String 后面有两个方括号。这表明您可以选择包含一个名称列表。每个名称都需要用逗号分隔。图 1-14 显示了一个例子。
图 1-14
使用-Name 参数的 Get-Process 示例
获取成员
如您所见,当使用 PowerShell CmdLets 从目标系统获取信息(或证据)时,它们会提供有用的结果。除了简单的输出之外,每个 CmdLet 还返回一个对象,该对象提供对附加属性和方法的访问。Get-Member CmdLet 将显示 CmdLet 的可用属性和方法。
请注意,与任何 CmdLet 一样,您可以利用 Get-Help CmdLet 来获取有关 Get-Member 的详细信息和示例。例如,该命令将是:
Get-Help Get-Member
为了说明获取 CmdLet 附加属性的价值,请看 Get-Service CmdLet 的标准输出,如图 1-15 所示。
图 1-15
Get-Service CmdLet 的标准输出
如果需要额外的信息证据呢?例如,如果知道服务是如何启动的很重要,那该怎么办?为了回答这个问题,我们需要询问并从对象中获得额外的属性。
为了提取对象的方法和属性细节,我们需要利用管道将输出对象定向到 Get-Member CmdLet。管道在大多数命令行和 shell 环境中的操作是相似的。然而,在 PowerShell 中,它们是特定于对象和上下文的。
在本例中,我们希望查询的 CmdLet Get-Service 没有执行,而是将对象信息传递给 Get-Member CmdLet,如图 1-16 所示。注意,我们正在寻找的属性的名称是 StartType。
图 1-16
获取成员示例
现在我们知道了名称,我们可以指定属性StartType
显示一个定制的输出,如图 1-17 所示。这是我们能做的最简单的管道形式。执行 Get-Service CmdLet,并将结果传送给 Select-Object CmdLet。
图 1-17
使用名称、状态和启动类型获取服务
然后,Select-Object CmdLet 显示指定的特定属性。Select-Object CmdLet 的-Property
参数接受要显示的字符串名称。同样,每个都用逗号分隔。
挑战问题:要探索的调查 CmdLets
为了熟悉 PowerShell、ISE 和在调查过程中可能会用到的 CmdLets,您需要直接对它们进行实验。为了帮助这个过程,我在每一章的结尾都列出了一系列挑战性的问题。记住对每个 cmdlet 使用 Get-Help,并确保在检查 cmdlet 时使用了-Detailed
和-Examples
选项。我还在附录中提供了每个挑战问题的解决方案,所以你可以自己尝试一下,然后检查你的结果。
挑战一:基于文件扩展名执行“查找”
你们中的许多人可能熟悉 Windows 命令行 dir 命令,它将列出特定目录的内容。所有传统的 Windows 和 DOS 命令都有等效的 PowerShell 命令。一种毫不费力的方法是使用一个 PowerShell CmdLet 来查找相关的 PowerShell CmdLet,如图 1-18 所示。要了解有关 Get-Alias 和 Get-ChildItem 的更多信息,请使用 PowerShell 帮助系统。
图 1-18
使用获取别名
现在您已经了解了 Get-ChildItem CmdLet,使用它可以通过。jpg 扩展名。
请随意试验 Get-ChildItem 提供的其他参数。此外,确保使用-Examples 开关访问 Get-Help 并研究这些示例。
挑战二:检查网络设置
此时,您可能会想,“如果 PowerShell 只是简单地替换了 Windows 命令行,那么为什么不直接使用 Windows 命令行呢?”正如本章前面所学,帮助系统可以提供围绕特定单词或短语的可用命令列表。
尝试键入:
Get-Help ip
这将提供所有涉及 IP 的 PowerShell CmdLets。您将看到许多可能的 CmdLets,允许您检查网络配置。请注意,这比使用 Windows 命令行要强大得多。在这个挑战中,请深入了解其中的三个 CmdLets:
Get-NetIPAddress
Get-NetIPConfiguration
Get-NetIPInterface
首先,使用 PowerShell 帮助系统来理解每个 CmdLet 的功能,并检查提供的示例。然后试验每个命令,仔细查看您自己的网络设置。你知道所有的设定吗?
挑战三:检查防火墙设置
对于此质询问题,请查找可能与防火墙相关的 CmdLets。特别是获取有关您系统上的防火墙设置的信息。检查完基本信息后,查找并执行一个 CmdLet,该 CmdLet 将检查任何已启用的“服务筛选器”。你发现什么惊喜了吗?
挑战四:你探索的机会
对于这个挑战,请使用帮助系统和您感兴趣的关键字来探测您的系统。
摘要
本章介绍了本书的目标,特别是 PowerShell 和 Python 的集成将如何为研究者提供价值。
此外,还简要介绍了 PowerShell 的发展,以便更好地理解 PowerShell 今天与调查的关系。提供了 PowerShell 的基本设置和执行,以及从哪里获取最新的可信版本。概述了 PowerShell ISE 和 PowerShell 帮助系统,以及更新帮助系统的重要性。接下来,介绍了 PowerShell CmdLets 和动词-名词术语,接着是关于如何识别感兴趣的特定 cmdlet 的简短讨论和示例。演示了几个 CmdLets 来提供关于可以使用 PowerShell 获取的信息深度的详细信息。最后,提出了一组挑战性问题,以鼓励您深入 PowerShell 并进行实验。
期待第二章,我们会发现 PowerShell CmdLets 的一个关键元素是能够创建 PowerShell 变量,并以一种称为管道化的方法将多个命令串在一起。我们将建立几个调查挑战,并使用 PowerShell 变量和流水线来解决它们。此外,我们将引入几个新的 CmdLets,它们将允许我们对输出进行排序、过滤和格式化。第二章是关键,因为它提供了我们将如何集成 PowerShell 和 Python 的前奏。
二、PowerShell 管道
管道是 PowerShell 中的关键特性,它将帮助我们促进 Python 和 PowerShell 的集成。选择本章中的示例和图示来解释管道,并提供对 CmdLet 和方法的深入了解,这些在调查过程中是有用的。
什么是 CmdLet 流水线操作?
CmdLet 管道创建了以特定顺序执行的命令的汇编行,同时也从每个 CmdLet 移动数据或结果。描述这一点的最佳方式是举几个与调查相关的例子。
示例 1:获取服务
假设我们想要查看我们正在调查的系统上当前有哪些服务正在运行。将输出从一个 CmdLet 向下筛选到另一个 CmdLet 是管道最常见的用途之一。此外,我们希望以表格的形式显示输出。图 2-1 是将解决这一挑战的示例管道。
图 2-1
显示正在运行的服务的管道图
如您所见,管道从 Get-Service CmdLet 开始,没有任何命令行参数。
注意
当然,您可以在管道符号 | 之前添加命令行参数,例如-ComputerName,这将允许 Get-Service CmdLet 在指定的计算机上远程执行。
Get-Service CmdLet 生成一个对象,该对象通过管道传递给链中的下一个 CmdLet。
Where-Object CmdLet 执行过滤操作,该操作评估 Get-Service CmdLet 对象属性状态等于“正在运行”Where-Object CmdLet 的结果输出会对结果进行筛选,以仅包括那些当前正在运行的服务。然后将结果传递给下一个管道 CmdLet。
最后,Format-Table CmdLet 使用与 Get-Service 相关联的默认输出来生成带有过滤器服务的表结果显示。图 2-2 描述了实际的命令——为了简洁起见,结果被截断了。
图 2-2
挑战解决方案
注意
通过使用 Get-Service | Get-Member 操作,您可以显示 Get-Service CmdLet 对象中所有可用的方法和属性,从而允许附加的筛选选项。
在调查过程中,报告哪些服务被停止也同样重要。例如,复杂的恶意软件会使病毒防护、防火墙和其他防护服务失效。图 2-3 更改命令,只显示当前停止的服务。同样,为了简洁起见,结果被截断。
图 2-3
显示停止的服务
最后一点:如果你想要更多关于格式表的信息,记得使用 Get-Help,如图 2-4 所示。
图 2-4
格式-表 CmdLet 概述
示例 2:获取流程
与正在运行的进程相关的详细信息也很重要,可以提供有关连接到哪些进程的附加信息。例如,在现场调查中,确定 Google Chrome 正在使用哪些活跃的互联网连接可能很重要。对于这个例子,让我们首先将它分解成单独的组件,并介绍 PowerShell 中变量的概念。
PowerShell 变量
什么是 PowerShell 变量:PowerShell 中的变量只是内存中指定用于保存数据值的命名位置。PowerShell 中的所有变量名都以一个 **\(** 开头,以便于识别。另一个注意事项:PowerShell 中的变量名不区分大小写;因此,\)ipAddress 和$IPaddress 表示同一个变量。您可以为变量赋值,例如:
$InvestigatorName = "Chet Hosmer"
或者
$CaseNumber = "BC-0234"
PowerShell 自动变量
此外,还有几个内置或自动变量可用,但用户不能更改。几个例子如图 2-5 所示。
图 2-5
自动变量的示例
分解示例 2 的 CmdLet 用法
现在我们对变量有了一个大致的概念,我们将把它们用于从 Get-Process 收集信息。为了减少 Get-Process 的输出,让我们只关注一个正在运行的进程。在我的测试系统上,我安装并运行了谷歌浏览器。在您的系统上,您可能正在使用其他浏览器,如 Internet Explorer 或 Firefox。替换您的浏览器的名称,以定位由它们创建的进程。此外,名为 svchost 的进程总是在运行,因此您也可以替换它。PowerShell 中的命令如下,结果如图 2-6 所示。
图 2-6
获取进程名称 Chrome
Get-Process -Name chrome
Get-Process CmdLet 需要的一条关键信息是我的示例中与 Google Chrome 相关联的进程 ID。我们可以使用该进程 ID 将该进程与相关的互联网活动相关联。正如你可能猜到的,我们将在 PowerShell 中使用另一个 CmdLet 来检查 Google Chrome 和互联网之间的连接。为了实现这一点,将构造一个命令来将 CmdLet 的结果存储到一个名为$id 的变量中,而不是简单地显示结果:
$id = Get-Process -Name Chrome `
| select -ExpandProperty Id
请注意,为了便于显示,我使用了勾号(`)字符,然后按 Shift+Enter 在下一行继续该命令。Get-Process -Name Chrome 命令的结果然后通过管道选择-ExpandProperty 命令,以仅指定 Id 字段。当然,您可以在一行中输入这个命令,但是这是一个很好的方法,可以使这个命令更具可读性。
图 2-7 将 Get-Process ID 值的结果存储到变量\(id 中。然后通过在下一行指定\)id 变量名(当然后面跟着 Enter 键),显示$id 变量的内容。
图 2-7
将 Get-Process CmdLet 结果存储在变量$id 中
添加 NetTCPConnections CmdLet
$id 变量现在可以用作其他 CmdLets 的参数。例如,CmdLet Get-NetTCPConnections 有一个参数-OwningProcess,它允许我们将 CmdLet 的输出限制为特定的进程 id。使用 Get-Help 检查 Get-NetTCPConnections,获得以下信息(参见图 2-8 )。
图 2-8
Get-NetTCPConnections 帮助
如何发现 CmdLets?
您可能会问的一个问题是,对于成千上万的 CmdLets,我如何知道使用哪一个来获取和关联与拥有进程的 TCP 连接?答案是使用 Get-Help。PowerShell 中内置的帮助系统的设计是充分利用 PowerShell 和相关 CmdLets 的关键。由于帮助系统每天更新,因此它被设计为与新 cmdlet 保持同步,这些新 cmdlet 是随着对现有 cmdlet 的任何更新而创建的。但是,您也可以找到与特定关键字相关的 CmdLets。例如,参见图 2-9 中如何使用关键字而不是 CmdLet 来使用 Get-Help。
图 2-9
使用关键字而不是 CmdLet 获取帮助
当您使用关键字提供 Get-Help 时,如本例中的 TCP ,它将报告与 TCP 有任何关联的已知 CmdLets。可以看到,Get-NetTCPConnection 是第一个命中的。一旦你知道了 CmdLet 的名字,你就可以使用 Get-Help 命令来决定如何使用它,就像我在图 2-8 中所做的那样。
对 CmdLets 使用 PowerShell 变量
使用-OwningProcess 参数执行 Get-NetTCPConnection CmdLet 并指定$id 将仅生成与之前使用 Get-Process 发现的 Google Chrome id 值关联的 TCP 连接。完成此操作的命令如下,示例输出如图 2-10 所示。
图 2-10
使用进程 ID 的变量执行 Get-NetTCPConnection
Get-NetTCPConnection -State Established -OwningProcess $id | Format-Table -Autosize
如您所见,使用了命令行参数-State 和-OwningProcess:
-
对于-State, Established 被指定为自变量。这将只列出当前连接的 TCP 连接,因为我现在只对当前连接感兴趣。
-
相反,对于-OwningProcess,指定了变量$id,它包含与 Google Chrome 关联的进程 id 列表。这样做的原因是 Get-Help 为参数 OwningProcess 提供的定义如下:
[-OwningProcess <UInt32[]>]
该定义声明-OwningProcess 需要一个长度为 32 位的无符号整数。UInt32 后面的两个方括号[]表示它可以接受值列表。
正如你所看到的,只有一个 Chrome 进程 id(特别是 108404)与已建立的互联网连接相关联。因此,被识别的其他 Google Chrome 进程并不直接连接互联网,只有 108404 可以。
这是一个很好的例子,说明了如何使用中间变量来存储命令的内容。但是,我们可以使用一个命令来执行这个操作。掌握了 Get-Process、PowerShell 变量和 Get-NetTCPConnections 的工作原理后,可以创建一个命令来消除对$id 变量的需要。为了进行下一步,需要 ForEach-Object CmdLet。
ForEach-对象
ForEach-Object 允许处理管道上前一个命令的每个后续结果。在本例中,这将是 Get-Process -Name Chrome 命令生成的每个结果。
图 2-11 使用 Get-Help 提供 For-Each 对象的解释。
图 2-11
ForEach-Object 的获取帮助概述
创建示例 2 的单一管道解决方案
Get-Process -Name Chrome | ForEach-Object {Get-NetTCPConnection -State Established -OwningProcess $_.Id -ErrorAction SilentlyContinue}| Format-Table -Autosize
在本例中(参见图 2-12 中的操作结果),组件分解如下:
-
获取所有名为 Chrome 的进程的详细信息。
ForEach-Object { }
-
处理每次迭代(更简单地说,是 Get-Process 通过管道提供的每次输出。
{Get-NetTCPConnection -State Established -OwningProcess $_.Id -ErrorAction SilentlyContinue}
-
对每个结果执行 Get-NetTCPConnection CmdLet。
-
-State Established 过滤输出,仅包括当前建立的连接。
-
-OwningProcess \(_。Id 指定将提取连接信息的进程 ID。的\)_。Id 语法用于从 Get-Process CmdLet 的每个迭代结果中获取所属进程的进程 ID。使用以下语法处理特定属性:
-
$_.身份
This syntax breaks down as follows:
-
$_ 表示通过管道的当前对象。
-
。Id 指定哪个特定属性值与操作相关联。
-
-
-
-ErrorAction -SilentlyContinue 用于忽略 Get-NetTCPConnection CmdLet 期间可能出现的任何错误。例如,如果进程 ID 未链接到指定的 TCPConnection,CmdLet 将引发异常。该参数允许忽略这些异常。
-
Format-Table -Autosize 用于将输出格式化为更紧凑的格式。
Get-Process -Name Chrome
图 2-12
映射谷歌浏览器 IP 连接的最终解决方案
解析远程 IP 地址
这些结果引出了下一个调查问题,Chrome 浏览器引用的 IP 地址指的是什么?当然有一个 CmdLet 可以直接发现这些信息。IP 地址 72.21.207.216 是从图 2-12 的列表中任意选择的。然后,Resolve-DnsName CmdLet 用于获取有关此远程 IP 地址的信息。
Resolve-DnsName 72.21.207.216
Resolve-DnsName CmdLet 成功解析了 developer.amazonservices.com 的 IP 地址(参见图 2-13 )。
图 2-13
解析 DnsName
要了解有关 Resolve-DnsName 的更多信息,请尝试使用 Get-Help。
添加副本以跟踪您的活动
记录你的调查行为是很重要的(至少可以这么说)。捕获操作和结果数据的一个简单方法是在 PowerShell 中使用另一个 CmdLet:
Start-Transaction
Stop-Transaction
与 PowerShell 中的所有 cmdlet 一样,获取有关 cmdlet 的用法和选项的信息是通过使用 Get-Help。这听起来可能有点多余;然而,许多人仍然转向谷歌或其他搜索引擎来获取这些知识。这在某些情况下当然是有用的,但是 PowerShell 中的帮助系统不仅功能强大、考虑周全,而且每天都在更新。因此,为了获得有关 CmdLets 的最新、最准确的信息,请使用 Get-Help。图 2-14 提供了与开始转录相关的结果。
图 2-14
获取帮助开始-抄本
在这个例子中,指定-Path 参数是为了将脚本的输出指向一个特定的文件,如图 2-15 所示。为了演示 Start-script 的-Append 参数,使用了 Transcript CmdLet,然后重新启动了 script。为此,只需使用相同的路径参数启动第二个 Start-Transcript CmdLet,然后添加-Append 选项,如图 2-15 所示。这允许您在同一个输出文件中连接 PowerShell 会话。
图 2-15
PowerShell 启动和停止记录
清单 2-1 描述了生成的脚本文件。请注意,这里还添加了另一个新的 CmdLet,Out-File–这将 Get-Process CmdLet 的输出定向到桌面上的 IP-Result.txt 文件。因此,脚本不包括 Get-Process 或 Get-Service 输出,而是将结果存储在指定的输出文件中。这可能是你的文件夹。突出显示每个附加事务的开始和结束时间字符串。请注意,PowerShell 使用本地时间;在这个例子中,记录开始于 2018 年 11 月 27 日,16:09:03,或下午 4:09。
**********************
Windows PowerShell transcript start
Start time: 20181127160903
Username: PYTHON-3\cdhsl
RunAs User: PYTHON-3\cdhsl
Configuration Name:
Machine: PYTHON-3 (Microsoft Windows NT 10.0.17134.0)
Host Application: C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe
Process ID: 148432
PSVersion: 5.1.17134.407
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.17134.407
BuildVersion: 10.0.17134.407
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\Users\cdhsl\PS-TRANSCRIPTS\DEMO.txt
PS C:\WINDOWS\system32> Get-Process -Name chrome | Out-File C:\Users\cdhsl\Desktop\IP-Result.txt
PS C:\WINDOWS\system32> Stop-Transcript
**********************
Windows PowerShell transcript end
End time: 20181127160930
**********************
**********************
Windows PowerShell transcript start
Start time: 20181127161013
Username: PYTHON-3\cdhsl
RunAs User: PYTHON-3\cdhsl
Configuration Name:
Machine: PYTHON-3 (Microsoft Windows NT 10.0.17134.0)
Host Application: C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe
Process ID: 148432
PSVersion: 5.1.17134.407
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.17134.407
BuildVersion: 10.0.17134.407
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is C:\Users\cdhsl\PS-TRANSCRIPTS\DEMO.txt
PS C:\WINDOWS\system32> Get-Service | Format-Table -AutoSize | Out-File C:\Users\cdhsl\Desktop\Services.txt
PS C:\WINDOWS\system32> Stop-Transcript
**********************
Windows PowerShell transcript end
End time: 20181127161306
**********************
Listing 2-1
PowerShell Transcript
挑战问题:CmdLet 实验
仅仅通过阅读本文或任何其他相关内容是无法学会使用 PowerShell 的。相反,你必须通过与 PowerShell 互动来体验它。表 2-1 提供了一些在调查过程中有用的流行 CmdLets 的简短列表。我只选择了检索或获取信息的 CmdLets 供您试验。
表 2-1
质询问题 CmdLets
| 获取流程 | 获取服务 | | Get-NetIPAddress | Get-NetIPConfiguration | | get-net IP v4 协议 | get-net IPv6 协议 | | Get-NetTCPConnection | 测试网络连接 | | 获取网络路由 | get-MP 计算机状态 | | get-mpthread | Get-NetFirewallSetting | | Get-NetFirewallPortFilter | 获取-体积 | | get-childitem | Get-ItemProperty | | 获取事件日志 | Get-LocalUser | | Get-LocalGroup(获取-局部组) | 获取内容 | | 获取位置 | 设置位置 | | 开始-抄本 | 停止转录 | | 格式-表格 | |警告
如果您决定用修改系统的其他 CmdLets 进行试验,那么风险自负。PowerShell CmdLets 可以修改、损坏、删除甚至破坏您的系统。
对于表 2-1 中指定的每个 CmdLets,执行以下操作:
-
查看每个 CmdLet 的帮助,包括详细信息和示例,即,
-
获取帮助详细信息
-
获取帮助-示例
-
-
查看后,描述 CmdLet 的作用,并考虑它在调查中的价值。
-
使用最少一个参数执行每个 CmdLet,也可以尝试其他参数。
-
使用管道来组装 CmdLet,从简单的事情开始,比如将 CmdLet 输出管道化到 Format-Table CmdLet,然后尝试其他选项。
-
确保你的开始和结束记录在你的实验中,这将作为你行动和结果的记录。当您以后试图复制一个复杂的命令时,可以参考这些命令。
这个挑战性问题的解决方案可以在本书的附录和源代码中找到,可在 www.apress.com/9781484245033
获得。
摘要
本章重点介绍了 PowerShell 的几个关键领域,并介绍了几个新的 CmdLets 及其应用。此外,还介绍了 PowerShell 变量的创建和使用。创建了两个示例管道来演示如何在 PowerShell 中实现管道。第三章将引入新的 CmdLets,开发多个完整的 PowerShell 脚本。
三、PowerShell 脚本目标调查
本章将超越单行命令和管道,以创建实际的 PowerShell 脚本。PowerShell 脚本提供了自动化需要特定 CmdLets、管道、变量、结构等的重复性任务的能力。描述 PowerShell 脚本的另一种简单方式是,它们允许您创建新的、更强大、更有针对性的 CmdLets 来解决特定的挑战。一旦您开发了一个完全符合您需求的命令,那么创建一个封装或抽象命令复杂性的脚本是非常有益的。
在这一章,我们将通过两个例子。一个是创建一个特定的、最终有用的调查脚本,该脚本将获取并处理系统事件日志。第二个例子是我们检查 USB 设备使用情况的场景。
关于 PowerShell 脚本的基本事实
在我们开始之前,这里有一些关于 PowerShell 脚本的基本事实:
-
脚本是包含一系列 PowerShell 命令的简单文本文件。
-
为了防止恶意脚本的执行,PowerShell 实施了一个执行策略,该策略默认设置为“受限”,这样 PowerShell 脚本在默认情况下将不会执行。因此,您必须设置执行策略以允许脚本执行。
-
要执行 PowerShell 脚本,您必须在 PowerShell ISE 中执行脚本并提供脚本的完整路径,或者包含脚本的目录必须在您的 Windows 路径中。
示例 EventProcessor PowerShell 脚本
从事件日志中获取数据是法医调查和事件响应活动中的常见做法。这也是系统管理员每天都要执行的一项有用的活动。
从可能分布在整个调查环境中的日志文件中收集有意义的数据可能非常耗时,如果没有一致和完整地完成,将会导致问题。因此,开发一个有针对性的 PowerShell 脚本来执行这个操作将会给调查人员带来巨大的价值。
事件日志 CmdLets
当然,PowerShell 已经包含了通用的 CmdLets,用于从事件日志中收集基本的数据;因此,识别和选择一个可用的 CmdLets 是第一步。为此,我们再次求助于内置的 PowerShell 帮助系统。使用关键字 EventLog 请求帮助会返回 CmdLet 列表,如图 3-1 所示。
图 3-1
引用关键字 EventLog 的 CmdLets
在查看了概要之后,Get-EventLog 似乎是从事件日志中获取事件的一个可能的目标 CmdLet。
图 3-2 显示了与 Get-EventLog CmdLet 相关的基本帮助信息和用法。
图 3-2
Get-Help 获取事件日志结果
图 3-3 描述了几个使用示例。每个标识一个不同的日志文件,并请求最新的 20 个事件。请注意,如果请求 安全 事件日志,您必须拥有管理权限才能访问该日志。
图 3-3
Get-EventLog 请求示例
检索更具体的事件日志信息
图 3-4 显示了执行 Get-EventLog 后的结果。
图 3-4
Get-EventLog 示例结果
Get-EventLog -logName system -Newest 20
基于我们在第二章中学到的关于 PowerShell 管道的知识,我们可以执行更具体或更有针对性的事件日志数据采集。例如,如果我们只想查看类型为错误或警告的事件,并过滤掉一般的信息性消息,该怎么办?
考虑到图 3-5 中所示的 Get-Help Get-EventLog 结果摘录,列出的可能条目类型如下:
图 3-5
Get-EventLog 的获取帮助摘录
-
错误
-
信息
-
失败审计
-
成功编辑
-
警告
基于此,可以创建一个更精确的命令,只提取目标事件警告或错误,并指定与要显示的事件日志相关的特定属性。
Get-Eventlog -LogName system -Newest 20 | Select-Object -Property TimeGenerated, Source, EntryType, Message | where {$_.EntryType -eq "warning" -or $_.EntryType -eq "error"}
该命令产生如图 3-6 所示的结果。
图 3-6
get-带有特定字段和条目类型警告或错误的事件日志
创建脚本
基于对 Get-EventLog 的基本理解,让我们定义一个挑战问题。
第一步:定义挑战
在编写脚本之前,请考虑调查人员在检索事件日志时面临的基本挑战,以及如何开发 PowerShell 脚本来应对这些挑战。问问自己:
-
需要收集什么事件日志?根据调查,是否需要获取特定的事件日志?
-
应该从哪台或哪些计算机上收集日志文件?
-
应该收集多少条最新记录?
-
基于 EventType 的可选过滤器有用吗?
-
事件日志中应该生成哪些特定字段?
- 通过使用 Get-Member,我们可以看到感兴趣的公共属性包括:类别、条目类型、事件 ID、机器名、消息、源、时间生成、时间写入和用户名。
-
在哪里生成输出,即文件的标准输出?
-
其他人将如何使用该脚本?
-
我们需要提供帮助吗?
-
他们将如何输入参数?
-
一旦您确定了挑战并能够回答它们,您现在就有了脚本的工作定义,可以继续第二步了。
第二步:分阶段创建脚本
根据第一步中创建的定义,需要为我们的脚本定义特定的参数:
-
目标日志
-
目标计算机
-
目标计数
-
TargetEntryType
-
报告标题
清单 3-1 显示了完整的 EventProcessor 脚本。稍后,我还将展示 Get-Help 结果、样本执行和结果报告。
<#
.synopsis
EventProcessor EventLog Capture Automation Version 1.0
- User Specified Target EventLog
- User Specifies the number of newest Log Entries to Report
- User Specifies the Entry Type to target, for example warning, error, information etc.
- User Specifies the target computer or computers to extract the logs
- User Specifies the HTML Report Title
The script will produce an HTML output file containing details of the EventLog acquisition.
.Description
This script automates the extraction of information from the specified log file
.parameter targetLogName
Specifies the name of the log file to process
.parameter eventCount
Specifies the maximum number of newest events to consider in the search
.parameter eventType
Specifies the eventType of interest
.parameter targetComputer
Specifies the computer or computers to obtain the logs from
.parameter reportTitle
Specifies the HTML Report Title
.example
EventProcessor
Execution of EventProcessor without parameters uses the default settings of
eventLog system
eventType warning
eventCount 20
targetComputer the computer running the script
.example
EventProcessor -targetLogName security
This example specifies the target eventLog security
and uses the default parameters
eventType warning
eventCount 20
targetComputer the computer running the script
.example
EventProcessor -reporTitle "ACME Computer Daily Event Log Report"
This example provides a custom Report Title
.example
EventProcessor -targetLogName security -eventCount 20 -entryType warning -targetComputer Python-3
This example specifies all the parameters, targetLogName, eventCount, entryType and targetComputer
#>
# Parameter Definition Section
param(
[string]$targetLogName = "system",
[int]$eventCount = 20,
[string]$eventType="Error",
[string]$reportTitle="Event Log Daily Report",
[string[]]$targetComputer=$env:COMPUTERNAME
)
# Get the current date and tme
$rptDate=Get-Date
$epoch=([DateTimeOffset]$rptDate).ToUnixTimeSeconds()
# Create HTML Header Section
$Header = @"
<style>
TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
</style>
<p>
<b> $reportTitle $rptDate </b>
<p>
Event Log Selection: <b>$targetLogName </b>
<p>
Target Computer(s) Selection: <b> $targetComputer </b>
<p>
Event Type Filter: <b> $eventType </b>
<p>
"@
# Report Filename Creation
$ReportFile = ".\Report-"+$epoch+".HTML"
# CmdLet Pipeline execution
Get-Eventlog -ComputerName $targetComputer -LogName $targetLogName -Newest $eventCount -EntryType $eventType |
ConvertTo-HTML -Head $Header -Property TimeGenerated, EntryType, Message |
Out-File $ReportFile
Listing 3-1
EventProcessor Script
EventProcessor 脚本分为四个主要部分。为了完整起见,PowerShell 脚本的开发应该包括这些部分。
-
脚本标题(包括帮助和示例)
-
参数定义
-
局部变量定义
-
使用参数和局部变量执行 CmdLet
让我们更深入地了解一下脚本构造。
注意
您可以使用这个示例作为基线,因为它为 PowerShell 脚本提供了一个很好的样板。
脚本标题
脚本头包含用于定义脚本的关键信息,并且符合严格的格式,以便在由 Get-Help CmdLet 处理时提供帮助详细信息。
。概要部分
的。概要部分提供了脚本目的和用户期望的快速概述(列表 3-2 )。
<#
.synopsis
EventProcessor EventLog Capture Automation Version 1.0
- User Specified Target EventLog
- User Specifies the number of newest Log Entries to Report
- User Specifies the Entry Type to target, for example warning, error, information etc.
- User Specifies the target computer or computers to extract the logs
- User Specifies the HTML Report Title
The script will produce an HTML output file containing details of the EventLog acquisition
.
Listing 3-2.Synopsis Section
。描述部分
那个。描述部分提供了脚本的简洁定义(清单 3-3 )。
.Description
This script automates the extraction of information from the specified log file
Listing 3-3.Description Section
。参数部分
本节详细定义了脚本使用的每个命令行参数(清单 3-4 )。
.parameter targetLogName
Specifies the name of the log file to process
.parameter eventCount
Specifies the maximum number of newest events to consider in the search
.parameter eventType
Specifies the eventType of interest
.parameter targetComputer
Specifies the computer or computers to obtain the logs from
.parameter reportTitle
Specifies the HTML Report Title
Listing 3-4.Parameters Section
注意,在这个脚本中,所有参数都是可选的,因为在定义过程中,正如您将在后面看到的,每个参数的默认值都是提供的。这允许用户通过键入以下命令来执行脚本:
.\EventProcessor
。示例部分
在本节中,提供了几个脚本命令行执行示例,以及每个变体所提供内容的定义(清单 3-5 )。
.example
EventProcessor
Execution of EventProcessor without parameters uses the default settings of
eventLog system
eventType warning
eventCount 20
targetComputer the computer running the script
.example
EventProcessor -targetLogName security
This example specifies the target eventLog security
and uses the default parameters
eventType warning
eventCount 20
targetComputer the computer running the script
.example
EventProcessor -reporTitle "ACME Computer Daily Event Log Report"
This example provides a custom Report Title
.example
EventProcessor -targetLogName security -eventCount 20 -entryType warning -targetComputer Python-3
This example specifies all the parameters, targetLogName, eventCount, entryType and targetComputer
#>
Listing 3-5.Examples Section
参数定义
脚本的参数定义部分定义了脚本的每个可用参数的详细信息(清单 3-6 )。
# Parameter Definition Section
param(
[string]$targetLogName = "system",
[int]$eventCount = 20,
[string]$eventType="Error",
[string]$reportTitle="Event Log Daily Report",
[string[]]$targetComputer=$env:COMPUTERNAME
)
Listing 3-6
Parameter Definition Section
每个参数定义一个类型、名称和分配的默认值。例如:
-
$reportTitle 参数的类型为 string,默认值为“事件日志每日报告”。
-
$targetComputer 参数也是 string 类型,但是一组值是可能的。换句话说,用户可以输入多个计算机名称,每个名称用逗号分隔。这也包含一个默认值。这是一个 PowerShell 自动变量,它定义了正在执行脚本的计算机的名称。
-
\(targetLogName 参数定义作为目标的事件日志。请注意,这可能已经用\)targetComputer 定义为接受日志名称列表。但是,标准 CmdLet Get-EventLog 仅支持单个目标日志。为了支持列表,Get-EventLog CmdLet 需要针对每个已识别的日志执行多次。这肯定会使脚本更复杂,但也可能更有用。
-
$EventType 参数允许指定报告应该包含的事件类型。换句话说,只过滤所需的事件类型。
-
最后,$eventCount 参数被定义为一个整数值。它指定要显示的符合指定标准的日志条目的最大数量。
局部变量定义
局部变量部分用于创建该脚本所需的几个局部变量(清单 3-7 )。
# Get the current date and tme
$rptDate=Get-Date
$epoch=([DateTimeOffset]$rptDate).ToUnixTimeSeconds()
# Create HTML Header Section
$Header = @"
<style>
TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
</style>
<p>
<b> $reportTitle $rptDate </b>
<p>
Event Log Selection: <b>$targetLogName </b>
<p>
Target Computer(s) Selection: <b> $targetComputer </b>
<p>
Event Type Filter: <b> $eventType </b>
<p>
"@
# Report Filename Creation
$ReportFile = ".\Report-"+$epoch+".HTML"
Listing 3-7Local Variable Definition Section
局部变量如下:
-
$ReportDate:获取要在报告中使用的当前系统日期。
-
$epoch:获取自当前 epoch 以来经过的秒数。请注意,这对于每个操作系统都是不同的。这个变量将被用来创建一个唯一的 HTML 文件名。
-
$Header:定义一个标准的 HTML 头部分,在生成结果 HTML 文件时使用。请注意,该变量使用参数 ReportTitle 来自定义报告标题。
-
$ReportFile:该变量将字符串“Report-”与纪元值和扩展名. html 结合起来。
CmdLet 管道执行
该脚本的核心是使用管道执行 Get-EventLog CmdLet,以包含指定的参数(清单 3-8 )。
# CmdLet Pipeline execution
Get-Eventlog -ComputerName $targetComputer -LogName $targetLogName -Newest $eventCount -EntryType $eventType |
ConvertTo-html -Head $Header -Property TimeGenerated, EntryType, Message |
Out-File $ReportFile
Listing 3-8CmdLet Pipeline Execution
管道有几个关键组件和过渡:
-
Get-EventLog CmdLet 使用参数\(targetComputer、\)targetLogName、\(eventCount 和\)eventType 指定-ComputerName、-LogName、-Newest 和 EntryType。
-
Get-EventLog CmdLet 的输出通过管道传输到 ConvertTo-html CmdLet,该 CmdLet 利用本地变量$Header 和从 Get-EventLog CmdLet 传递的属性 TimeGenerated、EntryType 和 Message 来形成 html 报告的列。
-
最后,ConvertTo-html 的输出通过管道传输到 Out-File CmdLet,该 CmdLet 利用本地变量$ReportFile 作为文件名来写入结果。
EventProcessor 获取帮助结果
由于该脚本包含一个详细的标题部分,因此可以使用 Get-Help CmdLet 为那些将使用新创建的脚本的人提供帮助。以下示例使用-Full 选项提供了 Get-Help CmdLet 的输出,该选项提供了所有详细信息和示例(清单 3-9 )。
PS C:\PS> Get-Help .\EventProcessor.ps1 -Full
NAME
C:\PS\EventProcessor.ps1
SYNOPSIS
EventLog Automation Version 1.0
Step One
- User Specified Target EventLog
- User Specifies the number of newest Log Entries to Report
- User Specifies the Entry Type to target, for example warning, error, information etc.
- User Specifies the target computer or computers to extract the logs
- User Specifies the HTML Report Title
SYNTAX
C:\PS\EventProcessor.ps1 [[-targetLogName] <String>] [[-eventCount] <Int32>] [[-eventType] <String>] [[-reportTitle]
<String>] [[-targetComputer] <String[]>] [<CommonParameters>]
DESCRIPTION
This script automates the extraction of information from the specified log file
PARAMETERS
-targetLogName <String>
Specifies the name of the log file to process
Required? false
Position? 1
Default value system
Accept pipeline input? false
Accept wildcard characters? false
-eventCount <Int32>
Specifies the maximum number of newest events to consider in the search
Required? false
Position? 2
Default value 20
Accept pipeline input? false
Accept wildcard characters? false
-eventType <String>
Specifies the eventType of interest
Required? false
Position? 3
Default value Error
Accept pipeline input? false
Accept wildcard characters? false
-reportTitle <String>
Specifies the HTML Report Title
Required? false
Position? 4
Default value Event Log Daily Report
Accept pipeline input? false
Accept wildcard characters? false
-targetComputer <String[]>
Specifies the computer or computers to obtain the logs from
Required? false
Position? 5
Default value $env:COMPUTERNAME
Accept pipeline input? false
Accept wildcard characters? false
<CommonParameters>
This cmdlet supports the common parameters: Verbose, Debug, ErrorAction, ErrorVariable, WarningAction,
WarningVariable, OutBuffer, PipelineVariable, and OutVariable. For more information, see about_Common
Parameters (https:/go.microsoft.com/fwlink/?LinkID=113216).
INPUTS
OUTPUTS
------------------------ EXAMPLE 1 ------------------------
PS C:\>EventProcessor
Execution of EventProcessor without parameters uses the default settings of
eventLog system
eventType warning
eventCount 20
targetComputer the computer running the script
------------------------ EXAMPLE 2 ------------------------
PS C:\>EventProcessor -targetLogName security
This example specifies the target eventLog security
and uses the default parameters
eventType warning
eventCount 20
targetComputer the computer running the script
------------------------ EXAMPLE 3 ------------------------
PS C:\>EventProcessor -reporTitle "ACME Computer Daily Event Log Report"
This example provides a custom Report Title
------------------------ EXAMPLE 4 ------------------------
PS C:\>EventProcessor -targetLogName security -eventCount 20 -entryType warning -targetComputer Python-3
This example specifies all the parameters, targetLogName, eventCount, entryType and targetComputer
Listing 3-9EventProcessor Get-Help
EventProcessor 脚本执行
为了说明脚本的执行,这里提供了一个示例命令和结果:
PS C:\PS> .\EventProcessor.ps1 -reportTitle "Python Forensics Daily Log Report" -eventCount 100 -eventType error
结果目录
按照设计,该脚本生成一个 HTML 报告文件,其中附加了表示脚本执行时间的 Epoch 值(参见图 3-7 )。自从。html 扩展名,文件系统会正确地将结果文件识别为 Google Chrome HTML 文档。
图 3-8
生成的 HTML 报告
图 3-7
结果报告 HTML 文件
HTML 输出报告
使用浏览器检查报告文件Report-1544369607
提供了 PowerShell 脚本执行的示例结果。输出包括定义的报告标题、选择的事件日志、目标计算机、选择的事件类型以及产生的最后 100 个事件类型为错误的事件。注意,为了简洁起见,这里的结果被截断了。
远程存取
注意
使用-ComputerName 选项(可用于许多 CmdLets)设置对远程系统的访问可能很难在工作组内设置。如果有域控制器,或者您的环境使用了 active directory,这就容易多了。因此,在尝试使用-ComputerName CmdLet 参数时,请咨询您的系统管理员。
有一种更简单的方法可以提供更大的灵活性,并且更加安全。方法是创建一个与目标机器的远程 PowerShell 会话。建立会话后,您在 PowerShell 或 PowerShell ISE 中输入的命令将在远程连接的计算机上执行。优点不仅是简单,而且它还允许您执行任何 CmdLet,甚至是那些不支持-ComputerName 作为参数的 CmdLet。
下面是一个简单的示例,它创建了一个 PowerShell 会话,该会话与我的本地网络上的一台计算机进行连接,计算机名为 levo VO-upside。为了创建会话,您必须为远程计算机上具有管理员权限的用户提供凭据。该命令会弹出一个对话框,要求输入指定账户的密码,如图 3-9 所示。
图 3-9
Enter-PSSession 凭据请求
建立连接后,您可以看到 PowerShell 提示符变成了:
[Lenovo-Upstairs]: PS C:\Users\Remote-Admin\Documents>
此时,输入的 PowerShell 命令正在楼上的远程计算机 Lenovo 上执行,而不是在本地计算机上执行。在图 3-10 所示的例子中,获取了联想楼上机器的系统事件日志中包含的最新的 20 条警告消息。
图 3-10
远程访问系统事件日志
为了退出远程会话,发出 CmdLet Exit-PSSession,PowerShell 现在再次在本地计算机上运行。如图 3-10 所示。
示例 2: USB 设备使用情况发现
在执行取证调查或事件响应行动时,获取最近使用的 USB 设备当然很重要。这可以帮助确定信息是否从系统中泄漏,或者 USB 插入是否可能是恶意软件感染的原因。
该过程的第一部分是确定检测到了哪些 USB 设备。在 Microsoft Windows 系统上,注册表通过检查保存在 HKEY 本地机器下的详细信息来提供所连接设备的历史记录。图 3-11 显示了在我的本地机器上找到的特定 USBSTOR 键。
图 3-11
USB 访问的注册表历史记录
注意
在不同版本的 Windows 上,感兴趣的注册表项可能不同。如果是这样,您将需要更改本示例中使用的注册表项定义。
创建脚本
现在我们已经理解了这个场景,让我们再次执行这两个步骤来创建我们需要的脚本。
第一步:最近访问 USB 活动
问题是如何使用 PowerShell 收集 USB 活动的证据?此外,是否可以开发一个脚本来汇总整个网络的 USB 使用情况?
让我们从访问本地机器上的注册表和 USBSTOR 开始。
PowerShell 提供了一个通用的 CmdLet,可以应用于包括注册表在内的许多项目:CmdLet 是 Get-ItemProperty。
Get-ItemProperty 的 Get-Help 如清单 3-10 所示。
PS C:\PS> Get-Help Get-ItemProperty
NAME
Get-ItemProperty
SYNOPSIS
Gets the properties of a specified item.
SYNTAX
Get-ItemProperty [[-Name] <String[]>] [-Credential <PSCredential>] [-Exclude <String[]>] [-Filter <String>] [-Include
<String[]>] -LiteralPath <String[]> [-UseTransaction] [<CommonParameters>]
Get-ItemProperty [-Path] <String[]> [[-Name] <String[]>] [-Credential <PSCredential>] [-Exclude <String[]>] [-Filter
<String>] [-Include <String[]>] [-UseTransaction] [<CommonParameters>]
DESCRIPTION
The Get-ItemProperty cmdlet gets the properties of the specified items. For example, you can use this cmdlet to get the value
of the LastAccessTime property of a file object. You can also use this cmdlet to view registry entries and their values.
RELATED LINKS
Online Version: http://go.microsoft.com/fwlink/?LinkId=821588
Clear-ItemProperty
Copy-ItemProperty
Move-ItemProperty
New-ItemProperty
Remove-ItemProperty
Rename-ItemProperty
Set-ItemProperty
REMARKS
To see the examples, type: "get-help Get-ItemProperty -examples".
For more information, type: "get-help Get-ItemProperty -detailed".
For technical information, type: "get-help Get-ItemProperty -full".
For online help, type: "get-help Get-ItemProperty -online"
Listing 3-10Get-Help Get-ItemProperty
使用这个 CmdLet 获取最近的 USB 活动可以像这样完成。为了更容易理解,在本例中,将获取 USB 设备的“友好名称”属性。请参见图 3-12 。
图 3-13
访问远程计算机上的 USB 活动
图 3-12
使用 Get-ItemProperty CmdLet 获取 USB 活动
PS C:\PS> Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Enum\USBSTOR\*\* | Select FriendlyName
使用远程访问方法,我们现在获得了远程计算机 Lenovo-楼上的 USB 活动。为此,使用 Enter 和 Exit PSSession 方法,并在远程计算机上执行命令。如您所见,SanDisk Cruzer USB 设备在本地和远程计算机上都被识别。
调用命令 PowerShell CmdLet
在只需要执行单个远程命令的情况下,可以通过使用 Invoke-Command PowerShell CmdLet 来完成,而不是设置远程 PowerShell 会话。这在开发将从多台计算机获取证据的脚本时非常有用。像往常一样,使用 Get-Help 将提供关于如何利用 Invoke-Command CmdLet 的细节(清单 3-11 )。
PS C:\PS> Get-Help Invoke-Command
NAME
Invoke-Command
SYNOPSIS
Runs commands on local and remote computers.
SYNTAX
Invoke-Command [[-ConnectionUri] <Uri[]>] [-ScriptBlock] <ScriptBlock> [-AllowRedirection] [-ArgumentList <Object[]>] [-AsJob]
[-Authentication {Default | Basic | Negotiate | NegotiateWithImplicitCredential | Credssp | Digest | Kerberos}] [-CertificateThumbprint
<String>] [-ConfigurationName <String>] [-Credential <PSCredential>] [-EnableNetworkAccess] [-HideComputerName] [-InDisconnectedSession]
[-InputObject <PSObject>] [-JobName <String>] [-SessionOption <PSSessionOption>] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [[-ConnectionUri] <Uri[]>] [-FilePath] <String> [-AllowRedirection] [-ArgumentList <Object[]>] [-AsJob] [-Authentication
{Default | Basic | Negotiate | NegotiateWithImplicitCredential | Credssp | Digest | Kerberos}] [-ConfigurationName <String>] [-Credential
<PSCredential>] [-EnableNetworkAccess] [-HideComputerName] [-InDisconnectedSession] [-InputObject <PSObject>] [-JobName <String>]
[-SessionOption <PSSessionOption>] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [[-ComputerName] <String[]>] [-ScriptBlock] <ScriptBlock> [-ApplicationName <String>] [-ArgumentList <Object[]>] [-AsJob]
[-Authentication {Default | Basic | Negotiate | NegotiateWithImplicitCredential | Credssp | Digest | Kerberos}] [-CertificateThumbprint
<String>] [-ConfigurationName <String>] [-Credential <PSCredential>] [-EnableNetworkAccess] [-HideComputerName] [-InDisconnectedSession]
[-InputObject <PSObject>] [-JobName <String>] [-Port <Int32>] [-SessionName <String[]>] [-SessionOption <PSSessionOption>] [-ThrottleLimit
<Int32>] [-UseSSL] [<CommonParameters>]
Invoke-Command [[-ComputerName] <String[]>] [-FilePath] <String> [-ApplicationName <String>] [-ArgumentList <Object[]>] [-AsJob]
[-Authentication {Default | Basic | Negotiate | NegotiateWithImplicitCredential | Credssp | Digest | Kerberos}] [-ConfigurationName
<String>] [-Credential <PSCredential>] [-EnableNetworkAccess] [-HideComputerName] [-InDisconnectedSession] [-InputObject <PSObject>]
[-JobName <String>] [-Port <Int32>] [-SessionName <String[]>] [-SessionOption <PSSessionOption>] [-ThrottleLimit <Int32>] [-UseSSL]
[<CommonParameters>]
Invoke-Command [[-Session] <PSSession[]>] [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>] [-AsJob] [-HideComputerName]
[-InputObject <PSObject>] [-JobName <String>] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [[-Session] <PSSession[]>] [-FilePath] <String> [-ArgumentList <Object[]>] [-AsJob] [-HideComputerName] [-InputObject
<PSObject>] [-JobName <String>] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [-VMId] <Guid[]> [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>] [-AsJob] [-ConfigurationName <String>] -Credential
<PSCredential> [-HideComputerName] [-InputObject <PSObject>] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>] [-AsJob] [-ConfigurationName <String>] -Credential <PSCredential>
[-HideComputerName] [-InputObject <PSObject>] [-ThrottleLimit <Int32>] -VMName <String[]> [<CommonParameters>]
Invoke-Command [-VMId] <Guid[]> [-FilePath] <String> [-ArgumentList <Object[]>] [-AsJob] [-ConfigurationName <String>] -Credential
<PSCredential> [-HideComputerName] [-InputObject <PSObject>] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [-FilePath] <String> [-ArgumentList <Object[]>] [-AsJob] [-ConfigurationName <String>] -Credential <PSCredential>
[-HideComputerName] [-InputObject <PSObject>] [-ThrottleLimit <Int32>] -VMName <String[]> [<CommonParameters>]
Invoke-Command [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>] [-AsJob] [-ConfigurationName <String>] -ContainerId <String[]>
[-HideComputerName] [-InputObject <PSObject>] [-JobName <String>] [-RunAsAdministrator] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [-FilePath] <String> [-ArgumentList <Object[]>] [-AsJob] [-ConfigurationName <String>] -ContainerId <String[]>
[-HideComputerName] [-InputObject <PSObject>] [-JobName <String>] [-RunAsAdministrator] [-ThrottleLimit <Int32>] [<CommonParameters>]
Invoke-Command [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>] [-InputObject <PSObject>] [-NoNewScope] [<CommonParameters>]
DESCRIPTION
The Invoke-Command cmdlet runs commands on a local or remote computer and returns all output from the commands, including errors. By using a single Invoke-Command command,
you can run commands on multiple computers.
To run a single command on a remote computer, use the ComputerName parameter. To run a series of related commands that share data, use the New-PSSession cmdlet to create a
PSSession (a persistent connection) on the remote computer, and then use the Session parameter of Invoke-Command to run
the command in the PSSession. To run a command in a disconnected session, use the InDisconnectedSession parameter. To run a command in a background job, use the
AsJob parameter
.
You can also use Invoke-Command on a local computer to evaluate or run a string in a script block as a command. Windows PowerShell converts the script block to a command
and runs the command immediately in the current scope, instead of just echoing the string at the command line.
To start an interactive session with a remote computer, use the Enter-PSSession cmdlet. To establish a persistent connection to a remote computer, use the New-PSSession
cmdlet.
Before using Invoke-Command to run commands on a remote computer, read about_Remote (http://go.microsoft.com/fwlink/?LinkID=135182).
RELATED LINKS
Online Version: http://go.microsoft.com/fwlink/?LinkId=821493
Enter-PSSession
Exit-PSSession
Get-PSSession
New-PSSession
Remove-PSSession
Listing 3-11
Invoke-Command
使用 USB 活动获取方法作为起点,Invoke-Command 方法可用于远程执行该命令。在本例中,目标和用户首先被创建为变量。该命令嵌入在-ScriptBlock 中。如前所述,用户必须输入远程计算机的管理员凭证(图 3-14 )。
图 3-14
调用命令方法 USBAcquire
调用命令的结果如图 3-15 所示。
图 3-15
调用命令方法 USBAcquire 结果
第二步:创建 USBAcquire PowerShell 脚本
现在我们已经完善了这个方法,可以创建一个简单的 PowerShell 脚本来为我们执行这个操作,用户提供目标计算机名,管理员用户提供目标计算机名。完整的脚本在这里被列为清单 3-12 。稍后我将展示 Get-Help 结果和一个执行示例。
<#
.synopsis
Collect USB Activity from target computer
- User Specifies the target computer
The script will produce details of USB Activity
on the specified target computer
.Description
This script collects USB Activity and target computers
.parameter targetComputer
Specifies the computer to collect the USB Activity
.parameter UserName
Specifies the Administrator UserName on the Target Computer
.example
USBAcquire ComputerName
Collects the USB Activity on the target Computer
#>
# Parameter Definition Section
param(
[string]$User,
[string]$targetComputer
)
Invoke-Command -ComputerName $targetComputer -Credential $User -ScriptBlock {Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Enum\USBSTOR\*\* | Select FriendlyName}
Listing 3-12
USBAcquire Script
如您所见,USBAcquire 有与示例一中的 EventProcessor 脚本相同的四个主要部分:脚本头参数定义、局部变量定义以及使用参数和局部变量的 cmdlet 执行。如果你需要复习的话,请回到那一节。
USBAcquire 脚本执行
脚本的执行和结果如图 3-16 和 3-17 所示。
图 3-17
结果 USBAcquire PowerShell 脚本
图 3-16
USBAcquire 脚本执行请求凭据
PS C:\PS> .\USBAcquire.ps1 -targetComputer PYTHON-3 -user PYTHON-3\USER-NAME-HIDDEN
USBAcquire 获取帮助结果
该脚本包含一个适当的标题部分;因此,可以使用 Get-Help CmdLet 获得用户帮助,如清单 3-13 所示。
PS C:\PS> Get-Help .\USBAcquire.ps1
NAME
C:\PS\USBAcquire.ps1
SYNOPSIS
Collect USB Activity from target computer
- User Specifies the target computer
The script will produce details of USB Activity
on the specified target computer
SYNTAX
C:\PS\USBAcquire.ps1 [[-User] <String>] [[-targetComputer] <String>] [<CommonParameters>]
DESCRIPTION
This script collects USB Activity and target computers
RELATED LINKS
REMARKS
To see the examples, type: "get-help C:\PS\USBAcquire.ps1 -examples".
For more information, type: "get-help C:\PS\USBAcquire.ps1 -detailed".
For technical information, type: "get-help C:\PS\USBAcquire.ps1 -full".
Listing 3-13USBAcquire Get-Help
挑战问题:使用哈希创建文件清单列表
根据您对 PowerShell 脚本和远程访问方法的了解,您的挑战是利用这些知识来解决以下问题。
开发一个 PowerShell 脚本,该脚本将创建一个计算机清单,详细列出找到的所有目录和文件。该脚本将允许用户指定:
-
目标计算机
-
开始目录
-
输出文件
您的脚本应该生成一个包含以下信息的 HTML 文件:
-
目录
-
文件名
-
文件大小
-
修改时间
-
物主
-
文件属性(即只读、隐藏、系统、归档)
该脚本将递归从起始目录开始的所有文件夹。
暗示
您将关注 CmdLet Get-ChildItem。
最后,您的脚本将包含完整的帮助信息。
在附录 A 和 www.apress.com/9781484245033
中可以找到一个示例脚本解决方案。
摘要
本章重点介绍了调查人员可以用来从事件日志和最近的 USB 活动中获取信息的 PowerShell 脚本的构造。Get-EventLog CmdLet 和 Get-ItemProperty 是我们收购的重点。
此外,当使用 Enter-PSSession CmdLet 获得正确凭据时,创建 PowerShell 会话是从远程计算机获取证据的另一种方法。此外,还介绍了 Invoke-Command PowerShell CmdLet,它允许在不创建持久会话的情况下执行单个命令或脚本。
第四章将介绍、比较和对比 PowerShell 和 Python,并开始结合这两种强大的脚本语言的过程。
四、Python 和现场调查/采集
搜索是数字调查的主体。在过去十年中发生变化的是要搜索的大量数据、要搜索的各种类型的内容以及将具体犯罪活动联系起来所需的信息类型。
如今,数字数据与所有犯罪活动都有关联。使用这些数据来理解(并可能证明)犯罪的动机、机会和/或手段是至关重要的。在许多情况下,我们可以利用这些数据来建立嫌疑人的档案并预测未来的活动。此外,我们可以发现特定数字设备的位置、行为和内容,无论它们是电话、平板电脑、计算机、无人机、手表还是各种物联网设备。
目前,许多人仍然认为数字证据是在我们对数字媒体成像后检查的静态数据。当然,这种情况正在发生变化,尤其是在数字取证事件响应或 DFIR 活动中。收集、检验和推理“活”证据并不新鲜——我早在 2006 年就开始写这方面的文章并开发解决方案。 1
随着对即时反应、早期指示和警告、异常行为检测以及在不良行为发生前对其进行预测的需求在社会中变得至关重要,“现场”法医学最终将与传统的尸检实践携手合作。因此,通过利用 PowerShell 来获取特定的目标证据,我们可以在动作发生时采取下一步的处理和推理。
所有这些都为开发新的检测、推理、分析方法,当然还有犯罪活动的证据提供了重要的机会。然而,在我们能够飞行、奔跑、行走甚至爬行之前,我们需要解决一些基本的挑战,并开发将 PowerShell 驱动的获取与 Python 的能力相集成的软件。有两种基本方法可以解决这个问题:
-
方法 1:启动 PowerShell CmdLets 或脚本,然后在 Python 中收集和后处理结果。
-
方法 2:执行 PowerShell CmdLets 或脚本,并将结果传送给等待的 Python 脚本。
方法 1 将在本章中讨论,方法 2 将在第五章中讨论。在这两种情况下,将通过示例来探索这些方法。
什么是“以身作则”?
现有数百本关于 Python 的书籍,大多数都集中在如何编程上,并且通常采用教授您这门语言的复杂性的方法。这些文本是为那些从事计算机科学、软件工程、网络开发或大数据处理的人设计的。
我们的目标是将 Python 应用于具体的数字调查挑战,并结合 Python 和 PowerShell 来创建解决方案。有趣的是,在这个过程中,您将学习新的脚本技术。
我能想到的最好的类比是学习一种新的文化。你可以阅读玛雅文化,观看关于他们历史的电影,并查看他们居住的国家的地图。或者你可以去那里旅行,走进他们的世界,与玛雅人交谈,探索他们的圣地,亲身体验他们的文化。
用 Python 指导 PowerShell
由于 Python 2.7 的结束日期即将到来,本书中所有基于 Python 的例子都将使用 Python 3.7。Python 2 和 3 包含了大量的内置标准库以及数以千计的第三方库。尽可能使用 Python 标准库,以确保最广泛的跨平台兼容性。可以直接从 www.python.org
获取 Python 3.7。截至本文撰写之时,可用的最新版本是 Python 3.7.2,如图 4-1 所示。
图 4-1
下载 Python 3.7.2 ( www.python.org
除了最新版本的 Python,我强烈推荐使用 Python 集成开发环境。我最喜欢的是 WingIDE。
个人版是免费的,可以很好地应对大多数 Python 开发和脚本挑战。该网站提供了大量关于如何配置和使用 WingIDE 的教程,可以在以下位置找到:
图 4-2
翼服/翼服首页( www.wingware.com
从 Python 启动 PowerShell CmdLets
现在您已经有了基本的工具(PowerShell 已安装并运行,Python 已安装并运行,WingIDE 已准备好进行实验),您可以执行 Python 和 PowerShell 的第一次集成了。
在第 1 和 2 章中,涵盖了 CmdLets 的发现、使用和取证应用。我相信您已经尝试了各种附加的 CmdLets。因此,如果我们可以从 Python 执行 PowerShell CmdLet 并捕获结果,会怎么样呢?由于 PowerShell 是一个可执行的进程,所以我们将使用 Python 的标准库来提供启动进程的能力。这是使用子过程标准库完成的。在 Python 中,为了利用任何标准或第三方库,您必须导入它们。这是通过一个简单的 import 语句完成的。在这种情况下,语句简单地说就是:
import subprocess
这提供了对子进程库中包含的方法和属性的访问。有许多选项可用——最流行的是使用 check.output 方法,它执行指定的过程并返回结果。这里有一个例子:
runningProcesses = subprocess.check_output("powershell -Executionpolicy ByPass -Command Get-Process")
WingIDE Python 集成开发的一个很好的特性是能够在交互式 shell 中试验命令,如图 4-3 所示。三个大于号(> > >)是交互式 shell 提示符。如果从命令行或终端窗口启动 Python,您会收到同样的提示。
图 4-3
从 Python shell 执行 PowerShell CmdLet
子过程代码各元素的细分如下,如图 4-4 所示。
-
A. 该命令的结果将存储在名为
runningProcesses
的变量中。当然,您可以使用任何允许的变量名。在 Python 中定义变量时,我使用 camel case,以小写字母开始,然后将每个后续单词大写。这使得识别代码中的变量变得容易。 -
B. 赋值运算符 or =等号将子流程命令的结果分配给变量
runningProcesses
。 -
c .是从子过程库中选择的方法。它接受一个用引号括起来的参数,并定义您希望执行的命令行。
-
D. 括号内的引用字符串指定要执行的命令。E-H 定义了要执行的 powershell 命令的每个元素。
-
e .
powershell
是命令,或者在本例中是要执行的流程。
*** F. -Executionpolicy ByPass
,默认情况下,PowerShell 不会在没有明确许可的情况下执行脚本或 CmdLets。参数- Executionpolicy
指定 PowerShell 命令的策略。参数ByPass
告诉 PowerShell 不要阻止任何东西,也不要发出警告或提示。
* **G.** `-Command`指定后面是 PowerShell 命令。在这种情况下,它是一个简单的 CmdLet,但也可能是一个更复杂的基于管道的命令。如果您希望执行 PowerShell 脚本,这将被更改为`-File`,后跟一个有效的. ps1 文件名。
* **h .**是要执行的特定 CmdLet。在此示例中,Get-Process CmdLet 在没有参数的情况下执行。**
**
图 4-4
Python 子进程命令分解
在 Python 3.x 中,subprocess.check_output()方法返回一个字节字符串,而在 Python 2.7 中,它返回一个简单的字符串。因此,要显示该命令的输出,需要对 runningProcesses 变量进行解码,如下所示:
print(runningProcesses.decode())
在 WingIDE Python 交互式 shell 中执行该命令会产生如图 4-5 所示的结果。注意,为简洁起见,结果被截断。
图 4-5
打印出 runningProcesses 变量的内容
此时,您可能会说,我为什么要大费周章地从 Python 中执行 PowerShell 命令或 CmdLet 呢?为了回答这个问题,让我们把这个例子带到下一个层次。
使用 PowerShell 和 Python 创建系统文件基线
假设您希望建立当前安装在 Windows 下的驱动程序的基线,特别是 c:\windows\system32\drivers\。就此而言,您可以将任何目录、子目录或整个系统作为目标,但是系统驱动程序以特权运行,并且在调查过程中检测新的驱动程序、现有驱动程序的修改或驱动程序的删除可能会很有用。
获取有关文件的信息是使用 PowerShell 中的 Get-ChildItem CmdLet 完成的。此 CmdLet 有许多相关的功能、属性和方法。我们对创建基线感兴趣的是:
-
每个文件的哈希,用于创建取证软件使用的已知良好哈希集
-
每个文件的名称
使用如下所示的管道命令从 PowerShell 获取这些信息非常简单。截断结果如图 4-6 所示,命令分解如图 4-7 所示。
Get-ChildItem c:\windows\system32\drivers\ |
Get-FileHash | Select-object -Property Hash, Path | Format-Table -HideTableHeaders
图 4-7
PowerShell 管道细分 Get-ChildItem、Get-FileHash、Select-Object 和 Format-Table
图 4-6
使用 PowerShell 获取文件哈希和路径(注意输出被截断)
流水线命令的分解如下所示,如图 4-7 所示。
-
A. Get-ChildItem CmdLet 指定目标文件夹 windows\system32\drivers。
-
b .Get-child item CmdLet 的输出通过管道传输到 Get-FileHash CmdLet,默认情况下,Get-file hash CmdLet 将生成每个文件的 SHA-256 哈希。
-
c .Get-File hash CmdLet 的结果将通过管道传输到 Select-Object CmdLet,该 CmdLet 将只提取所需的两个输出的 SHA-256 哈希值和文件路径。
-
d .Select-Object CmdLet 的结果然后被传递给 Format-Table CmdLet,该 CmdLet 从输出中移除表格标题。
创建一个带有输入参数的 PowerShell 脚本将使这个命令更有用,更可重用。完整的脚本如清单 4-1 所示。
<#
.synopsis
Collect Hash and Filenames from specified folder
- User Specifies the target computer
- User Specifies the target folder
The script will produce a simple ascii output file containing
SHA-256Hash and FilePath
.Description
This script collects Hash and Filenames from specified computer and folder
.parameter targetComputer
Specifies the computer to collect the specified file hash information
.parameter UserName
Specifies the Administrator UserName on the Target Computer
.parameter outFile
Specifies the full path of the output file
.example
HashAcquire
Collects the file hashes on the target Computer
#>
# Parameter Definition Section
param(
[string]$TargetFolder="c:/windows/system32/drivers/",
[string]$ResultFile="c:/PS/baseline.txt"
)
Get-ChildItem $TargetFolder | Get-FileHash | Select-Object -Property Hash, Path | Format-Table -HideTableHeaders | Out-File $ResultFile -Encoding ascii
Listing 4-1
HashAquire.ps1 Script
该脚本具有标准的部分,以便提供适当的 Get-Help 支持,如清单 4-2 所示。
PS C:\PS> Get-Help .\HashAcquire.ps1
NAME
C:\PS\HashAcquire.ps1
SYNOPSIS
Collect Hash and Filenames from specified folder
- User Specifies the target computer
- User Specifies the target folder
The script will produce a simple ascii output file containing
SHA-256Hash and FilePath
SYNTAX
C:\PS\HashAcquire.ps1 [[-TargetFolder] <String>] [[-ResultFile] <String>] [<CommonParameters>]
DESCRIPTION
This script collects Hash and Filenames from specified computer and folder
RELATED LINKS
REMARKS
To see the examples, type: "get-help C:\PS\HashAcquire.ps1 -examples".
For more information, type: "get-help C:\PS\HashAcquire.ps1 -detailed".
For technical information, type: "get-help C:\PS\HashAcquire.ps1 -full".
Listing 4-2Get-Help Results for the HashAquire.ps1 PowerShell Script
该脚本包含两个输入参数 TargetFolder 和 ResultFile。
# Parameter Definition Section
param(
[string]$TargetFolder="c:/windows/system32/drivers/",
[string]$ResultFile="c:/PS/baseline.txt"
)
该脚本使用默认参数创建 baseline.txt 文件。简略结果如图 4-8 所示。通过提供指定目标文件夹的参数,这个脚本现在可以应用于任何合法的文件夹。
图 4-8
baseline.txt 简略结果
注意
访问某些文件夹需要管理员权限。确保您以管理员身份运行 PowerShell。
PS C:\PS> .\HashAcquire.ps1
使用 Python 创建基线
现在我们有了一个使用 HashAcquire.ps1 PowerShell 脚本提取散列和文件名的可靠方法,我们可以使用 Python 从这些结果中创建一个基线。然而,为此我们将创建一个 Python 脚本/程序,而不是使用交互式 shell。
计划是从 Python 启动 PowerShell 脚本,并从创建的文本文件中提取结果。您可以使用脚本提供的 ResultFile 参数来指定结果文件的名称和位置。
注意
当前的 PowerShell 脚本仅处理指定的目录。但是,Get-ChildItem CmdLet 有一个可选参数,也可用于指定子文件夹获取。该参数是-recurse,通过使用:
Get-Help Get-ChildItem
你会发现 Get-ChildItem 有很多选项和用法示例。
下一步是将提取的结果存储在 Python 字典中,以生成基线。一旦创建了字典基线,就可以存储生成的字典并用于比较。这样,您可以从目标文件夹中检测任何新的、修改的或删除的文件。
注意
Python 字典很像传统的 Webster 风格的字典,有一个键和值,通常称为键/值对。在 Python 中,键和值都可以很复杂,唯一的规则是键必须是可哈希类型,比如整数、长整型、字符串或元组。键/值对的值部分可以是列表或其他不可散列的数据类型。此外,字典的键必须是唯一的(很像真正的字典)。
完整的 CreateBaseline.py 脚本如清单 4-3 所示。
注意
对于本书其余部分中的 PowerShell 和 Python 脚本,创建了目录 c:\PS 来保存脚本和结果。
另外,不要试图从书中复制和粘贴 Python 脚本。Python 使用一种严格缩进的方法,这种方法可能会在复制和粘贴过程中被破坏。发布者已经提供了对位于 www.apress.com/9781484245033
的源代码文件的访问。
'''
Step One Create a baseline hash list of target folder
December 2018, Python Forensics
'''
''' LIBRARY IMPORT SECTION '''
import subprocess # subprocess library
import argparse # argument parsing library
import os # Operating System Path
import pickle # Python object serialization
'''ARGUMENT PARSING SECTION '''
def ValidatePath(thePath):
''' Validate the Folder thePath
it must exist and we must have rights
to read from the folder.
raise the appropriate error if either
is not true
'''
# Validate the path exists
if not os.path.exists(thePath):
raise argparse.ArgumentTypeError('Path does
not exist')
# Validate the path is readable
if os.access(thePath, os.R_OK):
return thePath
else:
raise argparse.ArgumentTypeError('Path is not readable')
#End ValidatePath
''' Specify and Parse the command line, validate the arguments and return results'''
parser = argparse.ArgumentParser('File System Baseline Creator with PowerShell- Version 1.0 December 2018')
parser.add_argument('-b', '--baseline',
required=True,
help="Specify the resulting dictionary baseline file")
parser.add_argument('-p', '--Path',
required=True, type= ValidatePath,
help="Specify the target folder to baseline")
parser.add_argument('-t', '--tmp',
required=True,
help="Specify a temporary result file for the PowerShell Script")
args = parser.parse_args()
baselineFile = args.baseline
targetPath = args.Path
tmpFile = args.tmp
''' MAIN SCRIPT SECTION '''
if __name__ == '__main__':
try:
''' POWERSHELL EXECUTION SECTION '''
command = "powershell -ExecutionPolicy ByPass
-File C:/PS/HashAcquire.ps1"+"
-TargetFolder "+ targetPath+" -ResultFile "+ tmpFile
print(command)
powerShellResult = subprocess.run(command, stdout=subprocess.PIPE)
if powerShellResult.stderr == None:
''' DICTIONARY CREATION SECTION '''
baseDict = {}
with open(tmpFile, 'r') as inFile:
for eachLine in inFile:
lineList = eachLine.split()
if len(lineList) == 2:
hashValue = lineList[0]
fileName = lineList[1]
baseDict[hashValue] = fileName
else:
continue
with open(baselineFile, 'wb') as outFile:
pickle.dump(baseDict, outFile)
print("Baseline: ", baselineFile,
" Created with:", "{:,}".format(len(baseDict)), "Records")
print("Script Terminated Successfully")
else:
print("PowerShell Error:", p.stderr)
except Exception as err:
print ("Cannot Create Output File: "+str(err))
quit()
Listing 4-3
CreateBaseLine Python Script
不熟悉 Python 的人可能会觉得这个脚本有点复杂。因此,该脚本在这里被分成以下几个部分:
-
库导入
-
参数解析
-
主要的
-
POWERSHELL 执行
-
词典创作
库导入:顾名思义,这是加载所需 Python 库的地方。它们包括:
-
子进程:用于启动 PowerShell 脚本
-
os:用于文件和文件夹验证
-
argparse:用于解析命令行参数
-
pickle:用于将结果字典存储到一个文件中以备后用
参数解析:这个部分设置并处理用户命令行参数。对于此脚本,必需的参数包括:
-
-b 指定生成的字典基线文件名。
-
-p 指定 PowerShell 脚本用来存储提取的哈希和文件名的目标路径。
-
-t 指定 PowerShell 脚本将用来存储哈希数据的 tmp 文件。
Python 中的 argparse 库会自动处理命令行,并验证用户是否输入了所有必需的参数,并在用户请求时提供帮助。图 4-9 描述了测试文件夹和只使用-h 选项执行脚本的结果。
图 4-9
执行请求帮助的 CreateBaseline.py 脚本
参数处理部分会创建三个变量:
-
[-b] baselineFile:指定生成的基线字典文件。该文件将由 Python 脚本创建。
-
[-p] targetPath:传递给 PowerShell 脚本以指定基线化哪个文件夹。这由 PowerShell 脚本使用。
-
[-t] tmpFile:它被传递给 PowerShell 脚本以指定将保存中间结果的临时文本文件。Python 脚本使用 PowerShell 脚本生成的临时文件。
MAIN :初步设置完成后,MAIN 部分执行脚本的核心元素。
POWERSHELL 执行:这个部分启动 POWERSHELL 脚本。它首先创建一个名为命令的变量,subprocess.run()方法将使用该变量启动 PowerShell 脚本。请注意,本例中的执行指定了一个文件,-File,而不是一个命令,-Command,这在前面的示例中使用过。它指定 PowerShell 脚本 HashAcquire.ps1。在完成 subprocess 命令后,将检查标准错误或 stderr 结果是否成功完成。结果应该是零。否则,Python 脚本将报告返回的错误。
字典创建:如果 PowerShell 命令成功完成,Python 脚本将处理临时结果文件以创建字典。由于结果文件的格式是在 PowerShell 脚本中定义的,因此可以使用 Python 迭代循环来处理文件的每一行以提取哈希值和文件路径。使用哈希值作为键/值对的键,使用文件路径作为键/值对的值,为每一行创建一个字典条目。处理完所有行之后,Python pickle 库用于将创建的字典存储在命令行上指定的文件中,该文件现在包含在变量 baselineFile 中。Python 脚本将报告脚本的详细信息。如果 Python 脚本运行期间出现任何错误或异常,脚本将会报告异常。
图 4-10 显示了 CreateBaseline.py Python 与 HashAcquire.ps1 PowerShell 脚本的成功执行。如您所见,该脚本为 c:/windows/system32/drivers/文件夹中包含的文件生成了 447 个字典条目。此外,在 c:/PS/文件夹中创建了两个指定的文件 baseline.txt 和 baseline.pickle。
图 4-10
Python/PowerShell 脚本组合脚本执行
使用 Python 验证基线
下一步是创建一个 Python 脚本,该脚本将验证所选文件夹的当前版本是否已更改。基本上,我们正在创建一个简单的绊网。验证脚本应该完成哪些具体的验证?
-
是否添加了任何文件?
-
有文件被删除了吗?
-
是否有文件被更改?
我们将重用 HashAcquire.ps1 PowerShell 脚本,并对 HashAcquire.ps1 返回的每个条目的处理进行一些修改。唯一的修改包括:
-
添加基线字典加载部分
-
增加了字典测试部分和相关的字典验证功能
清单 4-4 包含了完整的验证 Python 脚本。注意,HashAcquire.ps1 PowerShell 脚本保持不变。
'''
Step Two Verify a baseline hash list against a target folder
December 2018, Python Forensics
'''
''' LIBRARY IMPORT SECTION '''
import subprocess # subprocess library
import argparse # argument parsing library
import os # Operating System Path
import pickle # Python object serialization
"'ARGUMENT PARSING SECTION "'
def ValidatePath(thePath):
''' Validate the Folder thePath
it must exist and we must have rights
to read from the folder.
raise the appropriate error if either
is not true
'''
# Validate the path exists
if not os.path.exists(thePath):
raise argparse.ArgumentTypeError('Path does not exist')
# Validate the path is readable
if os.access(thePath, os.R_OK):
return thePath
else:
raise argparse.ArgumentTypeError('Path is not readable')
#End ValidatePath ===================================
''' Specify and Parse the command line, validate the arguments and return results'''
parser = argparse.ArgumentParser('File System Baseline Validation with PowerShell- Version 1.0 December 2018')
parser.add_argument('-b', '--baseline',required=True,
help="Specify the source baseline file to verify")
parser.add_argument('-p', '--Path',
type= ValidatePath, required=True,
help="Specify the target folder to verify")
parser.add_argument('-t', '--tmp', required=True,
help="Specify a temporary result file for the PowerShell Script")
args = parser.parse_args()
baselineFile = args.baseline
targetPath = args.Path
tmpFile = args.tmp
def TestDictEquality(d1,d2):
""" return True if all keys and values are the same
otherwise return False """
if all(k in d2 and d1[k] == d2[k] for k in d1):
if all(k in d1 and d1[k] == d2[k] for k in d2):
return True
else:
return False
else:
return False
'''
return all(k in d2 and d1[k] == d2[k]
for k in d1) \
and all(k in d1 and d1[k] == d2[k]
for k in d2)
'''
def TestDictDiff(d1, d2):
""" return the subset of d1 where the keys don't exist in d2 or the values in d2 are different, as adict """
diff = {}
for k,v in d1.items():
if k in d2 and v in d2[k]:
continue
else:
diff[k+v] = "Baseline Missmatch"
return diff
''' MAIN SCRIPT SECTION '''
if __name__ == '__main__':
try:
''' POWERSHELL EXECUTION SECTION '''
print()
command = "powershell -ExecutionPolicy ByPass -File C:/PS/HashAcquire.ps1"+" -TargetFolder "+ targetPath+" -ResultFile "+ tmpFile
print(command)
print()
powerShellResult = subprocess.run(command, stdout=subprocess.PIPE)
if powerShellResult.stderr == None:
''' BASELINE DICTIONARY LOAD SECTION '''
# Load in the baseline dictionary
with open(baselineFile, 'rb') as baseIn:
baseDict = pickle.load(baseIn)
''' DICTIONARY CREATION SECTION '''
# Create a new dictionary for the target folder
newDict = {}
with open(tmpFile, 'r') as inFile:
for eachLine in inFile:
lineList = eachLine.split()
if len(lineList) == 2:
hashValue = lineList[0]
fileName = lineList[1]
newDict[hashValue] = fileName
else:
continue
''' DICTIONARY TEST SECTION '''
if TestDictEquality(baseDict, newDict):
print("No Changes Detected")
else:
diff = TestDictDiff(newDict, baseDict)
print(diff)
else:
print("PowerShell Error:", p.stderr)
except Exception as err:
print ("Cannot Create Output File: "+str(err))
quit()
Listing 4-4
Verify Baseline Python Script
VerifyBaseline.py 中新代码部分的概述
DICTIONARY LOAD: 这个部分从保存的 pickle 文件中加载指定的字典,该文件是在 CreateBaseline.py 脚本中创建的。pickle.load()方法用于从指定文件中恢复字典。
字典测试:这个部分使用了两个新创建的函数:
-
TestDictEquality()
-
TestDictDiff()
TestDictEquality 函数将目标文件夹中新创建的字典与使用 pickle.load()方法加载的已保存字典进行比较。两本词典
-
基础词典
-
新词典
包含要比较的词典。这些字典包含每个字典的 SHA-256 散列(键)和文件名(值)。Python 提供了许多有用的内置机制来比较和遍历字典。TestDictEquality 函数验证这两个字典是否完全匹配。如果是,函数将返回 True。如果它们不相等,那么函数返回 False。为了确定存在哪些差异,仅当不相等时才调用 TestDictDiff()函数。
TestDictDiff 函数将 baseDict 的内容与 newDict 的内容进行比较,并创建一个新的字典来保存任何不匹配的值。包含任何差异的字典由 TestDictDiff 函数返回。一旦返回,就会显示 diffDictionary 的内容。
图 4-11 显示了 VerifyBaseline.py 脚本的执行,包括新的帮助结果和未检测到的更改。
图 4-11
验证基线执行并帮助不做任何更改
图 4-12 显示了 VerifyBaseline.py 脚本的执行,该脚本识别了添加到 c:/windows/system32/drivers 目录中的两个无害文件。
图 4-12
用检测到的变更验证基线执行
使用 PowerShell 执行 Python 概述
这个例子为 Python 中 PowerShell 结果的执行和后处理提供了一个很好的模型。更重要的是,这个模型可以扩展到其他用途。例如:
-
通过修改 PowerShell 脚本和参数,可以添加目标计算机名。PowerShell 脚本接下来可以添加 Invoke-Command CmdLet,然后执行远程获取,这在 Python 中要困难得多。因此,我们使用 PowerShell 作为采集引擎,使用 Python 作为后台处理器。下面是一个必要的修改后的 PowerShell 命令的示例:
Invoke-Command -ComputerName $targetComputer -Credential $User -ScriptBlock {Get-ChildItem $TargetFolder | Get-FileHash | Select-Object -Property Hash, Path | Format-Table -HideTableHeaders | Out-File $ResultFile -Encoding ascii}
-
获取 CmdLet Get-ChildItem 可以用大量其他面向获取的 CmdLet 替换,例如:
-
获取流程
-
获取服务
-
Get-NetTCPConnections
-
Get-NetFirewallSetting
-
、或任何其他具有调查兴趣的本地或网络值
然后,无需修改即可应用 Python CreateBaseline 和 VerifyBaseline 脚本来创建基线,然后检测环境中的任何变化。
-
-
使用 subprocess.run()的接口模型可以应用于 PowerShell 脚本的其他获取。使用创建可以从 Python 中逐行获取的简单 ASCII 结果文件的模型,在 Python 和 PowerShell 之间建立一个可靠的接口。你当然可以通过标准输出返回数据。但是,当从 PowerShell 生成大量输出时,这种方法不太稳定。
挑战问题:执行远程脚本执行
利用您从 Python 和所提供的模型中学到的关于 PowerShell 脚本执行的知识:
-
通过探索提供调查或事件响应值的其他 PowerShell CmdLets 来扩展所提供的解决方案。根据需要调整 PowerShell 和 Python 脚本。
-
获取流程
-
获取服务
-
Get-NETTCPConnections
-
get-防火墙设置
-
-
修改 PowerShell 和 Python 脚本,以包括对其他计算机的访问。这将需要更改这两个脚本,以便提供附加计算机的名称。此外,PowerShell 脚本需要添加适当的 Invoke-Command CmdLet。
摘要
本章重点介绍了通过 Python 执行 PowerShell CmdLets 和脚本。本章讲述了使用 Python 子进程库与 PowerShell 交互的主要方法。
此外,还讨论了将 PowerShell 结果交付给 Python 进行后处理的方法。这种集成的可重用模型为 PowerShell 和 Python 的集成提供了基线。
最后,通过示例讨论了 Python 语言、库和数据类型。这些包括参数解析、子进程用法、字典、函数和一般 Python 程序结构。
第五章将通过额外的例子和方法来扩展 PowerShell 和 Python 的集成。
**五、PowerShell/Python 调查示例
在事件响应情况下收集远程活动的能力是 PowerShell 的主要优势之一。最新版本的 PowerShell 提供的基础架构显著减少了所需的网络设置,并提供了显著的安全性。
集成 PowerShell 和 Python 为本地和远程调查提供了一个可行的平台。远程连接到机器的“老”方法是使用 DCOM(分布式组件对象模型)和/或 RPC(远程过程调用)。根据需要配置的端口数量,这些集成方法非常复杂,在某些情况下还存在漏洞。
这种新方法被称为 PowerShell Remoting。记住,我们在第三章中看到了使用 Invoke-Command CmdLet 的基础知识。在本章中,我们将更深入地了解 PowerShell Remoting。但是,在使用新的 PowerShell 远程功能之前,可能需要在您的环境中启用它。PowerShell Remoting 的一个很好的特性是它在 HTTPS 上运行,并且是通过一个端口——端口 5985 来完成的。
启用 PowerShell 远程处理
第一步是在您的调查机器上启用 PowerShell Remoting(执行调查的机器)。您可能已经猜到我们将使用 PowerShell CmdLet 来完成这项工作。有趣的是,这一项的标题是 Enable-PSRemoting。和往常一样,您从 Get-Help 开始,以便理解参数和选项(清单 5-1 )。
PS C:\PS> Get-Help Enable-PSRemoting
NAME
Enable-PSRemoting
SYNOPSIS
Configures the computer to receive remote commands.
SYNTAX
Enable-PSRemoting [-Confirm] [-Force] [-SkipNetworkProfileCheck] [-WhatIf] [<CommonParameters>]
DESCRIPTION
The Enable-PSRemoting cmdlet
configures the computer to receive Windows PowerShell remote commands that are sent by using the WS-Management technology.
By default, on Windows Server® 2012, Windows PowerShell remoting is enabled. You can use Enable-PSRemoting to enable Windows PowerShell remoting on other supported versions of Windows and to re-enable remoting on Windows Server 2012 if it becomes disabled.
You have to run this command only one time on each computer that will receive commands. You do not have to run it on computers that only send commands. Because the configuration starts listeners, it is prudent to run it only where it is needed.
Beginning in Windows PowerShell 3.0, the Enable-PSRemoting cmdlet can enable Windows PowerShell remoting on client versions of Windows when the computer is on a public network.
For more information, see the description of the SkipNetworkProfileCheck parameter.
The Enable-PSRemoting cmdlet performs the following operations:
- Runs the Set-WSManQuickConfighttp://go.microsoft.com/fwlink/?LinkID=141463 cmdlet, which performs the following tasks:
----- Starts the WinRM service.
----- Sets the startup type on the WinRM service to Automatic.
----- Creates a listener to accept requests on any IP address, if one does not already exist.
----- Enables a firewall exception for WS-Management communications.
----- Registers the Microsoft.PowerShell and Microsoft.PowerShell.Workflow session configurations, if it they are not already registered.
----- Registers the Microsoft.PowerShell32 session configuration on 64-bit computers, if it is not already registered.
----- Enables all session configurations.
----- Changes the security descriptor of all session configurations to allow remote access.
----- Restarts the WinRM service to make the preceding changes effective.
To run this cmdlet, start Windows PowerShell by using the Run as administrator option.
CAUTION: On systems that have both Windows PowerShell 3.0 and Windows PowerShell 2.0, do not use Windows PowerShell 2.0 to run the Enable-PSRemoting and Disable-PSRemoting cmdlets. The commands might appear to succeed, but the remoting is not configured correctly. Remote commands and later attempts to enable and disable remoting, are likely to fail.
RELATED LINKS
Online Version: http://go.microsoft.com/fwlink/?LinkId=821475
Disable-PSSessionConfiguration
Enable-PSSessionConfiguration
Get-PSSessionConfiguration
Register-PSSessionConfiguration
Set-PSSessionConfiguration
Disable-PSRemoting
REMARKS
To see the examples, type: "get-help Enable-PSRemoting -examples".
For more information, type: "get-help Enable-PSRemoting -detailed".
For technical information, type: "get-help Enable-PSRemoting -full".
For online help, type: "get-help Enable-PSRemoting -online"
Listing 5-1Get-Help Enable-PSRemoting
当执行 PSRemoting 时,使用-Force 选项来消除整个过程中用户确认的需要。图 5-1 描述了 CmdLet 的执行。
图 5-1
启用 PowerShell 远程处理
注意
因为这已经在本地机器上启用了,所以它提供了以下反馈。启用 PSRemoting 时可能需要 Windows 远程管理(WinRM)。每个系统、网络和操作系统的配置都是不同的,因此请咨询您的系统管理员以获得帮助。Microsoft 和第三方提供了有关正确设置的信息。请查阅这些指南了解更多信息。此外,还需要在您希望调查的计算机上进行这种设置。
https://docs.microsoft.com/en-us/windows/desktop/winrm/winrm-powershell-commandlets
www.howtogeek.com/117192/how-to-run-powershell-commands-on-remote-computers/
注意
关于启用 PowerShell 远程处理的最后一点说明。出于安全原因,所有适配器的网络配置必须设置为专用,而不是公共。请再次联系您的系统管理员进行这些更改,因为参数取决于您使用的操作系统和版本。
收集和分析远程证据
为了扩大我们的调查范围,利用 PowerShell 和 Python 的组合从我们正在运行的系统之外的系统收集证据是至关重要的。让我们首先来看一个对于本地和远程调查都非常有用的 PowerShell CmdLet:Get-DNSClientCache。
DNS 客户端缓存,或 DNS 解析器缓存,是由操作系统维护的本地数据库。它包含最近访问网站和其他互联网位置的证据。简而言之,DNS 客户端缓存只是最近 DNS 查找的记录,它加速了对已经解析的网站 IP 地址的访问。请注意,清除 web 浏览器的历史记录以隐藏您的活动不包括操作系统 DNS 解析器缓存。许多清理程序会清除此缓存,但用户可能会忽略它,它可能会提供最近活动的重要证据。
DNS,即域名系统,提供了从友好名称如 microsoft.com、google.com 和 python-forensic.org 到它们所在的 IP 地址的转换。每次你在浏览器中输入一个类似 www.amazon.com
的地址,就会执行一次 DNS 查询,将人类可读的地址转换成可以访问的 IP 地址。
清除缓存后启动 Get DNSClientCache 进程会产生以下结果。
PS C:\WINDOWS\system32> Get-DnsClientCache | Select-Object -Property Entry
当然,CmdLet 不会返回任何内容,因为缓存是空的。
如图 5-2 所示,为了向 DnsClientCache 添加数据,打开网络浏览器并加载 Google 主页。
图 5-2
启动浏览器并导航至谷歌主页
执行 CmdLet 现在会产生一些预期和非预期的结果(清单 5-2 )。
PS C:\WINDOWS\system32> Get-DnsClientCache | Select-Object -Property Entry
Entry
-----
beacons.gcp.gvt2.com
beacons.gcp.gvt2.com
beacons.gcp.gvt2.com
google.com
google.com
google.com
google.com
google.com
google.com
bolt.dropbox.com
Listing 5-2Results from the Get-DnsClientCache CmdLet
因为 google.com 页面已经打开,所以存储的 google.com 的 DNS 位置当然是可以预期的。然而,什么是 beacons.gcp.gvt.com 查找?根据在线研究,它属于谷歌,谷歌使用它来跟踪活动,并在你输入谷歌搜索窗口时提供自动帮助。bolt.dropbox.com 与 www.google.com
访问无关,而是由于 Dropbox 在系统上运行时的例行同步而被访问。
与其他 CmdLets 一样,Get-ClientDnsCache 具有与其关联的附加属性和成员函数。可以通过将 Get-ClientDnsCache 的输出管道化到 Get-Member 来检查它们,如图 5-3 所示。
图 5-3
Get-DnsClientCache 的成员方法和属性
一个很好的例子是 TimeToLive 属性,它提供了关于 DNS 客户端缓存条目将持续多长时间(以秒为单位)的信息。知道这些条目只在特定时期存在,当然需要在调查期间紧急收集这些信息。参见清单 5-3 。
PS C:\WINDOWS\system32> Get-DnsClientCache | Select-Object -Property Entry, TimetoLive
Entry TimetoLive
----- ----------
www.gstatic.com 17
ssl.gstatic.com 292
www.google.com 244
apis.google.com 131
apis.google.com 131
apis.google.com 131
apis.google.com 131
apis.google.com 131
apis.google.com 131
apis.google.com 131
google.com 292
google.com 292
google.com 292
google.com 292
google.com 292
google.com 292
fonts.gstatic.com 292
fonts.gstatic.com 292
encrypted-tbn0.gstatic.com 292
Listing 5-3Obtaining the Time to Live for Each DnsClientCache Entry
调用远程访问
Get-DnsClientCache 的一个更重要的应用当然是执行这个 CmdLet 来远程定位被调查的系统。清单 5-4 显示了使用 Invoke-Command 定位 Lenovo-upstreet 计算机以捕获最近的 DnsClientCaches。为了突出显示更有趣的地点,特别是对 dfinews.com、forensicsmag.com 和 steganography.com 的访问,输出被简化了。
PS C:\WINDOWS\system32> Invoke-Command -ComputerName Lenovo-Upstairs -Credential Lenovo-Upstairs\Remote-Admin -ScriptBlock {Get-DnsClientCache | Select-Object -Property Entry |Out-String}
Entry
-----
www.dfinews.com
www.dfinews.com
www.forensicmag.com
www.forensicmag.com
www.forensicmag.com
www.forensicmag.com
www.forensicmag.com
...
... reduced results for brevity
...
steganography.com
steganography.com
www.wired.com
www.wired.com
www.wired.com
www.wired.com
Listing 5-4Remote Invocation of Get-DnsClientCache
为 DnsCache 采集构建 PowerShell 脚本
不幸的是,当这个 CmdLet 启动时,有数百个缓存条目需要排序。对调查人员来说,过滤或搜索这些结果将是一个乏味的过程。因此,为什么不创建一个 Python 脚本,利用 PowerShell 脚本根据可疑网站或感兴趣的关键字列表来搜索结果呢?使用在第四章中创建的 PowerShell 脚本模型,只需做一些简单的调整就可以应用于此:
-
改变大纲
-
更改描述
-
修改输入参数
-
利用 Get-ClientDnsCache CmdLet
清单 5-5 展示了 PowerShell 脚本。
<#
.synopsis
Collect ClientDnsCache
- User Specifies the target computer
The script will produce a simple ascii output file containing the recent DnsCache from the target computer
.Description
This script collects DnsCache from the Target Computer
.parameter targetComputer
Specifies the computer to collect the USB Activity
.parameter user
Specifies the Administrator UserName on the Target Computer
.parameter resultFile
Specifies the full path of the output file
.example
./CacheAcquire.ps1 -user Lenovo-Upstairs\Remote-Admin -targetComputer Lenovo-Upstairs -resultFile cache.txt
Collects the recent DnsCache from the target computer
#>
# Parameter Definition
Section
param(
[string]$user,
[string]$targetComputer,
[string]$resultFile
)
# Obtain the ClientDnsCache from target computer and store the result in a local variable
$r = Invoke-Command -ComputerName $targetComputer -Credential $user -ScriptBlock {Get-DnsClientCache | Select-Object -Property Entry | Out-String}
# Write the resulting list in simple ascii to a specified local file
$r | Out-File $resultFile -Encoding ascii
Listing 5-5CacheAcquire.ps1 PowerShell Script
一个重要的注意事项:当使用 Invoke-Command 时,任何输出文件的创建都发生在远程系统上。因此,将脚本的结果捕获到一个变量中(本例中为 r),然后将该变量通过管道传输到所请求的本地文件。
PowerShell ISE 中的脚本执行示例如图 5-4 至 5-6 所示。
图 5-6
结果 cache.txt 文件
图 5-5
结果缓存列表
图 5-4
CacheAcquire.ps1 执行和凭证条目
与以前的 PowerShell 脚本一样,使用 Get-Help 将提供必要的细节,以允许其他用户也利用该脚本(清单 5-6 )。
PS C:\PS> Get-Help .\CacheAcquire.ps1
NAME
C:\PS\CacheAcquire.ps1
SYNOPSIS
Collect ClientDnsCache
- User Specifies the target computer
The script will produce a simple ascii output file containing the recent DnsCache from the target computer
SYNTAX
C:\PS\CacheAcquire.ps1 [[-user] <String>] [[-targetComputer] <String>] [[-resultFile] <String>] [<CommonParameters>]
DESCRIPTION
This script collects DNS cache from the Target Computer
RELATED LINKS
REMARKS
To see the examples, type: "get-help C:\PS\CacheAcquire.ps1 -examples".
For more information, type: "get-help C:\PS\CacheAcquire.ps1 -detailed".
For technical information, type: "get-help C:\PS\CacheAcquire.ps1 -full".
Listing 5-6Display Help for the CacheAcquire PowerShell Script
Python 脚本和 PowerShell CacheAquire 脚本
现在我们有了一个可靠的 PowerShell 脚本来从远程计算机获取 DNS 缓存,下一步是构建一个 Python 脚本来启动 PowerShell 脚本,然后搜索后续结果。一般概念是使用一组从文件提供给 Python 脚本的关键字来搜索获得的 DNS 缓存。参见清单 5-7 。
'''
Acquire DNS Scripts from a Remote Computer
Version 1.0 January 2018
Author: Chet Hosmer
PYTHON Version 3.x is Required
'''
''' LIBRARY IMPORT SECTION '''
import subprocess # subprocess library
import argparse # argument parsing library
import os # Operating System Path
''' ARGUMENT PARSING SECTION '''
def ValidateFile(theFile):
''' Validate the File exists
it must exist and we must have rights
to read from the folder.
raise the appropriate error if either
is not true
'''
# Validate the file exists
if not os.path.exists(theFile):
raise argparse.ArgumentTypeError('File does not exist')
# Validate the file is readable
if os.access(theFile, os.R_OK):
return theFile
else:
raise argparse.ArgumentTypeError('File is not readable')
#End ValidateFile ===================================
''' Specify and Parse the command line, validate the arguments and return results'''
parser = argparse.ArgumentParser('Remote Client DNS Cache with PowerShell - Version 1.0 January 2018')
parser.add_argument('-c', '--computer', required=True,
help="Specify a target Computer for Aquistion")
parser.add_argument('-u', '--user', required=True,
help="Specify the remote user account")
parser.add_argument('-t', '--tmp', required=True,
help="Specify a temporary result file for the PowerShell Script")
parser.add_argument('-s', '--srch', required=True,
type=ValidateFile, help="Specify the keyword search file")
args = parser.parse_args()
computer = args.computer
user = args.user
tmp = args.tmp
srch = args.srch
print("DNS Cache Acquisition\n")
print("Target: ", computer)
print("User: ", user)
print("Keyword File: ", srch)
'''KEYWORD LOADING SECTION '''
print("Processing Keyword Input")
try:
with open(srch, 'r') as keywordFile:
words = keywordFile.read()
word = words.lower()
words = words.strip()
wordList = words.split()
wordSet = set(wordList)
keyWordList = list(wordSet)
print("\nKeywords to search")
for eachKeyword in keyWordList:
print(eachKeyword)
print()
except Exception as err:
print("Error Processing Keyword File: ", str(err))
quit()
''' MAIN SCRIPT SECTION '''
if __name__ == '__main__':
try:
"' POWERSHELL EXECUTION SECTION "'
print()
command = "powershell -ExecutionPolicy ByPass -File C:/PS/CacheAcquire.ps1"+" -targetComputer "+
computer+ " -user "+user+ "
-resultFile "+tmp
print("Executing: ", command)
print()
powerShellResult = subprocess.run(command, stdout=subprocess.PIPE)
if powerShellResult.stderr == None:
'''DNS CACHE SEARCHING SECTION '''
hitList = []
try:
with open(tmp, 'r') as results:
for eachLine in results:
eachLine = eachLine.strip()
eachLine = eachLine.lower()
for eachKeyword in keyWordList:
if eachKeyword in eachLine:
hitList.append(eachLine)
except Exception as err:
print("Error Processing Result File: ", str(err))
'''RESULT OUTPUT SECTION '''
print("Suspicous DNS Cache Entries Found")
for eachEntry in hitList:
print(eachEntry)
print("\nScript Complete")
else:
print("PowerShell Error:", p.stderr)
except Exception as err:
print ("Cannot Create Output File: "+str(err))
quit()
Listing 5-7
AcquireDNS.py
该脚本被分成以下几个部分。每一个都将被解释:
-
库导入
-
参数解析
-
关键词加载
-
POWERSHELL 执行
-
DNS 缓存搜索
-
结果输出
库导入:顾名思义,这是加载所需 Python 库的地方。它们包括:
-
子进程:用于启动 PowerShell 脚本
-
os:用于文件和文件夹验证
-
argparse:用于解析命令行参数
参数解析:这个部分设置并处理用户命令行参数。对于此脚本,必需的参数包括:
-
-c 指定目标计算机名。
-
-u 指定远程计算机用户名。
-
-t 指定 PowerShell 脚本将用来存储获取的 DNS 缓存数据的 tmp 文件。
-
-s 指定包含要搜索的关键字的本地文件。
Python 中的 argparse 库自动处理命令行,并验证用户是否输入了所有必需的参数。如果需要,图书馆也会提供帮助。要获得帮助,只需执行带有-h 选项的脚本,如清单 5-8 所示。
usage: Remote Client DNS Cache with PowerShell- Version 1.0 January 2018
[-h] -c COMPUTER -u USER -t TMP -s SRCH
optional arguments:
-h, --help show this help message and exit
-c COMPUTER, --computer COMPUTER
Specify a target Computer for Aquistion
-u USER, --user USER Specify the remote user account
-t TMP, --tmp TMP Specify a temporary result file for the PowerShell Script
-s SRCH, --srch SRCH Specify the keyword search file
Listing 5-8Python Script Help Output Using the -h Switch
关键字加载:该部分打开指定的关键字文件,并创建在文件中找到的唯一关键字列表(图 5-7 )。该部分从每个条目中去除任何无关字符,并确保所有条目都是小写的,以实现最佳搜索匹配。
图 5-7
示例关键字文件
POWERSHELL 执行:这个部分启动 POWERSHELL 脚本。它首先创建一个名为命令的变量,subprocess.run()方法将使用该变量启动 PowerShell 脚本。它指定 PowerShell 脚本 CacheAcquire.ps1。在完成子进程命令后,将检查标准错误或 stderr 结果是否成功完成。结果应该是零。否则,Python 脚本将报告 PowerShell 生成的错误。
DNS 缓存搜索:该部分处理 PowerShell 生成的缓存结果中的每一行。然后检查每一行,以确定是否找到任何唯一的关键字。如果检测到一个关键字,整行都存储在 Python hitList 变量中。
结果输出:这个部分遍历 Python hitList 变量的每个条目,并将每个结果打印到屏幕上。
图 5-8 描绘了利用 CacheAcquire.ps1 PowerShell 脚本的 AcquireDNS.py Python 脚本的成功执行。该脚本是使用管理员权限从 Windows 命令行执行的。
C:\PS>python AcquireDNS.py -c PYTHON-3
-u PYTHON-3\USER-HIDDEN -t c:\ps\tmp.txt -s c:\ps\keywords.txt
图 5-8
获取运行中的 DNS 远程
脚本输出首先显示:
-
提取的命令行参数的详细信息:
-
目标计算机
-
远程用户名
-
本地关键字文件
-
-
从本地关键字文件中提取的解码后的关键字列表
-
从输入中生成的 PowerShell 命令行的详细信息
-
包含关键字列表中的关键字的匹配 DNS 缓存条目
客户端 DNS 缓存获取和搜索概述
这个示例通过一个可以搜索结果的 Python 脚本扩展了利用 PowerShell 获取优势的模型。更重要的是,该模型用于使用 Invoke-Command CmdLet 从指定的远程计算机获取客户端 DNS 缓存数据。
Python 脚本可以扩展为包括计算机和相关用户帐户的列表,以便按需自动获取和自动搜索客户端 DNS 缓存。
挑战问题:多目标计算机 DNSCache 获取
利用您从 Python 和所提供的模型中学到的关于 PowerShell 脚本执行的知识:
-
通过加载目标计算列表以及所需的用户帐户,扩展所提供的解决方案。
-
除了搜索得到的每个客户端 DNS 缓存结果之外,还要确定哪些 DNS 条目在所有被访问的计算机上是通用的。
摘要
本章重点介绍通过 Python 执行 PowerShell CmdLets 和脚本,以从本地计算机和指定的远程设备获取客户端 DNS 缓存。本章还提供了另一个 PowerShell 脚本,可以独立使用,也可以由附带的 Python 脚本驱动来访问、处理和搜索结果。
最后,通过示例讨论了 Python 语言、库和数据类型。这些包括参数解析、子进程用法、字典、函数和一般 Python 程序结构。
第六章将讨论一些未来的考虑事项,这些考虑事项可以扩展 PowerShell 和 Python 的组合,用于调查用途。此外,附录还提供了 PowerShell 和 Python/PowerShell 的组合示例,为将来的调查和扩展提供了坚实的基础。
六、从 PowerShell 启动 Python
到目前为止,集成 Python 和 PowerShell 的方法一直是将 PowerShell 脚本作为子流程从 Python 中启动。在本章中,角色将被颠倒,PowerShell 将向 Python 脚本提供数据。PowerShell 的关键元素之一是将一个 CmdLet 的结果传输到下一个 CmdLet 的过程流水线化。考虑到这一点,为什么不把 Python 当作另一个管道元素,并执行由 PowerShell 获取的数据驱动的 Python 脚本呢?
从 PowerShell 到 Python 的角色互换
PowerShell 脚本和 Python 脚本都是演示这种方法所必需的。我们将从一个简单的 PowerShell 脚本开始,通过管道传递一串数据,并显示来自 Python 脚本的数据。
检查 PowerShell 脚本
让我们检查一下图 6-1 中显示的 PowerShell 脚本的细节。该脚本分为四个简单的步骤:
-
使用您选择的 Python 可执行文件的完整路径定义一个局部变量$Python。对于这个例子,将再次使用 Python 3.x。
-
定义一个局部变量$Script,该变量定义将要执行的 Python 脚本的完整路径。
-
定义一个局部变量$Message,该变量将通过管道传递给 Python 脚本。
-
这一行将变量消息的内容传递给 Python 脚本。这里的关键元素是指示 PowerShell 启动外部程序的&符号。
图 6-1
BasicOne.ps1 PowerShell 脚本
检查相应的 Python 脚本
检查图 6-2 中显示的相应 Python 脚本,我们看到它也被分成四个部分:
-
定义脚本将执行的内容的注释块。
-
Python 标准库 sys 的导入。这是处理通过管道传递的数据所需要的。
-
打印 Python 发送的消息,以证明 Python 脚本正在执行。
-
处理通过管道传递给脚本的每一行,并打印每一行的内容。请注意,在这个示例中,只传递了一行。
图 6-2
BasicOne.py Python 脚本
执行 PowerShell 到 Python 的组合脚本
图 6-3 描绘了 PowerShell 脚本驱动 Python 脚本所生成的结果输出。您会注意到 PowerShell 脚本(write-host CmdLet)和 Python (print)语句的输出都出现在 PowerShell 输出中。
图 6-3
执行 BasicOne.ps1 驱动 BasicOne.py
使用这种方法,现在让我们检查一下这里显示的 BasicOne 方法的一个更有趣的用法。
从文本文档中提取可能的专有名词
在此示例中,PowerShell 脚本将利用 Get-ChildItem CmdLet 和 Get-Content CmdLet 来获取文本文件的内容,并将全部内容传递给 Python 脚本。Python 脚本将处理传递的内容,再次使用 BasicOne 方法并尝试提取可能的专有名称。
当在法庭调查期间检查简单的文本数据时,按照出现次数最多来提取和排列专有名称通常是有用的。Python 语言具有内置的功能,可以快速轻松地执行这种提取。
但是首先,什么是合适的名字?
语言学将专有名词定义为代表一个人、一个地方、一个团体、一个组织或一件事物的单词,通常以大写字母开头。例如,单个单词中的专有名称(如 David、Smith、Carol、Washington、Canada、Pentagon、Congress 或 Apple)可以为调查提供上下文和价值。在正常的文本中,这些专有名词很可能是大写,并且很容易剥离、识别、计数和排序。应该注意的是,不是每个人都会习惯性地大写专有名词;然而,智能手机、短信应用程序、电子邮件程序、文字处理软件,甚至 Skype 聊天窗口都会自动为我们利用这些信息。因此,对它们进行提取和排序可以提供快速浏览,并为调查提供视角。
检查 PowerShell 脚本
图 6-4 显示了 PowerShell 脚本,该脚本将这些文件的内容交付给更复杂的 Python 脚本,该脚本将执行可能的专有名称的提取和排序。注意,对于这个例子,已经添加了一个新元素来允许处理多个文件。
图 6-4
PowerShell 属性名脚本
这个剧本被分成了六个步骤。这里定义了每个步骤:
-
使用您选择的 Python 可执行文件的完整路径定义一个局部变量$Python。
-
定义一个局部变量$Script,它标识将要执行的 Python 脚本的完整路径。
-
定义一个局部变量$targetPath,它标识要处理的目标路径和文件类型。
-
利用 Get-ChildItem CmdLet 获取与提供的扩展名匹配的文件名。
-
将包含 Get-ChildItem CmdLet 发现的文件列表的信息写入主机。
-
使用 ForEach 循环,处理局部变量$files 中列出的每个文件。在循环中,脚本打印出每个文件的名称,然后提取文件的原始内容,并将结果内容传输到 Python 脚本。
检查相应的 Python 属性名脚本
清单 6-1 中显示的 Python 脚本被分成六个主要部分,如下所述:
-
库导入
-
停用字词列表定义
-
定义伪常数
-
提取专有名称
-
主程序入口
-
打印可能的专有名称
库导入:顾名思义,这是加载所需 Python 库的地方。它们包括:
-
sys:如 BasicOne 所示,这个库允许我们处理 PowerShell 提供的命令行输入。
-
关于:Python 正则表达式库在这个脚本中用于从文本中去除无关字符,以简化对专有名称的搜索。
-
datetime:顾名思义,这个库提供了显示和计算时间和日期细节的方法。
停用词列表定义:此部分创建停用词列表,用于在脚本中删除在评估专有名称时不提供证明价值的词。事实上,它们是通常以大写字母开头的单词。因此,从结果中消除这些单词会产生更好的结果。
定义伪常量:传统的常量在 Python 语言中并不存在,然而,通过将这些变量大写来提醒读者这些变量不应该被改变。在这种情况下,变量 MIN_SIZE 和 MAX_SIZE 定义了可能的专有名称的限制。通过更改这些值,您可以扩大或缩小可能的专有名称的范围。
提取专有名称函数:这是脚本的核心函数,用于处理从 PowerShell 脚本传输的内容。从标准输入处理的每一行都将调用该函数。该函数从字符串输入中提取可能的专有名称,并将它们添加到字典中。如果该名称已经存在于字典中,则该函数更新字典值,该字典值包含该特定的可能专有名称的出现。
主程序入口 : 主程序首先打印几条标题信息。然后创建一个空的 properNamesDictionary。然后,与 BasicOne.py 示例一样,该脚本处理 PowerShell 脚本提供的系统标准输入中的每一行。然后使用正则表达式转换每一行,以消除任何非字母字符。每个转换后的字符串都被传递给 ExtractProperNames 函数和当前的 properNamesDictionary。然后对提供给脚本的每一行重复这个过程。
打印结果可能的专有名称:最后一部分按照出现次数对结果字典进行排序(最高的第一个),然后打印出每个专有名称和相关的计数。
'''
Copyright (c) 2019 Python Forensics and Chet Hosmer
Permission is hereby granted, free of charge, to any person obtaining a copy of this softwareand associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
ProperNames Demonstration
Version 1.3
January 2019
Requirement: Python 3.x
usage:
stdin | python properNames.py
Script will process the piped data
'''
''' LIBRARY IMPORT SECTION '''
# import standard module sys
import sys
# import the regular expression library
# in order to filter out unwanted characters
import re
# import datetime method from Standard Library
from datetime import datetime
''' STOP WORDS LIST DEFINITION SECTION '''
# COMMON STOP WORDS LIST
# What are stop_words: Words which are
# typically filtered
# out when processing natural language data (text)
# feel free to add additional words
to the list
STOP_WORDS = [
"able","about","above","accordance","according",
"accordingly","across","actually","added","affected",
"affecting","affects","after","afterwards","again",
"against","almost","alone","along","already","also",
"although","always","among","amongst","announce",
"another","anybody","anyhow","anymore","anyone",
"anything","anyway","anyways","anywhere","apparently",
"approximately","arent","arise","around","aside",
"asking","auth","available","away","awfully","back",
"became","because","become","becomes","becoming",
"been","before","beforehand","begin","beginning",
"beginnings","begins","behind","being",
"believe","below","beside","besides","between",
"beyond","both","brief","briefly","came","cannot",
"cause","causes","certain","certainly","come",
"comes","contain","containing","contains","could",
"couldnt","date","different","does","doing","done",
"down","downwards","during","each","effect","eight",
"eighty","either","else","elsewhere","end",
"ending","enough","especially","even","ever",
"every","everybody","everyone","everything",
"everywhere","except","fifth","first","five",
"followed","following","follows","former","formerly",
"forth","found","four","from","further",
"furthermore","gave","gets","getting",
"give","given","gives","giving","goes",
"gone","gotten","happens","hardly","has","have",
"having","hence","here","hereafter","hereby",
"herein","heres","hereupon","hers","herself",
"himself","hither","home","howbeit","however",
"hundred","immediate","immediately","importance",
"important","indeed","index","information",
"instead","into","invention","inward","itself",
"just","keep","keeps","kept","know","known",
"knows","largely","last","lately","later","latter",
"latterly","least","less","lest","lets","like",
"liked","likely","line","little","look","looking",
"looks","made","mainly","make","makes","many",
"maybe","mean","means","meantime","meanwhile",
"merely","might","million","miss","more","moreover",
"most","mostly","much","must","myself","name",
"namely","near","nearly","necessarily","necessary",
"need","needs","neither","never","nevertheless",
"next","nine","ninety","nobody","none","nonetheless",
"noone","normally","noted","nothing","nowhere",
"obtain","obtained","obviously","often","okay",
"omitted","once","ones","only","onto","other",
"others","otherwise","ought","ours","ourselves",
"outside","over","overall","owing","page","pages",
"part","particular","particularly","past","perhaps",
"placed","please","plus","poorly","possible","possibly",
"potentially","predominantly","present","previously",
"primarily","probably","promptly","proud","provides",
"quickly","quite","rather","readily","really","recent",
"recently","refs","regarding","regardless",
"regards","related","relatively","research",
"respectively","resulted","resulting","results","right",
"run","said","same","saying","says","section","see",
"seeing","seem","seemed","seeming","seems","seen",
"self","selves","sent","seven","several","shall",
"shed","shes","should","show","showed","shown",
"showns","shows","significant","significantly",
"similar","similarly","since","slightly","some",
"somebody","somehow","someone","somethan",
"something","sometime","sometimes","somewhat",
"somewhere","soon","sorry","specifically","specified",
"specify","specifying","still","stop","strongly",
"substantially","successfully","such","sufficiently",
"suggest","sure","take","taken","taking","tell",
"tends","than","thank","thanks","thanx","that",
"thats","their","theirs","them","themselves","then",
"thence","there","thereafter","thereby","thered",
"therefore","therein","thereof","therere",
"theres","thereto","thereupon","there've","these",
"they","think","this","those","thou","though","thought",
"thousand","through","throughout","thru","thus",
"together","took","toward","towards","tried","tries",
"truly","trying","twice","under","unfortunately",
"unless","unlike","unlikely","until","unto","upon",
"used","useful","usefully","usefulness","uses","using",
"usually","value","various","very","want","wants",
"was","wasnt","welcome","went","were","what","whatever",
"when","whence","whenever","where","whereafter","whereas",
"whereby","wherein","wheres","whereupon","wherever",
"whether","which","while","whim","whither","whod",
"whoever","whole","whom","whomever","whos","whose",
"widely","will","willing","wish","with","within","without",
"wont","words","world","would","wouldnt",
"your","youre","yours","yourself","yourselves"]
''' DEFINING PSEUDO CONSTANTS SECTION '''
# PSEUDO CONSTANTS,
# Feel Free to change the minimum and
# maximum name length
MIN_SIZE = 3 # Minimum length of a proper name
MAX_SIZE = 20 # Maximum length of a proper name
''' EXTRACT PROPER NAMES SECTION '''
def ExtractProperNames(theString, dictionary):
''' Input String to search,
Output Dictionary of Proper Names
'''
# Extract each continuous string of characters
wordList = theString.split()
# Now, let's determine which words
are possible
# proper names and create a list of them.
'''
For this example words are considered possible
proper names if they are:
1) Title case
2) Meet the minimum and maximum length criteria
3) The word is NOT in the stop word list
The Python built in string method string.istitle()
is used to identify title case
'''
for eachWord in wordList:
if eachWord.istitle() and len(eachWord) >=
MIN_SIZE and len(eachWord) <= MAX_SIZE and
eachWord.lower() not in STOP_WORDS:
'''
if the word meets the specified conditions
it is added to the properNamesDictionary
'''
try:
# if the word exists in the dictionary
# then add 1 to the occurances
cnt = properNamesDictionary[eachWord]
properNamesDictionary[eachWord] =
cnt + 1
except:
# If the word is not yet in the
# dictionary
# add it and set the number of
# occurances to 1
properNamesDictionary[eachWord] = 1
else:
# otherwise loop to the next possible word
continue
# the function returns the created
# properNamesDictionary
return properNamesDictionary
# End Extract Proper Names Function
''' MAIN PROGRAM ENTRY SECTION '''
'''
Main program for Extract Proper Names
'''
if __name__ == "__main__":
''' Main Program Entry Point '''
print("\nPython Proper Name Extraction ")
print("Python Forensics, Inc. \n")
print("Script Started", str(datetime.now()))
print()
# Create empty dictionary
properNamesDictionary = {}
for eachLine in sys.stdin:
txt = re.sub("[^A-Za-z']", ' ', eachLine)
'''
Call the ExtractProperNames function
which returns a Python dictionary of possible
proper names along with the number of occurances
of that name.
This function performs all the heavy lifting
of extracting out each possible proper name
'''
properNamesDictionary =
ExtractProperNames(txt,
properNamesDictionary)
# Once all the standard input lines are read
# the value is the number of occurrences of the
# proper name
# This approach will print out the possible
# proper names with
# the highest occurrence first
'''
PRINT RESULTING POSSIBLE PROPER NAMES
SECTION '''
print()
for eachName in sorted(properNamesDictionary,
key=properNamesDictionary.get, reverse=True):
print('%4d' %
properNamesDictionary[eachName],end="")
print( '%20s' % eachName)
print("\n\nScript Ended", str(datetime.now()))
print()
# End Main Function
Listing 6-1
Python ProperNames.py Script
执行 PowerShell 到 Python ProperNames 的组合脚本
PowerShell 脚本随后针对一个小的文本文件目录执行。为了便于查阅,这些文件储存在 C:\PS\Text 文件夹中。您可以更改目标文件夹变量 targetPath 来修改目标文件夹。见图 6-5 。
图 6-5
PowerShell/Python 组合的结果输出(为简洁起见,减少了输出)
输出分为三个部分:
-
第一部分:这是由 PowerShell 脚本中的 Write-Host CmdLet 生成的输出。
-
第 2–3 部分:这些是 Python 脚本处理 BookOne.txt 生成的结果。当 PowerShell 遍历指定目录中的所有文本文件时,会对 BookTwo.txt 重复输出。
在检查了 PowerShell/Python 组合脚本的输出(甚至是缩写输出)之后,您将能够确定这些可能的专有名称是从哪个文本中提取的。这只是处理 PowerShell 获取的文件内容,然后将输出提交给 Python 进行后处理的一种可能性。
这种组合提供了一个基线模型,可以复制以获得更多的结果。此外,通过在 PowerShell 脚本中插入调用命令序列,您可以收集整个企业中的文件和文件内容。现在让我们看看另一种方法,它将文件名列表传递给 Python 脚本,而不是文件本身的内容。
从照片中提取 EXIF 数据
对于此示例,PowerShell 脚本将保持较小,而繁重的工作将由 Python 脚本来完成,在 Python 脚本中,我们将利用密钥库来提取 EXIF 数据,包括 JPEG 图像的 EXIF 标题中包含的地理位置信息。
PowerShell 脚本
图 6-6 中的 PowerShell 脚本被分解成四个共同的元素,并稍作改动。
-
使用您选择的 Python 可执行文件的完整路径定义一个局部变量$Python。
-
定义一个局部变量$Script,该变量定义将要执行的 Python 脚本的完整路径。
-
定义一个本地变量\(files,用于存储符合搜索条件*.jpg 的文件集。\)jpegList 本地变量提取每个文件的完整路径,并删除文件头,只留下我们要处理的文件列表。
-
这一行将局部变量$jpegList 的内容传递给 Python 脚本。这里的关键元素是指示 PowerShell 启动外部程序的&符号。Python 脚本将接收 PowerShell 脚本获取的每个完整路径名,通过 stdin 传递一行一个。
图 6-6
PowerShell PhotoMap.ps1 脚本
pyGeo.py Python 脚本
清单 6-2 中描述的 Python 脚本分为八个主要部分,如下所述:
-
库导入
-
定义伪常数
-
提取 GPS 字典
-
提取纬度和经度
-
将 gps 坐标转换为度数
-
主程序入口
-
生成结果表
-
生成 CSV 文件
库导入:顾名思义,这是加载所需 Python 库的地方。它们包括:
-
os:Python 标准 OS 库用于访问操作系统方法,例如验证文件或目录的存在。
-
sys : 如 BasicOne 所示,这个库允许我们处理 PowerShell 提供的命令行输入。
-
datetime : 顾名思义,这个库提供了显示和计算时间和日期细节的方法。
-
PIL : 第三方 Python 图像库提供了访问和提取包括地理位置信息在内的 EXIF 数据的方法。
-
prettytable : 第三方 Python 库提供了在一个简单的基于文本的表结构中制表的能力。
EXTRACT GPS DICTIONARY: 这个函数被传递一个要处理的文件名,并验证该文件是一个有效的图像,并且包含地理位置信息。如果是,则收集地理位置信息,并返回 GPS 字典和基本 EXIF 数据。
提取纬度和经度:这个函数从提供的 GPS 字典中提取 GPS 纬度和 GPS 经度以及相关的引用。这些值并不像大多数绘图程序要求的那样存储为度数。因此,使用 ConvertToDegress 函数将它们转换为度数。然后相应地设置方向。例如,如果纬度参考是南方,那么纬度(以度为单位)必须设置为负值。
转换为度数:该功能将存储在 EXIF 数据中的 GPS 坐标转换为度数。
主程序进入:主程序首先打印几条标题信息。然后创建一个空的图片列表。然后,与 BasicOne.py 示例一样,该脚本处理 PowerShell 脚本提供的系统标准输入中的每一行。每行包含由关联的 PowerShell 脚本标识的文件的完整路径。然后将每个文件名追加到图片列表中。
接下来,创建一个空的 latLonList 来保存从每个图片中提取 GPS 的结果。验证每个文件是否存在,然后调用提取 GPS 字典。如果生成的 GPS 字典包含数据,则调用提取纬度经度函数。如果找到了有效的纬度/经度数据,文件的基本名称、纬度和经度数据将被附加到 latLonList 中。
生成结果表:生成结果表部分从列表中生成一个漂亮的结果表。一旦创建了表,就会打印出来,这样提取的结果就可以显示在 PowerShell 中。
生成 CSV 文件:最后,脚本生成一个逗号分隔值(CSV)文件 LatLon.csv。该文件经过格式化,可以导入到基于 Web 的制图工具中。
'''
EXIF Data Acquistion
January 2019
Version 1.1
'''
'''
Copyright (c) 2019 Chet Hosmer, Python Forensics
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
'''
# Usage Example:
# fileList | python pyExif.py
#
# Requirement: Python 3.x
#
# Requirement: 3rd Party Library that is
# utilized is: PILLOW
# to install PILLOW utilize the follow CMD
# from the command line
#
# pip install PILLOW
#
# The Script will extract the EXIF/GEO data from jpeg
# files piped into the script and generate tabular list # of the extracted EXIF and geo location data along with # the creation of a CSV file with LAT/LON Data
#
''' LIBRARY IMPORT SECTION '''
# Python Standard: Operating System Methods
import os
# Python Standard : System Methods
import sys
# Python Standard datetime method from Standard Library
from datetime import datetime
# import the Python Image Library
# along with TAGS and GPS related TAGS
# Note you must install the PILLOW Module
# pip install PILLOW
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
# Import the PrettyTable Library to produce
# tabular results
from prettytable import PrettyTable
''' EXTRACT GPS DICTIONARY SECTION '''
#
# Extract EXIF Data
#
# Input: Full Pathname of the target image
#
# Return: gps Dictionary and selected exifData list
#
def ExtractGPSDictionary(fileName):
try:
pilImage = Image.open(fileName)
exifData = pilImage._getexif()
except Exception:
# If exception occurs from PIL processing
# Report the
return None, None
# Interate through the exifData
# Searching for GPS Tags
imageTimeStamp = "NA"
cameraModel = "NA"
cameraMake = "NA"
gpsData = False
gpsDictionary = {}
if exifData:
for tag, theValue in exifData.items():
# obtain the tag
tagValue = TAGS.get(tag, tag)
# Collect basic image data if available
if tagValue == 'DateTimeOriginal':
imageTimeStamp =
exifData.get(tag).strip()
if tagValue == "Make":
cameraMake = exifData.get(tag).strip()
if tagValue == 'Model':
cameraModel = exifData.get(tag).strip()
# check the tag for GPS
if tagValue == "GPSInfo":
gpsData = True;
# Found it !
# Use a Dictionary to hold the GPS Data
# Loop through the GPS Information
for curTag in theValue:
gpsTag = GPSTAGS.get(curTag, curTag)
gpsDictionary[gpsTag] =
theValue[curTag]
basicExifData = [imageTimeStamp,
cameraMake, cameraModel]
return gpsDictionary, basicExifData
else:
return None, None
# End ExtractGPSDictionary ============================
''' EXTRACT LATTITUDE AND LONGITUDE SECTION '''
#
# Extract the Lattitude and Longitude Values
# From the gpsDictionary
#
def ExtractLatLon(gps):
# to perform the calcuation we need at least
# lat, lon, latRef and lonRef
try:
latitude = gps["GPSLatitude"]
latitudeRef = gps["GPSLatitudeRef"]
longitude = gps["GPSLongitude"]
longitudeRef = gps["GPSLongitudeRef"]
lat = ConvertToDegrees(latitude)
lon = ConvertToDegrees(longitude)
# Check Latitude Reference
# If South of the Equator then
lat value is negative
if latitudeRef == "S":
lat = 0 - lat
# Check Longitude Reference
# If West of the Prime Meridian in
# Greenwich then the Longitude value is negative
if longitudeRef == "W":
lon = 0- lon
gpsCoor = {"Lat": lat,
"LatRef":latitudeRef,
"Lon": lon,
"LonRef": longitudeRef}
return gpsCoor
except:
return None
# End Extract Lat Lon =======================================
''' CONVERT GPS COORDINATES TO DEGRESS '''
#
# Convert GPSCoordinates to Degrees
#
# Input gpsCoordinates value from in EXIF Format
#
def ConvertToDegrees(gpsCoordinate):
d0 = gpsCoordinate[0][0]
d1 = gpsCoordinate[0][1]
try:
degrees = float(d0) / float(d1)
except:
degrees = 0.0
m0 = gpsCoordinate[1][0]
m1 = gpsCoordinate[1][1]
try:
minutes = float(m0) / float(m1)
except:
minutes=0.0
s0 = gpsCoordinate[2][0]
s1 = gpsCoordinate[2][1]
try:
seconds = float(s0) / float(s1)
except:
seconds = 0.0
floatCoordinate = float (degrees + (minutes / 60.0) + (seconds / 3600.0))
return floatCoordinate
''' MAIN PROGRAM ENTRY SECTION '''
if __name__ == "__main__":
'''
pyExif Main Entry Point
'''
print("\nExtract EXIF Data from JPEG Files")
print("Python Forensics, Inc. \n")
print("Script Started", str(datetime.now()))
print()
''' PROCESS PIPED DATA FROM POWERSHELL SECTION '''
pictureList = []
# Process data from standard input as a file list
for eachLine in sys.stdin:
entry = eachLine.strip()
if entry:
pictureList.append(entry)
print("Processing Photos ...")
print()
# CDH
# Created a mapping object
''' PROCESS EACH JPEG FILE SECTION '''
latLonList = []
for targetFile in pictureList:
if os.path.isfile(targetFile):
gpsDictionary, exifList =
ExtractGPSDictionary(targetFile)
if exifList:
TS = exifList[0]
MAKE = exifList[1]
MODEL = exifList[2]
else:
TS = 'NA'
MAKE = 'NA'
MODEL = 'NA'
if (gpsDictionary != None):
# Obtain the Lat Lon values from
# the gpsDictionary
# Converted to degrees
# The return value is a dictionary
# key value pairs
dCoor = ExtractLatLon(gpsDictionary)
if dCoor:
lat = dCoor.get("Lat")
latRef = dCoor.get("LatRef")
lon = dCoor.get("Lon")
lonRef = dCoor.get("LonRef")
if ( lat and lon and
latRef and lonRef):
latLonList.append(
[os.path.basename(targetFile),
'{:4.4f}'.format(lat),
'{:4.4f}'.format(lon),
TS, MAKE, MODEL])
else:
print("WARNING",
"No GPS EXIF Data for ",
targetFile)
else:
continue
else:
continue
else:
print("WARNING", " not a valid file", targetFile)
# Create Result Table Display using PrettyTable
''' GENERATE RESULTS TABLE SECTION '''
''' Result Table Heading '''
resultTable = PrettyTable(['File-Name',
'Lat','Lon',
'TimeStamp',
'Make', 'Model'])
for loc in latLonList:
resultTable.add_row( [loc[0], loc[1],
loc[2], loc[3],
loc[4], loc[5] ])
resultTable.align = "l"
print(resultTable.get_string(sortby="File-Name"))
''' GENERATE CSV FILE SECTION '''
# Create Simple CSV File Result
with open("LatLon.csv", "w") as outFile:
# Write Heading
outFile.write("Name, Lat, Long\n")
# Process All entries and write
# each line comma separated
for loc in latLonList:
outFile.write(loc[0]+","+
loc[1]+","+
loc[2]+"\n")
print("LatLon.csv File Created Successfully")
print("\nScript Ended", str(datetime.now()))
print()
Listing 6-2pyGeo.py Python Script
执行 PowerShell 到 Python exifxtract 的组合脚本
最后一步是执行 PowerShell 脚本,它会将识别出的文件名传递给 Python 脚本。文件夹 C:\PS\Photos 包含一组要检查的 JPEG 照片。通过更改 PowerShell 脚本中的$files 变量,您可以指定要检查的备用目录。见图 6-7 。
图 6-7
执行 photoMap.ps1
该脚本处理了一个包含九个 JPEG 图像文件的样本目录。结果包括与提取的纬度/经度值相关联的文件名的表格。还创建了 LatLon.csv 文件。由此产生的纬度/经度结果可以导入到 web 资源中,如 Google Maps,以提供结果的可视化映射。
摘要
本章重点介绍了从 PowerShell 执行 Python 脚本的模型的开发。该模型使用标准 PowerShell 管道模型来获取特定数据,并使用 PowerShell 管道方法将输出提供给指定的 Python 脚本。
这些示例主要关注执行离散采集的小型 PowerShell 脚本,然后最终使用 Python 丰富的功能来执行处理结果的繁重工作。
这个模型为 PowerShell 和 Python 的实验、获取和结合提供了丰富的基础。在某些方面,该模型似乎比用于从 Python 执行 PowerShell 脚本的子流程方法更精简。当然,无论是控制和自动化现有的 PowerShell 脚本,还是将输出从 PowerShell 驱动到 Python,二者都有各自的位置。
七、遗留问题和未来考虑
已经开发了两种 PowerShell 和 Python 集成的可靠方法(即 Python 子处理和 PowerShell 管道化),还有一些未解决的问题和未来的考虑需要解决。
松散的一端
第一种是使用 PowerShell Invoke-Command CmdLet,而不需要每次都响应登录弹出窗口,如图 7-1 所示。
图 7-1
Windows PowerShell 凭据请求
这可以通过使用 PowerShell 系统管理自动化 PSCredential 系统创建新的凭据对象来实现。图 7-2 显示了一个简单的 PowerShell 脚本,它使用 Remote-Admin 用户凭证从计算机 PLUTO 获取系统事件日志。这只需要四个步骤:
-
创建两个本地 PowerShell 变量:\(targetComputer(您希望访问的计算机名)和\)userName(远程计算机上的用户名)。
-
使用与远程用户相关联的密码创建一个明文字符串$password。注意密码在这里被涂黑了。在 PowerShell 脚本中嵌入密码时,保护脚本免受未经授权的访问至关重要。
-
这一步包含两个重要部分:
-
首先,明文密码被转换为安全字符串$securePassword。ConvertTo-secure string CmdLet 创建的安全字符串然后可以用于需要 SecureString 类型参数的其他 CmdLet 或函数。
-
接下来,创建安全凭证对象\(credential。这需要\)userName 和新创建的$securePassword 作为参数。
-
-
最后,新创建的$credential PowerShell 变量可以作为 Invoke-Command CmdLet 中的-Credential 参数传递。
图 7-2
使用嵌入式凭据收集远程事件日志的 PowerShell 脚本
脚本的执行从 PLUTO 计算机获取系统事件日志,如图 7-3 所示。注意,为了简洁起见,输出被截断了。
图 7-3
EventProcessCred.ps1 示例执行
第二个改进利用了嵌入式凭据方法。嵌入凭证的主要原因(除了方便之外)是脚本可以从同一个脚本从多个远程计算机获取数据,而不需要交互。实现这一点的一种方法是创建一个要访问的目标计算机名称列表。PowerShell 列表非常有用,可以使用 foreach 操作符在多个选择中循环。图 7-4 显示了从 PowerShell 列表中定义的两台计算机获取系统日志的示例。
图 7-4
使用嵌入式凭据从多台目标计算机获取系统事件日志
注意
在本例中,为了简化说明,每个目标的用户名和密码都是相同的。当然,该示例还可以扩展为包括每个目标的唯一用户名和密码。
该脚本分为三个步骤:
-
本节创建一个 PowerShell 对象$listOfTargets,它是一个简单的字符串列表。每个字符串代表一台目标计算机的名称。新创建的列表没有元素。然后使用与所创建的 PowerShell 列表对象相关联的 Add 方法填充 listOfTargets。
-
默认的\(remoteUser 变量被创建并设置为“Remote-Admin ”,这是将要使用的远程用户管理帐户。此外,还创建了\)securePassword,用于访问每个远程目标。请注意,还没有创建$credential,因为它需要为每个目标收购单独创建。
-
最后,创建一个循环,它将执行以下操作:
-
显示每次通过循环处理的主机的名称。
-
将当前的 targetComputer 和默认的 remoteUser 名称组合起来,为该目标创建唯一的 userName。例如:
冥王星\远程管理。
-
使用 PowerShell 系统。Automation 功能,那么每次通过循环,使用\(userName 和\)securePassword PowerShell 变量创建唯一的$credential。
-
然后,使用当前的\(targetComputer 和访问所需的相关\)credential 来执行获取系统事件日志的 Invoke-Command。
-
简短的脚本输出如图 7-5 所示。
图 7-5
多目标计算机系统事件日志执行
未来的考虑
集成 PowerShell 和 Python 并结合两个非常强大的脚本环境是一件令人愉快的事情。研究、实验和模型创建一直在尝试;然而,结果是两个可行和有用的方法,将允许调查解决方案的扩展。
数以千计的 PowerShell CmdLets 可用于从本地或远程目标计算机获取物证,这为数字调查人员提供了丰富的基础。将这一点与 Python 环境的多功能性和强大功能相结合,为无限的创新和解决方案带来了机会。
鉴于这两种集成模式,我要求您开发和扩展结合两种环境优点的新解决方案。我仍然认为 PowerShell 是一个强大的获取引擎,Python 是后端分析和处理组件。然而,这只是我的观点——你可能有不同的想法。所以,也用这些来运行,这里提供的模型可以支持广泛的可能性。
摘要
这一章主要关注一些细节,通过在 PowerShell 脚本中嵌入凭证来提高 PowerShell 的自动化程度。这种嵌入支持同时获取多个证据,然后这些证据可以交付给 Python 元素或由 Python 元素驱动。这肯定会扩大调查人员的调查范围,加快获取和分析证据的速度。
祝你好运,我期待着就以独特的方式结合 PowerShell 和 Python 的新调查解决方案进行交流和合作。