AutorunScan.psm1

#Requires -Version 5.1
# AutorunScan - https://github.com/dooblpls/AutorunScan - MIT License
# Enumerates Windows autostart locations (Sysinternals Autoruns scope + extras) with MITRE ATT&CK mapping.

$script:Mitre = @{
    'T1037.001' = 'Boot or Logon Initialization Scripts: Logon Script (Windows)'
    'T1053.005' = 'Scheduled Task/Job: Scheduled Task'
    'T1137.001' = 'Office Application Startup: Office Template Macros'
    'T1137.002' = 'Office Application Startup: Office Test'
    'T1137.004' = 'Office Application Startup: Outlook Home Page'
    'T1137.006' = 'Office Application Startup: Add-ins'
    'T1176.001' = 'Software Extensions: Browser Extensions'
    'T1197'     = 'BITS Jobs'
    'T1543.003' = 'Create or Modify System Process: Windows Service'
    'T1546'     = 'Event Triggered Execution'
    'T1546.001' = 'Event Triggered Execution: Change Default File Association'
    'T1546.002' = 'Event Triggered Execution: Screensaver'
    'T1546.003' = 'Event Triggered Execution: WMI Event Subscription'
    'T1546.007' = 'Event Triggered Execution: Netsh Helper DLL'
    'T1546.008' = 'Event Triggered Execution: Accessibility Features'
    'T1546.009' = 'Event Triggered Execution: AppCert DLLs'
    'T1546.010' = 'Event Triggered Execution: AppInit DLLs'
    'T1546.012' = 'Event Triggered Execution: Image File Execution Options Injection'
    'T1546.013' = 'Event Triggered Execution: PowerShell Profile'
    'T1546.015' = 'Event Triggered Execution: Component Object Model Hijacking'
    'T1547.001' = 'Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder'
    'T1547.002' = 'Boot or Logon Autostart Execution: Authentication Package'
    'T1547.003' = 'Boot or Logon Autostart Execution: Time Providers'
    'T1547.004' = 'Boot or Logon Autostart Execution: Winlogon Helper DLL'
    'T1547.005' = 'Boot or Logon Autostart Execution: Security Support Provider'
    'T1547.006' = 'Boot or Logon Autostart Execution: Kernel Modules and Extensions'
    'T1547.008' = 'Boot or Logon Autostart Execution: LSASS Driver'
    'T1547.010' = 'Boot or Logon Autostart Execution: Port Monitors'
    'T1547.012' = 'Boot or Logon Autostart Execution: Print Processors'
    'T1547.014' = 'Boot or Logon Autostart Execution: Active Setup'
    'T1548.002' = 'Abuse Elevation Control Mechanism: Bypass User Account Control'
    'T1556'     = 'Modify Authentication Process'
    'T1556.002' = 'Modify Authentication Process: Password Filter DLL'
    'T1556.008' = 'Modify Authentication Process: Network Provider DLL'
    'T1564'     = 'Hide Artifacts'
    'T1574.001' = 'Hijack Execution Flow: DLL'
    'T1574.012' = 'Hijack Execution Flow: COR_PROFILER'
}

