Python-和-PowerShell-协作教程-一-

209 阅读36分钟

Python 和 PowerShell 协作教程(一)

原文:PowerShell and Python Together

协议:CC BY-NC-SA 4.0

一、面向调查人员的 PowerShell 简介

PowerShell 提供了一个强大的获取引擎,可以从实时系统、服务器、外围设备、移动设备和数据驱动的应用程序(如 Active Directory)中获取大量信息。

由于微软决定开放 PowerShell 并提供从其他非微软平台(如 Mac 和 Linux)获取信息的能力,可以访问的信息范围实际上是无限的(通过适当的凭证)。再加上大量的内置和第三方 CmdLets(发音为“command let”),可以对其进行过滤、排序和整合,您就拥有了最终的获取引擎。

通过添加从 PowerShell 到 Python 的桥梁,我们现在可以利用丰富的逻辑机器学习和对 PowerShell 获取的原始信息的深度分析。图 1-1 描绘了我们将在本书中集成的核心组件。其结果将是为现场调查和事故响应应用开发新的创新方法的工作台。

img/448944_1_En_1_Fig1_HTML.jpg

图 1-1

PowerShell 和 Python

PowerShell 的一点历史

PowerShell 是一个微软框架,包括一个命令外壳和一个脚本语言。PowerShell 传统上由系统管理员、IT 团队、事件响应小组和法医调查人员使用,以获取有关他们管理的基础架构的操作信息。如图 1-2 所示,在过去十年中发生了显著的变化。

img/448944_1_En_1_Fig2_HTML.jpg

图 1-2

PowerShell 发展

PowerShell 如今使用情况如何?

PowerShell 通常用于自动化管理任务和检查运行的桌面、服务器和移动设备的细节。它用于检查使用通用对象模型(COM)和 Windows 管理界面(WMI)的本地和远程系统。今天,它可以用来检查和管理使用公共信息模型(CIM)的远程 Linux、Mac 和网络设备。

你是如何用 PowerShell 做实验的?

PowerShell 通常已经安装在现代的 Windows 桌面和服务器平台上。如果没有,可以直接打开自己喜欢的浏览器,搜索“Windows 管理框架 5”,然后下载安装 PowerShell。PowerShell 和 PowerShell ISE(集成脚本环境)是免费的。

我更喜欢使用 PowerShell ISE,因为它提供了:

  1. 有助于发现和试验 CmdLets 的集成环境

  2. 编写、测试和调试脚本的能力

  3. 轻松访问上下文相关的帮助

  4. 自动完成命令,加速开发和学习

导航 PowerShell ISE

安装 PowerShell ISE 后,您可以在 Windows 平台上通过单击开始菜单(Windows 8-10 的左下角)启动它,然后搜索 PowerShell ISE 并单击如图 1-3 所示的应用程序。

img/448944_1_En_1_Fig3_HTML.jpg

图 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 和脚本。

img/448944_1_En_1_Fig4_HTML.jpg

图 1-4

Windows 任务栏以管理员身份启动 PowerShell ISE

启动后,ISE 有三个主窗口,如图 1-5 所示。请注意,默认情况下不显示脚本窗格,但可以从工具栏中选择查看。我已经注释了应用程序的三个主要部分:

img/448944_1_En_1_Fig5_HTML.jpg

图 1-5

PowerShell ISE 界面

  1. 脚本面板:该面板提供了使用附带的 PowerShell 脚本语言创建合并了多个命令的 PowerShell 脚本的能力。请注意,这不是我们在开发 PowerShell 脚本时通常开始的地方。相反,我们首先在直接命令输入面板中进行实验;一旦我们完善了我们的方法,我们就可以创建脚本。

  2. 直接命令输入面板:此面板用于执行 PowerShell CmdLets。这里输入的命令比祖先的 Windows 命令行或 DOS 命令强大得多。此外,这些命令的格式和结构有很大不同,并且遵循一些严格的规则。我将在下一节解释动词-名词的格式和结构,并提供更多的细节和一些例子。

  3. 命令帮助面板:该面板提供关于我们可用的每个 CmdLet 的详细帮助和信息。但是,我很少使用这个区域,而是使用 Get-Help CmdLet 请求直接帮助,以获得有关感兴趣的 CmdLet 的信息,了解它们如何操作,获得它们的使用示例,以及获得所有可用选项的详细信息。

