Initial public release

* inital commit

* wc

* wc

* wc

* wc

* wc

* wc

* moving to module

* restructing dirs

* switch to module

* moving lib into EventTrace module

* add stop session

* WIP commit

* rewording

* Add support for multiple ETW providers in single session

* working commit

* Deleting old folders and adding new functions

* Adding gitignore

* Starting to add pester tests

* Adding more pester testing

* rearrange get-etwsession function

* small change to etwsession function

* Add more pester testing

* Updating readme

* Rearranged session enumeration functions

* Adds provider directory and some notes

* Add in keyword object

* Fix failing pester test

* Fix start-session to accept object

* Pester tests now invoke full etw session

* small change to tests

* Get-ProviderKeywords to Get-ETWProviderKeywords

* First parser commit

* Small update to process parser

* Ignoring etl files

* Adding Providers and notes on DNS

* Adds DNS Event Ids and Descriptions

* Adding more DNS info

* Adds some PoC work for parsing DNS events to objects

* Adds event parsing, but there's a bug

* Add parser for kernel process

* Updating function docs

* Remove failing test

* Starting to add network events

* Adding network connection support

* Small change to docs

* Add Start-ETWForensicCollection function

* Minor spelling fix

* adding more notes to kernel process events

* Adding provider exists check

* Adds .DS_Store

* Moves synopsis inside function

* starting kernel file

* Adding in file event parser

* Fixing bug in process id parsing

* Add logic for process reuse

* Fix bug in process reuse code

* Adding DLLs for kernel session

* Adds DNS to Start-ETWForensicCollection

* Fixing image load bug with process end time

* Adding DNS to etwforensic collection and log parsing

* Updating DNS parsing

* Fixing bug in filtering and dns

* Removing debug strings

* Add in EnableVerbosity option to etwforensic func

* Adding in thread start/stop events

* Add in thread keyword to forensic collection func

* Major reword to support thread tracking

* Fixing bug in DNS tracking

* fixing minor bug in thread net connections

* Updating readme

* small change to readme

* Add in kernel session support to capture command line

* Updating kernel session parsing code

* Updating readme

* Fixing path and infinite loop bugs

* Add new write-log function for console logging

* Adding support for different logging levels

* fixing a bunch of bugs

* Adding more example in the readme

* Renaming module

* Adding files for rename

* Final rename commit

* Adding demo content

* Updating readme

* More Readme updates

* Readme changes

* Last readme updates for the night

* Adds dcdemo files

* Fixing small thread parsing bug

* Updating demo.txt

* Adding new demo.xml

* Minor tweaks to demo.txt

* More demo updates

* Demo all the things

* Adding powershell logging

* Adds compressed etl files

* Updating demo materials

* Demo updates

* Demo tweaks

* other minor demo tweaks