# Kind: Values = each value of the key (Filter = regex on value name, '' = all, '^$' = default value)
# SubValues = matching values in all subkeys (recursive) | ClsidKeys / ClsidValues = COM CLSIDs -> InprocServer32
# ProgIdKeys = subkey names are ProgIDs. Split = regex to split one value into several entries.
# HKCU:\ is expanded to every loaded user hive, HKLM:\SOFTWARE\ additionally to Wow6432Node. '*' = wildcard, '[*]' = literal '*' key.
$script:Rows = @'
Category;Kind;Mitre;Filter;Split;Path
Logon;Values;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Logon;Values;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
Logon;SubValues;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx
Logon;Values;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices
Logon;Values;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce
Logon;Values;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
Logon;Values;T1547.001;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\Run
Logon;Values;T1547.001;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce
Logon;SubValues;T1547.001;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnceEx
Logon;Values;T1547.001;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\RunServices
Logon;Values;T1547.001;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce
Logon;Values;T1547.001;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
Logon;Values;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\Run
Logon;Values;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\RunOnce
Logon;SubValues;T1547.001;;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Windows\CurrentVersion\RunOnceEx
Logon;Values;T1547.001;^StartupPrograms$;,;HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd
Logon;Values;T1547.001;^InitialProgram$;;HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp
Logon;Values;T1547.001;^(Load|Run)$;;HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Windows
Logon;Values;T1547.001;^IconServiceLib$;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
Logon;Values;T1547.001;^Shell$;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
Logon;Values;T1547.001;^Shell$;;HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\System
Logon;Values;T1547.001;^AlternateShell$;;HKLM:\SYSTEM\CurrentControlSet\Control\SafeBoot
Logon;Values;T1547.001;^(Startup|Common Startup)$;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Logon;Values;T1547.001;^Startup$;;HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
Logon;SubValues;T1547.014;^StubPath$;;HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components
Logon;Values;T1037.001;^UserInitMprLogonScript$;;HKCU:\Environment
Logon;SubValues;T1037.001;^Script$;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts
Logon;SubValues;T1037.001;^Script$;;HKCU:\Software\Microsoft\Windows\CurrentVersion\Group Policy\Scripts
Winlogon;Values;T1547.004;^Userinit$;,;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
Winlogon;Values;T1547.004;^(Shell|Taskman|AppSetup|VmApplet|GinaDLL)$;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
Winlogon;Values;T1547.004;^(Shell|Userinit|Taskman)$;;HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon
Winlogon;SubValues;T1547.004;^DllName$;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify
Winlogon;SubValues;T1547.004;^DllName$;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions
Winlogon;ClsidKeys;T1556;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers
Winlogon;ClsidKeys;T1556;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Provider Filters
Winlogon;ClsidKeys;T1556;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\PLAP Providers
Boot Execute;Values;T1547.001;^(BootExecute|BootExecuteNoPnpSync|SetupExecute|Execute|S0InitialCommand|PlatformExecute)$;;HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager
Boot Execute;Values;T1547.001;^ImagePath$;;HKLM:\SYSTEM\CurrentControlSet\Control\BootVerificationProgram
Boot Execute;Values;T1547.001;^CmdLine$;;HKLM:\SYSTEM\Setup
AppInit;Values;T1546.010;^AppInit_DLLs$;[ ,];HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
AppInit;Values;T1546.009;;;HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls
Known DLLs;Values;T1574.001;;;HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
Image Hijacks;SubValues;T1546.012;^(Debugger|VerifierDlls)$;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
Image Hijacks;SubValues;T1546.012;^MonitorProcess$;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit
Image Hijacks;Values;T1546;^AutoRun$;;HKLM:\SOFTWARE\Microsoft\Command Processor
Image Hijacks;Values;T1546;^AutoRun$;;HKCU:\Software\Microsoft\Command Processor
Image Hijacks;Values;T1546;^Debugger$;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug
Image Hijacks;Values;T1546;^DbgManagedDebugger$;;HKLM:\SOFTWARE\Microsoft\.NETFramework
Image Hijacks;Values;T1546;^(Debugger|ReflectDebugger)$;;HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\Hangs
Image Hijacks;Values;T1574.012;^(COR_PROFILER|COR_PROFILER_PATH.*|CORECLR_PROFILER|CORECLR_PROFILER_PATH.*)$;;HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
Image Hijacks;Values;T1574.012;^(COR_PROFILER|COR_PROFILER_PATH.*|CORECLR_PROFILER|CORECLR_PROFILER_PATH.*)$;;HKCU:\Environment
LSA;Values;T1547.002;^Authentication Packages$;;HKLM:\SYSTEM\CurrentControlSet\Control\Lsa
LSA;Values;T1556.002;^Notification Packages$;;HKLM:\SYSTEM\CurrentControlSet\Control\Lsa
LSA;Values;T1547.005;^Security Packages$;;HKLM:\SYSTEM\CurrentControlSet\Control\Lsa
LSA;Values;T1547.005;^Security Packages$;;HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\OSConfig
LSA;Values;T1547.005;^SecurityProviders$;,;HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders
LSA;Values;T1547.008;^Extensions$;;HKLM:\SYSTEM\CurrentControlSet\Control\LsaExtensionConfig\LsaSrv
LSA;Values;T1547.008;^LsaDbExtPt$;;HKLM:\SYSTEM\CurrentControlSet\Services\NTDS
Print Monitors;SubValues;T1547.010;^Driver$;;HKLM:\SYSTEM\CurrentControlSet\Control\Print\Monitors
Print Monitors;SubValues;T1547.012;^Driver$;;HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\*\Print Processors
Time Providers;SubValues;T1547.003;^DllName$;;HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders
Netsh;Values;T1546.007;;;HKLM:\SOFTWARE\Microsoft\NetSh
Screensaver;Values;T1546.002;^SCRNSAVE\.EXE$;;HKCU:\Control Panel\Desktop
Screensaver;Values;T1546.002;^SCRNSAVE\.EXE$;;HKCU:\Software\Policies\Microsoft\Windows\Control Panel\Desktop
Winsock;SubValues;;^LibraryPath$;;HKLM:\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\NameSpace_Catalog5
Winsock;Values;;^AutodialDLL$;;HKLM:\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters
Explorer;ClsidValues;T1546.015;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad
Explorer;ClsidKeys;T1546.015;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellServiceObjects
Explorer;ClsidValues;T1546.015;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SharedTaskScheduler
Explorer;ClsidValues;T1546.015;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks
Explorer;ClsidKeys;T1546.015;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers
Explorer;ClsidKeys;T1546.015;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers
Explorer;ClsidKeys;T1546.015;;;HKLM:\SOFTWARE\Classes\PROTOCOLS\Handler
Explorer;ClsidKeys;T1546.015;;;HKLM:\SOFTWARE\Classes\PROTOCOLS\Filter
Internet Explorer;ClsidKeys;T1176.001;;;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects
Internet Explorer;ClsidKeys;T1176.001;;;HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects
Internet Explorer;ClsidValues;T1176.001;;;HKLM:\SOFTWARE\Microsoft\Internet Explorer\Toolbar
Internet Explorer;SubValues;T1176.001;^(Exec|Script|ClsidExtension)$;;HKLM:\SOFTWARE\Microsoft\Internet Explorer\Extensions
Internet Explorer;SubValues;T1176.001;^(Exec|Script|ClsidExtension)$;;HKCU:\Software\Microsoft\Internet Explorer\Extensions
Internet Explorer;ClsidValues;T1176.001;;;HKCU:\Software\Microsoft\Internet Explorer\UrlSearchHooks
Browser;Values;T1176.001;;;HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist
Browser;Values;T1176.001;;;HKLM:\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallForcelist
Codecs;Values;;;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32
Codecs;Values;;;;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Font Drivers
Codecs;ClsidKeys;;;;HKLM:\SOFTWARE\Classes\Filter
Codecs;ClsidKeys;;;;HKLM:\SOFTWARE\Classes\CLSID\{083863F1-70DE-11d0-BD40-00A0C911CE86}\Instance
Codecs;ClsidKeys;;;;HKLM:\SOFTWARE\Classes\CLSID\{AC757296-3522-4E11-9862-C17BE5A1767E}\Instance
Codecs;ClsidKeys;;;;HKLM:\SOFTWARE\Classes\CLSID\{7ED96837-96F0-4812-B211-F13C24117ED3}\Instance
Codecs;ClsidKeys;;;;HKLM:\SOFTWARE\Classes\CLSID\{ABE3B9A4-257D-4B97-BD1A-294AF496222E}\Instance
Office;ProgIdKeys;T1137.006;;;HKLM:\SOFTWARE\Microsoft\Office\*\Addins
Office;ProgIdKeys;T1137.006;;;HKCU:\Software\Microsoft\Office\*\Addins
Office;Values;T1137.006;^OPEN\d*$;;HKCU:\Software\Microsoft\Office\*\Excel\Options
Office;Values;T1137.002;^$;;HKLM:\SOFTWARE\Microsoft\Office test\Special\Perf
Office;Values;T1137.002;^$;;HKCU:\Software\Microsoft\Office test\Special\Perf
Office;SubValues;T1137.004;^URL$;;HKCU:\Software\Microsoft\Office\*\Outlook\WebView
Office;Values;T1137.004;^(Stamp|UserDefinedUrl)$;;HKCU:\Software\Microsoft\Office\*\Outlook\Today
'@
 | ConvertFrom-Csv -Delimiter ';'