PowerShell CmdLets

在我们直接进入 PowerShell CmdLets 之前,有一些警告:

  1. 实际上有成千上万种可能的 CmdLets。

  2. 如果你考虑所有可能的变化,有成千上万种可能的选择。

  3. 每天都有新的 cmdlet、变体和对现有 cmdlet 的更新被创建。

  4. 每个 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 和数百个其他模块,允许购买大量设备和服务。

img/448944_1_En_1_Fig6_HTML.jpg

图 1-6

更新-帮助 CmdLet 执行

一些关键 CmdLets 的介绍

您可能会问的第一个问题是,“有哪些 CmdLets 可用?”或者更具体地说,“有哪些针对特定信息的 CmdLets?”本节将向您介绍几个关键的 cmdlet:Get-HelpGet-ProcessGet-Member

获得帮助

假设我们对获取当前正在运行的服务的信息感兴趣。为了找到与此主题相关的 CmdLets,我将输入:

Get-Help services

注意,我没有请求关于特定 CmdLet 的信息,而是请求帮助系统向我提供关于可能与服务相关的任何 CmdLet 的信息。图 1-7 显示了一个简化的输出。

img/448944_1_En_1_Fig7_HTML.jpg

图 1-7

搜索与服务相关的 CmdLets

请注意,根据您使用的 PowerShell 版本、帮助文件的当前版本以及安装的 CmdLets,您的列表可能会有所不同。

下一步是选择一个或多个 cmdlet 并获取这些 cmdlet 的帮助。浏览这个简短的列表,Get-Service 听起来很有前途,所以我将通过键入以下命令来请求关于这个特定 CmdLet 的帮助:

Get-Help Get-Service

图 1-8 显示了简化的输出。请注意,有多个选项与 Get-Help CmdLet 的执行相关。对于这个例子,我使用了最简单的形式。但是,我也可以选择使用其他形式的 CmdLet,例如:

img/448944_1_En_1_Fig8_HTML.jpg

图 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 )。

img/448944_1_En_1_Fig9_HTML.jpg

图 1-9

获取示例帮助

获取流程

