Skip to content

Instantly share code, notes, and snippets.

@joshfinley
Last active March 31, 2025 17:07
Show Gist options
  • Save joshfinley/2c6f9b2d0b81580b292dabadd6ccb622 to your computer and use it in GitHub Desktop.
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.
# 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