foreach ($h in 'HKLM:\SOFTWARE\Classes', 'HKCU:\Software\Classes') {
    foreach ($t in 'exefile', 'batfile', 'cmdfile', 'comfile', 'piffile', 'scrfile', 'htafile', 'htmlfile', 'VBSFile', 'JSFile', 'WSFFile') {
        $script:Rows += [pscustomobject]@{ Category = 'Image Hijacks'; Kind = 'Values'; Mitre = 'T1546.001'; Filter = '^$'; Split = ''; Path = "$h\$t\shell\open\command" }
    }
    foreach ($o in '[*]', 'AllFileSystemObjects', 'Directory', 'Directory\Background', 'Folder', 'Drive') {
        foreach ($x in 'ContextMenuHandlers', 'PropertySheetHandlers', 'CopyHookHandlers', 'DragDropHandlers', 'ColumnHandlers') {
            $script:Rows += [pscustomobject]@{ Category = 'Explorer'; Kind = 'ClsidKeys'; Mitre = 'T1546.015'; Filter = ''; Split = ''; Path = "$h\$o\ShellEx\$x" }
        }
    }
}
foreach ($t in 'ms-settings', 'mscfile', 'Folder', 'exefile') {   # classic UAC bypass hijacks
    $script:Rows += [pscustomobject]@{ Category = 'Image Hijacks'; Kind = 'Values'; Mitre = 'T1548.002'; Filter = '^(|DelegateExecute)$'; Split = ''; Path = "HKCU:\Software\Classes\$t\shell\open\command" }
}

