Files
artif/windows/Get-SearchHistory.ps1
mnerv 9b910a65f3 feat: Add search history extraction (Get-SearchHistory.ps1)
Extract Windows Search queries, typed paths, and Run dialog history
from registry (WordWheelQuery, TypedPaths, RunMRU).

Includes warnings about Windows 11 limitations - modern search without
Microsoft account doesn't persist history due to cloud-first design.

Update TODO.md.
2026-02-03 22:31:39 +01:00

196 lines
7.3 KiB
PowerShell

# Get-SearchHistory.ps1
# Extracts Windows Search history from WordWheelQuery registry
param(
[int]$MaxResults = 50,
[switch]$ShowAll
)
Write-Host "=== Windows Search History ===" -ForegroundColor Cyan
Write-Host "Search queries from Windows Search Bar and File Explorer`n"
Write-Host "Note: Windows 11 without Microsoft account may not persist search history" -ForegroundColor Yellow
Write-Host "Modern search uses cloud sync and memory-only caching`n" -ForegroundColor Yellow
if ($ShowAll) {
$MaxResults = [int]::MaxValue
}
$foundAny = $false
# WordWheelQuery - Windows Search history
Write-Host "--- WordWheelQuery (Search Bar / File Explorer) ---" -ForegroundColor Yellow
try {
$wordWheelPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\WordWheelQuery"
if (Test-Path $wordWheelPath) {
$props = Get-ItemProperty -Path $wordWheelPath -ErrorAction SilentlyContinue
# Get MRUListEx to determine order
if ($props.MRUListEx) {
$searches = @()
# Parse MRUListEx (4-byte integers)
for ($i = 0; $i -lt $props.MRUListEx.Length - 4; $i += 4) {
$index = [BitConverter]::ToInt32($props.MRUListEx, $i)
if ($index -eq -1) { break } # End marker
# Get the search term from the indexed property
$propName = "$index"
if ($props.$propName) {
# Convert binary data to Unicode string
$searchTerm = [System.Text.Encoding]::Unicode.GetString($props.$propName)
# Remove null terminators
$searchTerm = $searchTerm -replace '\x00', ''
if ($searchTerm) {
$searches += $searchTerm
}
}
}
if ($searches.Count -gt 0) {
$foundAny = $true
Write-Host "Registry: $wordWheelPath`n" -ForegroundColor Gray
$count = 0
foreach ($search in ($searches | Select-Object -First $MaxResults)) {
$count++
Write-Host " $count. $search" -ForegroundColor Green
}
if ($searches.Count -gt $MaxResults) {
Write-Host "`n ... and $($searches.Count - $MaxResults) more" -ForegroundColor Gray
}
Write-Host "`nTotal searches: $($searches.Count)" -ForegroundColor Green
} else {
Write-Host " No search history found" -ForegroundColor Gray
}
} else {
Write-Host " No MRUListEx found" -ForegroundColor Gray
}
} else {
Write-Host " Registry key not found" -ForegroundColor Gray
}
} catch {
Write-Host " Error: $_" -ForegroundColor Red
}
# TypedPaths - File Explorer address bar typed paths
Write-Host "`n--- TypedPaths (File Explorer Address Bar) ---" -ForegroundColor Yellow
try {
$typedPathsPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\TypedPaths"
if (Test-Path $typedPathsPath) {
$paths = Get-ItemProperty -Path $typedPathsPath -ErrorAction SilentlyContinue
$pathList = @()
# TypedPaths stores as url1, url2, etc.
for ($i = 1; $i -le 50; $i++) {
$urlKey = "url$i"
if ($paths.$urlKey) {
$pathList += $paths.$urlKey
}
}
if ($pathList.Count -gt 0) {
$foundAny = $true
Write-Host "Registry: $typedPathsPath`n" -ForegroundColor Gray
$count = 0
foreach ($path in ($pathList | Select-Object -First $MaxResults)) {
$count++
Write-Host " $count. $path" -ForegroundColor Green
}
Write-Host "`nTotal typed paths: $($pathList.Count)" -ForegroundColor Green
} else {
Write-Host " No typed paths found" -ForegroundColor Gray
}
} else {
Write-Host " Registry key not found" -ForegroundColor Gray
}
} catch {
Write-Host " Error: $_" -ForegroundColor Red
}
# RunMRU - Run dialog history
Write-Host "`n--- RunMRU (Run Dialog History) ---" -ForegroundColor Yellow
try {
$runMRUPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU"
if (Test-Path $runMRUPath) {
$runProps = Get-ItemProperty -Path $runMRUPath -ErrorAction SilentlyContinue
$runList = @()
# RunMRU uses letter keys (a, b, c, etc.) and MRUList for order
if ($runProps.MRUList) {
$mruOrder = $runProps.MRUList.ToCharArray()
foreach ($char in $mruOrder) {
$key = [string]$char
if ($runProps.$key) {
# Remove \1 terminator if present
$command = $runProps.$key -replace '\\1$', ''
if ($command) {
$runList += $command
}
}
}
}
if ($runList.Count -gt 0) {
$foundAny = $true
Write-Host "Registry: $runMRUPath`n" -ForegroundColor Gray
$count = 0
foreach ($cmd in ($runList | Select-Object -First $MaxResults)) {
$count++
Write-Host " $count. $cmd" -ForegroundColor Green
}
Write-Host "`nTotal run commands: $($runList.Count)" -ForegroundColor Green
} else {
Write-Host " No run history found" -ForegroundColor Gray
}
} else {
Write-Host " Registry key not found" -ForegroundColor Gray
}
} catch {
Write-Host " Error: $_" -ForegroundColor Red
}
# Recent searches in specific apps (if available)
Write-Host "`n--- Cortana/Search App (if available) ---" -ForegroundColor Yellow
try {
# Check for modern search database
$searchPackage = Get-ChildItem "$env:LOCALAPPDATA\Packages" -Filter "Microsoft.Windows.Search_*" -Directory -ErrorAction SilentlyContinue | Select-Object -First 1
if ($searchPackage) {
$searchCache = Join-Path $searchPackage.FullName "LocalState\DeviceSearchCache"
if (Test-Path $searchCache) {
Write-Host " Search cache found: $searchCache" -ForegroundColor Cyan
Write-Host " (Binary database - requires specialized tools to parse)" -ForegroundColor Yellow
} else {
Write-Host " Search cache not found" -ForegroundColor Gray
}
} else {
Write-Host " Modern search app not found" -ForegroundColor Gray
}
} catch {
Write-Host " Error: $_" -ForegroundColor Red
}
if (-not $foundAny) {
Write-Host "`nNo search history found" -ForegroundColor Gray
Write-Host "This is common on Windows 11 without a Microsoft account" -ForegroundColor Yellow
}
Write-Host "`nNote: Search history shows user queries and typed paths" -ForegroundColor Cyan
Write-Host "This can reveal file names, apps, and topics of interest" -ForegroundColor Cyan
Write-Host "`nWindows 11 Limitation: Modern search without Microsoft account" -ForegroundColor Yellow
Write-Host "does not persist history to registry (cloud-first design)" -ForegroundColor Yellow
Write-Host "This works best on Windows 10 or systems with Microsoft accounts" -ForegroundColor Yellow