# Experiment for retrobar.dev: StartAllBack, third attempt. The first two installed it but it never loaded into # Explorer. This one records how it hooks into Windows (files, Run entries, scheduled tasks, registry), runs whatever # it registered for sign-in (the runners cannot sign out and in), restarts Explorer, and if that is not enough tries # the all-users install. Installer: the URL, SHA-256 and switches in Microsoft's winget manifest. Results: verify//. $OutName = if ($env:VERIFY_OUT) { $env:VERIFY_OUT } else { 'results-sab' } . (Join-Path (Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)) 'verify\lib.ps1') New-Item -ItemType Directory -Force -Path (Join-Path $out 'shots') | Out-Null $results = [ordered]@{ started = (Get-Date).ToString('o'); attempts = [ordered]@{} } function Step($name, [scriptblock]$body) { Log "== $name"; try { & $body } catch { Log "STEP FAILED ($name): $_"; $results["error_$name"] = "$_" }; Save 'results.json' $results } function TbShot($name) { Dismiss-Oobe; Hide-Consoles; Start-Sleep 1 Shot "shots/$name" $tb = [N]::FindWindow('Shell_TrayWnd', $null); $rc = New-Object N+RECT; [N]::GetWindowRect($tb, [ref]$rc) | Out-Null $png = Join-Path $out "shots/$name.png" if ($rc.Right -gt $rc.Left) { Crop-Png $png (Join-Path $out "shots/$name-strip.png") $rc.Left $rc.Top ($rc.Right - $rc.Left) ($rc.Bottom - $rc.Top) 1 Crop-Png $png (Join-Path $out "shots/$name-start-2x.png") $rc.Left $rc.Top 600 ($rc.Bottom - $rc.Top) 2 } } function Restart-Explorer { Stop-Process -Name explorer -Force; Start-Sleep 8; if (-not (Get-Process explorer -ErrorAction SilentlyContinue)) { Start-Process explorer.exe; Start-Sleep 8 }; Start-Sleep 5 } # Every module loaded into Explorer that is not part of Windows, and whether any belongs to StartAllBack function Explorer-Modules { @(Get-Process explorer -ErrorAction SilentlyContinue | ForEach-Object { try { $_.Modules | Where-Object { $_.FileName -notmatch '(?i)\\Windows\\(System32|SysWOW64|WinSxS|SystemApps|SysArm32|Microsoft\.NET)\\' } | ForEach-Object { $_.FileName } } catch { "error: $_" } }) | Select-Object -Unique } function Sab-Loaded { [bool](Explorer-Modules | Where-Object { $_ -match '(?i)startallback|startisback' }) } function Diagnose($label) { $d = [ordered]@{} $d.files = @(foreach ($p in "$env:LOCALAPPDATA\StartAllBack", "$env:ProgramFiles\StartAllBack", "${env:ProgramFiles(x86)}\StartAllBack") { if (Test-Path $p) { Get-ChildItem $p -Recurse -File | ForEach-Object { "$($_.FullName) $($_.Length)" } } }) $d.run = [ordered]@{} foreach ($k in 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run', 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run', 'HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce', 'HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce') { $p = Get-ItemProperty $k -ErrorAction SilentlyContinue if ($p) { foreach ($n in $p.PSObject.Properties.Name | Where-Object { $_ -notmatch '^PS' }) { $d.run["$k\$n"] = "$($p.$n)" } } } $d.tasks = @(Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.TaskName -match '(?i)startallback|startisback' -or (($_.Actions | ForEach-Object { "$($_.Execute) $($_.Arguments)" }) -join ' ') -match '(?i)startallback|startisback' } | ForEach-Object { "$($_.TaskPath)$($_.TaskName) | $(($_.Actions | ForEach-Object { "$($_.Execute) $($_.Arguments)" }) -join '; ') | $($_.State)" }) $d.registry = [ordered]@{} foreach ($k in 'HKCU:\Software\StartIsBack', 'HKLM:\Software\StartIsBack', 'HKCU:\Software\StartAllBack') { if (Test-Path $k) { foreach ($i in @(Get-Item $k) + @(Get-ChildItem $k -Recurse -ErrorAction SilentlyContinue)) { $p = Get-ItemProperty $i.PSPath -ErrorAction SilentlyContinue; foreach ($n in $p.PSObject.Properties.Name | Where-Object { $_ -notmatch '^PS' }) { $d.registry["$($i.Name)\$n"] = "$($p.$n)" } } } } $d.enableLUA = (Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System' -ErrorAction SilentlyContinue).EnableLUA $d.com = @(foreach ($root in 'HKCU:\Software\Classes\CLSID', 'HKLM:\Software\Classes\CLSID') { Get-ChildItem $root -ErrorAction SilentlyContinue | ForEach-Object { $ip = Join-Path $_.PSPath 'InprocServer32'; $v = (Get-ItemProperty $ip -ErrorAction SilentlyContinue).'(default)'; if ($v -match '(?i)startallback|startisback') { "$root\$($_.PSChildName) = $v" } } }) $d.processes = @(Get-Process | Where-Object { $_.ProcessName -match '(?i)startallback|startisback' -or ($_.Path -match '(?i)startallback') } | ForEach-Object { "$($_.ProcessName) $($_.Path)" }) $d.explorerModules = Explorer-Modules $d.loaded = Sab-Loaded $d.topWindows = Get-TopWindows $results.attempts[$label] = $d Log "$label : loaded=$($d.loaded) files=$($d.files.Count) run=$($d.run.Count) tasks=$($d.tasks.Count) procs=$($d.processes.Count)" return $d.loaded } # Run what StartAllBack registered for sign-in: its Run entries and scheduled tasks function Simulate-SignIn($label) { $done = @() foreach ($e in $results.attempts[$label].run.GetEnumerator() | Where-Object { $_.Value -match '(?i)startallback|startisback' }) { $cmd = $e.Value; $exe = if ($cmd -match '^"([^"]+)"\s*(.*)$') { $matches[1] } else { $cmd.Split(' ')[0] }; $a = if ($cmd -match '^"[^"]+"\s*(.*)$') { $matches[1] } else { ($cmd.Split(' ', 2) + '')[1] } try { if ($a) { Start-Process $exe -ArgumentList $a } else { Start-Process $exe }; $done += "run: $cmd" } catch { $done += "run failed: $cmd : $_" } } foreach ($t in Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.TaskName -match '(?i)startallback|startisback' }) { try { Start-ScheduledTask -InputObject $t; $done += "task: $($t.TaskName)" } catch { $done += "task failed: $($t.TaskName) : $_" } } Start-Sleep 6 $results.attempts[$label].signInActions = $done Log "sign-in simulation: $($done -join ' ; ')" } Step 'machine' { Set-Screen $cv = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $results.machine = [ordered]@{ os = (Get-CimInstance Win32_OperatingSystem).Caption; edition = $cv.EditionID; installationType = $cv.InstallationType; build = "$($cv.CurrentBuild).$($cv.UBR)"; displayVersion = $cv.DisplayVersion; arch = $env:PROCESSOR_ARCHITECTURE } $ex = Get-Process explorer | Select-Object -First 1 $results.machine.explorerPath = $ex.Path TbShot 'sab-00-windows-taskbar' } Step 'manifest' { $h = @{ 'User-Agent' = 'retrobar.dev-verify' }; if ($env:GITHUB_TOKEN) { $h.Authorization = "Bearer $env:GITHUB_TOKEN" } $base = 'https://api.github.com/repos/microsoft/winget-pkgs/contents/manifests/s/StartIsBack/StartAllBack' $vers = @(Invoke-RestMethod $base -Headers $h | Where-Object { $_.type -eq 'dir' } | ForEach-Object { $_.name }) $latest = $vers | Sort-Object { [version]($_ -replace '[^0-9.]', '') } | Select-Object -Last 1 $files = Invoke-RestMethod "$base/$latest" -Headers $h $inst = $files | Where-Object { $_.name -match 'installer\.yaml$' } | Select-Object -First 1 $yaml = (Invoke-WebRequest $inst.download_url -UseBasicParsing).Content $url = ([regex]::Match($yaml, 'InstallerUrl:\s*(\S+)')).Groups[1].Value $sha = ([regex]::Match($yaml, 'InstallerSha256:\s*(\S+)')).Groups[1].Value $script:Setup = Join-Path $env:TEMP ([IO.Path]::GetFileName(([uri]$url).AbsolutePath)) Invoke-WebRequest $url -OutFile $script:Setup -UseBasicParsing $got = (Get-FileHash $script:Setup -Algorithm SHA256).Hash $sig = Get-AuthenticodeSignature $script:Setup $results.setup = [ordered]@{ version = $latest; url = $url; sha256 = $got.ToLower(); matchesManifest = ($got -eq $sha); signature = "$($sig.Status)" } if ($got -ne $sha) { throw 'hash does not match the winget manifest' } } Step 'per-user' { $p = Start-Process $script:Setup -ArgumentList '/silent' -PassThru; $null = $p.Handle; $p.WaitForExit(180000) | Out-Null $results.setup.perUserExit = $p.ExitCode Start-Sleep 10 Shot 'shots/sab-01-after-install' if (Diagnose 'afterInstall') { return } Simulate-SignIn 'afterInstall' Restart-Explorer $ok = Diagnose 'afterSignInSimulation' TbShot 'sab-02-after-signin' Key @(0x5B); Start-Sleep 3; Shot 'shots/sab-03-after-signin-start'; Key @(0x1B); Start-Sleep 2 } Step 'all-users' { if ($results.attempts['afterSignInSimulation'].loaded -or $results.attempts['afterInstall'].loaded) { Log 'already loaded; skipping all-users'; return } # The setup will not change the installation type while a per-user copy exists: remove it first (its quiet uninstall) $cfg = Join-Path $env:LOCALAPPDATA 'StartAllBack\StartAllBackCfg.exe' if (Test-Path $cfg) { $u = Start-Process $cfg -ArgumentList '/uninstall /silent' -PassThru; $null = $u.Handle; $u.WaitForExit(120000) | Out-Null; $results.setup.perUserUninstallExit = $u.ExitCode; Start-Sleep 8 } $results.setup.perUserFolderLeft = Test-Path (Join-Path $env:LOCALAPPDATA 'StartAllBack\StartAllBackX64.dll') if (-not (Get-Process explorer -ErrorAction SilentlyContinue)) { Start-Process explorer.exe; Start-Sleep 8 } $p = Start-Process $script:Setup -PassThru; $null = $p.Handle; Start-Sleep 8 $w = $null; foreach ($x in $UIA::RootElement.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition)) { if ($x.Current.Name -match 'StartAllBack installation') { $w = $x; break } } if (-not $w) { Log 'setup window not found'; return } $results.setup.windowTexts = @($w.FindAll([System.Windows.Automation.TreeScope]::Descendants, [System.Windows.Automation.Condition]::TrueCondition) | ForEach-Object { $_.Current.Name } | Where-Object { $_ }) $el = FindName 'Install for everyone' 3 $w if ($el) { $r = $el.Current.BoundingRectangle; Click ([int]($r.X + $r.Width / 2)) ([int]($r.Y + $r.Height / 2)); $results.setup.clicked = 'Install for everyone' } else { $r = $w.Current.BoundingRectangle; Click ([int]($r.X + $r.Width * 0.45)) ([int]($r.Y + $r.Height * 0.55)); $results.setup.clicked = 'by position' } $done = $p.WaitForExit(180000) $results.setup.allUsersExit = if ($done) { $p.ExitCode } else { 'timeout' } Start-Sleep 10 Shot 'shots/sab-04-after-allusers-install' Diagnose 'afterAllUsersInstall' | Out-Null Restart-Explorer Diagnose 'afterAllUsersRestart' | Out-Null TbShot 'sab-04-after-allusers' Key @(0x5B); Start-Sleep 3; Shot 'shots/sab-05-after-allusers-start'; Key @(0x1B); Start-Sleep 2 } Step 'styles' { if (-not (Sab-Loaded)) { Log 'StartAllBack not loaded; styles skipped'; return } $cfg = Get-ChildItem "$env:ProgramFiles\StartAllBack", "$env:LOCALAPPDATA\StartAllBack" -Filter 'StartAllBackCfg.exe' -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 foreach ($name in 'Remastered 7', 'Kinda 10', 'Proper 11') { Start-Process $cfg.FullName; Start-Sleep 5 $w = $null; foreach ($x in $UIA::RootElement.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition)) { if ($x.Current.Name -match 'StartAllBack') { $w = $x; break } } if (-not $w) { continue } $el = FindName $name 3 $w; if (-not $el) { continue } Invoke-El $el | Out-Null; Start-Sleep 6 $slug = $name.ToLower() -replace '[^a-z0-9]+', '-' try { (Get-Process -Id $w.Current.ProcessId).CloseMainWindow() | Out-Null } catch { }; Start-Sleep 3 TbShot "sab-06-$slug" Key @(0x5B); Start-Sleep 3; Shot "shots/sab-07-$slug-start"; Key @(0x1B); Start-Sleep 2 } } $results.finished = (Get-Date).ToString('o') Save 'results.json' $results