PowerShellでTCP/IPパケットを取得する

## Windowsでのパケットキャプチャは netsh や Wireshark が鉄板だけど...
Windows環境におけるパケットキャプチャは、netsh コマンドやパケットキャプチャツールの Wireshark が有名ですが、netsh は2/17に20周年を迎えましたし、Wireshark はインフラの環境によってはインストールが制限されることが多いです。

PowerShell 5.0 には `NetEventPacketCapture` と呼ばれるコマンドレット群があり、これらを用いてパケットをキャプチャすることができます。

今回は `NetEventPacketCapture` を使ったTCP/IPパケットのキャプチャと、生成されるETLファイルの簡単な解析までを行います。

## 環境
```powershell
> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.18362.145
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.18362.145
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
```

## ネットワークイベントセッションを作成する
ネットワークパケットを取得するためにはまず、セッションというものを作る必要があります。
PowerShellを管理者権限で開き、以下のコマンドを入力します。

`New-NetEventSession -Name "Session1" -LocalFilePath "$HOME\Desktop\Session1.etl"`

`-Name`でセッション名を Session1 に、`-LocalFilePath`でキャプチャしたログファイルをカレントユーザーのデスクトップに作成するよう指定します。
実行すると、以下の出力が得られます。

```powershell
> New-NetEventSession -Name "Session1" -LocalFilePath "$HOME\Desktop\Session1.etl"

Name               : Session1
CaptureMode        : SaveToFile
LocalFilePath      : C:\Users\Goggle555\Desktop\Session1.etl
MaxFileSize        : 250 MB
TraceBufferSize    : 0 KB
MaxNumberOfBuffers : 0
SessionStatus      : NotRunning
```

## キャプチャするプロバイダーをTCP/IPに指定する
キャプチャするTCP/IPプロバイダーの名前を、以下のコマンドで探します。

```powershell
> (Get-NetEventProvider -ShowInstalled).Name | Select-String "TCP"

TCPIP Service Trace
Microsoft-Windows-TCPIP
```

`Add-NetEventProvider`を使い、Session1のプロバイダーを`Microsoft-Windows-TCPIP`に指定します。


```powershell
> Add-NetEventProvider -Name "Microsoft-Windows-TCPIP" -SessionName "Session1"

Name            : Microsoft-Windows-TCPIP
SessionName     : Session1
Level           : 4
MatchAnyKeyword : 0xFFFFFFFFFFFFFFFF
MatchAllKeyword : 0x0
```

## パケットキャプチャを開始する
`Start-NetEventSession`でセッションがパケットキャプチャを開始します。
このコマンドを実行しても出力が表示されないため、`Get-NetEventSession`でセッションが実行されていることを確認します。

```powershell
> Start-NetEventSession -Name "Session1"
> Get-NetEventSession

Name               : Session1
CaptureMode        : SaveToFile
LocalFilePath      : C:\Users\Goggle555\Desktop\Session1.etl
MaxFileSize        : 250 MB
TraceBufferSize    : 64 KB
MaxNumberOfBuffers : 38
SessionStatus      : Running
```

`SessionStatus`の値が`Running`と表示されていれば、キャプチャが開始されています。
またこのときに、`New-NetEventSession -LocalFilePath`で指定した場所にETLファイルが生成されています。

```powershell
> ls "$HOME\Desktop" -filter "*.etl"

    ディレクトリ: C:\Users\Goggle555\Desktop

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
------        2020/02/29     12:06              0 Session1.etl
```

## キャプチャを停止する
`Stop-NetEventSession`でパケットキャプチャを停止します。
このコマンドでも出力が表示されないため、`Get-NetEventSession`でセッションが停止していることを確認します。

```powershell
> Stop-NetEventSession -Name "Session1"
> Get-NetEventSession

Name               : Session1
CaptureMode        : SaveToFile
LocalFilePath      : C:\Users\Goggle555\Desktop\Session1.etl
MaxFileSize        : 250 MB
TraceBufferSize    : 64 KB
MaxNumberOfBuffers : 38
SessionStatus      : NotRunning
```

`SessionStatus`の値が`NotRunning`と表示されていれば、キャプチャは停止しています。

ETLファイルの状態を見てみると、キャプチャしていた時間にもよりますが容量が増えていることがわかります。

```powershell
> ls "$HOME\Desktop" -filter "*.etl"

    ディレクトリ: C:\Users\Goggle555\Desktop

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2020/02/29     12:27        6488064 Session1.etl
```

## ネットワークイベントセッションを削除する
セッションを停止したあとは、`Remove-NetEventSession`でセッションを削除しましょう。
セッションは1つしか作れず、前のセッションを放置してると`New-NetEventSession`をした際にエラーになってしまうためです。

```powershell
> Remove-NetEventSession
> Get-NetEventSession
>
```

## ETLファイルを読み込む
ETL(Event Trace Log)ファイルとは、netshやlogmanなどでも取得できるバイナリ形式のログファイルです。
`Get-WinEvent`コマンドレットで配列オブジェクトとして読み込むことができます。