$GuidRx = '^\{[0-9a-fA-F-]{36}\}$'
$script:SearchDirs = @("$env:windir\System32", $env:windir, "$env:windir\System32\wbem", "$env:windir\System32\drivers",
    "$env:windir\System32\spool\prtprocs\x64", "$env:windir\System32\spool\prtprocs\w32x86") + ($env:Path -split ';' | Where-Object { $_ })

function Find-File([string]$p) {
    $p = $p.Trim().Trim('"').TrimStart('@')
    if (-not $p) { return }
    foreach ($q in $p, ($p -replace ',.*$'), ($p -replace '\|.*$')) {
        try { $rooted = [IO.Path]::IsPathRooted($q) } catch { continue }
        $cands = if ($rooted) { $q, "$q.exe", "$q.dll" } else { foreach ($d in $script:SearchDirs) { "$d\$q"; "$d\$q.exe"; "$d\$q.dll" } }
        foreach ($c in $cands) { if ([IO.File]::Exists($c)) { return [IO.Path]::GetFullPath($c) } }
    }
}

<#
.SYNOPSIS
    Resolves an autostart command line to the file that actually gets loaded (handles quoting, environment variables,
    \SystemRoot\, \??\, unquoted paths with spaces, rundll32 targets and bare file names on the search path).
.EXAMPLE
    Resolve-AutorunImage 'rundll32.exe shell32.dll,Control_RunDLL'
#>

function Resolve-AutorunImage([string]$Command) {
    if (-not $Command.Trim().Trim('"')) { return }
    $c = [Environment]::ExpandEnvironmentVariables($Command.Trim()) -replace '^\\\?\?\\' -replace '^\\SystemRoot\\', "$env:windir\" -replace '^(?i)system32\\', "$env:windir\System32\" -replace '^(?i)autocheck\s+'
    $f = $null
    if ($c -match '^"([^"]+)"') { $f = Find-File $matches[1] }
    else {
        # unquoted "C:\Program Files\x.exe -a": grow token by token until a file exists
        $tok = $c -split ' '
        for ($i = 0; $i -lt $tok.Count -and -not $f; $i++) { $f = Find-File ($tok[0..$i] -join ' ') }
    }
    if ($f -match '\\rundll32\.exe$' -and $c -match 'rundll32(?:\.exe)?"?\s+"?([^",]+)') {
        $dll = Find-File $matches[1]
        if ($dll) { $f = $dll }
    }
    $f
}

# direct .NET access: the registry provider is ~100x slower for thousands of lookups
function Get-RegValue([string]$Path, [string]$Name = '') {
    if ($Path -notmatch '^(HKLM:|Registry::HKEY_USERS)\\(.+)$') { return }
    $hive = if ($matches[1] -eq 'HKLM:') { [Microsoft.Win32.Registry]::LocalMachine } else { [Microsoft.Win32.Registry]::Users }
    $k = try { $hive.OpenSubKey($matches[2]) } catch { Write-Verbose "$_" }
    if ($k) { $k.GetValue($Name, $null, 'DoNotExpandEnvironmentNames') }
}

function Resolve-Clsid([string]$Clsid, [string]$UserRoot) {
    if ($Clsid -notmatch $GuidRx) { return }
    $roots = @(if ($UserRoot) { "$UserRoot\Software\Classes" }) + 'HKLM:\SOFTWARE\Classes', 'HKLM:\SOFTWARE\Classes\Wow6432Node'
    foreach ($r in $roots) {
        foreach ($s in 'InprocServer32', 'LocalServer32') {
            $v = Get-RegValue "$r\CLSID\$Clsid\$s"
            if ($v) { return $v }
        }
    }
}

function Get-UserRoot($Key) { if ($Key.Name -match '^HKEY_USERS\\([^\\]+)') { "Registry::HKEY_USERS\$($matches[1])" } }

