Last active
March 31, 2025 17:07
-
-
Save joshfinley/2c6f9b2d0b81580b292dabadd6ccb622 to your computer and use it in GitHub Desktop.
Search for GUIDs in a target file or directory and check if it might be an ETW provider GUID using logman. Inspired by chapter 8 of "Evading EDR" by Matt Hand.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Search for GUIDs in a target file or directory and check | |
# if it might be an ETW provider GUID using logman. | |
# | |
# Inspired by chapter 8 of "Evading EDR" by Matt Hand. | |
# | |
# Not fast but easy to deploy and use. | |
param ( | |
[string]$Path | |
) | |
function Get-GUIDsFromFile($filePath) { | |
$regex = '[{(]?[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}[)}]?' | |
$foundGuids = @{} | |
# Process file in chunks | |
$stream = [System.IO.File]::OpenRead($filePath) | |
$buffer = New-Object byte[] 4MB | |
$totalBytesRead = 0 | |
try { | |
$overlap = 36 # GUID can be up to 36 chars, need to keep this overlap between chunks | |
$carryover = "" | |
while ($true) { | |
$bytesRead = $stream.Read($buffer, 0, $buffer.Length) | |
if ($bytesRead -eq 0) { break } | |
# Convert bytes to ASCII replacing non-printable chars with spaces | |
$text = -join ($buffer[0..($bytesRead-1)] | ForEach-Object { | |
if ($_ -ge 32 -and $_ -le 126) { [char]$_ } else { ' ' } | |
}) | |
# Combine with carryover from previous chunk | |
$textToSearch = $carryover + $text | |
# Find GUIDs | |
$matches = [regex]::Matches($textToSearch, $regex) | |
foreach ($match in $matches) { | |
$matchOffset = $totalBytesRead - $carryover.Length + $match.Index | |
# Only add if we haven't seen this GUID before | |
if (-not $foundGuids.ContainsKey($match.Value)) { | |
$foundGuids[$match.Value] = $matchOffset | |
} | |
} | |
# Save overlap for next chunk | |
$carryover = $text.Substring([Math]::Max(0, $text.Length - $overlap)) | |
$totalBytesRead += $bytesRead | |
# Force garbage collection periodically | |
[System.GC]::Collect() | |
} | |
} | |
finally { | |
$stream.Close() | |
$stream.Dispose() | |
} | |
# Return results as objects | |
$results = @() | |
foreach ($guid in $foundGuids.Keys | Sort-Object) { | |
$results += [PSCustomObject]@{ | |
GUID = $guid | |
Offset = $foundGuids[$guid] | |
} | |
} | |
return $results | |
} | |
function Is-ETWProviderGUID($guid) { | |
# Cache ETW provider GUIDs to avoid repeated lookups | |
if (-not (Test-Path variable:global:ETWProviderCache)) { | |
$global:ETWProviderCache = @{} | |
# Pre-populate cache with all ETW providers - do this only once | |
Write-Host "Caching ETW providers..." -ForegroundColor Yellow | |
$providers = logman query providers | |
foreach ($provider in $providers) { | |
if ($provider -match '(?:\{|\()([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(?:\}|\))') { | |
$global:ETWProviderCache[$Matches[1].ToLower()] = $true | |
} | |
} | |
} | |
$cleanedGuid = $guid.Trim('{}()').ToLower() | |
return $global:ETWProviderCache.ContainsKey($cleanedGuid) | |
} | |
# Main script | |
if (-not (Test-Path $Path)) { | |
Write-Error "Path not found: $Path" | |
exit 1 | |
} | |
$files = if ((Get-Item $Path).PSIsContainer) { | |
Get-ChildItem -Path $Path -Recurse -File | |
} else { | |
Get-Item $Path | |
} | |
# Define colors for highlighting | |
$etwColor = "Green" | |
$unknownColor = "Gray" | |
$defaultColor = (Get-Host).UI.RawUI.ForegroundColor | |
foreach ($file in $files) { | |
Write-Host "`nScanning: $($file.FullName)" -ForegroundColor Cyan | |
# Process each file individually and clean up after | |
$guidResults = Get-GUIDsFromFile $file.FullName | |
foreach ($result in $guidResults) { | |
$guid = $result.GUID | |
$offset = $result.Offset | |
$cleaned = $guid.Trim('{}()') | |
$offsetHex = "0x{0:X8}" -f $offset | |
if (Is-ETWProviderGUID $cleaned) { | |
Write-Host " [ETW] " -ForegroundColor $etwColor -NoNewline | |
Write-Host "$guid " -ForegroundColor $etwColor -NoNewline | |
Write-Host "at offset $offsetHex" -ForegroundColor $etwColor | |
} else { | |
Write-Host " [UNK] " -ForegroundColor $unknownColor -NoNewline | |
Write-Host "$guid " -ForegroundColor $defaultColor -NoNewline | |
Write-Host "at offset $offsetHex" -ForegroundColor $defaultColor | |
} | |
} | |
# Force cleanup after each file | |
Remove-Variable guidResults -ErrorAction SilentlyContinue | |
[System.GC]::Collect() | |
} | |
# Clean up the cache at the end | |
Remove-Variable -Name ETWProviderCache -Scope Global -ErrorAction SilentlyContinue |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment