Python 和 PowerShell 协作教程(二)
五、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 脚本的完整路径。
-
定义一个本地变量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 变量:userName(远程计算机上的用户名)。
-
使用与远程用户相关联的密码创建一个明文字符串$password。注意密码在这里被涂黑了。在 PowerShell 脚本中嵌入密码时,保护脚本免受未经授权的访问至关重要。
-
这一步包含两个重要部分:
-
首先,明文密码被转换为安全字符串$securePassword。ConvertTo-secure string CmdLet 创建的安全字符串然后可以用于需要 SecureString 类型参数的其他 CmdLet 或函数。
-
接下来,创建安全凭证对象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。
-
默认的securePassword,用于访问每个远程目标。请注意,还没有创建$credential,因为它需要为每个目标收购单独创建。
-
最后,创建一个循环,它将执行以下操作:
-
显示每次通过循环处理的主机的名称。
-
将当前的 targetComputer 和默认的 remoteUser 名称组合起来,为该目标创建唯一的 userName。例如:
冥王星\远程管理。
-
使用 PowerShell 系统。Automation 功能,那么每次通过循环,使用securePassword PowerShell 变量创建唯一的$credential。
-
然后,使用当前的credential 来执行获取系统事件日志的 Invoke-Command。
-
简短的脚本输出如图 7-5 所示。
图 7-5
多目标计算机系统事件日志执行
未来的考虑
集成 PowerShell 和 Python 并结合两个非常强大的脚本环境是一件令人愉快的事情。研究、实验和模型创建一直在尝试;然而,结果是两个可行和有用的方法,将允许调查解决方案的扩展。
数以千计的 PowerShell CmdLets 可用于从本地或远程目标计算机获取物证,这为数字调查人员提供了丰富的基础。将这一点与 Python 环境的多功能性和强大功能相结合,为无限的创新和解决方案带来了机会。
鉴于这两种集成模式,我要求您开发和扩展结合两种环境优点的新解决方案。我仍然认为 PowerShell 是一个强大的获取引擎,Python 是后端分析和处理组件。然而,这只是我的观点——你可能有不同的想法。所以,也用这些来运行,这里提供的模型可以支持广泛的可能性。
摘要
这一章主要关注一些细节,通过在 PowerShell 脚本中嵌入凭证来提高 PowerShell 的自动化程度。这种嵌入支持同时获取多个证据,然后这些证据可以交付给 Python 元素或由 Python 元素驱动。这肯定会扩大调查人员的调查范围,加快获取和分析证据的速度。
祝你好运,我期待着就以独特的方式结合 PowerShell 和 Python 的新调查解决方案进行交流和合作。