commit 9befcdbdbd8a22af0ede09f17c50ff1e74fb8e53 Author: Thumbscrew Date: Fri Nov 1 08:20:21 2019 +0000 Init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbe9c82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ \ No newline at end of file diff --git a/PSWinFW.psd1 b/PSWinFW.psd1 new file mode 100644 index 0000000..2f46ce6 Binary files /dev/null and b/PSWinFW.psd1 differ diff --git a/PSWinFW.psm1 b/PSWinFW.psm1 new file mode 100644 index 0000000..4043b47 --- /dev/null +++ b/PSWinFW.psm1 @@ -0,0 +1,14 @@ +# Get public and private function definition files. +$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue -Recurse ) +$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue -Recurse ) + +# Dot source the files +Foreach ($import in @($Public + $Private)) { + Try { + . $import.fullname + } Catch { + Write-Error -Message "Failed to import function $($import.fullname): $_" + } +} + +Export-ModuleMember -Function '*' -Alias '*' \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7553e31 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# **PSWinFW (beta)** + +**Note: Module is WIP. Testing is done in a domain environment (not sure how it will fair on standalone machines). Pull Requests welcome.** + +## **Description** +A powershell module for retrieving Windows Firewall logs and displaying them in a nicer, more useful format. + +## **Installation** +``` +git clone https://github.com/Thumbscrew/PSWinFW.git +Import-Module PSWinFW\PSWinFW.psm1 +``` +## **Example Usage** +Get last 1000 Windows Firewall log lines at a specific path: +```powershell +Get-PSFirewallLog -Path C:\Windows\system32\logfiles\firewall\pfirewall.log -Tail 1000 +``` +Get Windows Firewall log by specifying the log directory and filename separately: +```powershell +Get-PSFirewallLog -LogDirectory C:\Windows\system32\logfiles\firewall\ -LogFileName domainfw.log +``` +Get Windows Firewall log by retrieving the path automatically from the registry on the local machine: +```powershell +Get-PSFirewallLog -LogProfile Domain +``` +Get Windows Firewall log on a remote computer using the Remote Registry service to get the log path: +```powershell +Get-PSFirewallLog -LogProfile Public -ComputerName MyRemoteComputer -Verbose +``` \ No newline at end of file diff --git a/public/Get-PSFirewallLog.ps1 b/public/Get-PSFirewallLog.ps1 new file mode 100644 index 0000000..e55e033 --- /dev/null +++ b/public/Get-PSFirewallLog.ps1 @@ -0,0 +1,148 @@ +function Get-PSFirewallLog { + [CmdletBinding(DefaultParameterSetName = 'direct')] + param ( + # Path to firewall log. Defaults to $ENV:SystemRoot\system32\LogFiles\Firewall\pfirewall.log if parameter not supplied. + [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline, ParameterSetName = 'direct')] + [string] + $Path = "$ENV:SystemRoot\system32\LogFiles\Firewall\pfirewall.log", + + # Path to firewall log directory. Defaults to $ENV:SystemRoot\system32\LogFiles\Firewall\ if parameter not supplied. + [Parameter(Mandatory = $false, ParameterSetName = 'indirect')] + [string] + $LogDirectory = "$ENV:SystemRoot\system32\LogFiles\Firewall\", + + # Log file name. + [Parameter(Mandatory = $true, ParameterSetName = 'indirect')] + [string] + $LogFileName, + + # Retrieve a profile's log using registry settings of the local machine + [Parameter(Mandatory = $true, ParameterSetName = 'auto')] + [Parameter(ParameterSetName = 'remote')] + [ValidateSet('Public','Private','Domain')] + [string] + $LogProfile, + + # Number of firewall events to retrieve. Defaults to 0 (All events). + [Parameter(Mandatory = $false)] + [int] + $Tail = 0, + + # Include extended TCP information (TCP Flags, TCP Sequence Number, TCP ACK Number, TCP Window Size). Defaults to false. + [Parameter(Mandatory = $false)] + [switch] + $IncludeTcpInfo, + + # Include extended ICMP information (ICMP Type and Code). Defaults to false. + [Parameter(Mandatory = $false)] + [switch] + $IncludeIcmpInfo, + + # Include Info field. Defaults to false. + [Parameter(Mandatory = $false)] + [switch] + $IncludeInfo, + + # ComputerName to retrieve log from + [Parameter(Mandatory = $false, ParameterSetName = 'remote')] + [string] + $ComputerName + ) + + begin { + if($PSCmdlet.ParameterSetName -eq 'auto') { + # $Path = [Environment]::ExpandEnvironmentVariables((Get-ItemProperty -Path ("HKLM:\SOFTWARE\Policies\Microsoft\WindowsFirewall\{0}Profile\Logging" -f $LogProfile) -Name "LogFilePath").LogFilePath) + $Path = Get-PSFirewallLogPath -LogProfile $LogProfile -Verbose:$VerbosePreference + } + elseif($PSCmdlet.ParameterSetName -eq 'remote') { + $Path = Get-PSFirewallLogPath -LogProfile $LogProfile -ComputerName $ComputerName -Verbose:$VerbosePreference + } + } + + process { + + if($PSCmdlet.ParameterSetName -eq 'indirect') { + # Check for trailing slash and add if necessary + if(!$LogDirectory.EndsWith('\')) { + $LogDirectory += '\' + } + + $logPath = $LogDirectory + $LogFileName + } + else { + $logPath = $Path + } + + if(Test-Path $logPath) { + $log = Get-Content $logPath + + if($log.Length -gt 0) { + # Remove header lines + $log = $log[5..($log.Length - 1)] + + if($Tail -gt 0) { + $startIndex = if($Tail -lt $log.Length) { $log.Length - $Tail } else { 0 } + + $log = $log[$startIndex..($log.Length - 1)] + } + + $members = @{ + "Date" = 0 + "Time" = 1 + "Action" = 2 + "Protocol" = 3 + "SourceIP" = 4 + "DestinationIP" = 5 + "SourcePort" = 6 + "DestinationPort" = 7 + "Size" = 8 + } + + if($IncludeTcpInfo) { + $tcpMembers = @{ + "TcpFlags" = 9 + "TcpSyn" = 10 + "TcpAck" = 11 + "TcpWin" = 12 + } + + $members += $tcpMembers + } + + if($IncludeIcmpInfo) { + $icmpMembers = @{ + "IcmpType" = 13 + "IcmpCode" = 14 + } + + $members += $icmpMembers + } + + if($IncludeInfo) { + $members += @{ "Info" = 15 } + } + + $members += @{ "Path" = 16 } + + $log | ForEach-Object { + $line = $_ + $split = $line -split ('\s') + + $fwEvent = New-Object PSCustomObject + + foreach($member in $members.GetEnumerator() | Sort-Object Value) { + $fwEvent | Add-Member NoteProperty -Name $member.Name -Value $split[$member.Value] + } + + $fwEvent + } + } + else { + Write-Error "File $logPath has zero length." + } + } + else { + Write-Error "Failed to retrieve log at $logPath." + } + } +} \ No newline at end of file diff --git a/public/Get-PSFirewallLogPath.ps1 b/public/Get-PSFirewallLogPath.ps1 new file mode 100644 index 0000000..f542c17 --- /dev/null +++ b/public/Get-PSFirewallLogPath.ps1 @@ -0,0 +1,100 @@ +function Get-PSFirewallLogPath { + param( + # Log Profile to retrieve path for + [Parameter(Mandatory = $true)] + [ValidateSet('Public','Private','Domain')] + [string] + $LogProfile, + + # Remote Host to retrieve from + [Parameter(Mandatory = $false, ParameterSetName = 'remote')] + [string] + $ComputerName + ) + + process { + $serviceName = "RemoteRegistry" + + if($PSCmdlet.ParameterSetName -eq 'remote') { + $startTypeChanged = $false + $statusChanged = $false + + Write-Verbose "Retrieving path from registry on host $ComputerName." + $remoteRegistry = Get-Service -ComputerName $ComputerName -Name $serviceName + + if($remoteRegistry.StartType -eq "Disabled") { + Write-Verbose "$serviceName service is Disabled. Attempting to change to Manual startup." + Set-Service -StartupType "Manual" -Name $serviceName -ComputerName $ComputerName + $modifiedRemoteRegistry = Get-Service -Name $serviceName -ComputerName $ComputerName + + if($modifiedRemoteRegistry.StartType -ne "Manual") { + Write-Warning "Unable to change startup of $serviceName on host $ComputerName." + return $null + } + else { + Write-Verbose "$serviceName startup changed to Manual." + $startTypeChanged = $true + } + } + + if($remoteRegistry.Status -ne "Running") { + Write-Verbose "$serviceName service is not running. Attempting to start." + Set-Service -Status "Running" -Name $serviceName -ComputerName $ComputerName + $modifiedRemoteRegistry = Get-Service -Name $serviceName -ComputerName $ComputerName + + if($modifiedRemoteRegistry.Status -eq "Stopped") { + Write-Warning "Unable to start $serviceName service on host $ComputerName." + return $null + } + else { + Write-Verbose "$serviceName service started OK." + $statusChanged = $true + } + } + + $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ComputerName) + $regKey = $reg.OpenSubKey("SOFTWARE\\Policies\\Microsoft\\WindowsFirewall\\{0}Profile\Logging" -f $LogProfile) + $localPath = [Environment]::ExpandEnvironmentVariables($RegKey.GetValue("LogFilePath")) + + # Set Remote Registry back the way we found it if we had to change it + + if($statusChanged) { + Write-Verbose ("Reverting status of $serviceName Service to {0}." -f $remoteRegistry.Status) + # Set-Service -Name $serviceName -ComputerName $ComputerName -Status $remoteRegistry.Status + # Need to use Invoke-Command as Set-Service won't stop a service that has dependancies + Invoke-Command -ComputerName $ComputerName -ScriptBlock { Stop-Service -Name "RemoteRegistry" } + + # Verify that service has been restored to its original state. + $revertedRemoteRegistry = Get-Service -Name $serviceName -ComputerName $ComputerName + if($remoteRegistry.Status -ne $revertedRemoteRegistry.Status) { + Write-Warning "Failed to revert $serviceName status to $($remoteRegistry.Status)!" + } + } + + if($startTypeChanged) { + Write-Verbose ("Reverting Startup of $serviceName Service to {0}." -f $remoteRegistry.StartType) + Set-Service -Name $serviceName -ComputerName $ComputerName -StartupType $remoteRegistry.StartType + + # Verify that service has been restored to its original state. + if($remoteRegistry.StartType -ne $revertedRemoteRegistry.StartType) { + Write-Warning "Failed to revert startup type of $serviceName to $($remoteRegistry.StartType)!" + } + } + + # Do the conversion to UNC path + $path = "\\$ComputerName\" + $localPath.replace(':', '$') + + return $path + } + else { + # Get local registry key entry + $path = [Environment]::ExpandEnvironmentVariables((Get-ItemProperty -Path ("HKLM:\SOFTWARE\Policies\Microsoft\WindowsFirewall\{0}Profile\Logging" -f $LogProfile) -Name "LogFilePath").LogFilePath) + + if($null -eq $path) { + $path = "$ENV:SystemRoot\system32\LogFiles\Firewall\pfirewall.log" + } + + return $path + } + } +} \ No newline at end of file diff --git a/tests/PathRetrieval.ps1 b/tests/PathRetrieval.ps1 new file mode 100644 index 0000000..7c6b980 --- /dev/null +++ b/tests/PathRetrieval.ps1 @@ -0,0 +1,5 @@ +Import-Module .\PSWinFW.psm1 -Force + +$path = Get-PSFirewallLogPath -LogProfile Public -ComputerName stb603970 -Verbose + +Write-Host $path \ No newline at end of file