```powershell
> (Get-WinEvent -Path "$HOME\Desktop\Session1.etl").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
```

ログの先頭10個を見てみましょう。
毎回ETLファイルを直接読み込むと時間がかかるので、`$Session1`変数に格納します。

```powershell
> $Session1 = Get-WinEvent -Path $HOME\Desktop\Session1.etl -Oldest
> $Session1[1..10]

   ProviderName: Microsoft-Windows-TCPIP

TimeCreated                      Id LevelDisplayName Message
-----------                      -- ---------------- -------
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c94b54b90 (local=[240d:1a:593:2400:4c89:1e12:405d:7347]:58656 remote=[2404:6800:4004:80b::200a]:443) exists. State = EstablishedS...
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c8d26ab80 (local=192.168.1.2:57657 remote=168.61.148.205:9354) exists. State = EstablishedState. PID = 14188.
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c8dab5010 (local=192.168.1.2:58666 remote=54.240.252.52:443) exists. State = EstablishedState. PID = 144.
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98ca3a6dba0 (local=192.168.1.2:58493 remote=103.2.48.247:443) exists. State = CloseWaitState. PID = 6492.
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c94e708c0 (local=[240d:1a:593:2400:4c89:1e12:405d:7347]:58655 remote=[2404:6800:4004:80b::200a]:443) exists. State = EstablishedS...
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c908e0a10 (local=127.0.0.1:65001 remote=127.0.0.1:49717) exists. State = EstablishedState. PID = 4832.
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c87f49b80 (local=[240d:1a:593:2400:4c89:1e12:405d:7347]:58601 remote=[2404:6800:4004:81d::200a]:443) exists. State = EstablishedS...
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c9c576050 (local=[240d:1a:593:2400:4c89:1e12:405d:7347]:58659 remote=[2406:da14:add:900:f2cf:9253:2438:7cf6]:443) exists. State =...
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c896e6ba0 (local=192.168.1.2:51329 remote=40.90.189.152:443) exists. State = EstablishedState. PID = 5156.
2020/02/29 12:06:32            1300 情報             TCP: connection 0xffffa98c8ca804b0 (local=127.0.0.1:9100 remote=127.0.0.1:49875) exists. State = EstablishedState. PID = 9932.
```
ID 別にどれくらいパケットが記録されているか見ることもできます。

```powershell
> $Session1 | group id -NoElement | sort count -Descending

Count Name
----- ----
13799 1454
 5848 1170
 5278 1169
 4272 1074
 3228 1332
 2159 1157
 1821 1167
 1752 1330
 1562 1159
(...)
```

一番カウントの多い`Id 1454`の中身を見てみましょう。

```powershell
> $Session1.Where({$_.id -eq 1454})[0..4]

   ProviderName: Microsoft-Windows-TCPIP

TimeCreated                      Id LevelDisplayName Message
-----------                      -- ---------------- -------
2020/02/29 12:06:32            1454 情報             INETINSPECT: Owner = 0xffffa98c93f962a0, InspectHandle = 0xffffa98c93f962e0, InspectType = CreateEndpoint, Action = Allow, Status = STATUS_SUCCESS
2020/02/29 12:06:32            1454 情報             INETINSPECT: Owner = 0xffffa98c93f962a0, InspectHandle = 0xffffa98c8e5c6a20, InspectType = BindEndpointRequest, Action = Allow, Status = STATUS_SUCCESS
2020/02/29 12:06:32            1454 情報             INETINSPECT: Owner = 0xffffa98c93f962a0, InspectHandle = 0xffffa98c8e5c6a20, InspectType = BindEndpoint, Action = Allow, Status = STATUS_SUCCESS
2020/02/29 12:06:32            1454 情報             INETINSPECT: Owner = 0xffffa98c93f962a0, InspectHandle = 0xffffa98c8e5c6a20, InspectType = ConnectRequest, Action = Allow, Status = STATUS_SUCCESS
2020/02/29 12:06:32            1454 情報             INETINSPECT: Owner = 0xffffa98c967a7560, InspectHandle = 0xffffa98c9c8717d0, InspectType = ReceiveDatagram, Action = Allow, Status = STATUS_SUCCESS
```

`Message`に`success`が含まれているパケットをカウントしてみたり。

```powershell
> $Session1.message | Select-String -SimpleMatch "success" | measure

Count    : 16741
Average  :
Sum      :
Maximum  :
Minimum  :
Property :
```

## 参考
[Packet Sniffing with PowerShell: Getting Started](https://devblogs.microsoft.com/scripting/packet-sniffing-with-powershell-getting-started/)
[NetEventPacketCapture](https://docs.microsoft.com/en-us/powershell/module/neteventpacketcapture/?view=win10-ps&redirectedfrom=MSDN&viewFallbackFrom=winserverr2-ps)
[windows-powershell-docs/NetEventPacketCapture.md at master · MicrosoftDocs/windows-powershell-docs](https://github.com/MicrosoftDocs/windows-powershell-docs/blob/master/docset/winserver2012r2-ps/neteventpacketcapture/NetEventPacketCapture.md)