Windows 自启动项的 增 / 删 / 改 / 查 桌面工具。PowerShell + WPF,零依赖,自带 UAC 提权。 看得见任务管理器看不见的地方,每一步都可撤销,可选接入大模型做研判。
BootScope — a Windows startup-item manager that sees what Task Manager can't: hidden registry hives (
HKU\S-1-5-18,HKU\.DEFAULT), risk scoring with Authenticode verification, per-step undo, and optional AI triage. Single-file PowerShell + WPF, zero dependencies.
双击 启视.vbs → 确认 UAC 提权 → 主界面。
VBS 只是一层隐藏窗口的启动器(避免闪黑框);提权由 BootScope.ps1 自己完成。
也可以直接跑:pwsh -STA -ExecutionPolicy Bypass -File BootScope.ps1
-STA是硬性要求。WPF 只能在单线程单元(STA)里创建窗口,而 PowerShell 7 在部分宿主下默认 MTA。脚本会自检并在必要时带-STA重启自己(只重启一次, 失败会明确报错而不是反复弹 UAC)。
改完代码记得关掉窗口重开。 代码在启动那一刻就整个加载进内存了, 改文件对已经跑着的进程没有任何影响。标题栏「构建 MM-DD HH:mm」显示的是 主程序文件的修改时间 —— 如果你刚改过代码而这里还是旧时间,那就是在看旧实例。
- Windows 10 / 11
- PowerShell 7(优先)或系统自带的 Windows PowerShell 5.1
- 必须管理员权限 —— HKLM、服务、计划任务都需要
- AI 分析功能需要 Anthropic API Key 和外网连接(可选,不配置不影响其余功能)
| 类型 | 位置 |
|---|---|
| 注册表 Run | HKCU · HKLM · HKCU\WOW6432Node · HKLM\WOW6432Node |
| 注册表 RunOnce | HKCU · HKLM · HKLM\WOW6432Node |
| 注册表(易被忽略) | HKU\S-1-5-18(SYSTEM 账户)· HKU\.DEFAULT(新用户模板) |
| 启动文件夹 | 当前用户 · 所有用户 |
| 计划任务 | 登录 / 开机触发(\Microsoft\Windows\* 下的系统任务不列出) |
| 服务 | 启动类型为「自动」的服务,加上被本工具改成「手动」的服务 |
HKU\S-1-5-18 和 HKU\.DEFAULT 这两处在任务管理器和 msconfig 里都看不到,
是卸载残留和持久化的常见藏身处。
注:没有
HKCU\WOW6432Node\...\RunOnce。Windows 对 HKCU 的 Run 系列不做 WOW64 重定向,该位置实际不被系统使用。
| 操作 | Run 键 | 启动文件夹 | 计划任务 | 服务 |
|---|---|---|---|---|
| 查(枚举 / 搜索 / 筛选) | ✓ | ✓ | ✓ | ✓ |
| 增(新建) | ✓ 仅 HKCU / HKLM / HKLM32 | ✓ 生成 .lnk | ✓ 登录触发·最高权限 | — |
| 改(改名 / 改命令) | ✓ | ✓ 仅 .lnk | ✓ 单动作任务 | — |
| 改(启用 / 禁用) | ✓ | ✓ | ✓ | ✓ 自动 ⇄ 手动 |
| 删 | ✓ | ✓ 送回收站 | ✓ | ✓ sc delete |
RunOnce 与 HKU\S-1-5-18、HKU\.DEFAULT 没有 StartupApproved 机制,
因此不支持启用/禁用开关,只能删除 —— 详情面板的「可操作」一行会标出。
「新建」对话框只提供 HKCU / HKLM / HKLM32 三个注册表位置。RunOnce、SYSTEM、
.DEFAULT 只能查看和删除,不能新建。
计划任务的编辑(改程序路径 / 参数 / 改名)在此支持,可撤销。三条限制:
- 有多个动作的任务会被拒绝 —— 在这里只能整体替换成一条,会丢掉其余动作。这类请用「打开位置」。
\Microsoft\Windows\*下的系统任务不可编辑。- 改名的实现是「用同一份触发器 / 权限 / 设置在新名下重建任务,再注销旧任务」。 顺序是先建后删,中途失败也不会把原任务弄丢。
服务不支持在此编辑。改 binPath 风险高(改错服务就起不来,且本工具无法可靠回滚),
需要时请手工执行 sc config <服务名> binPath= <新路径>。
不删条目、不改命令,而是写 StartupApproved 的标志位字节:
HKCU|HKLM \...\Explorer\StartupApproved\{Run|Run32|StartupFolder}
启用 02 00 00 00 00 00 00 00 00 00 00 00
禁用 03 00 00 00 <8 字节 FILETIME 记录禁用时刻>
首字节偶数 = 启用,奇数 = 禁用
与任务管理器「启动」页完全互通 —— 在这里禁用的,任务管理器里也显示已禁用,反之亦然。
服务是例外:它没有 StartupApproved,禁用=把启动类型从「自动」改成「手动」。
被这样改过的服务会记在 managed-services.json 里,刷新后仍然留在列表中,
可以再点一次开关改回「自动」。
表格第一列是复选框。只要有勾选项,「删除」和「启用/禁用」就作用于全部勾选项 (跨筛选条件累积);没有勾选时才退回到表格里高亮的行,Ctrl / Shift 点选依然可用。 右上角实时显示「已勾选 N 项 · 当前筛选 X / Y」。
点某一行时,详情面板始终显示那一行;若同时存在勾选项,面板底部会提醒 「删除 / 启用禁用 会作用于那 N 项,而不是当前高亮行」。
刷新(含切换两个复选框)不再丢失勾选,按「类型+位置+名称」自动恢复。
勾选栏按钮:
| 按钮 | 作用 |
|---|---|
| 全选筛选结果 | 勾选当前筛选出的所有条目(先筛后选,这是最常用的组合) |
| 清空 | 取消全部勾选(含被筛选条件隐藏的) |
| 反选 | 对当前筛选结果取反 |
| 所有残留项 | 跨筛选一键勾中所有「目标文件不存在」的条目 |
| 危险 + 关注 | 跨筛选一键勾中所有危险与需关注的条目 |
后两个按钮会先清空已有勾选,并把三个筛选框和搜索框重置为全部,然后勾中命中项。
批量删除和批量启用/禁用前都会弹出可滚动的确认清单,列出每一项的风险 / 名称 / 类型 / 命令。删除清单还会单独警示其中有多少项属于「删除后无法自动撤销」 (计划任务、服务)或「文件内容无法重建」(启动文件夹里的脚本); 启用/禁用清单会说明「各自取反」会把哪些已禁用项重新启用。
| 键 | 作用 |
|---|---|
F5 |
刷新 |
Ctrl + F |
聚焦搜索框并全选 |
Delete |
删除当前目标(焦点不在搜索框时) |
Esc |
清空搜索框;搜索框已空时取消表格高亮 |
| 双击行 | 定位文件 |
新建 / 编辑 / AI 设置对话框:Enter 确定、Esc 取消。
删除确认框刻意不响应 Enter —— 破坏性操作不该按一下回车就执行。
删除和编辑前,条目的完整定义会追加写入
%LOCALAPPDATA%\BootScope\rollback.json,用「撤销上一步」还原。
一次批量删除算一步。 同批条目共用一个批次号,点一次「撤销上一步」整批还原, 不需要按删除次数点很多次。每项的启用/禁用状态也会逐项按原样恢复(删除与编辑都是)。
可撤销:Run 键的删除与编辑、启动文件夹 .lnk 的删除。 不可撤销:计划任务与服务的删除(需手动重建)、被删脚本文件的内容、 启动文件夹 .lnk 的编辑。
撤销确认框会在点下去之前列出这一批里有多少项恢复不了。撤销后整批出栈,
其中恢复失败的记录不会丢 —— 会存档到同目录的 unrecoverable.json 供手工重建。
启动文件夹里的文件删除时送回收站(而不是直接抹除),因为回滚日志只能重建 .lnk, 脚本文件的内容重建不出来 —— 回收站是这类文件唯一的第二道防线。
日志上限 2000 条,超出后丢弃最旧的。若日志文件损坏,会被另存为
rollback.json.corrupt-<时间戳> 并重新开始记录(不会静默覆盖历史)。
自动标注四档,仅作提示,判断依据都写在详情面板里。风险列同时用图标区分 (⛔ 危险 / ⚠ 关注 / ○ 残留 / ✓ 正常),不依赖颜色也能读出来:
- 危险 — 无有效签名且位于微软目录内(名称伪装)/位于临时目录/盗用系统进程名
- 关注 — 签名无效或缺失/脚本文件(内容决定风险)/经系统解释器执行脚本/位于所有用户可写目录/位于微软目录但签名未经确认
- 残留 — 目标文件不存在,且没有触发更严重的规则
- 正常 — 签名有效且路径正常
几个容易踩的点:
- 判定会区分「文件确实不存在」和「目录受 ACL 保护读不到」两种情况 —— 后者不算残留。
- 裸文件名(如
powershell.exe)会按PATH解析后再判断。 rundll32 xxx.dll,Start这类命令若首次解析不出有效路径,会退回只取第一个词重试, 避免把活着的持久化误判成无害残留。- 关掉「校验数字签名」后,风险说明会写「签名未经校验」,不会谎称「签名有效」。
- 签名是后台逐个补的(见「不占用主线程」)。补完之前「签名」列显示
校验中…, 风险等级尚未把签名纳入考虑;补完后风险和行底色会自动更新,并重新按风险排序。
选中或勾选条目 → 点「AI 分析」,把这些条目的元数据交给大模型研判: 它是什么软件、是否合法、是否有必要开机自启、能不能安全禁用 / 删除。 结果会写回主表的「AI 结论」列和详情面板,刷新不丢。
这项功能会把数据发送到你配置的服务器。 首次使用必须先在「⚙」里配置并确认。
- 会发送:条目名称、启动位置、命令行、目标路径、数字签名状态与签署者、 文件是否存在、本地规则给出的初步风险判断。
- 不会发送:任何文件内容、注册表其他键值、系统信息、你没有选中的启动项。
- 脱敏:默认开启,发送前把用户名 / 主机名 / 用户目录替换成
<USER>/<HOST>/%USERPROFILE%。命令行里经常夹带这些,建议保持开启。
操作栏上「AI 分析」右边就是配置下拉,选中即切换并立刻落盘,不用进设置对话框。 鼠标悬停能看到当前配置的协议、端点和模型。
「⚙」里可以新建 / 复制 / 删除配置,每套各自独立:
| 字段 | 说明 |
|---|---|
| 名称 | 显示在切换下拉里 |
| 协议 | anthropic / openai / openai-responses,见下表 |
| 地址 | 填到域名即可,程序自动补 /v1。https://x.cn、https://x.cn/、https://x.cn/v1 等价。右侧「自动探测」可自动判定协议 |
| API Key | DPAPI 加密后存本地;留空则回退到环境变量 |
| 模型 | 可手填,也可点「获取模型」从服务端拉清单再选 |
| 思考强度 | (不发送) 或 low–max。三种协议字段不同,非推理模型请选「(不发送)」 |
协议怎么选:
| 协议 | 端点 | 用在哪 |
|---|---|---|
anthropic |
/v1/messages |
Anthropic 官方,以及提供 Messages API 的中转站 |
openai |
/v1/chat/completions |
绝大多数 OpenAI 兼容服务:国内中转、Ollama、vLLM、LM Studio…… |
openai-responses |
/v1/responses |
OpenAI 新版 Responses API,以及跟进了它的中转站 |
三种协议是并列的,可以同时各配一套(比如同一个中转站,chat 和 responses 各存一条,
用主界面的下拉一键切换对比效果)。openai 和 openai-responses 都用 Bearer 认证、
共用 /v1/models,差别只在请求体和响应结构(见下表)。
不确定选哪个?填好地址和模型,点「自动探测」。 它会以当前协议为首、依次真发一次
最小请求,命中即停,并把「协议」自动设成能用的那个;三种都失败会把每种的端点和错误逐条列出来。
另外 404 的报错也会直接给建议 —— openai 报 404 会提示「该服务可能只提供 Responses API」,反之亦然。
直接 Invoke-RestMethod 调 REST,无 SDK 依赖。两种协议在同一套代码里分流:
| anthropic | openai | openai-responses | |
|---|---|---|---|
| 端点 | /v1/messages |
/v1/chat/completions |
/v1/responses |
| 认证 | x-api-key + anthropic-version |
Bearer |
Bearer |
| 系统提示 | 顶层 system |
messages[0] system 角色 |
input[0] system 角色 |
| 输出上限 | max_tokens |
max_tokens |
max_output_tokens |
| 结构化输出 | output_config.format |
response_format.json_schema.{name,schema,strict} |
text.format.{name,schema,strict} (拍平,不再套一层) |
| 思考强度 | output_config.effort |
reasoning_effort |
reasoning.effort |
| 取回正文 | content[].text |
choices[0].message.content |
output[] 里挑 type=message,再取 content[].type=output_text |
| 用量字段 | input_tokens/output_tokens |
prompt_tokens/completion_tokens |
input_tokens/output_tokens |
openai-responses 有三处必须单独处理,都做了:
output是混合数组 —— 推理模型会往里插type=reasoning的条目, 不能直接取output[0],得先挑出type=message。多个output_text分片会拼接。type=refusal—— 安全拒答时 content 里是refusal而不是output_text,直接报错而不是返回空。status=incomplete—— 输出被max_output_tokens截断时,返回的是半截 JSON, 这时直接抛错并提示减少条目数,而不是让ConvertFrom-Json去炸。
思考强度默认「(不发送)」。非推理模型收到 reasoning_effort / reasoning.effort 会直接报 400,
所以对两种 openai 协议都是显式 opt-in;anthropic 协议留空同样不发。
结构化输出(JSON Schema)保证结果可解析;对返回被 ```json 围栏包住的中转站会自动剥掉围栏。
服务端 fallback(安全分类器拒答时自动换备用模型重跑)只对 api.anthropic.com 官方端点启用 ——
中转站多半不认那个 beta 头,发过去反而会 400。
每次最多 8 条一批,超过自动分批并显示进度。请求全在后台 Runspace 里跑,不占用主线程。 「⚙ → 测试连接」用一条构造样本验证整条链路(地址 + Key + 模型 + 结构化输出)是否可用。
HTTP 错误会翻译成人话:401 Key 无效、403 无权访问该模型、404 地址或模型不对、 429 速率限制、连不上则提示检查地址/网络/代理,并附上实际请求的端点。
| 按钮 / 控件 | 作用 |
|---|---|
| 校验数字签名 | 关掉可显著加快刷新 —— 跳过 Authenticode 校验和可能的 CRL 联网 |
| 含系统托管服务 | 一并列出 svchost / lsass / spoolsv / SearchIndexer / sppsvc 承载的服务 |
| 定位文件 | 在资源管理器里选中目标文件 |
| 打开位置 | Run → regedit 跳到该键;启动文件夹 → 资源管理器;任务 / 服务 → 对应 mmc |
| 导出快照 | 导出 CSV 或 JSON(UTF-8 with BOM,Excel 打开中文不乱码) |
| 统计条 | 条目总数 / 开机生效 / 已禁用 / 危险 / 关注 / 残留 / 正常 |
统计条下方若出现黄色提示,说明有启动位置读取失败(权限、系统组件缺失等), 点它可以看到具体是哪些位置 —— 这时列表是不完整的。
pwsh -STA -ExecutionPolicy Bypass -File _selftest.ps1 # CRUD 链路
pwsh -STA -ExecutionPolicy Bypass -File _selftest_batch.ps1 # 批量功能
pwsh -STA -ExecutionPolicy Bypass -File _selftest_regression.ps1 # 界面构建 + 安全回归
pwsh -STA -ExecutionPolicy Bypass -File _selftest_async.ps1 # 两阶段加载 + 主线程卡顿测量(会短暂弹窗)
pwsh -STA -ExecutionPolicy Bypass -File _selftest_task.ps1 # 计划任务 改命令/改名/撤销/删除(需管理员)
_selftest_task.ps1 需要管理员权限才能注册测试任务;权限不足时会整体跳过而不是报失败。
前两个只在 HKCU 下造专用测试条目(__SM_SELFTEST__ / __SMBATCH_*),
第三个另外用一个专用测试键 HKCU\SOFTWARE\__SM_WildcardRegression__。
第四个只读取、不改动任何东西。跑完都自动清理,不触碰任何真实自启动项,无需管理员权限。
测试会把回滚日志等状态文件重定向到 %TEMP%,不污染你真实的 rollback.json。
载入主程序时若「跳过提权守卫」的替换失效(主程序结构变了),测试会直接中止
而不是带着 UAC 弹窗去操作真实系统。
回归测试覆盖的历史缺陷:注册表值名通配符转义(名为 * 的值会删光整个键,
名含 [ ] 的值静默删除失败)、枚举函数返回值形状、回滚记录位置白名单、
风险判定不再因「文件不存在」提前返回。
枚举和 AI 请求都是秒级到十几秒的活,全部放在独立 Runspace 里跑,主线程只负责画界面。
点「刷新」后 Load-Items 约 2ms 就返回。
逐个文件做 Authenticode 校验是刷新耗时的大头(还可能触发 CRL 联网),所以拆成两趟:
| 阶段 | 做什么 | 界面表现 |
|---|---|---|
| ① 枚举(约 4 秒) | 读注册表 / 启动文件夹 / 计划任务 / 服务,不校验签名 | 标题栏显示 读取计划任务… 已发现 N 项;完成后整张表一次上屏 |
| ② 补签名(约 2.5 秒) | 后台逐个校验,结果经并发队列回到主线程写入 | 「签名」列从 校验中… 逐个变成实际结果,风险等级和整行底色跟着变 |
第二趟结束后才按风险重新排序(中途排会让行在鼠标底下乱跳),并保留当前高亮行。 中途点刷新会作废上一轮的补填 —— 它填的是即将被替换掉的那批对象。
条目类型是 SmItem,一个实现了 INotifyPropertyChanged 的类(由 Add-Type 编译,
约 290ms 一次性成本)。这是渐进填充的前提:PSCustomObject 改属性界面不会动,
只能整表重绑,那会丢掉滚动位置和高亮行。顺带也解决了原先「程序化改勾选后必须重绑」的老问题。
万一 Add-Type 失败会自动退回 PSCustomObject + 单趟加载,功能不受影响,只是没有渐进填充。
_selftest_async.ps1 每 50ms 打一次心跳,测量主线程的空闲程度:
Load-Items 阻塞主线程 : 2 ms
① 列表上屏 : 4,145 ms
② 签名全部填完 : 6,566 ms
仍停在「校验中…」的条目 : 0
【工作期间】p95 间隔 : 78 ms <<< 后台干活期间界面持续可用
【工作期间】超 300ms 次数 : 0 / 82
【内容上屏】那一帧 : 521 ms (一次性,WPF 排版真实内容)
「工作期间」和「内容上屏那一帧」要分开看:前者变长才叫界面卡住, 后者是任何应用显示新内容都要付的绘制成本。判据用 p95 而不是最大值 —— 单次调度抖动不该算失败,持续阻塞才是。
表格开了行虚拟化(51 条只实例化 13 行)。列虚拟化反而刻意关掉: 只有 8 列,它的簿记开销比省下来的多(实测重绑 + 布局 478ms → 212ms)。 短的定值列(状态/风险/签名/AI 结论)也去掉了每格的 ToolTip 绑定。
实现上没有用
Start-Job—— 那是独立进程 + 序列化,几十个对象来回一趟比干活还慢, 而且 PSCustomObject 会被反序列化成死数据。同进程 Runspace 传的是活对象引用,零拷贝。 后台线程也绝不直接改已经绑在表格上的对象(会触发跨线程更新异常): 它只把结果丢进ConcurrentQueue,由主线程的定时器取出后写入。
DispatcherTimer 的回调必须用 GetNewClosure() 才能捕获 $keep / $cfg / $prog
这些自由变量。但闭包会把捕获的东西放进一个新建的动态模块,由此带来两个坑:
- 函数内创建的闭包读不到
$script:X。$script:限定符会去查闭包自己的模块作用域, 拿到$null。不带限定符的变量(如$UI)反而能被捕获。 → 需要的值先落成局部变量再进闭包($hasInpc = $script:HasInpc)。 - 闭包捕获的是值快照,不是变量引用。 被重新赋值的变量(如
$script:Items = @(...)) 闭包永远看到旧值;而哈希表、List 这类对象因为共享引用,改内容是可见的。 → 跨回调传递的状态统一放$script:Flags哈希表;所有收尾动作收敛成普通函数 (Set-LoadIdle/Set-AiIdle/Unregister-BgJob),函数没有这两个坑。
- 枚举本身仍需 4 秒左右,只是不再冻结界面。枚举期间会禁用会改动数据的按钮 —— 列表还没到位,那些操作没有意义;签名补填期间则不禁用,列表已经能用了。
- 签名补填完成前,「风险」列的判断尚未把签名纳入考虑,可能偏乐观。
- 计划任务只支持新建「登录时触发」;多动作任务和系统任务不能在此编辑。
- 服务只能启用/禁用和删除,不能新建或改路径(见「四类操作的支持范围」)。
- 启动文件夹里的脚本文件只能改名和删除,不能在此编辑内容。
\Microsoft\Windows\*下的系统计划任务不列出,且没有开关可以打开。- 风险判定是静态规则 + 可选的签名校验,只作提示,不能替代杀毒软件。
删掉本目录下的全部文件,再删掉状态目录即可,不写任何其他位置:
%LOCALAPPDATA%\BootScope\
rollback.json 回滚日志
unrecoverable.json 撤销失败的记录存档(可能不存在)
managed-services.json 被改成「手动」的服务清单(可能不存在)
ai-config.json AI 设置:多套 provider + 加密后的 Key(可能不存在)
更名前状态目录叫
StartupManager。程序启动时若发现旧目录存在而新目录还没有, 会自动整个搬过去一次(回滚日志、AI 配置、服务记录都不丢),之后两者互不干扰。
唯一的额外副作用:用过「打开位置」跳转 regedit 会写
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Applets\Regedit\LastKey
(regedit 自己也会写这个键,删不删都行)。
| 现象 | 原因 / 处理 |
|---|---|
| 改了代码但界面没变化 | 当前跑的是改动前启动的实例。对照标题栏的「构建」时间,关掉重开。 |
| UAC 弹窗点了取消 | 会提示「提权请求被取消」然后退出。本工具没有管理员权限无法工作。 |
| 反复弹 UAC | 已加保护:只重启一次,第二次仍不满足条件会明确报错并退出。 |
| 刷新很慢 | 关掉「校验数字签名」。慢的主因是逐个文件做 Authenticode 校验,可能触发 CRL 联网。 |
| 统计条下方出现黄色提示 | 有位置读不到,列表不完整。点提示看具体位置。 |
| 服务禁用后不见了 | 已修复。禁用过的服务会记录在案并继续显示,可再次启用。 |
| 导出的 CSV 中文乱码 | 已修复(改为 UTF-8 with BOM)。若仍乱码,用记事本另存为「UTF-8 带 BOM」。 |
| AI 分析报 401 / 403 | API Key 无效或无权访问该模型。用「⚙ → 测试连接」确认。 |
| AI 分析报 429 | 触发速率限制,稍后再试或减少一次分析的条目数。 |
Get-ScheduledTask 不存在 |
精简版系统缺少该模块,计划任务一节会读取失败并计入黄色提示。 |
启视.vbs 启动器(UTF-16 LE —— WSH 不认 UTF-8)
BootScope.ps1 主程序(UTF-8 with BOM —— PowerShell 5.1 需要 BOM 才能正确读中文)
_selftest.ps1 CRUD 自测
_selftest_batch.ps1 批量选择 / 批量删除 / 整批撤销 自测
_selftest_regression.ps1 界面构建 + 安全回归自测
_selftest_async.ps1 两阶段加载 + 主线程响应性测量
_selftest_task.ps1 计划任务编辑链路(需管理员)
README.md 本文件
两个文件的编码都不能随意改:.vbs 存成 UTF-8 会报「无效字符」,
.ps1 存成无 BOM 的 UTF-8 在 PowerShell 5.1 下中文会变乱码。