另一个有用的 CmdLet 是 Get-Process;与 Get-Service 非常相似,它返回关于在本地或远程计算机上运行的进程的信息。使用 Get-Help 更深入地观察 Get-Process(参见图 1-10 ,我们首先注意到 Get-Process 的六种不同的基本变体。从技术上讲,这些被称为参数集,允许我们以六种不同的方式运行 Get-Process CmdLet。

img/448944_1_En_1_Fig10_HTML.jpg

图 1-10

获取帮助获取过程

检查第一组参数(见图 1-11 ,我们发现所有参数都是可选的。这由每个参数周围的方括号表示。

img/448944_1_En_1_Fig11_HTML.jpg

图 1-11

获取流程

这允许我们简单地键入命令,而不包括任何额外的参数,如图 1-12 所示,带有缩写输出。

img/448944_1_En_1_Fig12_HTML.jpg

图 1-12

不带附加参数的 Get-Process

如果我只想获得与 Google Chrome 浏览器相关流程的信息,该怎么办?在图 1-13 中,我列出了我们需要利用的具体-Name参数,以实现这一点。

img/448944_1_En_1_Fig13_HTML.jpg

图 1-13

Get-Process -Name 参数

您会注意到-Name参数是可选的;但是,如果指定了它,您必须指定一个字符串来指示您必须提供的特定数据类型(其内容将是流程的名称)。您还会注意到单词 String 后面有两个方括号。这表明您可以选择包含一个名称列表。每个名称都需要用逗号分隔。图 1-14 显示了一个例子。

img/448944_1_En_1_Fig14_HTML.jpg

图 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 所示。

img/448944_1_En_1_Fig15_HTML.jpg

图 1-15

Get-Service CmdLet 的标准输出

如果需要额外的信息证据呢?例如,如果知道服务是如何启动的很重要,那该怎么办?为了回答这个问题,我们需要询问并从对象中获得额外的属性。

为了提取对象的方法和属性细节,我们需要利用管道将输出对象定向到 Get-Member CmdLet。管道在大多数命令行和 shell 环境中的操作是相似的。然而,在 PowerShell 中,它们是特定于对象和上下文的。

在本例中,我们希望查询的 CmdLet Get-Service 没有执行,而是将对象信息传递给 Get-Member CmdLet,如图 1-16 所示。注意,我们正在寻找的属性的名称是 StartType。

img/448944_1_En_1_Fig16_HTML.jpg

图 1-16

获取成员示例

现在我们知道了名称,我们可以指定属性StartType显示一个定制的输出,如图 1-17 所示。这是我们能做的最简单的管道形式。执行 Get-Service CmdLet,并将结果传送给 Select-Object CmdLet。

img/448944_1_En_1_Fig17_HTML.jpg

图 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 帮助系统。

img/448944_1_En_1_Fig18_HTML.jpg

图 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 是将解决这一挑战的示例管道。

img/448944_1_En_2_Fig1_HTML.jpg

图 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 描述了实际的命令——为了简洁起见,结果被截断了。

img/448944_1_En_2_Fig2_HTML.jpg

图 2-2

挑战解决方案

注意

通过使用 Get-Service | Get-Member 操作,您可以显示 Get-Service CmdLet 对象中所有可用的方法和属性,从而允许附加的筛选选项。

在调查过程中,报告哪些服务被停止也同样重要。例如,复杂的恶意软件会使病毒防护、防火墙和其他防护服务失效。图 2-3 更改命令,只显示当前停止的服务。同样,为了简洁起见,结果被截断。

img/448944_1_En_2_Fig3_HTML.jpg

图 2-3

显示停止的服务

最后一点:如果你想要更多关于格式表的信息,记得使用 Get-Help,如图 2-4 所示。

img/448944_1_En_2_Fig4_HTML.jpg

图 2-4

格式-表 CmdLet 概述

示例 2:获取流程

与正在运行的进程相关的详细信息也很重要,可以提供有关连接到哪些进程的附加信息。例如,在现场调查中,确定 Google Chrome 正在使用哪些活跃的互联网连接可能很重要。对于这个例子,让我们首先将它分解成单独的组件,并介绍 PowerShell 中变量的概念。

PowerShell 变量

什么是 PowerShell 变量:PowerShell 中的变量只是内存中指定用于保存数据值的命名位置。PowerShell 中的所有变量名都以一个 **开头,以便于识别。另一个注意事项:PowerShell中的变量名不区分大小写;因此,** 开头,以便于识别。另一个注意事项:PowerShell 中的变量名不区分大小写;因此,ipAddress 和$IPaddress 表示同一个变量。您可以为变量赋值,例如:

$InvestigatorName = "Chet Hosmer"

或者

$CaseNumber = "BC-0234"

PowerShell 自动变量

此外,还有几个内置或自动变量可用,但用户不能更改。几个例子如图 2-5 所示。

img/448944_1_En_2_Fig5_HTML.jpg

图 2-5

自动变量的示例

分解示例 2 的 CmdLet 用法

现在我们对变量有了一个大致的概念,我们将把它们用于从 Get-Process 收集信息。为了减少 Get-Process 的输出,让我们只关注一个正在运行的进程。在我的测试系统上,我安装并运行了谷歌浏览器。在您的系统上,您可能正在使用其他浏览器,如 Internet Explorer 或 Firefox。替换您的浏览器的名称,以定位由它们创建的进程。此外,名为 svchost 的进程总是在运行,因此您也可以替换它。PowerShell 中的命令如下,结果如图 2-6 所示。

img/448944_1_En_2_Fig6_HTML.jpg

图 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 中。然后通过在下一行指定id 变量名(当然后面跟着 Enter 键),显示$id 变量的内容。

img/448944_1_En_2_Fig7_HTML.jpg

图 2-7

将 Get-Process CmdLet 结果存储在变量$id 中

添加 NetTCPConnections CmdLet

$id 变量现在可以用作其他 CmdLets 的参数。例如,CmdLet Get-NetTCPConnections 有一个参数-OwningProcess,它允许我们将 CmdLet 的输出限制为特定的进程 id。使用 Get-Help 检查 Get-NetTCPConnections,获得以下信息(参见图 2-8 )。

img/448944_1_En_2_Fig8_HTML.jpg

图 2-8

Get-NetTCPConnections 帮助

如何发现 CmdLets?

您可能会问的一个问题是,对于成千上万的 CmdLets,我如何知道使用哪一个来获取和关联与拥有进程的 TCP 连接?答案是使用 Get-Help。PowerShell 中内置的帮助系统的设计是充分利用 PowerShell 和相关 CmdLets 的关键。由于帮助系统每天更新,因此它被设计为与新 cmdlet 保持同步,这些新 cmdlet 是随着对现有 cmdlet 的任何更新而创建的。但是,您也可以找到与特定关键字相关的 CmdLets。例如,参见图 2-9 中如何使用关键字而不是 CmdLet 来使用 Get-Help。

img/448944_1_En_2_Fig9_HTML.jpg

图 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 所示。

img/448944_1_En_2_Fig10_HTML.jpg

图 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 对象的解释。

img/448944_1_En_2_Fig11_HTML.jpg

图 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 指定将提取连接信息的进程 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

img/448944_1_En_2_Fig12_HTML.jpg

图 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 )。

img/448944_1_En_2_Fig13_HTML.jpg

图 2-13

解析 DnsName

要了解有关 Resolve-DnsName 的更多信息,请尝试使用 Get-Help。

添加副本以跟踪您的活动

记录你的调查行为是很重要的(至少可以这么说)。捕获操作和结果数据的一个简单方法是在 PowerShell 中使用另一个 CmdLet:

Start-Transaction
Stop-Transaction

与 PowerShell 中的所有 cmdlet 一样,获取有关 cmdlet 的用法和选项的信息是通过使用 Get-Help。这听起来可能有点多余;然而,许多人仍然转向谷歌或其他搜索引擎来获取这些知识。这在某些情况下当然是有用的,但是 PowerShell 中的帮助系统不仅功能强大、考虑周全,而且每天都在更新。因此,为了获得有关 CmdLets 的最新、最准确的信息,请使用 Get-Help。图 2-14 提供了与开始转录相关的结果。

img/448944_1_En_2_Fig14_HTML.jpg

图 2-14

获取帮助开始-抄本

在这个例子中,指定-Path 参数是为了将脚本的输出指向一个特定的文件,如图 2-15 所示。为了演示 Start-script 的-Append 参数,使用了 Transcript CmdLet,然后重新启动了 script。为此,只需使用相同的路径参数启动第二个 Start-Transcript CmdLet,然后添加-Append 选项,如图 2-15 所示。这允许您在同一个输出文件中连接 PowerShell 会话。

img/448944_1_En_2_Fig15_HTML.jpg

图 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,执行以下操作:

  1. 查看每个 CmdLet 的帮助,包括详细信息和示例,即,

    1. 获取帮助详细信息

    2. 获取帮助-示例

  2. 查看后,描述 CmdLet 的作用,并考虑它在调查中的价值。

  3. 使用最少一个参数执行每个 CmdLet,也可以尝试其他参数。

  4. 使用管道来组装 CmdLet,从简单的事情开始,比如将 CmdLet 输出管道化到 Format-Table CmdLet,然后尝试其他选项。

  5. 确保你的开始和结束记录在你的实验中,这将作为你行动和结果的记录。当您以后试图复制一个复杂的命令时,可以参考这些命令。

这个挑战性问题的解决方案可以在本书的附录和源代码中找到,可在 www.apress.com/9781484245033 获得。

摘要

本章重点介绍了 PowerShell 的几个关键领域,并介绍了几个新的 CmdLets 及其应用。此外,还介绍了 PowerShell 变量的创建和使用。创建了两个示例管道来演示如何在 PowerShell 中实现管道。第三章将引入新的 CmdLets,开发多个完整的 PowerShell 脚本。

三、PowerShell 脚本目标调查

本章将超越单行命令和管道,以创建实际的 PowerShell 脚本。PowerShell 脚本提供了自动化需要特定 CmdLets、管道、变量、结构等的重复性任务的能力。描述 PowerShell 脚本的另一种简单方式是,它们允许您创建新的、更强大、更有针对性的 CmdLets 来解决特定的挑战。一旦您开发了一个完全符合您需求的命令,那么创建一个封装或抽象命令复杂性的脚本是非常有益的。

在这一章,我们将通过两个例子。一个是创建一个特定的、最终有用的调查脚本,该脚本将获取并处理系统事件日志。第二个例子是我们检查 USB 设备使用情况的场景。

关于 PowerShell 脚本的基本事实

在我们开始之前,这里有一些关于 PowerShell 脚本的基本事实:

  1. 脚本是包含一系列 PowerShell 命令的简单文本文件。

  2. 为了防止恶意脚本的执行,PowerShell 实施了一个执行策略,该策略默认设置为“受限”,这样 PowerShell 脚本在默认情况下将不会执行。因此,您必须设置执行策略以允许脚本执行。

  3. 要执行 PowerShell 脚本,您必须在 PowerShell ISE 中执行脚本并提供脚本的完整路径,或者包含脚本的目录必须在您的 Windows 路径中。

示例 EventProcessor PowerShell 脚本

从事件日志中获取数据是法医调查和事件响应活动中的常见做法。这也是系统管理员每天都要执行的一项有用的活动。

从可能分布在整个调查环境中的日志文件中收集有意义的数据可能非常耗时,如果没有一致和完整地完成,将会导致问题。因此,开发一个有针对性的 PowerShell 脚本来执行这个操作将会给调查人员带来巨大的价值。

事件日志 CmdLets

当然,PowerShell 已经包含了通用的 CmdLets,用于从事件日志中收集基本的数据;因此,识别和选择一个可用的 CmdLets 是第一步。为此,我们再次求助于内置的 PowerShell 帮助系统。使用关键字 EventLog 请求帮助会返回 CmdLet 列表,如图 3-1 所示。

img/448944_1_En_3_Fig1_HTML.jpg

图 3-1

引用关键字 EventLog 的 CmdLets

在查看了概要之后,Get-EventLog 似乎是从事件日志中获取事件的一个可能的目标 CmdLet。

图 3-2 显示了与 Get-EventLog CmdLet 相关的基本帮助信息和用法。

img/448944_1_En_3_Fig2_HTML.jpg

图 3-2

Get-Help 获取事件日志结果

图 3-3 描述了几个使用示例。每个标识一个不同的日志文件,并请求最新的 20 个事件。请注意,如果请求 安全 事件日志,您必须拥有管理权限才能访问该日志。

img/448944_1_En_3_Fig3_HTML.jpg

图 3-3

Get-EventLog 请求示例

检索更具体的事件日志信息

图 3-4 显示了执行 Get-EventLog 后的结果。

img/448944_1_En_3_Fig4_HTML.jpg

图 3-4

Get-EventLog 示例结果

Get-EventLog -logName system -Newest 20

基于我们在第二章中学到的关于 PowerShell 管道的知识,我们可以执行更具体或更有针对性的事件日志数据采集。例如,如果我们只想查看类型为错误警告的事件,并过滤掉一般的信息性消息,该怎么办?

考虑到图 3-5 中所示的 Get-Help Get-EventLog 结果摘录,列出的可能条目类型如下:

img/448944_1_En_3_Fig5_HTML.jpg

图 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 所示的结果。

img/448944_1_En_3_Fig6_HTML.jpg

图 3-6

get-带有特定字段和条目类型警告或错误的事件日志

创建脚本

基于对 Get-EventLog 的基本理解,让我们定义一个挑战问题。

第一步:定义挑战

在编写脚本之前,请考虑调查人员在检索事件日志时面临的基本挑战,以及如何开发 PowerShell 脚本来应对这些挑战。问问自己:

  1. 需要收集什么事件日志?根据调查,是否需要获取特定的事件日志?

  2. 应该从哪台或哪些计算机上收集日志文件?

  3. 应该收集多少条最新记录?

  4. 基于 EventType 的可选过滤器有用吗?

  5. 事件日志中应该生成哪些特定字段?

    • 通过使用 Get-Member,我们可以看到感兴趣的公共属性包括:类别、条目类型、事件 ID、机器名、消息、源、时间生成、时间写入和用户名。
  6. 在哪里生成输出,即文件的标准输出?

  7. 其他人将如何使用该脚本?

    1. 我们需要提供帮助吗?

    2. 他们将如何输入参数?

一旦您确定了挑战并能够回答它们,您现在就有了脚本的工作定义,可以继续第二步了。

第二步:分阶段创建脚本

根据第一步中创建的定义,需要为我们的脚本定义特定的参数:

  • 目标日志

  • 目标计算机

  • 目标计数

  • 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 脚本的开发应该包括这些部分。

  1. 脚本标题(包括帮助和示例)

  2. 参数定义

  3. 局部变量定义

  4. 使用参数和局部变量执行 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参数定义作为目标的事件日志。请注意,这可能已经用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

管道有几个关键组件和过渡:

  1. Get-EventLog CmdLet 使用参数targetComputertargetComputer、targetLogName、eventCounteventCount 和eventType 指定-ComputerName、-LogName、-Newest 和 EntryType。

  2. Get-EventLog CmdLet 的输出通过管道传输到 ConvertTo-html CmdLet,该 CmdLet 利用本地变量$Header 和从 Get-EventLog CmdLet 传递的属性 TimeGenerated、EntryType 和 Message 来形成 html 报告的列。

  3. 最后,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 文档。

img/448944_1_En_3_Fig8_HTML.jpg

图 3-8

生成的 HTML 报告

img/448944_1_En_3_Fig7_HTML.jpg

图 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 所示。

img/448944_1_En_3_Fig9_HTML.jpg

图 3-9

Enter-PSSession 凭据请求

建立连接后,您可以看到 PowerShell 提示符变成了:

[Lenovo-Upstairs]: PS C:\Users\Remote-Admin\Documents>

此时,输入的 PowerShell 命令正在楼上的远程计算机 Lenovo 上执行,而不是在本地计算机上执行。在图 3-10 所示的例子中,获取了联想楼上机器的系统事件日志中包含的最新的 20 条警告消息。

img/448944_1_En_3_Fig10_HTML.jpg

图 3-10

远程访问系统事件日志

为了退出远程会话,发出 CmdLet Exit-PSSession,PowerShell 现在再次在本地计算机上运行。如图 3-10 所示。

示例 2: USB 设备使用情况发现

在执行取证调查或事件响应行动时,获取最近使用的 USB 设备当然很重要。这可以帮助确定信息是否从系统中泄漏,或者 USB 插入是否可能是恶意软件感染的原因。

该过程的第一部分是确定检测到了哪些 USB 设备。在 Microsoft Windows 系统上,注册表通过检查保存在 HKEY 本地机器下的详细信息来提供所连接设备的历史记录。图 3-11 显示了在我的本地机器上找到的特定 USBSTOR 键。

img/448944_1_En_3_Fig11_HTML.jpg

图 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 。

img/448944_1_En_3_Fig13_HTML.jpg

图 3-13

访问远程计算机上的 USB 活动

img/448944_1_En_3_Fig12_HTML.jpg

图 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 )。

