Skip to content

Instantly share code, notes, and snippets.

@kmahyyg
Last active March 7, 2026 16:12
Show Gist options
  • Select an option

  • Save kmahyyg/13ceb3feeb9f16290af6bff5dd3fe75b to your computer and use it in GitHub Desktop.

Select an option

Save kmahyyg/13ceb3feeb9f16290af6bff5dd3fe75b to your computer and use it in GitHub Desktop.
DefenderDLPTrace.ps1
#requires -RunAsAdministrator
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# -------------------------------
# Static config (non-interactive friendly)
# -------------------------------
$script:DDLP_Root = 'C:\Windows\TEMP'
$script:DDLP_WprpPath = Join-Path $script:DDLP_Root 'DefenderDLPTrace.wprp'
$script:DDLP_EtlPath = Join-Path $script:DDLP_Root 'DefenderDLPTrace.etl'
$script:DDLP_ProfileId = 'DefenderDLPTrace.Verbose'
$script:DDLP_ExpectedIds = @{
'Microsoft-Antimalware-RTP' = @(22,23,24)
'Microsoft-Antimalware-Service' = @(21,52,53,54)
}
function New-DefenderDLPTraceProfile {
[CmdletBinding()]
param()
if (-not (Test-Path $script:DDLP_Root)) {
New-Item -Path $script:DDLP_Root -ItemType Directory -Force | Out-Null
}
$wprp = @'
<?xml version="1.0" encoding="utf-8"?>
<WindowsPerformanceRecorder Version="1.0" Author="GPT-Codex-kmahyyg">
<Profiles>
<EventCollector Id="EC_DefenderDLP" Name="Defender DLP Event Collector">
<BufferSize Value="64"/>
<Buffers Value="128"/>
<MaximumFileSize Value="128" FileMode="Circular" />
</EventCollector>
<EventProvider Id="EP_AntimalwareRTP" Name="Microsoft-Antimalware-RTP">
<EventFilters FilterIn="true">
<!-- EventId Value="22"/ -->
<EventId Value="23"/>
<EventId Value="24"/>
</EventFilters>
</EventProvider>
<EventProvider Id="EP_AntimalwareService" Name="Microsoft-Antimalware-Service">
<EventFilters FilterIn="true">
<!-- EventId Value="21"/ -->
<EventId Value="52"/>
<EventId Value="53"/>
<EventId Value="54"/>
</EventFilters>
</EventProvider>
<Profile Id="DefenderDLPTrace.Verbose.File"
Name="DefenderDLPTrace"
Description="Capture only selected Defender DLP Event IDs"
DetailLevel="Verbose"
LoggingMode="File">
<Collectors>
<EventCollectorId Value="EC_DefenderDLP">
<EventProviders>
<EventProviderId Value="EP_AntimalwareRTP"/>
<EventProviderId Value="EP_AntimalwareService"/>
</EventProviders>
</EventCollectorId>
</Collectors>
</Profile>
</Profiles>
</WindowsPerformanceRecorder>
'@
Set-Content -Path $script:DDLP_WprpPath -Value $wprp -Encoding UTF8
return $script:DDLP_WprpPath
}
function Start-DefenderDLPTrace {
[CmdletBinding()]
param(
[switch]$OverwriteEtl
)
if (-not (Get-Command wpr.exe -ErrorAction SilentlyContinue)) {
throw "wpr.exe not found."
}
$wprpPath = New-DefenderDLPTraceProfile
if ($OverwriteEtl -and (Test-Path $script:DDLP_EtlPath)) {
Remove-Item -Path $script:DDLP_EtlPath -Force
}
# Best-effort cleanup; ignore failure if no active session
try { & wpr.exe -cancel | Out-Null } catch { }
$profileArg = "$wprpPath!$($script:DDLP_ProfileId)"
& wpr.exe -start $profileArg -filemode | Out-Null
[pscustomobject]@{
Action = 'Start'
Success = $true
Profile = $profileArg
EtlPath = $script:DDLP_EtlPath
Timestamp = (Get-Date).ToString('o')
}
}
function Stop-DefenderDLPTrace {
[CmdletBinding()]
param(
[switch]$SkipValidation
)
if (-not (Get-Command wpr.exe -ErrorAction SilentlyContinue)) {
throw "wpr.exe not found."
}
& wpr.exe -stop $script:DDLP_EtlPath | Out-Null
if (-not (Test-Path $script:DDLP_EtlPath)) {
throw "ETL not found after stop: $($script:DDLP_EtlPath)"
}
if ($SkipValidation) {
return [pscustomobject]@{
Action = 'Stop'
Success = $true
Validated = $false
EtlPath = $script:DDLP_EtlPath
Timestamp = (Get-Date).ToString('o')
}
}
$countsMap = @{} # key = "Provider|EventId" ; value = count
$totalEvents = 0
Get-WinEvent -Path $script:DDLP_EtlPath -Oldest | ForEach-Object {
$totalEvents++
$provider = $_.ProviderName
$id = [int]$_.Id
$key = "$provider|$id"
if ($countsMap.ContainsKey($key)) { $countsMap[$key]++ } else { $countsMap[$key] = 1 }
}
$counts = @(foreach ($k in $countsMap.Keys) {
$p = $k.Split('|',2)[0]
$i = [int]$k.Split('|',2)[1]
[pscustomobject]@{
Provider = $p
EventId = $i
Count = $countsMap[$k]
}
}) | Sort-Object Count -Descending
$unexpected = @(foreach ($row in $counts) {
if ($script:DDLP_ExpectedIds.ContainsKey($row.Provider) -and
($row.EventId -notin $script:DDLP_ExpectedIds[$row.Provider])) {
$row
}
})
[pscustomobject]@{
Action = 'Stop'
Success = $true
Validated = $true
ValidationPassed = ($unexpected.Count -eq 0)
EtlPath = $script:DDLP_EtlPath
TotalEvents = $totalEvents
Summary = $counts
Unexpected = @($unexpected)
Timestamp = (Get-Date).ToString('o')
}
}

Comments are disabled for this gist.