PowerShell 技能连载 - 识别位置和公共 IP

下面的示例返回您的公共 IP 地址和位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS> Invoke-RestMethod -Uri 'ipinfo.io/json'


ip : 84.183.236.178
hostname : p54b7ecb2.dip0.t-ipconnect.de
city : Hannover
region : Lower Saxony
country : DE
loc : 52.3705,9.7332
org : AS3320 Deutsche Telekom AG
postal : 30159
timezone : Europe/Berlin
readme : https://ipinfo.io/missingauth

PowerShell 技能连载 - 检测未知的网络监听器

通常,当您检查网络连接时,您只能看到远程访问者使用的IP地址。IP 地址通常没有通过 DNS 解析,因此您实际上并不知道谁连接了您的计算机。

如果您想找出谁拥有未知 IP 地址,可以使用免费的 RESTful 网络服务。此行代码将显示指定 IP 地址的所有权:

1
2
3
4
5
6
7
8
9
10
11
12
PS> Invoke-RestMethod -Uri 'http://ipinfo.io/51.107.59.180/json'


ip : 51.107.59.180
city : Zürich
region : Zurich
country : CH
loc : 47.3667,8.5500
org : AS8075 Microsoft Corporation
postal : 8001
timezone : Europe/Zurich
readme : https://ipinfo.io/missingauth

将此命令与其他命令结合使用,可以找出与您的计算机进行通信的人员。例如,Get-NetTcpConnection 列出了您的网络连接,现在您可以查找谁是您所连接的 IP 地址背后的所有者。