img/448944_1_En_3_Fig14_HTML.jpg

图 3-14

调用命令方法 USBAcquire

调用命令的结果如图 3-15 所示。

img/448944_1_En_3_Fig15_HTML.jpg

图 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 所示。

img/448944_1_En_3_Fig17_HTML.jpg

图 3-17

结果 USBAcquire PowerShell 脚本

img/448944_1_En_3_Fig16_HTML.jpg

图 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 所示。

img/448944_1_En_4_Fig1_HTML.jpg

图 4-1

下载 Python 3.7.2 ( www.python.org

除了最新版本的 Python,我强烈推荐使用 Python 集成开发环境。我最喜欢的是 WingIDE。

个人版是免费的,可以很好地应对大多数 Python 开发和脚本挑战。该网站提供了大量关于如何配置和使用 WingIDE 的教程,可以在以下位置找到:

www.wingware.com

img/448944_1_En_4_Fig2_HTML.jpg

图 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,您会收到同样的提示。

img/448944_1_En_4_Fig3_HTML.jpg

图 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 在没有参数的情况下执行。** 

**img/448944_1_En_4_Fig4_HTML.jpg

图 4-4

Python 子进程命令分解

在 Python 3.x 中,subprocess.check_output()方法返回一个字节字符串,而在 Python 2.7 中,它返回一个简单的字符串。因此,要显示该命令的输出,需要对 runningProcesses 变量进行解码,如下所示:

print(runningProcesses.decode())

在 WingIDE Python 交互式 shell 中执行该命令会产生如图 4-5 所示的结果。注意,为简洁起见,结果被截断。

img/448944_1_En_4_Fig5_HTML.jpg

图 4-5

打印出 runningProcesses 变量的内容

此时,您可能会说,我为什么要大费周章地从 Python 中执行 PowerShell 命令或 CmdLet 呢?为了回答这个问题,让我们把这个例子带到下一个层次。

使用 PowerShell 和 Python 创建系统文件基线

假设您希望建立当前安装在 Windows 下的驱动程序的基线,特别是 c:\windows\system32\drivers\。就此而言,您可以将任何目录、子目录或整个系统作为目标,但是系统驱动程序以特权运行,并且在调查过程中检测新的驱动程序、现有驱动程序的修改或驱动程序的删除可能会很有用。

获取有关文件的信息是使用 PowerShell 中的 Get-ChildItem CmdLet 完成的。此 CmdLet 有许多相关的功能、属性和方法。我们对创建基线感兴趣的是:

  1. 每个文件的哈希,用于创建取证软件使用的已知良好哈希集

  2. 每个文件的名称

使用如下所示的管道命令从 PowerShell 获取这些信息非常简单。截断结果如图 4-6 所示,命令分解如图 4-7 所示。

Get-ChildItem c:\windows\system32\drivers\ |
Get-FileHash | Select-object -Property Hash, Path | Format-Table -HideTableHeaders

img/448944_1_En_4_Fig7_HTML.jpg

图 4-7

PowerShell 管道细分 Get-ChildItem、Get-FileHash、Select-Object 和 Format-Table

img/448944_1_En_4_Fig6_HTML.jpg

图 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 所示。通过提供指定目标文件夹的参数,这个脚本现在可以应用于任何合法的文件夹。

img/448944_1_En_4_Fig8_HTML.jpg

图 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 的人可能会觉得这个脚本有点复杂。因此,该脚本在这里被分成以下几个部分:

  1. 库导入

  2. 参数解析

  3. 主要的

  4. POWERSHELL 执行

  5. 词典创作

库导入:顾名思义,这是加载所需 Python 库的地方。它们包括:

  • 子进程:用于启动 PowerShell 脚本

  • os:用于文件和文件夹验证

  • argparse:用于解析命令行参数

  • pickle:用于将结果字典存储到一个文件中以备后用

参数解析:这个部分设置并处理用户命令行参数。对于此脚本,必需的参数包括:

  • -b 指定生成的字典基线文件名。

  • -p 指定 PowerShell 脚本用来存储提取的哈希和文件名的目标路径。

  • -t 指定 PowerShell 脚本将用来存储哈希数据的 tmp 文件。

Python 中的 argparse 库会自动处理命令行,并验证用户是否输入了所有必需的参数,并在用户请求时提供帮助。图 4-9 描述了测试文件夹和只使用-h 选项执行脚本的结果。

img/448944_1_En_4_Fig9_HTML.jpg

图 4-9

执行请求帮助的 CreateBaseline.py 脚本

参数处理部分会创建三个变量:

  1. [-b] baselineFile:指定生成的基线字典文件。该文件将由 Python 脚本创建。

  2. [-p] targetPath:传递给 PowerShell 脚本以指定基线化哪个文件夹。这由 PowerShell 脚本使用。

  3. [-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。

img/448944_1_En_4_Fig10_HTML.jpg

图 4-10

Python/PowerShell 脚本组合脚本执行

使用 Python 验证基线

下一步是创建一个 Python 脚本,该脚本将验证所选文件夹的当前版本是否已更改。基本上,我们正在创建一个简单的绊网。验证脚本应该完成哪些具体的验证?

  1. 是否添加了任何文件?

  2. 有文件被删除了吗?

  3. 是否有文件被更改?

我们将重用 HashAcquire.ps1 PowerShell 脚本,并对 HashAcquire.ps1 返回的每个条目的处理进行一些修改。唯一的修改包括:

  1. 添加基线字典加载部分

  2. 增加了字典测试部分和相关的字典验证功能

清单 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 脚本的执行,包括新的帮助结果和未检测到的更改。

img/448944_1_En_4_Fig11_HTML.jpg

图 4-11

验证基线执行并帮助不做任何更改

图 4-12 显示了 VerifyBaseline.py 脚本的执行,该脚本识别了添加到 c:/windows/system32/drivers 目录中的两个无害文件。

img/448944_1_En_4_Fig12_HTML.jpg

图 4-12

用检测到的变更验证基线执行

使用 PowerShell 执行 Python 概述

这个例子为 Python 中 PowerShell 结果的执行和后处理提供了一个很好的模型。更重要的是,这个模型可以扩展到其他用途。例如:

  1. 通过修改 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}
    
    
  2. 获取 CmdLet Get-ChildItem 可以用大量其他面向获取的 CmdLet 替换,例如:

    • 获取流程

    • 获取服务

    • Get-NetTCPConnections

    • Get-NetFirewallSetting

    • 、或任何其他具有调查兴趣的本地或网络值

      然后,无需修改即可应用 Python CreateBaseline 和 VerifyBaseline 脚本来创建基线,然后检测环境中的任何变化。

  3. 使用 subprocess.run()的接口模型可以应用于 PowerShell 脚本的其他获取。使用创建可以从 Python 中逐行获取的简单 ASCII 结果文件的模型,在 Python 和 PowerShell 之间建立一个可靠的接口。你当然可以通过标准输出返回数据。但是,当从 PowerShell 生成大量输出时,这种方法不太稳定。

挑战问题:执行远程脚本执行

利用您从 Python 和所提供的模型中学到的关于 PowerShell 脚本执行的知识:

  1. 通过探索提供调查或事件响应值的其他 PowerShell CmdLets 来扩展所提供的解决方案。根据需要调整 PowerShell 和 Python 脚本。

    1. 获取流程

    2. 获取服务

    3. Get-NETTCPConnections

    4. get-防火墙设置

  2. 修改 PowerShell 和 Python 脚本,以包括对其他计算机的访问。这将需要更改这两个脚本,以便提供附加计算机的名称。此外,PowerShell 脚本需要添加适当的 Invoke-Command CmdLet。

摘要

本章重点介绍了通过 Python 执行 PowerShell CmdLets 和脚本。本章讲述了使用 Python 子进程库与 PowerShell 交互的主要方法。

此外,还讨论了将 PowerShell 结果交付给 Python 进行后处理的方法。这种集成的可重用模型为 PowerShell 和 Python 的集成提供了基线。

最后,通过示例讨论了 Python 语言、库和数据类型。这些包括参数解析、子进程用法、字典、函数和一般 Python 程序结构。

第五章将通过额外的例子和方法来扩展 PowerShell 和 Python 的集成。

Footnotes 1

https://gcn.com/Articles/2006/07/27/Special-Report%2D%2DLive-forensics-is-the-future-for-law-enforcement.aspx

 

**