function Expand-Hive([string]$Path) {
    if ($Path -like 'HKCU:\*') { foreach ($sid in $script:UserSids) { "Registry::HKEY_USERS\$sid\" + $Path.Substring(6) } }
    elseif ($Path -like 'HKLM:\SOFTWARE\*') { $Path; $Path -replace '^HKLM:\\SOFTWARE\\', 'HKLM:\SOFTWARE\Wow6432Node\' }
    else { $Path }
}

function Get-RegKey([string]$Path) {
    # own wildcard expansion: provider wildcards on HKCR take minutes. '[*]' = literal '*' key
    $seg = $Path -split '\\'
    $i = 0; while ($i -lt $seg.Count -and $seg[$i] -notmatch '[*?\[]') { $i++ }
    $keys = @(Get-Item -LiteralPath ($seg[0..($i - 1)] -join '\') -ErrorAction Ignore)
    for (; $i -lt $seg.Count; $i++) {
        $p = $seg[$i]
        $keys = foreach ($k in $keys) {
            if (-not $k) { continue }
            if ($p -match '[*?]' -and $p -ne '[*]') { $names = $k.GetSubKeyNames() -like $p } else { $names = $p -replace '^\[\*\]$', '*' }
            foreach ($n in $names) { try { $k.OpenSubKey($n) } catch { Write-Verbose "$_" } }
        }
    }
    $keys | Where-Object { $_ }
}

function Get-SubKeyTree($Key) {
    foreach ($n in $Key.GetSubKeyNames()) {
        try { $s = $Key.OpenSubKey($n) } catch { continue }
        if ($s) { $s; Get-SubKeyTree $s }
    }
}

function ConvertTo-AutorunEntry {
    param($Category, $Location, $Entry, $Command, $Mitre, $Info, $Image)
    if (-not $PSBoundParameters.ContainsKey('Image')) { $Image = Resolve-AutorunImage $Command }
    $vi = if ($Image) { try { [Diagnostics.FileVersionInfo]::GetVersionInfo($Image) } catch { Write-Verbose "$_" } }
    [pscustomobject]@{
        Category    = $Category
        Entry       = $Entry
        Command     = $Command
        ImagePath   = $Image
        Description = $vi.FileDescription
        Company     = $vi.CompanyName
        Signer      = $null
        SHA256      = $null
        Mitre       = $Mitre
        Technique   = $script:Mitre["$Mitre"]
        Info        = $Info
        Location    = $Location
    }
}

function Get-RegistryAutorun($Row) {
    $split = { param($d) foreach ($x in @($d)) { if ($Row.Split -and $x -is [string]) { $x -split $Row.Split } else { $x } } }
    foreach ($key in Get-RegKey $Row.Path) {
        $user = Get-UserRoot $key
        switch ($Row.Kind) {
            'Values' {
                foreach ($n in $key.GetValueNames()) {
                    if ($n -notmatch $Row.Filter) { continue }
                    foreach ($d in & $split $key.GetValue($n, $null, 'DoNotExpandEnvironmentNames')) {
                        if ("$d".Trim()) { ConvertTo-AutorunEntry $Row.Category $key.Name $(if ($n) { $n } else { '(Default)' }) "$d".Trim() $Row.Mitre }
                    }
                }
            }
            'SubValues' {
                foreach ($sk in Get-SubKeyTree $key) {
                    foreach ($n in $sk.GetValueNames()) {
                        if ($n -notmatch $Row.Filter) { continue }
                        foreach ($d in & $split $sk.GetValue($n, $null, 'DoNotExpandEnvironmentNames')) {
                            if ("$d".Trim()) { ConvertTo-AutorunEntry $Row.Category $sk.Name ($sk.Name -replace '^.*\\') "$d".Trim() $Row.Mitre $n }
                        }
                    }
                }
            }
            'ClsidValues' {
                foreach ($n in $key.GetValueNames()) {
                    $id = if ($n -match $GuidRx) { $n } else { "$($key.GetValue($n))" }
                    ConvertTo-AutorunEntry $Row.Category $key.Name $(if ($n) { $n } else { '(Default)' }) (Resolve-Clsid $id $user) $Row.Mitre $id
                }
            }
            'ClsidKeys' {
                foreach ($n in $key.GetSubKeyNames()) {
                    $sk = try { $key.OpenSubKey($n) } catch { Write-Verbose "$_" }
                    $id = @($n, "$($sk.GetValue(''))", "$($sk.GetValue('CLSID'))") -match $GuidRx | Select-Object -First 1
                    ConvertTo-AutorunEntry $Row.Category $key.Name $n (Resolve-Clsid $id $user) $Row.Mitre $id
                }
            }
            'ProgIdKeys' {
                foreach ($n in $key.GetSubKeyNames()) {
                    $sk = try { $key.OpenSubKey($n) } catch { Write-Verbose "$_" }
                    $cmd = $sk.GetValue('Manifest')
                    if (-not $cmd) {
                        foreach ($r in @(if ($user) { "$user\Software\Classes" }) + 'HKLM:\SOFTWARE\Classes') {
                            $id = Get-RegValue "$r\$n\CLSID"
                            if ($id) { $cmd = Resolve-Clsid $id $user; break }
                        }
                    }
                    ConvertTo-AutorunEntry $Row.Category $key.Name $n $cmd $Row.Mitre "LoadBehavior=$($sk.GetValue('LoadBehavior'))"
                }
            }
        }
    }
}

<#
.SYNOPSIS
    Enumerates Windows autostart locations (Sysinternals Autoruns scope plus more) with MITRE ATT&CK mapping.
.DESCRIPTION
    Categories: Logon, Winlogon, Explorer, Internet Explorer, Browser, Scheduled Tasks, Services, Drivers, Codecs,
    Boot Execute, Image Hijacks, AppInit, Known DLLs, Winsock, Print Monitors, LSA, Network Providers, Time Providers,
    Netsh, Screensaver, WMI, Office, PowerShell, Accessibility, COM Hijack, BITS.
    Run elevated in 64-bit PowerShell, otherwise other users' hives, some tasks, BITS jobs etc. are missing.
.PARAMETER Category
    Only scan these categories (wildcards allowed), e.g. 'Logon','Image*'.
.PARAMETER VerifySignature
    Check the Authenticode signature of each image (Signer column, catalog signatures included).
.PARAMETER HideMicrosoft
    Hide entries with a valid Microsoft signature (implies -VerifySignature).
.PARAMETER Hash
    Compute the SHA256 of each image file (e.g. for a VirusTotal lookup).
.EXAMPLE
    Get-Autorun -HideMicrosoft | Out-GridView
.EXAMPLE
    Get-Autorun -VerifySignature -Hash | Export-Csv autoruns.csv -NoTypeInformation -Encoding UTF8
.EXAMPLE
    Get-Autorun | Group-Object Mitre | Sort-Object Count -Descending
.LINK
    https://github.com/dooblpls/AutorunScan
#>

function Get-Autorun {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Category', Justification = 'Used inside the $want scriptblock')]
    [CmdletBinding()]
    param([string[]]$Category = '*', [switch]$VerifySignature, [switch]$HideMicrosoft, [switch]$Hash)

    if ([Environment]::Is64BitOperatingSystem -and -not [Environment]::Is64BitProcess) { Write-Warning '32-bit PowerShell on 64-bit Windows: System32 and registry are redirected, results are incomplete.' }
    $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole('Administrators')
    if (-not $isAdmin) { Write-Warning 'Not running elevated: other users, some scheduled tasks, BITS jobs etc. are missing.' }

    $want = { param($c) foreach ($p in $Category) { if ($c -like $p) { return $true } }; $false }
    $script:UserSids = (Get-ChildItem Registry::HKEY_USERS -ErrorAction Ignore).PSChildName -match '^S-1-5-(18|21-[\d-]+)$'
    $profiles = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -ErrorAction Ignore |
        ForEach-Object { $_.GetValue('ProfileImagePath') } | Where-Object { $_ -and (Test-Path -LiteralPath $_ -ErrorAction Ignore) }
    $sigCache = @{}
    $seen = [Collections.Generic.HashSet[string]]::new()

    & {
        foreach ($row in $script:Rows) {
            if (& $want $row.Category) { foreach ($p in Expand-Hive $row.Path) { Get-RegistryAutorun ([pscustomobject]@{ Category = $row.Category; Kind = $row.Kind; Mitre = $row.Mitre; Filter = $row.Filter; Split = $row.Split; Path = $p }) } }
        }

        # --- file system based ---
        $files = @(
            foreach ($p in $profiles) {
                'Logon', 'T1547.001', "$p\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\*"
                'PowerShell', 'T1546.013', "$p\Documents\WindowsPowerShell\*profile.ps1", "$p\Documents\PowerShell\*profile.ps1", "$p\OneDrive*\*\WindowsPowerShell\*profile.ps1", "$p\OneDrive*\*\PowerShell\*profile.ps1"
                'Office', 'T1137.001', "$p\AppData\Roaming\Microsoft\Word\STARTUP\*", "$p\AppData\Roaming\Microsoft\Excel\XLSTART\*"
                'Office', 'T1137.006', "$p\AppData\Roaming\Microsoft\AddIns\*"
                'Browser', 'T1176.001', "$p\AppData\Roaming\Mozilla\Firefox\Profiles\*\extensions\*"
            }
            'Logon', 'T1547.001', "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\*"
            'Logon', 'T1037.001', "$env:windir\System32\GroupPolicy\*\Scripts\*\*", "$env:windir\System32\GroupPolicyUsers\*\*\Scripts\*\*"
            'PowerShell', 'T1546.013', "$env:windir\System32\WindowsPowerShell\v1.0\*profile.ps1", "$env:ProgramFiles\PowerShell\*\*profile.ps1"
            'Office', 'T1137.001', "$env:ProgramFiles\Microsoft Office\root\Office*\STARTUP\*", "$env:ProgramFiles\Microsoft Office\root\Office*\XLSTART\*"
            'Accessibility', 'T1546.008', ('sethc', 'utilman', 'osk', 'Magnify', 'Narrator', 'DisplaySwitch', 'AtBroker' | ForEach-Object { "$env:windir\System32\$_.exe" })
        )
        $cat = $null; $mitre = $null
        foreach ($x in $files) {
            if ($x -notmatch '[\\/]') { if ($x -match '^T\d') { $mitre = $x } else { $cat = $x }; continue }
            if (-not (& $want $cat)) { continue }
            foreach ($f in Get-ChildItem -Path $x -Force -File -ErrorAction Ignore | Where-Object Name -ne 'desktop.ini') {
                $cmd = $f.FullName
                if ($f.Extension -eq '.lnk') {
                    $l = (New-Object -ComObject WScript.Shell).CreateShortcut($f.FullName)
                    $cmd = "$($l.TargetPath) $($l.Arguments)".Trim()
                }
                ConvertTo-AutorunEntry $cat $f.DirectoryName $f.Name $cmd $mitre $f.LastWriteTime.ToString('s')
            }
        }
        if (& $want 'Browser') {
            foreach ($p in $profiles) {
                foreach ($m in Get-ChildItem "$p\AppData\Local\*\User Data\*\Extensions\*\*\manifest.json", "$p\AppData\Local\*\*\User Data\*\Extensions\*\*\manifest.json" -ErrorAction Ignore) {
                    $j = try { Get-Content -LiteralPath $m.FullName -Raw | ConvertFrom-Json } catch { Write-Verbose "$_" }
                    $name = $j.name
                    if ($name -match '^__MSG_(.+)__$') {
                        # localized name: _locales\<default_locale>\messages.json, keys are case-insensitive
                        $msg = try { Get-Content -LiteralPath "$($m.DirectoryName)\_locales\$($j.default_locale)\messages.json" -Raw | ConvertFrom-Json } catch { Write-Verbose "$_" }
                        $prop = $msg.PSObject.Properties | Where-Object Name -eq $matches[1] | Select-Object -First 1
                        if ($prop) { $name = $prop.Value.message }
                    }
                    ConvertTo-AutorunEntry 'Browser' $m.Directory.Parent.Parent.FullName "$name [$($m.Directory.Parent.Name)]" $m.DirectoryName 'T1176.001' "v$($j.version)" -Image $null
                }
            }
        }

        # --- services / drivers ---
        if (& $want 'Services') {
            foreach ($s in Get-CimInstance Win32_Service) {
                $p = "HKLM:\SYSTEM\CurrentControlSet\Services\$($s.Name)"
                $dll = Get-RegValue "$p\Parameters" 'ServiceDll'
                ConvertTo-AutorunEntry 'Services' $p $s.Name $(if ($dll) { $dll } else { $s.PathName }) 'T1543.003' "$($s.StartMode)/$($s.State): $($s.DisplayName)"
                $fc = Get-RegValue $p 'FailureCommand'
                if ($fc) { ConvertTo-AutorunEntry 'Services' $p "$($s.Name) (FailureCommand)" $fc 'T1543.003' }
            }
        }
        if (& $want 'Drivers') {
            foreach ($d in Get-CimInstance Win32_SystemDriver) {
                ConvertTo-AutorunEntry 'Drivers' "HKLM:\SYSTEM\CurrentControlSet\Services\$($d.Name)" $d.Name $d.PathName 'T1547.006' "$($d.StartMode)/$($d.State): $($d.DisplayName)"
            }
        }
        if (& $want 'Network Providers') {
            $order = Get-RegValue 'HKLM:\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order' 'ProviderOrder'
            foreach ($n in "$order" -split ',' | Where-Object { $_ }) {
                $p = "HKLM:\SYSTEM\CurrentControlSet\Services\$n\NetworkProvider"
                ConvertTo-AutorunEntry 'Network Providers' $p $n (Get-RegValue $p 'ProviderPath') 'T1556.008'
            }
        }
        if (& $want 'Winsock') {
            foreach ($c in 'Catalog_Entries', 'Catalog_Entries64') {
                foreach ($k in Get-ChildItem "HKLM:\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\Protocol_Catalog9\$c" -ErrorAction Ignore) {
                    # PackedCatalogItem: char[260] DLL path, then WSAPROTOCOL_INFOW (szProtocol at +116)
                    $b = $k.GetValue('PackedCatalogItem')
                    if ($b.Length -lt 888) { continue }
                    $dll = [Text.Encoding]::Default.GetString($b, 0, 260).Split([char]0)[0]
                    $name = [Text.Encoding]::Unicode.GetString($b, 376, 512).Split([char]0)[0]
                    ConvertTo-AutorunEntry 'Winsock' $k.Name $name $dll '' $c
                }
            }
        }

        # --- scheduled tasks ---
        if (& $want 'Scheduled Tasks') {
            $tasks = @(Get-ScheduledTask -ErrorAction Ignore)
            foreach ($t in $tasks) {
                foreach ($a in $t.Actions) {
                    $cmd = if ($a.Execute) { "$($a.Execute) $($a.Arguments)".Trim() } else { Resolve-Clsid $a.ClassId }
                    ConvertTo-AutorunEntry 'Scheduled Tasks' "Task Scheduler$($t.TaskPath)" "$($t.TaskPath)$($t.TaskName)" $cmd 'T1053.005' "$($t.State)$(if ($a.ClassId) { " COM $($a.ClassId) $($a.Data)" })"
                }
            }
            if ($isAdmin) {
                # tasks hidden from the API (SD removed, e.g. Tarrask) still live in TaskCache\Tree
                $known = @{}; foreach ($t in $tasks) { $known["$($t.TaskPath)$($t.TaskName)"] = 1 }
                foreach ($k in Get-SubKeyTree (Get-Item 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree')) {
                    if (-not $k.GetValue('Id')) { continue }
                    $name = '\' + ($k.Name -replace '^.*\\TaskCache\\Tree\\')
                    if (-not $k.GetValue('SD') -or -not $known[$name]) {
                        ConvertTo-AutorunEntry 'Scheduled Tasks' $k.Name $name '' 'T1564' "HIDDEN: no SD or not visible via Task Scheduler API, Id $($k.GetValue('Id'))" -Image $null
                    }
                }
            }
        }

        # --- WMI event subscriptions ---
        if (& $want 'WMI') {
            foreach ($ns in 'root\subscription', 'root\default') {
                $filters = @{}; Get-CimInstance -Namespace $ns -ClassName __EventFilter -ErrorAction Ignore | ForEach-Object { $filters[$_.Name] = $_.Query }
                $bind = @{}; Get-CimInstance -Namespace $ns -ClassName __FilterToConsumerBinding -ErrorAction Ignore | ForEach-Object { $bind[$_.Consumer.Name] = $filters[$_.Filter.Name] }
                foreach ($c in Get-CimInstance -Namespace $ns -ClassName __EventConsumer -ErrorAction Ignore) {
                    $cmd = @($c.CommandLineTemplate, $c.ExecutablePath, $c.ScriptFileName, $c.ScriptText) | Where-Object { $_ } | Select-Object -First 1
                    ConvertTo-AutorunEntry 'WMI' "$ns\$($c.CimClass.CimClassName)" $c.Name "$cmd" 'T1546.003' "Filter: $($bind[$c.Name])"
                }
            }
        }

        # --- per-user COM objects (shadow HKLM = classic COM hijack) ---
        if (& $want 'COM Hijack') {
            foreach ($sid in $script:UserSids) {
                foreach ($k in Get-RegKey "Registry::HKEY_USERS\$sid\Software\Classes\CLSID") {
                    foreach ($id in $k.GetSubKeyNames()) {
                        foreach ($s in 'InprocServer32', 'LocalServer32', 'TreatAs', 'ScriptletURL') {
                            $sk = try { $k.OpenSubKey("$id\$s") } catch { Write-Verbose "$_" }
                            if (-not $sk -or -not $sk.GetValue('')) { continue }
                            $shadow = if (Test-Path -LiteralPath "HKLM:\SOFTWARE\Classes\CLSID\$id") { 'shadows HKLM CLSID' }
                            ConvertTo-AutorunEntry 'COM Hijack' $sk.Name $id $sk.GetValue('', $null, 'DoNotExpandEnvironmentNames') 'T1546.015' "$s $shadow".Trim()
                        }
                    }
                }
            }
        }

        if ((& $want 'BITS') -and $isAdmin) {
            foreach ($j in Get-BitsTransfer -AllUsers -ErrorAction Ignore) {
                ConvertTo-AutorunEntry 'BITS' "BITS\$($j.OwnerAccount)" $j.DisplayName "$($j.NotifyCmdLine)" 'T1197' "$($j.JobState): $($j.FileList.RemoteName -join ', ')"
            }
        }
    } | ForEach-Object {
        # shared (non-redirected) keys show up again under Wow6432Node
        if (-not $seen.Add("$($_.Category)|$($_.Entry)|$($_.Command)|$($_.Location -replace '\\Wow6432Node')")) { return }
        if (($VerifySignature -or $HideMicrosoft) -and $_.ImagePath) {
            if (-not $sigCache.ContainsKey($_.ImagePath)) {
                $s = Get-AuthenticodeSignature -LiteralPath $_.ImagePath -ErrorAction Ignore
                $sigCache[$_.ImagePath] = if ($s.SignerCertificate) { "($($s.Status)) " + ($s.SignerCertificate.Subject -replace '^CN="?([^,"]+).*', '$1') } else { "($($s.Status))" }
            }
            $_.Signer = $sigCache[$_.ImagePath]
        }
        if ($HideMicrosoft -and $_.Signer -like '(Valid) Microsoft*') { return }
        if ($Hash -and $_.ImagePath) { $_.SHA256 = (Get-FileHash -LiteralPath $_.ImagePath -ErrorAction Ignore).Hash }
        $_
    }
}

Export-ModuleMember -Function Get-Autorun, Resolve-AutorunImage