# take_snapshot.ps1 # One-shot umdh snapshot helper. Auto-finds acclient PID and the next snap_NNN filename. # # Usage: # take_snapshot.ps1 -PhaseDir artifacts\phase1 # # Requires: gflags +ust on acclient.exe (one-time, see Phase 1 step 1), # _NT_SYMBOL_PATH set to the directory containing acclient.pdb. param( [Parameter(Mandatory=$true)][string]$PhaseDir, [int]$ProcessId = 0, # 0 = auto-discover via Get-Process [string]$UmdhExe = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\umdh.exe", [int]$TimeoutSec = 600 # umdh on a fat 32-bit AC client can take 60-200s ) $ErrorActionPreference = "Stop" if (-not (Test-Path $PhaseDir)) { New-Item -ItemType Directory -Path $PhaseDir -Force | Out-Null } if (-not (Test-Path $UmdhExe)) { throw "umdh.exe not found at $UmdhExe" } if (-not $env:_NT_SYMBOL_PATH) { $env:_NT_SYMBOL_PATH = "C:\Users\acbot\leakhunt\pdb" } if ($ProcessId -eq 0) { $procs = Get-Process -Name acclient -ErrorAction SilentlyContinue if (-not $procs) { throw "acclient.exe not running" } if ($procs.Count -gt 1) { throw "multiple acclient.exe instances; specify -ProcessId" } $ProcessId = $procs[0].Id } # Find next snap_NNN filename $existing = Get-ChildItem -Path $PhaseDir -Filter "snap_*.txt" -ErrorAction SilentlyContinue | ForEach-Object { [int]([regex]::Match($_.BaseName, '\d+').Value) } $next = if ($existing) { ($existing | Measure-Object -Maximum).Maximum + 1 } else { 1 } $Out = Join-Path $PhaseDir ("snap_{0:D3}.txt" -f $next) Write-Host "[$(Get-Date -Format HH:mm:ss)] umdh -p:$ProcessId -f:$Out" $argList = @("-p:$ProcessId", "-f:$Out") $p = Start-Process -FilePath $UmdhExe -ArgumentList $argList -NoNewWindow -PassThru -RedirectStandardError "$Out.err" if (-not $p.WaitForExit($TimeoutSec * 1000)) { Stop-Process -Id $p.Id -Force throw "umdh timed out after $TimeoutSec s" } if (-not (Test-Path $Out)) { throw "umdh produced no output (exit $($p.ExitCode))" } $size = (Get-Item $Out).Length # Pull the leading metadata + a quick stats summary $head = (Get-Content $Out -TotalCount 6) -join "`n" $totalAllocs = (Select-String -Path $Out -Pattern '^\d+ bytes \+' -AllMatches).Matches.Count Write-Host ("[$(Get-Date -Format HH:mm:ss)] OK: {0} ({1:N0} bytes; {2} alloc-stack records)" -f $Out, $size, $totalAllocs) Write-Host "--- head ---" Write-Host $head # Emit metadata for the orchestrator [pscustomobject]@{ Path = $Out SizeBytes = $size Records = $totalAllocs Pid = $ProcessId Timestamp = (Get-Date).ToString("o") }