* Adding graph function
This commit is contained in:
Matt Hastings
2017-09-23 11:25:44 -04:00
committed by GitHub
parent 6b106d3c76
commit f0611ac6fe
33 changed files with 3584 additions and 1 deletions
+3
View File
@@ -0,0 +1,3 @@
.vscode/*
*.etl
.DS_Store
Regular → Executable
View File
+27
View File
@@ -0,0 +1,27 @@
@{
RootModule = "PSWasp.psm1"
ModuleVersion = "0.0.0.1"
Author = "Matt Hastings & Dave Hull"
# functions to export
FunctionsToExport = @(
'ConvertTo-ETWGuid',
'Get-ETWForensicEventLog',
'Get-ETWForensicGraph',
'Get-ETWProviderKeywords',
'Get-ETWProvider',
'New-ETWProviderConfig',
'Get-ETWSessionDetails',
'Get-ETWSessionNames',
'New-ETWProviderOption',
'Start-ETWForensicCollection',
'Start-ETWSession',
'Start-ETWKernelSession',
'Stop-ETWSession'
)
}
+914
View File
@@ -0,0 +1,914 @@
# Setup initial assembly import
$path = $PSScriptRoot + "\lib\Microsoft.Diagnostics.Tracing.TraceEvent.dll"
try {
Import-Module $path
}
catch {
throw "Could not find TraceEvent DLL at path: $path"
}
# Verify assembly loaded
if (-Not ([appdomain]::currentdomain.getassemblies()).location -contains $path) {
throw "Failed to load TraceEvent DLL"
}
Get-ChildItem $PSScriptRoot -Directory |
Where-Object { $_.Name -ne 'Tests' } |
Get-ChildItem -Recurse |
Where-Object { $_.Name -match ".*\.ps1$" } |
ForEach-Object { . $_.FullName }
# Start Private Functions
Function Test-IsSession
{
Param($SessionName)
( Get-ETWSessionNames ).Contains( $SessionName )
} # Test-IsSession
Function Test-IsProvider
{
Param($ProviderName)
( ( Get-ETWProvider ).Name ).Contains( $ProviderName )
} # Test-IsProvider
function Write-Log
{
param (
[Parameter(Mandatory)]
[string]$Message,
[Parameter()]
[ValidateSet('INFO','WARN', 'VERBOSE', 'DEBUG')]
[string]$Severity = 'INFO'
)
$DateTime = ( Get-Date -UFormat "%D %T" )
if ($Severity -eq 'INFO') { Write-Host ('{0} {1}: {2}' -f $DateTime, $Severity, $Message) }
if ($Severity -eq 'WARN') { Write-Warning ('{0} {1}: {2}' -f $DateTime, $Severity, $Message) }
if ($Severity -eq 'VERBOSE') { Write-Verbose ('{0} {1}: {2}' -f $DateTime, $Severity, $Message) }
if ($Severity -eq 'DEBUG') { Write-DEBUG ('{0} {1}: {2}' -f $DateTime, $Severity, $Message) }
} # Write-Log
# End private functions
Function New-ETWProviderConfig
{
<#
.SYNOPSIS
Initialzates new ETW session object
.DESCRIPTION
New-ETWProviderConfig is a function that initializations and returns a new ETWProviderConfig object.
The object is made of three properties: Name, Guid, and Keywords.
.EXAMPLE
$ProviderConfig = New-ETWProviderConfig
#>
$Config = New-Object psobject
# Name of ETW provider to be started (Optional if Guid property is provided)
$Config | Add-Member -NotePropertyName 'Name' -NotePropertyValue $null
# Guid of ETW provider to be started (Optional if Name property is provided)
$Config | Add-Member -NotePropertyName 'Guid' -NotePropertyValue $null
# List of ProviderDataItem objects used as session filters (optional)
# Initializes as an empty array
$Config | Add-Member -NotePropertyName 'Keywords' -NotePropertyValue @()
# Optional Provider options
$Config | Add-Member -NotePropertyName 'Options' -NotePropertyValue $null
$Config
} # New-ETWProviderConfig
Function ConvertTo-ETWGuid
{
<#
.SYNOPSIS
Converts an ETW provider name to a GUID
.DESCRIPTION
ConvertTo-ETWGuid is a function that takes a provider name [string] and return the its corresponding GUID
.PARAMETER ProviderName
Name of an ETW provider
.EXAMPLE
ConvertTo-ETWGuid -ProviderName Microsoft-Windows-Kernel-Process
Returns GUID associated with the Microsoft-Windows-Kernel-Process provider
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[Alias("Provider")]
[string]
$ProviderName)
$ProviderGuid = [Microsoft.Diagnostics.Tracing.Session.TraceEventProviders]::GetProviderGuidByName($ProviderName)
If ($ProviderGuid -eq [System.Guid]::Empty) {
throw "$ProviderName either does not exist or has empty GUID"
}
else {
$ProviderGuid
}
} # ConvertTo-ETWGuid
Function Get-ETWProviderKeywords
{
<#
.SYNOPSIS
Collects event keywords for a specified ETW provider
.DESCRIPTION
Get-ETWProviderKeywords is a function that collections keywords for a given ETW provider.
Keywords describe events collected by the provider and can be used to restrict what events are captured.
.PARAMETER Provider
Name of an ETW provider
.EXAMPLE
Get-ETWProviderKeywords -ProviderName Microsoft-Windows-Kernel-Process
Returns keywords associated with the Microsoft-Windows-Kernel-Process provider
#>
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
$ProviderName)
If ($ProviderName -is [string]) {
$ProviderName = ConvertTo-ETWGuid($ProviderName)
}
[Microsoft.Diagnostics.Tracing.Session.TraceEventProviders]::GetProviderKeywords($ProviderName)
} # Get-ETWProviderKeywords
Function Get-ETWProvider {
<#
.SYNOPSIS
Returns a ProviderMetadata object for each enabled ETW provider.
.DESCRIPTION
Get-ETWProvider is a function that returns objects representing ETW provider metadata
.EXAMPLE
Get-ETWProvider
Returns list of all ETW providers
#>
[System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.GetProviderNames() | ForEach-Object {
try {
[System.Diagnostics.Eventing.Reader.ProviderMetadata]($_)
}
catch {}
}
} # Get-ETWProvider
Function New-ETWProviderOption
{
<#
.SYNOPSIS
Creates new TraceEventProviderOptions object
.DESCRIPTION
New-ETWProviderOption is a function that creates a new TraceEventProviderOptions object with the EventIDsToEnable/Disable properties set as lists.
.EXAMPLE
New-ETWProviderOption
Creates a new TraceEventProviderOptions object with the EventIDsToEnable/Disable properties set as lists. Adding to these properties will limit what events are captured during an ETW session.
#>
$Options = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventProviderOptions
$Options.EventIDsToEnable = New-Object System.Collections.Generic.List[Int]
$Options.EventIDsToDisable = New-Object System.Collections.Generic.List[Int]
$Options
}
Function Get-ETWSessionNames {
<#
.SYNOPSIS
Generates list of ETW session names
.DESCRIPTION
Get-ETWSessionNames is a function that enumerates active ETW sessions and return their names in an array.
.EXAMPLE
Get-ETWSessionNames
Returns all active ETW sessions
#>
[Microsoft.Diagnostics.Tracing.Session.TraceEventSession]::GetActiveSessionNames()
}
Function Get-ETWSessionDetails {
<#
.SYNOPSIS
Returns an array of active ETW session GetProviderNames
.DESCRIPTION
Get-ETWSessionDetails is a function that returns a TraceEventSession object for a given session name
.PARAMETER SessionName
Name of ETW session
.Example
Get-ETWSessionDetails ProcessMonitor
Returns session details about the "ProcessMonitor" ETW session
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[string]
$SessionName
)
# Verify session exists
If ( -Not (Test-IsSession -SessionName $SessionName) ) {
throw "Session does not exist"
}
try {
[Microsoft.Diagnostics.Tracing.Session.TraceEventSession]::GetActiveSession($SessionName)
}
catch {
throw "Failed to get session details"
}
} # Get-ETWSession
Function Start-ETWSession
{
<#
.SYNOPSIS
Starts an event tracing session
.DESCRIPTION
Start-ETWSession is a function that starts an event tracing session with one or more ETW providers.
Events from this session are written to a file at the location given by the $OutputFile argument.
.PARAMETER ProviderConfig
PSObject containing the name and GUID of the ETW provider to be started.
Optionally the keyword property can restrict which events are enabled during the session.
.PARAMETER OutputFile
Location on disk where ETW events will be written. Full path should be provided.
.PARAMETER SessionName
Unique name describing the ETW session. This name will be visible from the Get-ETWSession function
.PARAMETER MaxFileSize
Max size of etl file before old events start being overwritten.
.EXAMPLE
Start-ETWSession -ProviderConfig $Config -OutputFile C:\test.etl -SessionName TestSession -MaxBufferSize 100
Start an ETW session named "TestSession" and write events to the file "C:\test.etl".
The ETW provider(s) name(s) and keywords are in the predefined variable "$Config.
The $Config variable should be a single instance or array of objects created from the New-ETWProviderConfig function.
The output file will grow up to 50 MB before the oldest events are overwritten
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[psobject]
$ProviderConfig,
[Parameter(Mandatory=$true)]
[string]
$SessionName,
[Parameter(Mandatory=$true)]
[string]
$OutputFile,
[Parameter(Mandatory=$false)]
[int]
$MaxFileSize = 50
)
BEGIN {
# Check if active session with same name already exists
If ( (Test-IsSession -SessionName $SessionName) ) {
throw $SessionName+ " already exists. Cannot create again"
}
$ProviderConfig | ForEach-Object {
# Verify either provider Name or Guid was provided
If ( ( $_.Name -eq $null ) -and ($_.Guid -eq $null ) ) {
throw "Must provide either provider name or GUID"
}
# Check that provider name exists
If ( -Not ( $_.Name -eq $null ) -and -Not ( Test-IsProvider -ProviderName $_.Name ) ) {
Throw $_.Name + " is not a valid ETW Provider name"
}
}
Write-Log "Session name set to $SessionName" -Severity "VERBOSE"
Write-Log "Provider set to $ProviderName" -Severity "VERBOSE"
$path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputFile)
}
PROCESS {
# Create our ETW Session options
$options = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventSessionOptions
$options.Create
# Create ETW session
$session = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventSession -ArgumentList @($SessionName, $path, $options)
# Setting StopOnDispose to false will not end a session if powershell ends
$session.StopOnDispose = $false
# Setting max file size
$session.CircularBufferMB = $MaxFileSize
# Set log level. Default, and only supported option at this time, is Verbose
$TraceEventLevel = 5 # Verbose
# Start session
$ProviderConfig | ForEach-Object {
# Keywords are used to filter what events are captured during an ETW session and are calculated via a bitmask
# Adding the value for the enables to correct events. If keywords are not provided no event filters are used
If ( $_.Keywords ) {
$MatchAnyKeywords = $_.Keywords | ForEach-Object { $_.Value } | Measure-Object -Sum | Select-Object -ExpandProperty sum
} else {
$MatchAnyKeywords = [uint64]::MaxValue
}
# Determine if provider should be enabled by GUID or name
If ( $_.Name ) {
$ProviderID = $_.Name
} else {
$ProviderID = $_.Guid
}
$result = $session.EnableProvider($ProviderID, $TraceEventLevel, $MatchAnyKeywords, $_.Options)
}
# EnableProvider returns false if session if not previously exist
If ( $result -eq $False ) {
Write-Log "Successfully started ETW session $SessionName" -Severity 'INFO'
}
else {
throw "Failed to start session $SessionName"
}
}
} # Start-ETWSession
Function Start-ETWKernelSession
{
<#
.SYNOPSIS
Enables Windows Kernel ETW Provider
.DESCRIPTION
In ETW there is a special provider to get certain events from the Windows kernel. In some earlier Windows versions (Win7 and 2008R2) there is only one allowed kernel session with a static name.
More recent versions allow for multiple sessions and multiple session names.
.PARAMETER OutputFile
Location on disk where Kernel ETW events will be written. Full path should be provided.
.PARAMETER MaxFileSize
Max size of etl file before old events start being overwritten.
.PARAMETER SessionName
Optional parameter to define a unique Kernel session name. On Win7 and 2008R2 this name has be to "NT Kernel Logger", which is the configured default value.
The default value will work on modern Windows operating systems, but may cause issues if another session with the same name already exists.
.EXAMPLE
Start-ETWKernelSession -OutputFile .\kernel_events.etl
This will start the kernel ETW provider, register a new sessin with the default name "NT Kernel Logger" and write output to the file "kernel_events.etl"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]
$OutputFile,
[Parameter(Mandatory=$false)]
[string]
$SessionName = [Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser]::KernelSessionName,
[Parameter(Mandatory=$false)]
[int]
$MaxFileSize = 50
)
# For kernel events we are mostly concerned with Process events
$Process = 0x00000001
$path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputFile)
# Create ETW session options
$options = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventSessionOptions
$options.Create
$session = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventSession -ArgumentList @($SessionName, $path)
$session.StopOnDispose = $false
# Setting max file size
$session.CircularBufferMB = $MaxFileSize
# Starts kernel session filtered to only collect process events
$result = $session.EnableKernelProvider($Process)
} # Start-ETWKernelSession
Function Stop-ETWSession {
<#
.SYNOPSIS
Stop an ETW session
.DESCRIPTION
Stop-ETWSession is a function that attaches to and stops an existing ETW session
.PARAMETER SessionName
Name of valid ETW Session
.PARAMETER StopKernelSession
Boolean parameter to also stop the default kernel provider session. Only works if kernel session name is "NT Kernel Logger"
.EXAMPLE
Stop-ETWSession ProcessMonitor
Stops ETW session name "ProcessMonitor"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[string]
$SessionName,
[Parameter(Mandatory=$false)]
[switch]
$StopKernelSession = $false
)
BEGIN {
if ( -Not (Test-IsSession -SessionName $SessionName ) ) {
throw "$SessionName is not a valid session"
}
}
PROCESS {
# Create our ETW Session options
$options = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventSessionOptions
$options.Attach
# Create ETW session
$session = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventSession -ArgumentList @($SessionName, "", $options)
# Stop session
$result = $session.stop()
if ($result) {
Write-Log "Successfully stopped ETW session $SessionName" -Severity 'INFO'
} else {
Write-Log "Failed to stop ETW session $SessionName" -Severity 'INFO'
}
If ($StopKernelSession) {
$KernelName = "NT Kernel Logger"
$session = New-Object -TypeName Microsoft.Diagnostics.Tracing.Session.TraceEventSession -ArgumentList @($KernelName, "", $options)
# Stop session
$KerResult = $session.stop()
if ($KerResult) {
Write-Log "Successfully stopped kernel ETW session" -Severity 'INFO'
} else {
Write-Log "Failed to stop kernel ETW session" -Severity 'INFO'
}
}
}
} # Stop-ETWSession
Function Get-ETWForensicEventLog
{
<#
.SYNOPSIS
Parses ETL event log for forensically significant events
.DESCRIPTION
Get-ETWForensicEventLog is a function tha parses an ETW event trace log for forensically significant information.
The raw log can be read using the builtin Get-WinEvent cmdlet.
.PARAMETER Path
Path of the etl file to parse
.EXAMPLE
Get-ETWForensicEventLog -Path C:\logs\process.etl
Converts process.etl for forensically significant ETW events for supported providers and returns objects representing the forenically signifant information.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]
$Path
)
if (-not (Test-Path $path )) {
$path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputFile)
}
# Hash table mapping of supported ETW providers to parser functions
$script:Providers = @{
'Microsoft-Windows-Kernel-Process' = 'KernelProcessParser'
'Microsoft-Windows-Kernel-Network' = 'KernelNetworkParser'
'Microsoft-Windows-Kernel-File' = 'KernelFileParser'
'Microsoft-Windows-DNS-Client' = 'DNSClientParser'
'Microsoft-Windows-PowerShell' = 'PowerShellParser'
}
$Events = @{}
Write-Log "Found etw output file at: $path" -Severity 'INFO'
Get-WinEvent -Path $Path -Oldest | Where-Object {
$script:Providers.ContainsKey($_.ProviderName) } | ForEach-Object {
&$script:Providers[$_.ProviderName] -Event $_ }
# Check if kernel output exists
$KernelSessPath = -join([IO.Path]::GetDirectoryName($path) + '\' + [IO.Path]::GetFileNameWithoutExtension($Path) + "_kernelsession" + [IO.Path]::GetExtension($Path))
If ( Test-Path $KernelSessPath ) {
Write-Log "Found kernel session etw file at: $KernelSessPath" -Severity 'INFO'
Get-WinEvent -Path $KernelSessPath -Oldest |
# Filter out any event that does not contain command line in eventpayload
ForEach-Object {
KernelSessionParser -EventPayload (([xml]$_.toxml()).Event.ChildNodes.EventPayload)[1]
}
}
$Events.Values
} # Get-ETWForensicEventLog
Function Get-ETWForensicGraph
{
<#
.SYNOPSIS
Visualizes parent and child process relationships
.DESCRIPTION
Get-ETWForensicGraph is a function that parses the output from Get-ETWForensicEventLog and visualizes parent and child relationships
.PARAMETER ETWObject
Output from Get-ETWForensicEventLog
.PARAMETER ParentProcessID
Starting process ID. Function will enumerate 5 layers of child processes from this starting point.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$True)]
$ETWObject,
[Parameter(Mandatory=$True)]
$ParentProcessID
)
$ImageName = $ETWObject | Where-Object { $_.ProcessId -eq $ParentProcessID } | ForEach-Object {
If ($_.ImageName) {
$_.ImageName
} else {"Unknown"}
}
$ChildProcs = $ETWObject | Where-Object { $_.ProcessId -eq $ParentProcessID } | Select-Object -ExpandProperty ChildProcesses
$IMG = @"
`t||
`t||
`t||
"@
$2IMG = @"
`t`t||
`t`t||
`t`t||
"@
$3IMG = @"
`t`t`t||
`t`t`t||
`t`t`t||
"@
$4IMG = @"
`t`t`t`t||
`t`t`t`t||
`t`t`t`t||
"@
$5IMG = @"
`t`t`t`t`t||
`t`t`t`t`t||
`t`t`t`t`t||
"@
Write-Host `n`n$ImageName "($ParentProcessID)" -ForegroundColor Green
$IMG
$ChildProcs |
ForEach-Object {
$Proc = $_.ProcessID
Write-Host `t $_.ImageName "($Proc)" -ForegroundColor Green
If ($_.ChildProcesses) {
$2IMG
$_.ChildProcesses | ForEach-Object {
$Proc = $_.ProcessId
Write-Host `t`t $_.ImageName "($Proc)" -ForegroundColor Green
If ($_.ChildProcesses) {
$3IMG
$_.ChildProcesses | ForEach-Object {
$Proc = $_.ProcessID
Write-Host `t`t`t $_.ImageName "($Proc)" -ForegroundColor Green
If ($_.ChildProcesses) {
$4IMG
$_.ChildProcesses | ForEach-Object {
$Proc = $_.ProcessID
Write-Host `t`t`t`t $_.ImageName "($Proc)" -ForegroundColor Green
If ($_.ChildProcesses) {
$5IMG
$_.ChildProcesses | ForEach-Object {
$Proc = $_.ProcessID
Write-Host `t`t`t`t`t $_.ImageName "($Proc)" -ForegroundColor Green
}
}
}
}
}
}
}
}
}
}
Function Start-ETWForensicCollection
{
<#
.SYNOPSIS
Initiates an ETW session to collect forensically significant events
.DESCRIPTION
Start-ETWForensicCollection is a function that initiates pre-defined event tracing providers and filters.
This configuration was designed to enable forensically important providers and event types.
.PARAMETER OutputFile
Location on disk where ETW events will be written. Full path should be provided.
.PARAMETER SessionName
Unique name describing the ETW session. This name will be visible from the Get-ETWSession function
.PARAMETER EnableVerbose
Enables verbose event capture (ex. all file writes). Should only be enabled for short event captures.
.PARAMETER DisableKernelProvider
Disables kernel session during forensic capture. Disabling this provider results in command lines not being captured.
.EXAMPLE
Start-ETWForensicCollection -OutputFile C:\test\out.etl -SessionName collection
Starts ETW session named collection and captured events are writen to the path 'C:\test\out.etl'.
#>
param(
[Parameter(Mandatory=$true)]
[string]
$SessionName,
[Parameter(Mandatory=$true)]
[string]
$OutputFile,
[Parameter(Mandatory=$False)]
[switch]
$EnableVerbose = $false,
[Parameter(Mandatory=$False)]
[switch]
$DisableKernelProvider = $false
)
# Resolve full path of output file
$OutputFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputFile)
$ProviderConfigs = @()
# Build config for kernel Process
$KernelProcessName = 'Microsoft-Windows-Kernel-Process'
$KernelProcessConfig = New-ETWProviderConfig
$KernelProcessConfig.Name = $KernelProcessName
# Only want to enable process start/stop and DLL load events
$ProcessRegex = '_PROCESS$|_IMAGE$|_THREAD$'
Get-ETWProviderKeywords -ProviderName $KernelProcessConfig.Name |
Where-Object { $_.Name -match $ProcessRegex } |
ForEach-Object { $KernelProcessConfig.Keywords += $_ }
$ProviderConfigs += $KernelProcessConfig
# Build config for PowerShell events
$PowerShellName = "Microsoft-Windows-PowerShell"
$PowerShellConfig = New-ETWProviderConfig
$PowerShellConfig.name = $PowerShellName
$ProviderConfigs += $PowerShellConfig
# Build config for network events
$KernelNetworkName = 'Microsoft-Windows-Kernel-Network'
$KernelNetworkConfig = New-ETWProviderConfig
$KernelNetworkConfig.Name = $KernelNetworkName
# List of event IDs to capture
$IDs = @( 12 ) # IPv4 connection attempted
$NetOptions = New-ETWProviderOption
$IDs | ForEach-Object {
$NetOptions.EventIDsToEnable.Add( $_ )
}
$KernelNetworkConfig.Options = $NetOptions
$ProviderConfigs += $KernelNetworkConfig
# Build config for file events
$KernelFileName = 'Microsoft-Windows-Kernel-File'
$KernelFileConfig = New-ETWProviderConfig
$KernelFileConfig.Name = $KernelFileName
# Only capturing file create, write, and delete events
If ( $EnableVerbose ) {
$FileRegex = "CREATE|WRITE|DELETE"
}
else { $FileRegex = "CREATE|DELETE" }
Get-ETWProviderKeywords -ProviderName $KernelFileConfig.Name |
Where-Object { $_.Name -match $FileRegex } |
ForEach-Object { $KernelFileConfig.Keywords += $_ }
$ProviderConfigs += $KernelFileConfig
# Build config for DNS capture
$DNSClientName = 'Microsoft-Windows-DNS-Client'
$DNSConfig = New-ETWProviderConfig
$DNSConfig.Name = $DNSClientName
# List of event IDs to capture
$IDs = @(3000, 3008)
$DNSOptions = New-ETWProviderOption
$IDs | ForEach-Object {
$DNSOptions.EventIDsToEnable.Add( $_ )
}
$DNSConfig.Options = $DNSOptions
# End DNS capture config
$ProviderConfigs += $DNSConfig
# Start ETW Session
try
{
Write-Log "Starting ETW forensic collection." -Severity 'INFO'
Write-Log "Output will be written to: $OutputFile" -Severity 'INFO'
Start-ETWSession -SessionName $SessionName -OutputFile $OutputFile -ProviderConfig $ProviderConfigs
} catch {
throw "Failed to start ETW forensic collection"
}
If (-not $DisableKernelProvider) {
try {
$KerProviderFName = [IO.Path]::GetFileNameWithoutExtension($OutputFile) + "_kernelsession" `
+ [IO.Path]::GetExtension($OutputFile)
$KernelFullPath = Join-Path (Split-Path $OutputFile) $KerProviderFName
Write-Log "Writing kernel output to: $KernelFullPath" -Severity 'INFO'
Start-ETWKernelSession -OutputFile $KernelFullPath
} catch {
throw "Failed to start ETW kernel session"
}
}
} # Start-ETWForensicCollection
@@ -0,0 +1,62 @@
ETW Kernel Session Event messages are not properly parsed by Get-WinEvent becuase the cmdlet does not have the correct xml format file
Therefore we have to parse relevant event properties using PowerShell structure parsing.
The first task is taking Get-WinEvent output and filtering it down to only return events with properties that contain the process command line
Here is an example event xml
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="" Guid="{3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c}" />
<EventID>0</EventID>
<Version>4</Version>
<Level>0</Level>
<Task>0</Task>
<Opcode>2</Opcode>
<Keywords>0x0</Keywords>
<TimeCreated SystemTime="2017-08-11T19:58:42.695093000Z" />
<EventRecordID>2505</EventRecordID>
<Correlation />
<Execution ProcessID="11068" ThreadID="4056" ProcessorID="6" KernelTime="0" UserTime="0" />
<Channel></Channel>
<Computer>office-pc</Computer>
<Security />
</System>
<ProcessingErrorData>
<ErrorCode>15003</ErrorCode>
<DataItemName></DataItemName>
<EventPayload>80403A9D859FFFFF3C2B0000700300000100000001000000001049C10000000009000000A03CAC4007B0FFFF00000000000000000105000000000005150000007E92A50F78D99D9072051408E903000048785473722E65786500220043003A005C00500072006F006700720061006D002000460069006C00650073005C00570069006E0064006F007700730041007000700073005C006D006900630072006F0073006F00660074002E00770069006E0064006F007700730063006F006D006D0075006E00690063006100740069006F006E00730061007000700073005F00310037002E0038003400300030002E00340030003700340035002E0030005F007800360034005F005F003800770065006B007900620033006400380062006200770065005C00480078005400730072002E00650078006500220020002D005300650072007600650072004E0061006D0065003A00480078002E004900500043002E0053006500720076006500720000006D006900630072006F0073006F00660074002E00770069006E0064006F007700730063006F006D006D0075006E00690063006100740069006F006E00730061007000700073005F00310037002E0038003400300030002E00340030003700340035002E0030005F007800360034005F005F003800770065006B007900620033006400380062006200770065000000700070006C006500610065003300380061006600320065003000300037006600340033003500380061003800300039006100630039003900610036003400610036003700630031000000</EventPayload>
</ProcessingErrorData>
</Event>
We are interested in the event payload. I've found the relevent messages always begin with "80".
This filter seems to work: Get-WinEvent -Path <path to file> -Oldest | ? { ([xml]$_.toxml()).Event.ChildNodes.EventPayload -match "^80" }
Next we need to grab only the EventPayload xml node: $a | % { (([xml]$_.toxml()).Event.ChildNodes.EventPayload)[1] }
Convert string to hex string: -split '(..)' | ? {$_} | % {[char][convert]::ToUInt32($_,16)}
Once we have the relevant events we need to parse out two properties: Process ID and Process Command Line
Process ID is stored as a UInt32 at offset 0x08
[bitconverter]::toint16($u[8..12], 0)
ProcessCommandLine is a bit trickier. The ImageName is the first variable string the moves immediately to the commandline in the message and always starts at offset 0x40 and should be read until a double null byte (0x00 0x00) terminates the string
Start = 64
#end = $array.length
Length = 0
$NullByte = $false
For Each
$u[$start..$u.length] |
ForEach-Object {
If ($NullByte -and ([uint16]$_ -eq 0)) {
break
}
Elseif
}
@@ -0,0 +1,92 @@
function Get-KernelProcPath
{
param($HexString)
# Struct immediately preceeding image name and path is a variable length array
# Count identifies the numer of items in array
$Count = [convert]::ToUInt32($HexString[53], 16)
# Fixed length per item
$Length = 4
# Start of image name and command line message
$Start = ($Count * $Length) + 60
$Length = 0
$NullByte = $false
$InString = $true
If (-not ($Start -gt $HexString.length) ){
Do
{
$HexString[$start..$HexString.length] |
ForEach-Object {
$char = [char][convert]::ToUInt32($_, 16)
If ($NullByte -eq $true -and ($char -eq 0)) {
$InString = $false
}
elseif ($char -eq 0) {
$length = $length + 1
$NullByte = $true
}
else {
$length = $length + 1
$NullByte = $false
}
}
} While ($InString -and (($Start + $Length) -lt $HexString.length))
}
$CommandBytes = -join($HexString[$Start..($Start + $Length)] |
ForEach-Object { [char][convert]::ToUInt32($_, 16) } |
Where-Object { $_ } )
# Remove image name
$FullCommand = ($CommandBytes -split '\.exe')[1..7]
# Bring everything back together
$FullCommand -join '.exe'
}
function Get-FullPathFromCommandLine
{
param($CommandLine)
If ($CommandLine[0] -eq '"' ) {
( $CommandLine -split '"' )[1]
}
else {
( $CommandLine -split ' ' )[0]
}
}
function KernelSessionParser
{
param($EventPayload)
If ($EventPayload) { Write-Log $EventPayload -Severity "VERBOSE" }
# Convert string to byte array
$EventPayload = $EventPayload |
ForEach-Object { $_ -split '(..)' } |
Where-Object { $_ }
try {
$ProcID = [convert]::ToUInt32(-join($EventPayload[11..8]), 16)
} catch {$ProcID = $null}
try {
$CommandLine = Get-KernelProcPath -HexString $EventPayload
} catch {$CommandLine = $null}
If ( $ProcID -and $CommandLine ) {
# Grab image name for comparison to events dictionary
try {
$ImageName = [IO.Path]::GetFileNameWithoutExtension( (Get-FullPathFromCommandLine -CommandLine $CommandLine) )
} catch { $ImageName = $null }
$Events.Values |
# Check if process id and process full path match entry in events output
Where-Object { $_.ImageName -and $_.ProcessId -eq $ProcID -and [IO.Path]::GetFileNameWithoutExtension($_.ImageName).ToLower() -eq $ImageName.ToLower() } |
# Confirm property does not already exist
ForEach-Object {
If ( ($_.PSObject.Properties.Name -match 'CommandLine').Count -lt 1 ) {
$_ | Add-Member -NotePropertyName "CommandLine" -NotePropertyValue $CommandLine
}
}
}
}
@@ -0,0 +1,684 @@
PS> Get-ETWProvider | Where-Object { $_.ProviderName -eq 'Microsoft-Windows-DNS-Client'} | % { $_.Events } | `
select id,description | ft -Wrap -Property Id,Description | out-string | `
Set-Content .\Providers\MS-Windows-DNS-Client\DNS-IDs-Descriptions.txt
Id Description
-- -----------
1000 There are currently no IPv4 DNS servers configured for any interface on this host. Please configure DNS server
settings, or renew your dynamic IP settings.
1001 Interface: %1 Total DNS Server Count: %2 Index: %3 Address: %6 (%4)
1002 The DNS server being queried for interface %1 has changed to %3
1003 The following DNS server(s) were successfully validated as active servers that can service this client. %2
1005 The client was unable to validate the following as active DNS server(s) that can service this client. The
server(s) may be temporarily unavailable, or may be incorrectly configured. %2
1007 The primary DNS suffix for this machine is missing. In the absence of a primary DNS suffix, short unqualified
names may not resolve through DNS
1008 The primary DNS suffix for this machine is missing. In the absence of a primary DNS suffix, short unqualified
names may not resolve through DNS
1009 The primary DNS suffix for this machine (%1) does not match the Active Directory domain (%2) that it is
currently joined to.
1010 The primary DNS suffix for this machine (%1) does not match the Active Directory domain (%2) that it is
currently joined to.
1011 There was an error while attempting to read the local hosts file.
1012 There was an error while attempting to read the local hosts file.
1013 Name resolution for the name %1 timed out after none of the configured DNS servers responded.
1014 Name resolution for the name %1 timed out after none of the configured DNS servers responded.
1015 Name resolution for the name %1 timed out after the DNS server %3 did not respond.
1016 A name not found error was returned for the name %1. Check to ensure that the name is correct. The response was
sent by the server at %3.
1017 The DNS server's response to a query for name %1 indicates that no records of the type queried are available,
but could indicate that other records for the same name are present.
1018 The response for the query %1 was a Link Local IP address %3. The response was sent by the server at %5.
1019 There are currently no IPv6 DNS servers configured for any interface on this host. Please configure DNS server
settings, or renew your dynamic IP settings.
1020 Read DNS Name Resolution Policy Table: Key Name %1: DNSSEC Settings: DnsSecValidationRequired %2,
DnsQueryOverIPSec %3, DnsEncryption %4 Direct Access Settings: DirectAccessServerList %5, EnableRemoteIPSEC %6
RemoteEncryption %7 ProxyType %8 ProxyName %9
1021 Matched Effective policy for query name %1: Key Name %2: DnsSecValidationRequired %3, DnsQueryOverIPSec %4,
DnsEncryption %5 DirectAccessServerList %6, ProxyType %7 ProxyName %8
1022 Name resolution for the name, %1, will not fall back to LLMNR or NetBIOS
1023 Name resolution policy table has been corrupted. DNS resolution will fail until it is fixed. Contact your
network administrator. For more information: read policy table for rule %1 failed with error %2
1024 Transaction ID of the response for query %1 from server %3 did not match
1025 The DNS server IP %3 of the response for query %1 is not configured on the client
1026 The question (%2) in the response from server %4 does not match the original question %1
1027 DNS Name resolution for the name, %1, failed because the client was unable to contact DNS servers. At least one
of the interfaces is not in a private network and name resolution will not fall back to LLMNR or NetBIOS
1028 Matched effective policy for query name %1: Key Name %2: DnsSecValidationRequired %3, DnsQueryOverIPSec %4,
DnsEncryption %5 DirectAccessServerList %6, ProxyType %7 ProxyName %8 GenericServerList %9 IdnConfig %10
3000 DNS Query is initiated for the name %1 and for the type %2 with query options %3
3001 DNS Query operation is completed with result %1
3002 DNS Cache lookup is initiated for the name %1 and for the type %2 with query options %3
3003 DNS Cache lookup operation for the name %1 and for the type %2 is completed with result %3
3004 DNS FQDN Query is initiated for the name %1 and for the type %2 with query options %3
3005 DNS FQDN Query operation for the name %1 and for the type %2 is completed with result %3
3006 DNS query is called for the name %1, type %2, query options %3, Server List %4, isNetwork query %5, network
index %6, interface index %7, is asynchronous query %8
3007 DnsQueryEx for the name %1 is pending
3008 DNS query is completed for the name %1, type %2, query options %3 with status %4 Results %5
3009 Network query initiated for the name %1 (is parallel query %2) on network index %3 with interface count %4 with
first interface name %5, local addresses %6 and Dns Servers %7
3010 DNS Query sent to DNS Server %3 for name %1 and type %2
3011 Received response from DNS Server %3 for name %1 and type %2 with response status %4
3012 NETBIOS query is initiated for name %1 on network index %2 with inteface count %3 with first interface name %4
and local addresses %5
3013 NETBIOS query is completed for name %1 with status %2 and results %3
3014 NETBIOS query for the name %1 is pending
3015 DnsQueryEx is canceled for the name %1
3016 Cache lookup called for name %1, type %2, options %3 and interface index %4
3018 Cache lookup for name %1, type %2 and option %3 returned %4 with results %5
3019 Query wire called for name %1, type %2, interface index %3 and network index %4
3020 Query response for name %1, type %2, interface index %3 and network index %4 returned %5 with results %6
8001 Unable to start DNS Client service. Could not start the Remote Procedure Call (RPC) interface for this service.
To correct the problem, you may restart the RPC and DNS Client services. To do so, use the following commands at
a command prompt: (1) type 'net start rpc' to start the RPC service, and (2) type 'net start dnscache' to start
the DNS Client service. See event details for specific error code information.
8002 Unable to start DNS Client service because the system failed to allocate memory and may be out of available
memory. Try closing any applications not in use or reboot the computer. See event details for specific error
code information.
8003 The system failed to register network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS Server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The cause of this DNS registration failure was because the DNS update request timed out after being sent to the
specified DNS Server. This is probably because the authoritative DNS server for the name being updated is not
running.
You can manually retry registration of the network adapter and its settings by typing 'ipconfig /registerdns' at
the command prompt. If problems still persist, contact your network systems administrator to verify network
conditions.
8004 The system failed to register network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The cause of this DNS registration failure was because of DNS server failure. This may be due to a zone transfer
that has locked the DNS server for the applicable zone that your computer needs to register itself with.
(The applicable zone should typically correspond to the Adapter-specific Domain Suffix that was indicated
above.) You can manually retry registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your network systems administrator to
verify network conditions.
8005 The system failed to register network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason it could not register was because either: (a) the DNS server does not support the DNS dynamic update
protocol, or (b) the primary zone authoritative for the registering names does not currently accept dynamic
updates.
To add or register a DNS host (A or AAAA) resource record using the specific DNS name for this adapter, contact
your DNS server or network systems administrator.
8006 The system failed to register network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason it could not register was because the DNS server refused the dynamic update request. This could
happen for the following reasons: (a) current DNS update policies do not allow this computer to update the DNS
domain name configured for this adapter, or (b) the authoritative DNS server for this DNS domain name does not
support the DNS dynamic update protocol.
To register a DNS host (A or AAAA) resource record using the specific DNS domain name for this adapter, contact
your DNS server or network systems administrator.
8007 The system failed to register network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The system could not register the DNS update request because of a security related problem. This could happen
for the following reasons: (a) the DNS domain name that your computer is trying to register could not be updated
because your computer does not have the right permissions, or (b) there might have been a problem negotiating
valid credentials with the DNS server to update.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator. See event details for specific error code information.
8008 The system failed to register network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the DNS update request could not be completed was because of a system problem. You can manually retry
DNS registration of the network adapter and its settings by typing 'ipconfig /registerdns' at the command
prompt. If problems still persist, contact your DNS server or network systems administrator. See event details
for specific error code information.
8009 The system failed to register pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The reason that the system could not register these RRs was because the update request that was sent to the
specified DNS server timed out. This is probably because the authoritative DNS server for the name being
registered is not running.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator. See event details for specific error code information.
8010 The system failed to register pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The cause was DNS server failure. This may be because the reverse lookup zone is busy or missing on the DNS
server that your computer needs to update. In most cases, this is a minor problem because it does not affect
normal (forward) name resolution.
If reverse (address-to-name) resolution is required for your computer, you can manually retry DNS registration
of the network adapter and its settings by typing 'ipconfig /registerdns' at the command prompt. If problems
still persist, contact your DNS server or network systems administrator. See event details for specific error
code information.
8011 The system failed to register pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The reason that the system could not register these RRs was because (a) either the DNS server does not support
the DNS dynamic update protocol, or (b) the authoritative zone where these records are to be registered does not
allow dynamic updates.
To register DNS pointer (PTR) resource records using the specific DNS domain name and IP addresses for this
adapter, contact your DNS server or network systems administrator.
8012 The system failed to register pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The reason that the system could not register these RRs was because the DNS server refused the update request.
The cause of this could be (a) your computer is not allowed to update the adapter-specified DNS domain name, or
(b) because the DNS server authoritative for the specified name does not support the DNS dynamic update protocol.
To register the DNS pointer (PTR) resource records using the specific DNS domain name and IP addresses for this
adapter, contact your DNS server or network systems administrator.
8013 The system failed to register pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The reason that the system could not register these RRs was because of a security related problem. The cause of
this could be (a) your computer does not have permissions to register and update the specific DNS domain name
set for this adapter, or (b) there might have been a problem negotiating valid credentials with the DNS server
during the processing of the update request.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator.
8014 The system failed to register pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The reason the system could not register these RRs during the update request was because of a system problem.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator. See event details for specific error code information.
8015 The system failed to register host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the system could not register these RRs was because the update request it sent to the DNS server
timed out. The most likely cause of this is that the DNS server authoritative for the name it was attempting to
register or update is not running at this time.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator.
8016 The system failed to register host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the system could not register these RRs was because the DNS server failed the update request. The
most likely cause of this is that the authoritative DNS server required to process this update request has a
lock in place on the zone, probably because a zone transfer is in progress.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator.
8017 The system failed to register host (A or AAAA) resource records for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
Either the DNS server does not support the DNS dynamic update protocol or the authoritative zone for the
specified DNS domain name does not accept dynamic updates.
To register the DNS host (A or AAAA) resource records using the specific DNS domain name and IP addresses for
this adapter, contact your DNS server or network systems administrator.
8018 The system failed to register host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the system could not register these RRs was because the DNS server contacted refused the update
request. The reasons for this might be (a) you are not allowed to update the specified DNS domain name, or (b)
because the DNS server authoritative for this name does not support the DNS dynamic update protocol.
To register the DNS host (A or AAAA) resource records using the specific DNS domain name and IP addresses for
this adapter, contact your DNS server or network systems administrator.
8019 The system failed to register host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the system could not register these RRs was because of a security related problem. The cause of this
could be (a) your computer does not have permissions to register and update the specific DNS domain name set for
this adapter, or (b) there might have been a problem negotiating valid credentials with the DNS server during
the processing of the update request.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator. See event details for specific error code information.
8020 The system failed to register host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the system could not register these RRs during the update request was because of a system problem.
You can manually retry DNS registration of the network adapter and its settings by typing 'ipconfig
/registerdns' at the command prompt. If problems still persist, contact your DNS server or network systems
administrator. See event details for specific error code information.
8021 The system failed to update and remove registration for the network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason for this failure is because the DNS server it sent the update request to timed out. The most likely
cause of this failure is that the DNS server authoritative for the zone where the registration was originally
made is either not running or unreachable through the network at this time.
8022 The system failed to update and remove registration for the network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason for this failure is because the DNS server it sent the update to failed the update request. A
possible cause of this failure is that the DNS server required to process this update request has a lock in
place on the zone, probably because a zone transfer is in progress.
8023 The system failed to update and remove registration for the network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason for this failure is because the DNS server sent the update either (a) does not support the DNS
dynamic update protocol, or (b) the authoritative zone for the specified DNS domain name does not currently
accept DNS dynamic updates.
8024 The system failed to update and remove registration for the network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the system could not perform the update request was the DNS server contacted refused update request.
The cause of this is (a) this computer is not allowed to update the specified DNS domain name, or (b) because
the DNS server authoritative for the zone that requires updating does not support the DNS dynamic update
protocol.
8025 The system failed to update and remove registration for the network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the system could not perform the update request was because of a security related problem. The cause
of this could be (a) your computer does not have permissions to register and update the specific DNS domain name
set for this adapter, or (b) there might have been a problem negotiating valid credentials with the DNS server
during the processing of the update request.
See event details for specific error code information.
8026 The system failed to update and remove the DNS registration for the network adapter with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The system could not update to remove this DNS registration because of a system problem. See event details for
specific error code information.
8027 The system failed to update and remove pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The system could not remove these PTR RRs because the update request timed out while awaiting a response from
the DNS server. This is probably because the DNS server authoritative for the zone that requires update is not
running.
8028 The system failed to update and remove pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address : %6
The system could not remove these PTR RRs because the DNS server failed the update request. A possible cause is
that a zone transfer is in progress, causing a lock for the zone at the DNS server authorized to perform the
updates for these RRs.
8029 The system failed to update and remove pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The system could not remove these PTR RRs because either the DNS server does not support the DNS dynamic update
protocol or the authoritative zone that contains these RRs does not accept dynamic updates.
8030 The system failed to update and remove pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The system could not remove these PTR RRs because the DNS server refused the update request. The cause of this
might be (a) this computer is not allowed to update the specified DNS domain name specified by these settings,
or (b) because the DNS server authorized to perform updates for the zone that contains these RRs does not
support the DNS dynamic update protocol.
8031 The system failed to update and remove pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The system could not remove these PTR RRs because of a security related problem. The cause of this could be that
(a) your computer does not have permissions to remove and update the specific DNS domain name or IP addresses
configured for this adapter, or (b) there might have been a problem negotiating valid credentials with the DNS
server during the processing of the update request. See event details for specific error code information.
8032 The system failed to update and remove pointer (PTR) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Adapter-specific Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address :
%6
The system could not remove these PTR RRs because because of a system problem. See event details for specific
error code information.
8033 The system failed to update and remove host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The system could not remove these host (A or AAAA) RRs because the update request timed out while
awaiting a response from the DNS server. This is probably because the DNS server authoritative for the zone
where these RRs need to be updated is either not currently running or reachable on the network.
8034 The system failed to update and remove host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The system could not remove these host (A or AAAA) RRs because the DNS server failed the update request. A
possible cause is that a zone transfer is in progress, causing a lock for the zone at the DNS server authorized
to perform the updates for these RRs.
8035 The system failed to update and remove host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason for this failure is because the DNS server sent the update either (a) does not support the DNS
dynamic update protocol, or (b) the authoritative zone for the DNS domain name specified in these host (A or
AAAA) RRs does not currently accept DNS dynamic updates.
8036 The system failed to update and remove host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The request to remove these records failed because the DNS server refused the update request. The cause of this
might be that either (a) this computer is not allowed to update the DNS domain name specified by these settings,
or (b) because the DNS server authorized to perform updates for the zone that contains these RRs does not
support the DNS dynamic update protocol.
8037 The system failed to update and remove host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason for this failure was because of a security related problem. The cause of this could be that (a) your
computer does not have permissions to remove and update the specific DNS domain name or IP addresses configured
for this adapter, or (b) there might have been a problem negotiating valid credentials with the DNS server
during the processing of the update request. See event details for specific error code information.
8038 The system failed to update and remove host (A or AAAA) resource records (RRs) for network adapter
with settings:
Adapter Name : %1
Host Name : %2
Primary Domain Suffix : %3
DNS server list :
%4
Sent update to server : %5
IP Address(es) :
%6
The reason the update request failed was because of a system problem. See event details for specific error code
information.
60004 Error: %1 Location: %2 Context: %3
60005 Warning: %1 Location: %2 Context: %3
60006 Transitioned to State: %1 Context: %2
60007 Updated Context: %1 Update Reason: %2
60008 Name resolution policy table has been corrupted. DNS resolution will fail until it is fixed. Contact your
network administrator. For more information: read policy table for rule %1 failed with error %2
60101 SourceAddress: %1 SourcePort: %2 DestinationAddress: %3 DestinationPort: %4 Protocol: %5 ReferenceContext: %6
60102 SourceAddress: %1 SourcePort: %2 DestinationAddress: %3 DestinationPort: %4 Protocol: %5 ReferenceContext: %6
60103 Interface Guid: %1 IfIndex: %2 Interface Luid: %3 ReferenceContext: %4
@@ -0,0 +1,65 @@
DNS Notes:
Get-ETWProvider | ? { $_.Name -match 'DNS' }
Only Microsoft-Windows-DNS-Client has keywords
Get Keywords:
Get-ETWProvider | ? { $_.Name -match 'Microsoft-Windows-DNS-Client' } | % { $_.Keywords }
This returns:
Name Value DisplayName
---- ----- -----------
ut:GenericEvent 256
ut:DnsAutoLogKeyword 268435456
ut:SendPath 4294967296
ut:ReceivePath 8589934592
ut:L3ConnectPath 17179869184
ut:L2ConnectPath 34359738368
ut:ClosePath 68719476736
ut:Authentication 137438953472
ut:Configuration 274877906944
ut:Global 549755813888
ut:Dropped 1099511627776
ut:PiiPresent 2199023255552
ut:Packet 4398046511104
ut:Address 8796093022208
ut:StdTemplateHint 17592186044416
ut:PolicyTable 35184372088832
ut:PerfCheckPoints 70368744177664
ut:RegistrationEvent 140737488355328
Creating an ETL file using this provider:
Start-ETWSession -ProviderName Microsoft-Windows-DNS-Client -OutputFile $env:temp\dnsout.etl -SessionName DNS-01
Above starts the session without any keywords. There is a Keywords argument, but I have not tried it yet.
Stopping the session:
Stop-ETWSession -SessionName DNS-01
Get-WinEvent may be used to read the ETL file (note: -Oldest is required):
Get-WinEvent -Path $env:temp\dnsout.etl -Oldest
There are likey other ways to read this file, I'm still investigating, but the above works.
ToDo: How can we create a session in memory, without writing to disk and have a "listener" that monitors for events we'e interested in?
This can be used to enumerate the methods on the given assembly, in this case Microsoft.Diagnostics.Tracing.TraceEvent:
try {
import-module X:\PSalander\EventTrace\lib\Microsoft.Diagnostics.Tracing.TraceEvent.dll
[Microsoft.Diagnostics.Tracing.TraceEvent].GetMethods() | Select-Object -ExpandProperty Name |
Foreach-Object {
[Microsoft.Diagnostics.Tracing.TraceEvent].GetMethod($_) | Select Name
}
} catch {
$_
} Finally {
Remove-Module Microsoft.Diagnostics.Tracing.TraceEvent
}
This does throw an exception on EventData because there are two EventData methods, resulting in an ambiguous call.
PIDs in the ETL file for dns lookups correspond to svchost processes.
Ids Purpose
1001
@@ -0,0 +1,25 @@
1002 The DNS server being queried for interface %1 has changed to %3
1026 The question (%2) in the response from server %4 does not match the original question %1
3000 DNS Query is initiated for the name %1 and for the type %2 with query options %3
3001 DNS Query operation is completed with result %1
3002 DNS Cache lookup is initiated for the name %1 and for the type %2 with query options %3
3003 DNS Cache lookup operation for the name %1 and for the type %2 is completed with result %3
3004 DNS FQDN Query is initiated for the name %1 and for the type %2 with query options %3
3005 DNS FQDN Query operation for the name %1 and for the type %2 is completed with result %3
3006 DNS query is called for the name %1, type %2, query options %3, Server List %4, isNetwork query %5, network
index %6, interface index %7, is asynchronous query %8
3007 DnsQueryEx for the name %1 is pending
3008 DNS query is completed for the name %1, type %2, query options %3 with status %4 Results %5
3009 Network query initiated for the name %1 (is parallel query %2) on network index %3 with interface count %4 with
first interface name %5, local addresses %6 and Dns Servers %7
3010 DNS Query sent to DNS Server %3 for name %1 and type %2
3011 Received response from DNS Server %3 for name %1 and type %2 with response status %4
3012 NETBIOS query is initiated for name %1 on network index %2 with inteface count %3 with first interface name %4
and local addresses %5
3013 NETBIOS query is completed for name %1 with status %2 and results %3
3014 NETBIOS query for the name %1 is pending
3015 DnsQueryEx is canceled for the name %1
3016 Cache lookup called for name %1, type %2, options %3 and interface index %4
3018 Cache lookup for name %1, type %2 and option %3 returned %4 with results %5
3019 Query wire called for name %1, type %2, interface index %3 and network index %4
3020 Query response for name %1, type %2, interface index %3 and network index %4 returned %5 with results %6
@@ -0,0 +1,245 @@
function Convert-DNSEventProperties {
<#
.SYNOPSIS
Converts MS-Windows-DNS-Client events properties first-order object noteproperties.
.DESCRIPTION
Convert-DNSEventProperties takes an array of MS-Windows-DNS-Client events and converts the
event properties to first-order noteproperties on the objects it returns
.PARAMETER Events
An array of MS-Windows-DNS-Client events
.EXAMPLE
$Events | Convert-DNSEventProperties | Format-List * -Force
TimeCreated : 5/26/2017 10:56:53 AM
Id : 3008
Message : DNS query is completed for the name v10.vortex-win.data.microsoft.com, type 1, query options 1073766400 with status 87 Results
Name : v10.vortex-win.data.microsoft.com
Type : 1
Options : 1073766400
Status : 87
Result :
TimeCreated : 5/26/2017 10:56:53 AM
Id : 3009
Message : Network query initiated for the name v10.vortex-win.data.microsoft.com (is parallel query 1) on network index 0 with
interface count 1 with first interface name Ethernet0, local addresses 192.168.123.53; and Dns Servers 192.168.123.10;
Name : v10.vortex-win.data.microsoft.com
isParallelQry : 1
NetworkIndex : 0
InterfaceCount : 1
FirstInterfaceName : Ethernet0
LocalAddresses : 192.168.123.53;
DNSServers : 192.168.123.10;
.NOTES
General notes
#>
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Diagnostics.Eventing.Reader.EventLogRecord[]]
$Events
)
Process {
# Create Object with common properties
$Events | Foreach-Object {
$_Event = $_
$Obj = New-Object psobject
$Obj | Add-Member -NotePropertyName 'TimeCreated' -NotePropertyValue $_Event.TimeCreated
$Obj | Add-Member -NotePropertyName 'Id' -NotePropertyValue $_Event.Id
$Obj | Add-Member -NotePropertyName 'Message' -NotePropertyValue $_Event.Message
# Add properties based on Event Id
switch ( $_Event.Id ) {
1002 {
# DNS server for interface
$Obj | Add-Member -NotePropertyName 'Interface' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'DNSServer' -NotePropertyValue $_Event.Properties[2].value
break
} # DNS server for interface
1026 {
# Response question doesn't match request question
$Obj | Add-Member -NotePropertyName 'ResponseQuestion' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'DNSServer' -NotePropertyValue $_Event.Properties[3].value
$Obj | Add-Member -NotePropertyName 'RequestQuestion' -NotePropertyValue $_Event.Properties[0].value
break
} # Response question doesn't match request question
3001 {
# Query result
$Obj | Add-Member -NotePropertyName 'Result' -NotePropertyValue $_Event.Properties[0].value
break
} # Query result
3002 {
# Cache lookup for name, type, query options
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Options' -NotePropertyValue $_Event.Properties[2].value
break
} # Cache lookup for name, type, query options
3003 {
# Cache lookup for name, type completed with result
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Result' -NotePropertyValue $_Event.Properties[2].value
break
} # Cache lookup for name, type completed with result
3004 {
# FQDN query initiated for name, type, with options
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Options' -NotePropertyValue $_Event.Properties[2].value
break
} # FQDN query initiated for name, type, with options
3005 {
# FQDN query for name, type completed with result
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Result' -NotePropertyValue $_Event.Properties[2].value
break
} # FQDN query for name, type completed with result
3006 {
# Query for name, type, options, server list, isNetwork, network index, interface index, asynchronous
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Options' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'ServerList' -NotePropertyValue $_Event.Properties[3].value
$Obj | Add-Member -NotePropertyName 'isNetworkQuery' -NotePropertyValue $_Event.Properties[4].value
$Obj | Add-Member -NotePropertyName 'NetworkIndex' -NotePropertyValue $_Event.Properties[5].value
$Obj | Add-Member -NotePropertyName 'InterfaceIndex' -NotePropertyValue $_Event.Properties[6].value
$Obj | Add-Member -NotePropertyName 'Asynchronous' -NotePropertyValue $_Event.Properties[7].value
break
} # Query for name, type, options, server list, isNetwork, network index, interface index, asynchronous
3007 {
# DNSQueryEx for name is pending
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
break
} # DNSQueryEx for name is pending
3008 {
# DNS query completed for name, type, options with status, and results
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Options' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'Status' -NotePropertyValue $_Event.Properties[3].value
$Obj | Add-Member -NotePropertyName 'Result' -NotePropertyValue $_Event.Properties[4].value
break
} # DNS query completed for name, type, options with status, and results
3009 {
# Network query initiated for name, is parallel query, on network index with iface count
# with first iface name, local addresses and DNS servers
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'isParallelQry' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'NetworkIndex' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'InterfaceCount' -NotePropertyValue $_Event.Properties[3].value
$Obj | Add-Member -NotePropertyName 'FirstInterfaceName' -NotePropertyValue $_Event.Properties[4].value
$Obj | Add-Member -NotePropertyName 'LocalAddresses' -NotePropertyValue $_Event.Properties[5].value
$Obj | Add-Member -NotePropertyName 'DNSServers' -NotePropertyValue $_Event.Properties[6].value
break
} # Network query initiated for name, is parallel query, on network index with iface count
# with first iface name, local addresses and DNS servers
3010 {
# Query sent to server for name and type
$Obj | Add-Member -NotePropertyName 'Server' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
break
} # Query sent to server for name and type
3011 {
# Received response from server for name and type with response status
$Obj | Add-Member -NotePropertyName 'Server' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Status' -NotePropertyValue $_Event.Properties[3].value
break
} # Received response from server for name and type with response status
3012 {
# NETBIOS query initiated for name, network index, interface count, first interface name and
# local addresses
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'NetworkIndex' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'InterfaceCount' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'FirstInterfaceName' -NotePropertyValue $_Event.Properties[3].value
$Obj | Add-Member -NotePropertyName 'LocalAddresses' -NotePropertyValue $_Event.Properties[4].value
break
} # NETBIOS query initiated for name, network index, interface count, first interface name and
# local addresses
3013 {
# NETBIOS query completed for name with status and result
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Status' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Results' -NotePropertyValue $_Event.Properties[2].value
break
} # NETBIOS query completed for name with status and result
3014 {
# NETBIOS query for name is pending
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
break
} # NETBIOS query for name is pending
3015 {
# DNSQueryEx canceled for name
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
break
} # DNSQueryEx canceled for name
3016 {
# Cache lookup called for name, type, options and interface index
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Options' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'InterfaceIndex' -NotePropertyValue $_Event.Properties[3].value
break
} # Cache lookup called for name, type, options and interface index
3018 {
# Cache lookup for name, type and option returned value with results
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'Option' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'Value' -NotePropertyValue $_Event.Properties[3].value
$Obj | Add-Member -NotePropertyName 'Results' -NotePropertyValue $_Event.Properties[4].value
break
} # Cache lookup for name, type and option returned value with results
3019 {
# Query wire called for name, type, interface index and network index
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'InterfaceIndex' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'NetworkIndex' -NotePropertyValue $_Event.Properties[3].value
break
} # Query wire called for name, type, interface index and network index
3020 {
# Response for name, type, interface index and network index returned value with results
$Obj | Add-Member -NotePropertyName 'Name' -NotePropertyValue $_Event.Properties[0].value
$Obj | Add-Member -NotePropertyName 'Type' -NotePropertyValue $_Event.Properties[1].value
$Obj | Add-Member -NotePropertyName 'InterfaceIndex' -NotePropertyValue $_Event.Properties[2].value
$Obj | Add-Member -NotePropertyName 'NetworkIndex' -NotePropertyValue $_Event.Properties[3].value
$Obj | Add-Member -NotePropertyName 'Value' -NotePropertyValue $_Event.Properties[4].value
$Obj | Add-Member -NotePropertyName 'Results' -NotePropertyValue $_Event.Properties[5].value
break
} # Response for name, type, interface index and network index returned value with results
} # End switch
$Obj
}
} # End Convert-DNSEventProperties
}
@@ -0,0 +1,115 @@
function Win10DNSResponse
{
param($Event)
$IPRegex = '([0-9]{1,3}\.){3}[0-9]{1,3}'
# Only type 28 responses contain domain to IP mappings
If ( $Event.Properties[1].value -eq 28 -and $Event.Properties[4].value -match $IPRegex ) {
$ProcID = [int32]$Event.ProcessID
$IPAddress = $matches[0]
$NewDNSObject = New-Object -TypeName psobject
$NewDNSObject | Add-Member -NotePropertyName 'DomainName' -NotePropertyValue $Event.Properties[0].value
$NewDNSObject | Add-Member -NotePropertyName 'IPv4Address' -NotePropertyValue $IPAddress
$NewDNSObject | Add-Member -NotePropertyName 'ThreadID' -NotePropertyValue $Event.ThreadID
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'DomainLookups').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'DomainLookups' -NotePropertyValue @()
}
$Events[ $ProcID ].DomainLookups += $NewDNSObject
$Events[$ProcID].Threads |
Where-Object { $_.threadID -eq $Event.ThreadID } |
ForEach-Object {
If ( ($_.PSObject.Properties.Name -match 'DomainLookups').Count -lt 1 ) {
$_ | Add-Member -NotePropertyName 'DomainLookups' -NotePropertyValue @()
}
$_.DomainLookups += $NewDNSObject
}
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'DomainLookups' -NotePropertyValue @()
$NewProcessObject.DomainLookups += $NewDNSObject
$Events.Add( $ProcID, $NewProcessObject )
}
}
}
function Win2012DNSResponse
{
param($Event)
$ProcID = [int32]$Event.ProcessID
$NewDNSObject = New-Object -TypeName psobject
$NewDNSObject | Add-Member -NotePropertyName 'DomainName' -NotePropertyValue $Event.Properties[0].value
If ( $Events.ContainsKey( [int32]$ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'DomainLookups').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'DomainLookups' -NotePropertyValue @()
}
$Events[ $ProcID ].DomainLookups += $NewDNSObject
$Events[$ProcID].Threads |
Where-Object { $_.threadID -eq $Event.ThreadID } |
ForEach-Object {
If ( ($_.PSObject.Properties.Name -match 'DomainLookups').Count -lt 1 ) {
$_ | Add-Member -NotePropertyName 'DomainLookups' -NotePropertyValue @()
}
$_.DomainLookups += $NewDNSObject
}
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'DomainLookups' -NotePropertyValue @()
$NewProcessObject.DomainLookups += $NewDNSObject
$Events.Add( $ProcID, $NewProcessObject )
}
}
function DNSClientParser
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Diagnostics.Eventing.Reader.EventRecord]
$Event
)
$script:KernProcEvents = @{
3008 = 'Win10DNSResponse'
3000 = 'Win2012DNSResponse'
}
If ( $script:KernProcEvents.ContainsKey($Event.Id) ) {
&$script:KernProcEvents[$_.Id] $Event
}
}
@@ -0,0 +1,6 @@
+ Identify interesting DNS Events
+ 25 events see Monitored-DNS-Events
- Determine best way to represent them
- As objects? Each event it's own object? Get-WinEvent already returns
objects, we just need code that can pull the properties we want from those
objects.
@@ -0,0 +1,86 @@
Get-ETWProvider | ? {$_.Name -match "kernel-file"} | % {$_.Events} | select id, keywords
# No descriptions for this provider
Id Keywords
-- --------
10 {, KERNEL_FILE_KEYWORD_FILENAME}
11 {, KERNEL_FILE_KEYWORD_FILENAME}
12 {, KERNEL_FILE_KEYWORD_CREATE, KERNEL_FILE_KEYWORD_FILEIO}
12 {, KERNEL_FILE_KEYWORD_CREATE, KERNEL_FILE_KEYWORD_FILEIO}
13 {, KERNEL_FILE_KEYWORD_FILEIO}
13 {, KERNEL_FILE_KEYWORD_FILEIO}
14 {, KERNEL_FILE_KEYWORD_FILEIO}
14 {, KERNEL_FILE_KEYWORD_FILEIO}
15 {, KERNEL_FILE_KEYWORD_READ, KERNEL_FILE_KEYWORD_FILEIO}
15 {, KERNEL_FILE_KEYWORD_READ, KERNEL_FILE_KEYWORD_FILEIO}
16 {, KERNEL_FILE_KEYWORD_WRITE, KERNEL_FILE_KEYWORD_FILEIO}
16 {, KERNEL_FILE_KEYWORD_WRITE, KERNEL_FILE_KEYWORD_FILEIO}
17 {, KERNEL_FILE_KEYWORD_FILEIO}
17 {, KERNEL_FILE_KEYWORD_FILEIO}
18 {, KERNEL_FILE_KEYWORD_FILEIO}
18 {, KERNEL_FILE_KEYWORD_FILEIO}
19 {, KERNEL_FILE_KEYWORD_FILEIO}
19 {, KERNEL_FILE_KEYWORD_FILEIO}
20 {, KERNEL_FILE_KEYWORD_FILEIO}
20 {, KERNEL_FILE_KEYWORD_FILEIO}
21 {, KERNEL_FILE_KEYWORD_FILEIO}
21 {, KERNEL_FILE_KEYWORD_FILEIO}
22 {, KERNEL_FILE_KEYWORD_FILEIO}
22 {, KERNEL_FILE_KEYWORD_FILEIO}
23 {, KERNEL_FILE_KEYWORD_FILEIO}
23 {, KERNEL_FILE_KEYWORD_FILEIO}
24 {, KERNEL_FILE_KEYWORD_OP_END, KERNEL_FILE_KEYWORD_FILEIO}
25 {, KERNEL_FILE_KEYWORD_FILEIO}
25 {, KERNEL_FILE_KEYWORD_FILEIO}
26 {, KERNEL_FILE_KEYWORD_DELETE_PATH}
26 {, KERNEL_FILE_KEYWORD_DELETE_PATH}
27 {, KERNEL_FILE_KEYWORD_RENAME_SETLINK_PATH}
27 {, KERNEL_FILE_KEYWORD_RENAME_SETLINK_PATH}
28 {, KERNEL_FILE_KEYWORD_RENAME_SETLINK_PATH}
28 {, KERNEL_FILE_KEYWORD_RENAME_SETLINK_PATH}
29 {, KERNEL_FILE_KEYWORD_FILEIO}
29 {, KERNEL_FILE_KEYWORD_FILEIO}
30 {, KERNEL_FILE_KEYWORD_CREATE_NEW_FILE}
30 {, KERNEL_FILE_KEYWORD_CREATE_NEW_FILE}
Get-ETWProviderKeywords -ProviderName "Microsoft-Windows-kernel-file"
Name Description Value
---- ----------- -----
KERNEL_FILE_KEYWORD_FILENAME ... 16
KERNEL_FILE_KEYWORD_FILEIO ... 32
KERNEL_FILE_KEYWORD_OP_END ... 64
KERNEL_FILE_KEYWORD_CREATE ... 128
KERNEL_FILE_KEYWORD_READ ... 256
KERNEL_FILE_KEYWORD_WRITE ... 512
KERNEL_FILE_KEYWORD_DELETE_PATH ... 1024
KERNEL_FILE_KEYWORD_RENAME_SETLINK_PATH ... 2048
KERNEL_FILE_KEYWORD_CREATE_NEW_FILE ... 4096
Microsoft-Windows-Kernel-File/Analytic ... 9223372036854775808
PS Z:\workspaces\PSalander> $a | % {$_.id} | Group-Object
Count Name Group
----- ---- -----
19338 15 {15, 15, 15, 15...}
6831 12 {12, 12, 12, 12...}
6466 16 {16, 16, 16, 16...}
28 30 {30, 30, 30, 30...}
1 26 {26}
General Notes
Event 30 tracks new files created and each event contains the process ID and file path
Event 26 tracks file deletion Events
Event 12 seems to be general calls to CreateFile (getting handle to file )
Event 16 records file writes. References FileObject and not path
In order to match this event back to file path need to first parse/track Event 12
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'><System><Provider Name='Microsoft-Windows-Kernel-File' Guid='{edd08927-9cc4-4e65-b970-c2560fb5c289}'/><EventID>16</EventID><Version>1</Version><Level>4</Level><Task>16</Task><Opcode>0</Opcode><Keywords>0x8000000000000220</Keywords><TimeCreated SystemTime='2017-06-26T01:37:12.555489800Z'/><EventRecordID>26930</EventRecordID><Correlation/><Execution ProcessID='4560' ThreadID='4372' ProcessorID='0' KernelTime='0' UserTime='1'/><Channel></Channel><Computer>DESKTOP-US5J4M3</Computer><Security/></System><EventData><Data Name='ByteOffset'>0</Data><Data Name='Irp'>0xffffc7810221e378</Data><Data Name='FileObject'>0xffffc780ff6048f0</Data><Data Name='FileKey'>0xffff96090b10ac00</Data><Data Name='IssuingThreadId'>4372</Data><Data Name='IOSize'>9</Data><Data Name='IOFlags'>395776</Data><Data Name='ExtraFlags'>0</Data></EventData></Event>
@@ -0,0 +1,144 @@
$OpenFiles = @{}
function FileOpen
{
param($Event)
$FileID = $Event.Properties[0].value
$FilePath = $Event.Properties[6].value
if ( -Not $OpenFiles.ContainsKey( $FileID ) ){
$OpenFiles.Add( $FileID, $FilePath )
}
} # FileOpen
function FileWrite
{
param($Event)
$FileID = $Event.Properties[1].value
# Confirm file object is known
If ( $OpenFiles.ContainsKey( $FileID ) ) {
$ProcID = [int32]$Event.ProcessId
$BytesWrite = $Event.Properties[5].value
$Action = "WRITE"
$NewFileObj = New-Object -TypeName psobject
$NewFileObj | Add-Member -NotePropertyName 'FilePath' -NotePropertyValue $OpenFiles[ $FileID ]
$NewFileObj | Add-Member -NotePropertyName 'Action' -NotePropertyValue $Action
$NewFileObj | Add-Member -NotePropertyName 'BytesWritten' -NotePropertyValue $BytesWrite
$NewFileObj | Add-Member -NotePropertyName 'ThreadID' -NotePropertyValue $Event.ThreadID
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'FileIO').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'FileIO' -NotePropertyValue @()
}
$Events[$ProcID].FileIO += $NewFileObj
$Events[$ProcID].Threads |
Where-Object { $_.threadID -eq $Event.ThreadID } |
ForEach-Object {
If ( ($_.PSObject.Properties.Name -match 'FileIO').Count -lt 1 ) {
$_ | Add-Member -NotePropertyName 'FileIO' -NotePropertyValue @()
}
$_.FileIO += $NewFileObj
}
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'FileIO' -NotePropertyValue @()
$NewProcessObject.FileIO += $NewFileObj
$Events.Add( $ProcID, $NewProcessObject )
}
}
} # FileWrite
function FileCreateDelete
{
param($Event)
$ProcID = [int32]$Event.ProcessId
$FilePath = $Event.Properties[6].value
If ( $Event.Id -eq 26 ) {
$Action = "DELETE"
}
Elseif ($Event.Id -eq 30) {
$Action = "CREATE"
}
$NewFileObj = New-Object -TypeName psobject
$NewFileObj | Add-Member -NotePropertyName 'FilePath' -NotePropertyValue $FilePath
$NewFileObj | Add-Member -NotePropertyName 'Action' -NotePropertyValue $Action
$NewFileObj | Add-Member -NotePropertyName 'ThreadID' -NotePropertyValue $Event.ThreadID
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'FileIO').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'FileIO' -NotePropertyValue @()
}
$Events[$ProcID].FileIO += $NewFileObj
$Events[$ProcID].Threads |
Where-Object { $_.threadID -eq $Event.ThreadID } |
ForEach-Object {
If ( ($_.PSObject.Properties.Name -match 'FileIO').Count -lt 1 ) {
$_ | Add-Member -NotePropertyName 'FileIO' -NotePropertyValue @()
}
$_.FileIO += $NewFileObj
}
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'FileIO' -NotePropertyValue @()
$NewProcessObject.FileIO += $NewFileObj
$Events.Add( $ProcID, $NewProcessObject )
}
} # FileCreateDelete
function KernelFileParser
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Diagnostics.Eventing.Reader.EventRecord]
$Event
)
$script:KernProcEvents = @{
12 = 'FileOpen'
16 = 'FileWrite'
26 = 'FileCreateDelete'
30 = 'FileCreateDelete'
}
If ( $script:KernProcEvents.ContainsKey($Event.Id) ) {
&$script:KernProcEvents[$_.Id] $Event
}
}
@@ -0,0 +1,73 @@
Get-ETWProvider | Where-Object { $_.Name -eq 'Microsoft-Windows-Kernel-Network'} | % { $_.Events } | select id,description | ft -Wrap -Property Id,Description
Id Description
-- -----------
10 TCPv4: %2 bytes transmitted from %4:%6 to %3:%5.
11 TCPv4: %2 bytes received from %4:%6 to %3:%5.
12 TCPv4: Connection attempted between %4:%6 and %3:%5.
13 TCPv4: Connection closed between %4:%6 and %3:%5.
14 TCPv4: %2 bytes retransmitted from %4:%6 to %3:%5.
15 TCPv4: Connection established between %4:%6 and %3:%5.
16 TCPv4: Reconnect attempt between %4:%6 and %3:%5.
17 TCPv4: Connection attempt failed with error code %2.
18 TCPv4: %2 bytes copied in protocol on behalf of user for connection between %4:%6 and %3:%5.
26 TCPv6: %2 bytes transmitted from %4:%6 to %3:%5.
27 TCPv6: %2 bytes received from %4:%6 to %3:%5.
28 TCPv6: Connection attempted between %4:%6 and %3:%5.
29 TCPv6: Connection closed between %4:%6 and %3:%5.
30 TCPv6: %2 bytes retransmitted from %4:%6 to %3:%5.
31 TCPv6: Connection established between %4:%6 and %3:%5.
32 TCPv6: Reconnect attempt between %4:%6 and %3:%5.
34 TCPv6: %2 bytes copied in protocol on behalf of user for connection between %4:%6 and %3:%5.
42 UDPv4: %2 bytes transmitted from %4:%6 to %3:%5.
43 UDPv4: %2 bytes received from %4:%6 to %3:%5.
49 UDPv4: Connection attempt failed with error code %2.
58 UDPv6: %2 bytes transmitted from %4:%6 to %3:%5.
59 UDPv6: %2 bytes received from %4:%6 to %3:%5.
Get-ETWProviderKeywords -ProviderName Microsoft-Windows-Kernel-Network
Name Description Value
---- ----------- -----
KERNEL_NETWORK_KEYWORD_IPV4  16
KERNEL_NETWORK_KEYWORD_IPV6  32
Microsoft-Windows-Kernel-Network/Analytic  9223372036854775808
Event Log xml
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Microsoft-Windows-Kernel-Network" Guid="{7dd42a49-5329-4832-8dfd-43d979153a88}" />
<EventID>12</EventID>
<Version>0</Version>
<Level>4</Level>
<Task>10</Task>
<Opcode>12</Opcode>
<Keywords>0x8000000000000010</Keywords>
<TimeCreated SystemTime="2017-06-09T23:42:28.277200000Z" />
<EventRecordID>0</EventRecordID>
<Correlation />
<Execution ProcessID="4" ThreadID="300" ProcessorID="0" KernelTime="1661" UserTime="0" />
<Channel></Channel>
<Computer>office-pc</Computer>
<Security />
</System>
<EventData>
<Data Name="PID">2456</Data>
<Data Name="size">0</Data>
<Data Name="daddr">1142973847</Data>
<Data Name="saddr">3388997642</Data>
<Data Name="dport">20480</Data>
<Data Name="sport">11985</Data>
<Data Name="mss">1396</Data>
<Data Name="sackopt">1</Data>
<Data Name="tsopt">0</Data>
<Data Name="wsopt">1</Data>
<Data Name="rcvwin">65612</Data>
<Data Name="rcvwinscale">8</Data>
<Data Name="sndwinscale">9</Data>
<Data Name="seqnum">0</Data>
<Data Name="connid">0</Data>
</EventData>4
@@ -0,0 +1,112 @@
function IPv4Connection
{
param($Event)
$ProcID = [int32]$Event.Properties[0].value
$DestIP = [System.Net.IPAddress]$Event.Properties[2].value
# Ports are returned in BIG-IP system encoding
# Converts the decimal port value to the equivalent 2-byte hexadecimal value.
# Reverses the order of the two hexadecimal bytes.
# Converts the resulting 2-byte hexadecimal value to its decimal equivalent.
# https://support.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/ltm-concepts-11-5-0/10.html
$DestPort = "{0:x}" -f [int]$Event.Properties[4].value |
ForEach-Object { $_ -split '(..)' } |
Where-Object { $_ }
[array]::Reverse($DestPort)
$DestPort = [Convert]::ToInt32( -join( $DestPort ), 16 )
$NewNetworkObj = New-Object -TypeName psobject
$NewNetworkObj | Add-Member -NotePropertyName 'DestinationIP' -NotePropertyValue $DestIP.IPAddressToString
$NewNetworkObj | Add-Member -NotePropertyName 'DestinationPort' -NotePropertyValue $DestPort
$NewNetworkObj | Add-Member -NotePropertyName 'ThreadID' -NotePropertyValue $Event.ThreadID
$NewNetworkObj | Add-Member -NotePropertyName 'Count' -NotePropertyValue 1
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'NetConnections').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'NetConnections' -NotePropertyValue @()
}
# This will only add connection if an identical one (port and IP) has not already been added
$found = $Events[ $ProcID ].NetConnections |
Where-Object { $_.DestinationIP -eq $NewNetworkObj.DestinationIP -and $_.DestinationPort -eq $NewNetworkObj.DestinationPort -and $_.ThreadID -eq $NewNetworkObj.ThreadID } |
ForEach-Object { $true }
# Add netork threading object if not there
$Events[$ProcID].Threads |
Where-Object { $_.threadID -eq $Event.ThreadID } |
ForEach-Object {
If ( ($_.PSObject.Properties.Name -match 'NetConnections').Count -lt 1 ) {
$_ | Add-Member -NotePropertyName 'NetConnections' -NotePropertyValue @()
}
}
If ( -Not $found ) {
$Events[ $ProcID ].NetConnections += $NewNetworkObj
$Events[$ProcID].Threads |
Where-Object { $_.threadID -eq $Event.ThreadID } |
ForEach-Object { $_.NetConnections += $NewNetworkObj }
} else {
# If connection does exist increment count by 1
$Events[ $ProcID ].NetConnections |
Where-Object { $_.DestinationIP -eq $NewNetworkObj.DestinationIP -and $_.DestinationPort -eq $NewNetworkObj.DestinationPort -and $_.ThreadID -eq $NewNetworkObj.ThreadID } |
ForEach-Object { $_.Count = $_.Count + 1 }
# Increment thread counter by 1
$Events[$ProcID].Threads |
Where-Object { $_.threadID -eq $Event.ThreadID } |
Where-Object { ($_.NetConnections).DestinationIP -eq $NewNetworkObj.DestinationIP -and `
($_.NetConnections).DestinationPort -eq $NewNetworkObj.DestinationPort } |
ForEach-Object { $_.NetConnections } |
ForEach-Object {$_.Count = $_.Count + 1}
}
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'NetConnections' -NotePropertyValue @()
$NewProcessObject.NetConnections += $NewNetworkObj
$Events.Add( $ProcID, $NewProcessObject )
}
} # IPv4Connection
function KernelNetworkParser
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Diagnostics.Eventing.Reader.EventRecord]
$Event
)
$script:KernProcEvents = @{
12 = 'IPv4Connection'
}
If ( $script:KernProcEvents.ContainsKey($Event.Id) ) {
&$script:KernProcEvents[$_.Id] $Event
}
}
@@ -0,0 +1,81 @@
Id Description
-- -----------
1 Process %1 started at time %2 by parent %3 running in session %4 with name %5.
2 Process %1 (which started at time %2) stopped at time %3 with exit code %4.
3 Thread %2 (in Process %1) started.
4 Thread %2 (in Process %1) stopped.
5 Process %3 had an image loaded with name %7.
6 Process %3 had an image unloaded with name %7.
7 Base CPU priority of thread %2 in process %1 was changed from %3 to %4.
8 CPU priority of thread %2 in process %1 was changed from %3 to %4.
9 Page priority of thread %2 in process %1 was changed from %3 to %4.
10 I/O priority of thread %2 in process %1 was changed from %3 to %4.
11 Execution of the process %1 has been suspended.
12 Execution of the process %1 has been resumed.
13 Job %1 started with status code %2.
14 Job %1 terminated with status code %2.
15 Enumerated process %1 had started at time %2 by parent %3 running in session %4 with name %6.
16
17
18
19
20
XML of process stop event properties
<Data Name='ProcessID'>5636</Data>
<Data Name='CreateTime'>2017-05-31T22:05:58.721944300Z</Data>
<Data Name='ExitTime'>2017-05-31T22:07:28.692039900Z</Data>
<Data Name='ExitCode'>1</Data>
<Data Name='TokenElevationType'>3</Data>
<Data Name='HandleCount'>208</Data>
<Data Name='CommitCharge'>4456448</Data>
<Data Name='CommitPeak'>4493312</Data>
<Data Name='CPUCycleCount'>199385866</Data>
<Data Name='ReadOperationCount'>0</Data>
<Data Name='WriteOperationCount'>0</Data>
<Data Name='ReadTransferKiloBytes'>0</Data>
<Data Name='WriteTransferKiloBytes'>0</Data>
<Data Name='HardFaultCount'>0</Data>
<Data Name='ImageName'>backgroundTask</Data>
XML of process start event properties
<Data Name='ProcessID'>5068</Data>
<Data Name='CreateTime'>2017-05-31T22:07:26.486024400Z</Data>
<Data Name='ParentProcessID'>852</Data>
<Data Name='SessionID'>1</Data>
<Data Name='Flags'>0</Data>
<Data Name='ImageName'>\Device\HarddiskVolume1\Windows\System32\dllhost.exe</Data>
<Data Name='ImageChecksum'>60860</Data>
<Data Name='TimeDateStamp'>1468636009</Data>
<Data Name='PackageFullName'></Data>
<Data Name='PackageRelativeAppId'></Data>
XML of image load event properties
<Data Name='ImageBase'>0x7ff7e4830000</Data>
<Data Name='ImageSize'>0x9000</Data>
<Data Name='ProcessID'>5068</Data>
<Data Name='ImageCheckSum'>60860</Data>
<Data Name='TimeDateStamp'>1468636009</Data>
<Data Name='DefaultBase'>0x7ff7e4830000</Data>
<Data Name='ImageName'>\Device\HarddiskVolume1\Windows\System32\dllhost.exe</Data>
Provider Keywords
Name Description Value
---- ----------- -----
WINEVENT_KEYWORD_PROCESS 16
WINEVENT_KEYWORD_THREAD 32
WINEVENT_KEYWORD_IMAGE 64
WINEVENT_KEYWORD_CPU_PRIORITY 128
WINEVENT_KEYWORD_OTHER_PRIORITY 256
WINEVENT_KEYWORD_PROCESS_FREEZE 512
WINEVENT_KEYWORD_JOB 1024
WINEVENT_KEYWORD_ENABLE_PROCESS_TRACING_CALLBACKS 2048
WINEVENT_KEYWORD_JOB_IO 4096
WINEVENT_KEYWORD_WORK_ON_BEHALF 8192
WINEVENT_KEYWORD_JOB_SILO 16384
Microsoft-Windows-Kernel-Process/Analytic 9223372036854775808
@@ -0,0 +1,237 @@
function ConvertTo-Hex
{
param($DecInt)
"0x{0:X}" -f $DecInt
}
function ThreadStart
{
param($Event)
$ProcID = [int32]$Event.Properties[0].value
# Thread property descriptions found at https://msdn.microsoft.com/fr-fr/dd765166
$NewThread = New-Object -TypeName psobject
$NewThread | Add-Member -NotePropertyName 'ThreadID' -NotePropertyValue $Event.Properties[1].value
$NewThread | Add-Member -NotePropertyName 'StackBase' -NotePropertyValue (ConvertTo-Hex $Event.Properties[2].value)
$NewThread | Add-Member -NotePropertyName 'StackLimit' -NotePropertyValue (ConvertTo-Hex $Event.Properties[3].value)
$NewThread | Add-Member -NotePropertyName 'UserStackBase' -NotePropertyValue (ConvertTo-Hex $Event.Properties[4].value)
$NewThread | Add-Member -NotePropertyName 'UserStackLimit' -NotePropertyValue (ConvertTo-Hex $Event.Properties[5].value)
$NewThread | Add-Member -NotePropertyName 'StartAddr' -NotePropertyValue (ConvertTo-Hex $Event.Properties[6].value)
$NewThread | Add-Member -NotePropertyName 'Win32StartAddr' -NotePropertyValue (ConvertTo-Hex $Event.Properties[7].value)
$NewThread | Add-Member -NotePropertyName 'TebBase' -NotePropertyValue (ConvertTo-Hex $Event.Properties[8].value)
$NewThread | Add-Member -NotePropertyName 'SubProcessTag' -NotePropertyValue $Event.Properties[9].value
$NewThread | Add-Member -NotePropertyName 'ThreadStartTime' -NotePropertyValue $Event.TimeCreated
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'Threads').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'Threads' -NotePropertyValue @()
}
$Events[$ProcID].Threads += $NewThread
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'Threads' -NotePropertyValue @()
$NewProcessObject.Threads += $NewThread
$Events.Add( $ProcID, $NewProcessObject )
}
# Add image load to corresponding process thread
# Need to calculate image end address
$Events[$ProcId].LoadedImages |
# Verify loadedimage property does not already exist
# Verify the thread start address is between the image load and end addresses
Where-Object { ( [uint64]$NewThread.Win32StartAddr -gt [uint64]$_.ImageBase) -and ([uint64]$NewThread.Win32StartAddr -lt [uint64](ConvertTo-Hex ([uint64]$_.ImageBase + [uint64]$_.ImageSize))) } |
ForEach-Object {
$NewThread | Add-Member -NotePropertyName 'LoadedImage' -NotePropertyValue $_.ImageName
$NewThread | Add-Member -NotePropertyName 'ImageBase' -NotePropertyValue $_.ImageBase
$NewThread | Add-Member -NotePropertyName 'ImageSize' -NotePropertyValue $_.ImageSize
}
} # ThreadStart
function ThreadStop
{
param($Event)
$ProcID = [int32]$Event.Properties[0].value
$ThreadID = $Event.Properties[1].value
If ( $Events.ContainsKey( [int32]$ProcID ) -and ($Events[[int32]$ProcID].Threads).ThreadID -contains $ThreadID ) {
$Events[$ProcId].Threads |
Where-Object {$_.ThreadID -eq $ThreadID} |
ForEach-Object {
If ( ($_.PSObject.Properties.Name -match 'ThreadEndTime').Count -lt 1 ) {
$_ | Add-Member -NotePropertyName 'ThreadEndTime' -NotePropertyValue $Event.TimeCreated
# The number of CPU clock cycles used by the thread. This value includes cycles spent in both user mode and kernel mode.
# Found at https://msdn.microsoft.com/en-us/library/windows/desktop/ms684943(v=vs.85).aspx
$_ | Add-Member -NotePropertyName 'CycleTime' -NotePropertyValue $Event.Properties[10].value
}
}
}
} # ThreadStop
function ImageLoad
{
param($Event)
$ProcID = [int32]$Event.Properties[2].value
$NewLoadImgObj = New-Object -TypeName psobject
$NewLoadImgObj | Add-Member -NotePropertyName 'ImageBase' -NotePropertyValue (ConvertTo-Hex $Event.Properties[0].value)
$NewLoadImgObj | Add-Member -NotePropertyName 'ImageSize' -NotePropertyValue (ConvertTo-Hex $Event.Properties[1].value)
$NewLoadImgObj | Add-Member -NotePropertyName 'ImageName' -NotePropertyValue $Event.Properties[6].value
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'LoadedImages').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'LoadedImages' -NotePropertyValue @()
}
$Events[$ProcID].LoadedImages += $NewLoadImgObj
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'LoadedImages' -NotePropertyValue @()
$NewProcessObject.LoadedImages += $NewLoadImgObj
$Events.Add( $ProcID, $NewProcessObject )
}
# Add image load to corresponding process thread
# Need to calculate image end address
$ImageEndAddr = ConvertTo-Hex ([uint64]$NewLoadImgObj.ImageBase + [uint64]$NewLoadImgObj.ImageSize)
$Events[$ProcId].Threads |
# Verify loadedimage property does not already exist
Where-Object { ($_.PSObject.Properties.Name -match 'LoadedImage').Count -lt 1 } |
# Verify the thread start address is between the image load and end addresses
Where-Object { ( [uint64]$_.Win32StartAddr -gt [uint64]$NewLoadImgObj.ImageBase) -and ([uint64]$_.Win32StartAddr -lt [uint64]$ImageEndAddr) } |
ForEach-Object {
$_ | Add-Member -NotePropertyName 'LoadedImage' -NotePropertyValue $NewLoadImgObj.ImageName
$_ | Add-Member -NotePropertyName 'ImageBase' -NotePropertyValue $NewLoadImgObj.ImageBase
$_ | Add-Member -NotePropertyName 'ImageSize' -NotePropertyValue $NewLoadImgObj.ImageSize
}
} # ImageLoad
function ProcessStart
{
param($Event)
$ParentPID = [int32]$Event.Properties[2].value
$ProcID = [int32]$Event.Properties[0].value
If ( $Events.ContainsKey( [int32]$ProcID ) ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'CreateTime' -NotePropertyValue $Event.Properties[1].value
$Events[$ProcID] | Add-Member -NotePropertyName 'ParentPID' -NotePropertyValue $ParentPID
$Events[$ProcID] | Add-Member -NotePropertyName 'SessionID' -NotePropertyValue $Event.Properties[3].value
$Events[$ProcID] | Add-Member -NotePropertyName 'ImageName' -NotePropertyValue $Event.Properties[5].value
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'CreateTime' -NotePropertyValue $Event.Properties[1].value
$NewProcessObject | Add-Member -NotePropertyName 'ParentPID' -NotePropertyValue $ParentPID
$NewProcessObject | Add-Member -NotePropertyName 'SessionID' -NotePropertyValue $Event.Properties[3].value
$NewProcessObject | Add-Member -NotePropertyName 'ImageName' -NotePropertyValue $Event.Properties[5].value
$Events.Add( $ProcID, $NewProcessObject )
}
# Check if parent process is known and add to list
If ( $Events.ContainsKey( $ParentPID ) ) {
# Check if property has been added and if not then add
If ( ($Events[ $ParentPID ].PSObject.Properties.Name -match 'ChildProcesses').Count -lt 1 ) {
$Events[ $ParentPID ] | Add-Member -NotePropertyName 'ChildProcesses' -NotePropertyValue @()
}
$Events[ $ParentPID ].ChildProcesses += $NewProcessObject
}
} # ProcessStart
function ProcessStop
{
param($Event)
$ProcID = [int32]$Event.Properties[0].value
If ( $Events.ContainsKey( $ProcId ) ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'EndTime' -NotePropertyValue $Event.Properties[2].value
$Events[$ProcID] | Add-Member -NotePropertyName 'ReadOperationCount' -NotePropertyValue $Event.Properties[9].value
$Events[$ProcID] | Add-Member -NotePropertyName 'WriteOperationCount' -NotePropertyValue $Event.Properties[10].value
$Events[$ProcID] | Add-Member -NotePropertyName 'ReadTransferKiloBytes' -NotePropertyValue $Event.Properties[11].value
$Events[$ProcID] | Add-Member -NotePropertyName 'WriteTransferKiloBytes' -NotePropertyValue $Event.Properties[12].value
# To account for PID reuse we have to rename process keys from PID when they are complete
$UniqueKey = Get-Random
# Add new entry with random number as key
$Events.Add( [int32]$UniqueKey, $Events[$ProcID] )
# Delete key/value with PID
$Events.Remove( $ProcID )
}
else {
$NewProcessObject = New-Object -TypeName psobject
$NewProcessObject | Add-Member -NotePropertyName 'ProcessID' -NotePropertyValue $ProcID
$NewProcessObject | Add-Member -NotePropertyName 'EndTime' -NotePropertyValue $Event.Properties[2].value
$NewProcessObject | Add-Member -NotePropertyName 'ReadOperationCount' -NotePropertyValue $Event.Properties[9].value
$NewProcessObject | Add-Member -NotePropertyName 'WriteOperationCount' -NotePropertyValue $Event.Properties[10].value
$NewProcessObject | Add-Member -NotePropertyName 'ReadTransferKiloBytes' -NotePropertyValue $Event.Properties[11].value
$NewProcessObject | Add-Member -NotePropertyName 'WriteTransferKiloBytes' -NotePropertyValue $Event.Properties[12].value
$UniqueKey = Get-Random
# Add new entry with random number as key
$Events.Add( [int32]$UniqueKey, $NewProcessObject )
}
}
function KernelProcessParser
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Diagnostics.Eventing.Reader.EventRecord]
$Event
)
$script:KernProcEvents = @{
1 = 'ProcessStart'
2 = 'ProcessStop'
3 = 'ThreadStart'
4 = 'ThreadStop'
5 = 'ImageLoad'
}
If ( $script:KernProcEvents.ContainsKey($Event.Id) ) {
&$script:KernProcEvents[$_.Id] $Event
}
}
@@ -0,0 +1,58 @@
function CmdletRun
{
param($Event)
$ProcID = [int32]$Event.ProcessID
$MessageArray = ($Event.Properties[2].Value) -split " "
# CmdletRun can have many different events for this we just want to capture what cmdlets were run
If ( $MessageArray[0] -eq "Command" -and $MessageArray[3].Trim() -eq "Started." ) {
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'Commands').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'Commands' -NotePropertyValue @()
}
$Events[$ProcID].Commands += $MessageArray[1]
}
}
}
function ScriptBlock
{
param($Event)
$ProcID = [int32]$Event.ProcessID
If ( $Events.ContainsKey( $ProcID ) ) {
If ( ($Events[$ProcID].PSObject.Properties.Name -match 'ScriptBlocks').Count -lt 1 ) {
$Events[$ProcID] | Add-Member -NotePropertyName 'ScriptBlocks' -NotePropertyValue @()
}
$Events[$ProcID].ScriptBlocks += $Event.Properties[2].Value
}
}
function PowerShellParser
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[System.Diagnostics.Eventing.Reader.EventRecord]
$Event
)
$script:KernProcEvents = @{
7937 = 'CmdletRun'
4104 = 'ScriptBlock'
}
If ( $script:KernProcEvents.ContainsKey($Event.Id) ) {
&$script:KernProcEvents[$_.Id] $Event
}
}
+211
View File
@@ -0,0 +1,211 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$TestDir = Split-Path $MyInvocation.MyCommand.Path -Parent
$RootModuleDir = Resolve-Path "$TestDir\.."
$Module = "$RootModuleDir\EventTrace.psd1"
Import-Module $Module -Force -ErrorAction Stop
# Global session name
$SessName = "PesterETWSession"
# Global output file
$OutputFile = Join-Path (Resolve-Path .) "etw_session.etl"
# Global provider
$ProviderName = "Microsoft-Windows-Kernel-Process"
Describe 'New-ETWProviderConfig' {
Context 'output validation' {
It 'Should return PS object' {
(New-ETWProviderConfig).PSObject.TypeNames[1] | Should Be 'System.Object'
}
It 'Should contain 3 properties' {
New-ETWProviderConfig | Get-Member | Where-Object {$_.MemberType -eq 'NoteProperty' } | `
Measure-Object | Select-Object -ExpandProperty count | Should be 4
}
}
}
Describe 'ConvertTo-ETWGuid' {
Context 'input validation' {
It 'Should should accept string input' {
{ ConvertTo-ETWGuid -ProviderName $ProviderName } | Should Not Throw
}
It "Should error on non-existent provider" {
{ ConvertTo-ETWGuid -ProviderName "DOES NOT EXIST" } | Should Throw
}
It 'Should generate errors when required parameters are not provided' {
{ ConvertTo-ETWGuid -ProviderName } | Should Throw
}
}
Context 'output validation' {
It 'Should return type GUID' {
{ ConvertTo-ETWGuid -ProviderName $ProviderName -is [System.Guid] } | Should Be $true
}
}
}
Describe 'Get-ETWProviderKeywords' {
Context 'input validation' {
It 'Should require input' {
{ Get-ETWProviderKeywords -Provider } | Should Throw
}
It 'Should accept string input' {
{ Get-ETWProviderKeywords -Provider $ProviderName }
}
It "Should error on non-existent provider" {
{ Get-ETWProviderKeywords -ProviderName "DOES NOT EXIST" } | Should Throw
}
}
Context 'output validation'{
It 'Should return properly formatted ProviderDataItem objects' {
$Result = Get-ETWProviderKeywords -Provider $ProviderName
$Result[0].PSObject.TypeNames[0] | Should be 'Microsoft.Diagnostics.Tracing.Session.ProviderDataItem'
}
}
}
Describe 'New-ETWProviderOption' {
Context 'output validation' {
It 'Should return provider options object' {
(New-ETWProviderOption).PSObject.TypeNames[0] | Should be 'Microsoft.Diagnostics.Tracing.Session.TraceEventProviderOptions'
}
}
}
Describe 'Get-ETWProvider' {
Context 'output validation' {
It 'Should generate output'{
{ Get-ETWProvider } | Should Not BeNullOrEmpty
}
It 'Should return properly formatted ProviderMetadata objects' {
$Result = Get-ETWProvider
$Result[0].PSObject.TypeNames[0] | Should be 'System.Diagnostics.Eventing.Reader.ProviderMetadata'
}
}
}
Describe 'Get-ETWSessionDetails' {
Context 'input validation' {
It 'Should require input' {
{ Get-ETWSessionDetails -SessionName } | Should Throw
}
It 'Should error when invalid name provided' {
{ Get-ETWSessionDetails -SessionName "not valid" } | Should Throw "Session does not exist"
}
}
Context 'output validation' {
It 'Should return TraceEventSessionObject' {
$SessionName = (Get-ETWSessionNames)[0]
$Result = Get-ETWSessionDetails -SessionName $SessionName
$Result[0].PSObject.TypeNames[0] | Should be 'Microsoft.Diagnostics.Tracing.Session.TraceEventSession'
}
}
}
Describe 'Start-ETWSession' {
$ProviderConfig = New-ETWProviderConfig
$ProviderConfig.Name = $ProviderName
$ProviderConfig.Keywords = Get-ETWProviderKeywords -Provider $ProviderName | Where-Object {
$_.Name -match "_process$|_image$|_thread$" }
Context 'input validation' {
It 'Should generate errors when required params are not provided' {
{ Start-ETWSession -SessionConfig -SessionName -OutputFile } | Should Throw
}
InModuleScope EventTrace{
It 'Should fail to run if session already exists' {
Mock Test-IsSession { return $true }
{ Start-ETWSession -ProviderConfig $null -OutputFile $OutputFile -SessionName $SessName } `
| Should Throw
}
}
It 'Output file should not exist' {
Test-Path $OutputFile | Should be $false
}
}
Context 'output validation' {
It 'Should create ETW session' {
(Start-ETWSession -SessionName $SessName -OutputFile $OutputFile -ProviderConfig $ProviderConfig)[1] | Should be $true
}
# sleep for 2 seconds to verify file is created and events generated
Start-Sleep -Seconds 2
It 'Should create etl output file' {
Test-Path $OutputFile | Should Be $true
}
}
}
Describe 'Stop-ETWSession' {
Context 'input validation' {
It 'Should generate an error when a non-existent session is provided' {
{ Stop-ETWSession -SessionName "does not exist" } | Should Throw
}
}
Context "output validation" {
It 'Should stop session' {
(Stop-ETWSession -SessionName $SessName)[1] | Should be $true
}
# size of blank etl file is 64 KB
It 'Output file should exist and be larger than 64 KB' {
(Get-ChildItem $OutputFile).Length / 1Kb | Should BeGreaterThan 64
}
}
}
Describe 'Get-ETWEventLog' {
Context 'input validation' {
It 'Should error when required parameter not provided' {
{ Get-ETWEventLog -Path } | Should Throw
}
}
# Context 'output validation' {
# It 'Should properly parse output' {
# (Get-ETWEventLog -Path $OutputFile).Count | Should BeGreaterThan 1
# }
# }
}
Describe 'Cleanup tests' {
Context 'Remove module' {
It 'Should not throw error removing module' {
{ Remove-Module 'EventTrace' } | Should Not Throw
}
}
Context 'Delete test etl file' {
It 'Should delete output file' {
{ Remove-Item $OutputFile -Force } | Should Not Throw
}
It 'Output file should not exist' {
Test-Path $OutputFile | Should be $false
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Regular → Executable
+78 -1
View File
@@ -1 +1,78 @@
# PSEventDetect
# PSWasp
PSWasp is a PowerShell project that enables users to easily interact with Event Tracing for Windows (ETW); specifically designed for forensic collection and analysis. Originally intended as a Windows debugging utility, ETW has evolved to support a myriad of diverse use cases. Modern Windows operating systems (8.1, 2012, Win10, and Server 2016) ship with hundreds of application and kernel layer ETW providers, any of which could capture and log critical information during an investigation. PSalandar enables users to easily start and capture events from one or many ETW providers.
## Requirements
- .NET 4.0 or greater
- PowerShell 3.0 or greater
## External Dependencies
- PSWasp uses and ships a copy of the Microsoft's TraceEvent DLL [License](https://www.microsoft.com/net/dotnet_library_license.htm)
## Examples
### List all ETW providers on a System
```Get-ETWProvider```
### List all active ETW sessions
```Get-ETWSessionNames```
### Enumerate details from active ETW sessions
Warning: Enumerating session details has been found to inadvertently stop ETW sessions in some cases.
```Get-ETWSessionDetails```
### Start Forensic Collection with PSWasp
#### Create provider object
Defines the provider name or GUID, filtering keywords, or other filtering options
This example configures the `Micorsoft-Windows-Kernel-Process` provider and only enables the `Process`, `Image`, and `Thread` keywords
```
$ProviderConfig = New-ETWProviderConfig
$ProviderConfig.Name = 'Microsoft-Windows-Kernel-Process'
$ProcessRegex = '_PROCESS$|_IMAGE$|_THREAD$'
Get-ETWProviderKeywords -ProviderName $ProviderConfig.Name |
Where-Object { $_.Name -match $ProcessRegex } |
ForEach-Object { $ProviderConfig.Keywords += $_ }
```
#### Start ETW Session
```Start-ETWSession -ProviderConfig $ProviderConfig -SessionName <unique session name> -OutputFile <path to etl file>```
### Stop ETW Session
```Stop-ETWSession -SessionName <previously provided unique session name>```
### Parse any .ETL Log
```Get-WinEvent -Path <path to ETL file> -Oldest```
### Start ETW forensic session with kernel session
Kernel session is an optional argument that starts a unique kernel session. Enabling this session allows for the capture of process command line arguments.
Note: Kernel session is enabled by default use `-DisableKernelProvider` to disable
```Start-ETWForensicCollection -SessionName <unique session name> -OutputFile <path to etl file>```
### Parse .ETL file generated from Start-ETWForensicCollection
Will automatically identify and parse any kernel session output files from the same session
```Get-ETWForensicEventLog -Path <path to ETL file>```
## Useful links
https://github.com/Microsoft/dotnetsamples/blob/master/Microsoft.Diagnostics.Tracing/TraceEvent/docs/TraceEvent.md
https://blogs.msdn.microsoft.com/vancem/2012/12/20/using-tracesource-to-log-etw-data-to-a-file/
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363668(v=vs.85).aspx
https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/TraceEventSession.cs
https://blogs.technet.microsoft.com/office365security/hidden-treasure-intrusion-detection-with-etw-part-1/
https://blogs.technet.microsoft.com/office365security/hidden-treasure-intrusion-detection-with-etw-part-2/
## Referenced Work
- Module design for PSWasp was heavily influenced by [CimSweep](https://github.com/PowerShellMafia/CimSweep). It should be a model for all PS modules follow.
- Zak Brown and the Microsoft OS365 folks do great ETW work and have their own open source ETW library [krabsetw](https://github.com/microsoft/krabsetw)
+210
View File
@@ -0,0 +1,210 @@
# Version: 1.1
function Start-Demo
{
param($file="demo\demo.txt", [int]$command=0)
$CommentColor = "Yellow"
$MetaCommandColor = "Red"
Clear-Host
$_Random = New-Object System.Random
$_lines = @(Get-Content $file)
$_starttime = [DateTime]::now
$_PretendTyping = $true
$_InterkeyPause = 200
# Write-Host -for $CommentColor @"
# NOTE: Start-Demo replaces the typing but runs the actual commands.
# .
# <Demo [$file] Started. Type `"?`" for help>
# "@
# We use a FOR and an INDEX ($_i) instead of a FOREACH because
# it is possible to start at a different location and/or jump
# around in the order.
for ($_i = $Command; $_i -lt $_lines.count; $_i++)
{
if ($_lines[$_i].StartsWith("#"))
{
Write-Host -NoNewLine $("`n[$_i]PS> ")
Write-Host -NoNewLine -Foreground $CommentColor $($($_Lines[$_i]) + " ")
continue
}else
{
# Put the current command in the Window Title along with the demo duration
$_Duration = [DateTime]::Now - $_StartTime
$Host.UI.RawUI.WindowTitle = "[{0}m, {1}s] {2}" -f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds, $($_Lines[$_i])
Write-Host -NoNewLine $("`n[$_i]PS> ")
$_SimulatedLine = $($_Lines[$_i]) + " "
for ($_j = 0; $_j -lt $_SimulatedLine.Length; $_j++)
{
Write-Host -NoNewLine $_SimulatedLine[$_j]
if ($_PretendTyping)
{
if ([System.Console]::KeyAvailable)
{
$_PretendTyping = $False
}
else
{
Start-Sleep -milliseconds $(10 + $_Random.Next($_InterkeyPause))
}
}
} # For $_j
$_PretendTyping = $true
} # else
$_OldColor = $host.UI.RawUI.ForeGroundColor
$host.UI.RawUI.ForeGroundColor = $MetaCommandColor
$_input=[System.Console]::ReadLine().TrimStart()
$host.UI.RawUI.ForeGroundColor = $_OldColor
switch ($_input)
{
################ HELP with DEMO
"?"
{
Write-Host -ForeGroundColor $CommentColor @"
Running demo: $file
.
(!) Suspend (#x) Goto Command #x (b) Backup (d) Dump demo
(fx) Find cmds using X (px) Typing Pause Interval (q) Quit (s) Skip
(t) Timecheck (?) Help
.
NOTE 1: Any key cancels "Pretend typing" for that line. Use <SPACE> unless you
want to run a one of these meta-commands.
.
NOTE 2: After cmd output, enter <CR> to move to the next line in the demo.
This avoids the audience getting distracted by the next command
as you explain what happened with this command.
.
NOTE 3: The line to be run is displayed in the Window Title BEFORE it is typed.
This lets you know what to explain as it is typing.
"@
$_i -= 1
}
#################### PAUSE
{$_.StartsWith("p")}
{
$_InterkeyPause = [int]$_.substring(1)
$_i -= 1
}
#################### Backup
"b" {
if($_i -gt 0)
{
$_i --
while (($_i -gt 0) -and ($_lines[$($_i)].StartsWith("#")))
{ $_i -= 1
}
}
$_i --
$_PretendTyping = $false
}
#################### QUIT
"q"
{
Write-Host -ForeGroundColor $CommentColor "<Quit demo>"
return
}
#################### SKIP
"s"
{
Write-Host -ForeGroundColor $CommentColor "<Skipping Cmd>"
}
#################### DUMP the DEMO
"d"
{
for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
{
if ($_i -eq $_ni)
{ Write-Host -ForeGroundColor $MetaCommandColor ("*" * 80)
}
Write-Host -ForeGroundColor $CommentColor ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
}
$_i -= 1
}
#################### TIMECHECK
"t"
{
$_Duration = [DateTime]::Now - $_StartTime
Write-Host -ForeGroundColor $CommentColor $(
"Demo has run {0} Minutes and {1} Seconds`nYou are at line {2} of {3} " -f
[int]$_Duration.TotalMinutes,
[int]$_Duration.Seconds,
$_i,
($_lines.Count - 1)
)
$_i -= 1
}
#################### FIND commands in Demo
{$_.StartsWith("f")}
{
for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
{
if ($_lines[$_ni] -match $_.SubString(1))
{
Write-Host -ForeGroundColor $CommentColor ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
}
}
$_i -= 1
}
#################### SUSPEND
{$_.StartsWith("!")}
{
if ($_.Length -eq 1)
{
Write-Host -ForeGroundColor $CommentColor "<Suspended demo - type 'Exit' to resume>"
function Prompt {"[Demo Suspended]`nPS>"}
$host.EnterNestedPrompt()
}else
{
trap [System.Exception] {Write-Error $_;continue;}
Invoke-Expression $(".{" + $_.SubString(1) + "}| out-host")
}
$_i -= 1
}
#################### GO TO
{$_.StartsWith("#")}
{
$_i = [int]($_.SubString(1)) - 1
continue
}
#################### EXECUTE
default
{
trap [System.Exception] {Write-Error $_;continue;}
Invoke-Expression $(".{" + $_lines[$_i] + "}| out-host")
$_Duration = [DateTime]::Now - $_StartTime
$Host.UI.RawUI.WindowTitle = "[{0}m, {1}s] {2}" -f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds, $($_Lines[$_i])
[System.Console]::ReadLine()
}
} # Switch
} # for
$_Duration = [DateTime]::Now - $_StartTime
Write-Host -ForeGroundColor $CommentColor $("<Demo Complete {0} Minutes and {1} Seconds>" -f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds)
Write-Host -ForeGroundColor $CommentColor $([DateTime]::now)
} # function
BIN
View File
Binary file not shown.
Binary file not shown.
+56
View File
@@ -0,0 +1,56 @@
Import-Module .\PSWasp
Get-Module PSWasp | select -Expand ExportedCommands
Get-ETWProvider | select -Expand id | Group-Object | Measure-Object
Get-ETWProvider | where name -match 'kernel-process$' | select *
Get-ETWProvider | where name -match 'kernel-process$' | select -Expand Keywords
cls
Get-ETWProvider | where name -match 'kernel-process$' | select -Expand tasks
Get-ETWProvider | where name -match 'kernel-process$' | % Events | select id, description
Get-ETWSessionNames
$EmpireSession = Import-Clixml demo\demo.xml
$EmpireSession | where imagename -match "powershell" | select processid, imagename
$EmpireSession | Where ProcessID -eq 5392 | gm -MemberType NoteProperty | select Name
cls
$EmpireSession | where ProcessID -eq 5392 | select -Expand Commands -Unique
$EmpireSession | where processid -eq 5392 | select -expand scriptblocks | select-string "Get-GPPPassword"
cls
$EmpireSession | where ProcessID -eq 5392 | select -Expand ChildProcesses | select Commandline
$EmpireSession | Where Imagename -match "certutil"
cls
$EmpireSession | Where imagename -match "certutil" | select -Expand Domainlookups
$EmpireSession | Where imagename -match "certutil" | select -Expand NetConnections
$EmpireSession | Where Imagename -match "runas"
$EmpireSession | Where Imagename -match "runas" | select -Expand ChildProcesses | select imagename
$EmpireSession | Where Imagename -match "runas" | select -Expand ChildProcesses | select -Expand childprocesses | select commandline
cls
$EmpireSession | Where Imagename -match "notepad" | select -Expand FileIO
$EmpireSession | Where Imagename -match "cscript"
$EmpireSession | Where Imagename -match "cscript" | select -Expand ChildProcesses | select processid, imagename
$EmpireSession | Where ProcessId -eq 6804 | select -Expand Commandline
$EmpireSession | where processid -eq 6804 | select -Expand commands -Unique | sort
$EmpireSession | where processid -eq 6804 | select -Expand scriptblocks | select-string "Invoke-Empire"
cls
$EmpireSession | Where ProcessId -eq 6804 | select -Expand FileIO
$EmpireSession | Where ProcessId -eq 6804 | select -Expand DomainLookups
$EmpireSession | Where ProcessId -eq 6804 | select -Expand NetConnections
$EmpireSession | where processid -eq 6804 | select ReadTransferKiloBytes, WriteTransferKiloBytes
$EmpireSession | Where ProcessId -eq 6804 | select -Expand LoadedImages
cls
$EmpireSession | Where DomainLookups -match "lets.popa.box" | select processid, imagename
$EmpireSession | Where ProcessID -eq 1924 | select imagename, commandline
$EmpireSession | Where ProcessID -eq 1924 | select -Expand FileIO
$EmpireSession | Where ProcessID -eq 1924 | select -Expand ChildProcesses | select -Expand CommandLine
$EmpireSession | Where ProcessID -eq 1924 | select -Expand DomainLookups
$EmpireSession | Where ProcessID -eq 1924 | select -Expand LoadedImages
cls
$EmpireSession | Where ProcessID -eq 1924 | select -Expand LoadedImages | where ImageName -match "automation"
$EmpireSession | Where ProcessID -eq 1924 | select -Expand threads
cls
$EmpireSession | Where ProcessID -eq 1924 | select -Expand threads | where ThreadID -eq 6204
$EmpireSession | where processid -eq 1924 | select -Expand Commands -Unique
$MimiKaz = $EmpireSession | where processid -eq 1924 | select -expand scriptblocks
$MimiKaz[7].Substring(0,800)
cls
Get-ETWForensicGraph -ETWObject $EmpireSession -ParentProcessID 1924
cls
Get-ETWForensicGraph -ETWObject $EmpireSession -ParentProcessID 5392
BIN
View File
Binary file not shown.