在下面的示例中,Get-NetTcpConnection 返回所有当前活动的 HTTPS 连接(端口443)。远程 IP 被自动解析,因此您可以知道哪个软件正在保持连接,以及该软件正在与谁通信:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$Process = @{
Name='Process'
Expression={
# return process path
(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Path

}
}

$IpOwner = @{
Name='RemoteAuthority'
Expression={
$ip = $_.RemoteAddress
$info = Invoke-RestMethod -Uri "http://ipinfo.io/$ip/json"
'{0} ({1})' -f $info.Org, $info.City
}
}

# get all connections to port 443 (HTTPS)
Get-NetTCPConnection -RemotePort 443 -State Established |
# where there is a remote address
Where-Object RemoteAddress |
# and resolve IP and Process ID
Select-Object -Property $IPOwner, RemoteAddress, OwningProcess, $Process

下面是一个实际示例:让我们转储本地管理员组。您可以按名称或不区分文化的 SID 来访问组:

RemoteAuthority                               RemoteAddress  OwningProcess Process
---------------                               -------------  ------------- -------
AS8075 Microsoft Corporation (Amsterdam)      52.114.74.221          14204 C:\Users\tobia\AppData\Local\Microsoft\Teams\current\Teams.exe
AS8075 Microsoft Corporation (Hampden Sydney) 52.114.133.169         13736 C:\Users\tobia\AppData\Local\Microsoft\Teams\current\Teams.exe
AS36459 GitHub, Inc. (Ashburn)                140.82.113.26          21588 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
AS8068 Microsoft Corporation (Redmond)        13.107.42.12            9432 C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE
AS8075 Microsoft Corporation (Zürich)         51.107.59.180          14484 C:\Program Files\PowerShell\7\pwsh.exe
AS8068 Microsoft Corporation (Redmond)        13.107.42.12            9432 C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE
AS8075 Microsoft Corporation (San Antonio)    52.113.206.137         13736 C:\Users\tobia\AppData\Local\Microsoft\Teams\current\Teams.exe
AS8075 Microsoft Corporation (Paris)          51.103.5.186           12752 C:\Users\tobia\AppData\Local\Microsoft\OneDrive\OneDrive.exe

有趣吧?

PowerShell 技能连载 - 更好的 NetStat(第 4 部分)

在上一个技巧中,我们介绍了 Get-NetTCPConnection cmdlet,它是 Windows 系统上 netstat.exe 网络实用程序的更好替代方案,但是此命令仅限于 Windows,并且需要大量其他代码才能将原始信息转化为有用的信息。解析的主机名和进程名。

netstat.exe 的最兼容替代方案是基于 .NET Core 的解决方案,无论 PowerShell 在哪里运行,它都可以跨平台使用。

只需自己获取开源的 GetNetStat 模块 (https://github.com/TobiasPSP/GetNetStat),它使用 .NET Core 代码来获取连接信息:

1
PS> Install-Module -Name GetNetStat -Scope CurrentUser

安装模块后,将有一个名为 Get-NetStat 的新命令。现在,列出打开的连接非常简单,现在您可以在 Windows PowerShell 和 PowerShell 7 中通过并行处理来解析主机名和进程名。

这个简单的命令列出了到 HTTPS(端口 443)的所有打开的连接,并解析主机名:

1
2
3
4
5
6
7
8
9
10
11
12
PS> Get-NetStat -RemotePort 443 -State Established -Resolve | Select-Object -Property RemoteIp, Pid, PidName

RemoteIp PID PIDName
-------- --- -------
1drv.ms 9432 WINWORD
lb-140-82-113-26-iad.github.com 21588 chrome
1drv.ms 9432 WINWORD
1drv.ms 9432 WINWORD
51.103.5.186 5464 svchost
51.103.5.186 12752 OneDrive
52.113.206.137 13736 Teams
51.107.59.180 14484 pwsh

PowerShell 技能连载 - 更好的 NetStat(第 3 部分)

在上一个技巧中,我们介绍了 Get-NetTCPConnection cmdlet,它是 Windows 系统上网络实用程序 netstat.exe 的更好替代方法,并且通过一些技巧,您甚至可以解析 IP 地址和进程 ID。但是,这会大大降低命令的速度,因为 DNS 查找可能会花费一些时间,尤其是在网络超时的情况下。

让我们研究一下新的 PowerShell 7 并行处理功能如何加快处理速度。

以下是具有传统顺序处理的原始代码,该代码转储所有连接到端口 443 并解析主机名和进程(缓慢):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$Process = @{
Name='Process'
Expression={
# return process path
(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Path

}
}

$HostName = @{
Name='Host'
Expression={
$remoteHost = $_.RemoteAddress
try {
# try to resolve IP address
[Net.Dns]::GetHostEntry($remoteHost).HostName
} catch {
# if that fails, return IP anyway
$remoteHost
}
}
}

# get all connections to port 443 (HTTPS)
Get-NetTCPConnection -RemotePort 443 -State Established |
# where there is a remote address
Where-Object RemoteAddress |
# and resolve IP and Process ID
Select-Object -Property $HostName, OwningProcess, $Process

这是一种利用 PowerShell 7 中新的“并行循环”功能的方法(快速):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# get all connections to port 443 (HTTPS)
Get-NetTCPConnection -RemotePort 443 -State Established |
# where there is a remote address
Where-Object RemoteAddress |
# start parallel processing here
# create a loop that runs with 80 consecutive threads
ForEach-Object -ThrottleLimit 80 -Parallel {
# $_ now represents one of the results emitted
# by Get-NetTCPConnection
$remoteHost = $_.RemoteAddress
# DNS resolution occurs now in separate threads
# at the same time
$hostname = try {
# try to resolve IP address
[Net.Dns]::GetHostEntry($remoteHost).HostName
} catch {
# if that fails, return IP anyway
$remoteHost
}
# compose the calculated information into one object
[PSCustomObject]@{
HostName = $hostname
OwningProcess = $_.OwningProcess
Process = (Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Path
}
}

如您所见,第二种方法比以前快得多,并且是 PowerShell 7 中新的“并行循环”的好用例。

但是,不利的一面是,该代码现在的兼容性更低,只能在 Windows 系统上运行,并且只能在 PowerShell 7 中运行。在本系列的最后一部分中,我们将展示一个更简单的解决方案,该解决方案可以在所有版本的 PowerShell 上运行。

PowerShell 技能连载 - 更好的 NetStat(第 2 部分)

在上一个技巧中,我们介绍了 Get-NetTCPConnection cmdlet,它是 Windows 系统上 netstat.exe 网络实用程序的更好替代方案。它可以列出打开的端口和连接,我们以列出所有与 HTTPS(端口443)的连接的示例作为总结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PS> Get-NetTCPConnection -RemotePort 443 -State Established

LocalAddress LocalPort RemoteAddress RemotePort State AppliedSetting OwningProcess
------------ --------- ------------- ---------- ----- -------------- -------------
192.168.2.105 58640 52.114.74.221 443 Established Internet 14204
192.168.2.105 56201 52.114.75.149 443 Established Internet 9432
192.168.2.105 56200 52.114.142.145 443 Established Internet 13736
192.168.2.105 56199 13.107.42.12 443 Established Internet 12752
192.168.2.105 56198 13.107.42.12 443 Established Internet 9432
192.168.2.105 56192 40.101.81.162 443 Established Internet 9432
192.168.2.105 56188 168.62.58.130 443 Established Internet 10276
192.168.2.105 56181 168.62.58.130 443 Established Internet 10276
192.168.2.105 56103 13.107.6.171 443 Established Internet 9432
192.168.2.105 56095 13.107.42.12 443 Established Internet 9432
192.168.2.105 56094 13.107.43.12 443 Established Internet 9432
192.168.2.105 55959 140.82.112.26 443 Established Internet 21588
192.168.2.105 55568 52.113.206.137 443 Established Internet 13736
192.168.2.105 55555 51.103.5.186 443 Established Internet 12752
192.168.2.105 49638 51.103.5.186 443 Established Internet 5464

该列表本身并不是很有用,因为它不能解析 IP 地址,也不会告诉您哪些程序保持着连接。但是,借助一点 PowerShell 魔术,您可以解决以下问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$Process = @{
Name='Process'
Expression={
# return process path
(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Path

}
}

$HostName = @{
Name='Host'
Expression={
$remoteHost = $_.RemoteAddress
try {
# try to resolve IP address
[Net.Dns]::GetHostEntry($remoteHost).HostName
} catch {
# if that fails, return IP anyway
$remoteHost
}
}
}

# get all connections to port 443 (HTTPS)
Get-NetTCPConnection -RemotePort 443 -State Established |
# where there is a remote address
Where-Object RemoteAddress |
# and resolve IP and process ID
Select-Object -Property $HostName, OwningProcess, $Process

Select-Object 选择要显示的对象。它支持“计算属性”。 $Process 定义了一个名为 “Process“ 的计算属性:它采用原始的 OwningProcess 属性,并通过 Get-Process 处理它的进程 ID,以获取应用程序的路径。

$HostName 中也会发生同样的情况:在此,用 .NET 的 GetHostEntry() 方法解析 IP 并返回解析的主机名。现在的结果如下所示:

Host                            OwningProcess Process
----                            ------------- -------
13.107.6.171                             9432 C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE
1drv.ms                                  9432 C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE
lb-140-82-113-26-iad.github.com         21588 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
1drv.ms                                  9432 C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE
52.113.206.137                          13736 C:\Users\tobia\AppData\Local\Microsoft\Teams\current\Teams.exe
51.103.5.186                            12752 C:\Users\tobia\AppData\Local\Microsoft\OneDrive\OneDrive.exe

不过这样做的成本可能很高,因为解析 IP 地址可能会花费很长时间,尤其是在查询超时时。在下一部分中,我们将介绍并行处理以加快处理速度。

PowerShell 技能连载 - 更好的 NetStat(第 1 部分)

在 Windows 系统上,netstat.exe 是一个有用的实用程序,用于检查打开的端口和侦听器。但是,该工具仅返回文本,具有隐式参数,并且无法跨平台使用。

在 Windows 系统上,可以使用名为 Get-NetTCPConnection 的新 PowerShell cmdlet,该 cmdlet 模仿 netstat.exe 中的许多功能。例如,您可以列出任何软件(浏览器)当前打开的所有 HTTPS 连接(端口443):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PS> Get-NetTCPConnection -RemotePort 443 -State Established

LocalAddress LocalPort RemoteAddress RemotePort State AppliedSetting OwningProcess
------------ --------- ------------- ---------- ----- -------------- -------------
192.168.2.105 58640 52.114.74.221 443 Established Internet 14204
192.168.2.105 56201 52.114.75.149 443 Established Internet 9432
192.168.2.105 56200 52.114.142.145 443 Established Internet 13736
192.168.2.105 56199 13.107.42.12 443 Established Internet 12752
192.168.2.105 56198 13.107.42.12 443 Established Internet 9432
192.168.2.105 56192 40.101.81.162 443 Established Internet 9432
192.168.2.105 56188 168.62.58.130 443 Established Internet 10276
192.168.2.105 56181 168.62.58.130 443 Established Internet 10276
192.168.2.105 56103 13.107.6.171 443 Established Internet 9432
192.168.2.105 56095 13.107.42.12 443 Established Internet 9432
192.168.2.105 56094 13.107.43.12 443 Established Internet 9432
192.168.2.105 55959 140.82.112.26 443 Established Internet 21588
192.168.2.105 55568 52.113.206.137 443 Established Internet 13736
192.168.2.105 55555 51.103.5.186 443 Established Internet 12752
192.168.2.105 49638 51.103.5.186 443 Established Internet 5464

不幸的是,Get-NetTCPConnection 有严格的限制。例如,它无法解析 IP 地址或进程 ID,因此您无法轻松发现所连接的服务器名称以及维护连接的程序。并且仅在 Windows 系统上可用。

在接下来的部分中,让我们一一消除这些限制。

PowerShell 技能连载 - 获取开机以来经历的时间

PowerShell 7 附带了一个名为 Get-Uptime 的新 cmdlet。它返回一个 timepan 对象,该对象具有自上次重新引导以来已过去的时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS> Get-Uptime


Days : 9
Hours : 23
Minutes : 21
Seconds : 14
Milliseconds : 0
Ticks : 8616740000000
TotalDays : 9,9730787037037
TotalHours : 239,353888888889
TotalMinutes : 14361,2333333333
TotalSeconds : 861674
TotalMilliseconds : 861674000

提交 -Since 参数时,它将返回上次重新启动的日期。

Get-Uptime 在 Windows PowerShell 中不可用,但是自行创建此命令很简单。运行以下代码在 Windows PowerShell 中创建自己的 Get-Uptime 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Get-Uptime
{
param([Switch]$Since)

$date = (Get-CimInstance -Class Win32_OperatingSystem).LastBootUpTime
if ($Since)
{
return $date
}
else
{
New-Timespan -Start $date
}
}

PowerShell 技能连载 - 查找未使用(或使用过的)驱动器号

通过连接类型转换的结果,您可以轻松创建字母列表:

1
2
3
4
5
6
PS> [Char[]](65..90)
A
B
C
D
...

从此列表中,您可以生成正在使用的驱动器号的列表:

1
2
3
PS> [Char[]](65..90) | Where-Object { Test-Path "${_}:\" }
C
D

同样,要查找空闲的(未使用的)驱动器号,请尝试以下操作:

1
2
3
4
5
6
PS> [Char[]](65..90) | Where-Object { (Test-Path "${_}:\") -eq $false }
A
B
E
F
...

PowerShell 技能连载 - 了解 PowerShell 中的 REST Web 服务

Web 服务类型很多,PowerShell 可以使用 Invoke-RestMethod 访问其中的许多服务。这是一个快速入门,可帮助您起步。

这是最简单的形式,Web 服务就是上面带有结构化数据的网页。您可以在浏览器中使用相同的 URL 来查看数据。PowerShell 使用 Invoke-RestMethod 检索数据。这为您提供了最新的 PowerShell 团队博客:

1
2
Invoke-RestMethod -Uri https://blogs.msdn.microsoft.com/powershell/feed/ |
Select-Object -Property Title, pubDate

像上面的那种简单的 REST Web 服务可以接受参数。这些参数可以作为 URL 的一部分提交。这是一个可以接受任何数字并返回有关该数字的部分意义信息的 Web 服务。下次您被邀请参加 25 周年纪念日时,您可能想查询一些有关数字 25 的有趣信息:

1
2
3
4
5
6
7
8
PS> Invoke-RestMethod http://numbersapi.com/25
25 is the number of cents in a quarter.

PS> Invoke-RestMethod http://numbersapi.com/25
25 is the (critical) number of Florida electoral votes for the 2000 U.S. presidential election.

PS> Invoke-RestMethod http://numbersapi.com/25
25 is the minimum age of candidates for election to the United States House of Representatives.

其他 Web 服务通过不可见的 POST 数据传入用户数据(类似于网站上的表单数据)。它们可能还需要会话状态、cookie 和/或登录。

这是最后一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# https://4bes.nl/2020/08/23/calling-a-rest-api-from-powershell/

$Body = @{
Cook = "Freddy"
Meal = "PastaConCarne"
}

$Parameters = @{
Method = "POST"
Uri = "https://4besday4.azurewebsites.net/api/AddMeal"
Body = ($Body | ConvertTo-Json)
ContentType = "application/json"
}
Invoke-RestMethod @Parameters -SessionVariable cookie
Invoke-RestMethod "https://4besday4.azurewebsites.net/api/AddMeal" -WebSession $cookie

哈希表类似于您要发送到 Web 服务的参数。它们将转换为 JSON 格式。由 Web 服务确定接受用户输入的格式。然后,使用 POST方法将数据传输到 Web 服务。

如果您为向指定名字的厨师发送一个请求,则会从 Web 服务中获取一条通知,告知您正在准备食品。确保更改 Cook 和 Meal 的数据。

如您所见,Invoke-RestMethod 使用了两次。第一次调用获取会话状态和 cookie,并将其存储在使用 -SessionVariable 参数定义的 $cookie 变量中。

第二个调用通过 -WebSession 参数提交会话状态。这样,Web 服务可以保留每次调用之间的状态并清楚地标识您。

PowerShell 技能连载 - 更好的递归

当函数调用自身时,称为“递归”。当脚本想要遍历文件系统的一部分时,您会经常看到这种技术:一个函数处理文件夹内容,当它遇到子文件夹时,它会调用自身。

递归的功能很强大,但是却很难调试,并且有潜在的危险。因为当您犯错时,您将陷入无休止的循环。此外,递归深度过高时,始终存在堆栈溢出的风险。

许多通常需要递归的任务也可以通过使用“队列”来设计:当您的代码遇到新任务时,无需再次调用自身,而是将新任务放在队列中,一旦完成初始任务,开始解决队列中的任务。

感谢 Lee Holmes,这是一个遍历整个驱动器 C:\ 但不使用递归的简单示例。相反,您可以看到正在使用的队列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# get a new queue
[System.Collections.Queue]$queue = [System.Collections.Queue]::new()
# place the initial search path(s) into the queue
$queue.Enqueue('c:\')
# add as many more search paths as you need
# they will eventually all be traversed
#$queue.Enqueue('D:\')

# while there are still elements in the queue...
while ($queue.Count -gt 0)
{
# get one item off the queue
$currentDirectory = $queue.Dequeue()
try
{
# find all subfolders and add them to the queue
# a classic recurse approach would have called itself right here
# this approach instead pushes the future tasks just onto
# the queue for later use
[IO.Directory]::GetDirectories($currentDirectory) | ForEach-Object {
$queue.Enqueue($_)
}
}
catch {}

try
{
# find all files in this folder with the given extensions
[IO.Directory]::GetFiles($currentDirectory, '*.psm1')
[IO.Directory]::GetFiles($currentDirectory, '*.ps1')
}
catch{}
}

以下是 UMU 提供的,不用递归实现本任务的方法:

1
[IO.Directory]::EnumerateFiles('path', '*', 1)