Windows 开机自启的六种方式:从启动文件夹到服务,以及怎么把它们一次看全

启动文件夹、注册表 Run 键、任务计划程序、Windows 服务、设置里的「启动应用」、UWP 的 StartupTask——六层机制各有各的时机和权限。附上工作目录、UAC、AC 电源这几个最容易踩的坑,以及用 Autoruns 一次性排查全部持久化位置的方法。

#Windows#开机自启#注册表#任务计划程序#Autoruns#Windows 服务

「怎么让程序开机自启」这个问题,答案不是一个,是六个。它们的区别不在于「哪个更正确」,而在于启动时机、运行权限、能不能带界面这三件事——选错了的典型症状是:程序确实自启了,但要么弹 UAC 弹到烦,要么开机后卡三十秒,要么根本读不到自己的配置文件。

先上总览,后面逐层拆。

机制启动时机权限能有界面适合谁
启动文件夹登录后、桌面加载完当前用户普通桌面程序,图省事
注册表 Run 键登录后、比启动文件夹稍早当前用户 / 各用户绝大多数软件的「勾选自启」
任务计划程序可自定义(登录时、开机时、延迟…)可提升到最高,且不弹 UAC要管理员权限或要延迟的
Windows 服务开机即启,不用等登录SYSTEM 等服务账户不能无界面后台程序
设置 →「启动应用」——————不是机制,是上面几种的管理界面
UWP StartupTask登录后应用沙箱内商店应用,开发者才管得着

一、启动文件夹——最直观的那个

两个位置,Win + R 输入这两个 shell 名就能直达:

作用范围shell 命令实际路径
仅当前用户shell:startupC:\Users\<用户名>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup(即 %AppData%\Microsoft\Windows\Start Menu\Programs\Startup
所有用户shell:common startupC:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup

原理很朴素:登录后 Explorer 遍历这两个文件夹,把里面的东西挨个跑一遍。放快捷方式、.bat.vbs 都行,不一定非得是 .lnk

优点是可见——任何人打开文件夹就知道有什么,删一个文件就取消了,不需要碰注册表。缺点有两条:启动时机晚(要等桌面加载完),以及没法带管理员权限静默运行,需要提权的程序在这里要么弹 UAC 要么直接失败。

[ 顺带一提:往「所有用户」那个文件夹里丢东西需要管理员权限,但丢进去之后,它是以每个登录用户各自的权限运行的,不是以管理员运行。这两件事经常被搞混。 ]

二、注册表 Run 键——软件们真正在用的那个

键的完整路径作用范围 / 语义
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run当前用户,每次登录都跑
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run所有用户,每个人登录时各跑一次
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce当前用户,跑一次后系统自动删掉这个值
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce所有用户,同上,跑完即删
HKEY_LOCAL_MACHINE\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run64 位系统上的 32 位程序(RunOnce 同样有 WOW6432Node 版本)

RunOnce 就是「跑一次就把自己删了」的 Run,键名和 Run 平级、只差最后一个词,常用于安装程序重启后的收尾动作(复制文件、删临时目录、注册组件)。

你在软件设置里勾的那个「开机时启动」,背后九成写的就是 HKCU 那条。值的类型是 REG_SZ(少数用 REG_EXPAND_SZ,为了能写 %windir% 这类环境变量),值的名字随便取——它只是软件用来找到自己那条记录的标识,系统不解析;值的数据才是真正要执行的命令行。

HKLM Run 键里的一条:值名 SunloginClient,值数据是带引号的完整路径加 --cmd=autorun 参数

上图是向日葵远程控制写的那条:值名 SunloginClient,值数据 "E:\Tools\向日葵远程\SunloginClient\SunloginClient.exe" --cmd=autorun注意后面那个参数——很多软件靠它区分「开机自启拉起来的」和「用户双击打开的」,前者往往直接缩到托盘、不弹主窗口。你自己写自启功能时也该学这一手。

WOW6432Node 那条容易漏:64 位系统上,32 位程序写 HKLM\Software 会被注册表重定向到 WOW6432Node 下,只看主键会漏掉一批。HKCU 没有这个问题——重定向只作用于 HKLM\SoftwareHKEY_CLASSES_ROOT

WOW6432Node 下的 Run 键,里面四条:游戏启动器、两条 Teams 卸载残留、VMware 托盘

我自己机器上这个键里躺着四条:一个游戏启动器、两条 Teams 的卸载残留、VMware 的托盘程序——全是 32 位程序,在主键里一条都看不到。

RunOnce 有两个前缀约定,装机脚本会用到——前缀加在值名上,不是加在命令上:

1、值名以 ! 开头(如 !MyCleanup)—— 命令执行成功之前不删除这个值,也就是失败了下次开机还会再试。

2、值名以 * 开头(如 *MyCleanup)—— 安全模式下也运行(正常情况下 RunOnce 在安全模式是不跑的)。

命令行加一条的写法:

bash
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v MyApp /t REG_SZ /d "\"D:\Tools\myapp.exe\" --silent" /f

注意那圈转义引号:路径带空格时,外面这层引号该加

不加会怎样?回头看上面那张 WOW6432Node 的图——里面 E:\Delta Force\launcher\startup_runner.exe 就是不带引号的,而它照样能自启。原因是 CreateProcess 在没有引号时会从左往右逐段试:先试 E:\Delta.exe,不存在,再试 E:\Delta Force\launcher\startup_runner.exe,命中。

所以真正的问题不是「跑不起来」,而是两件事:

1、命令行还带参数时会歧义——路径和参数的边界靠空格切,猜错了程序就收不到自己要的参数。 2、这是一个提权后门。只要有人能在 E:\ 根目录放一个 Delta.exe,下次开机 Windows 就会先找到它并执行。这就是常说的 unquoted service path 漏洞,Run 键和服务的 binPath 都吃这一套。

写引号的成本是零,别省。

HKLM Run 键里火绒的 Sysdiag 项,值数据是一个用引号包住的 exe 路径

上图是 HKLM\...\Run 下的另一条(火绒安全的托盘进程):路径本身并不含空格,照样整个包了引号——你不知道用户会把软件装到哪个带空格的目录里。同一张列表里 SecurityHealth 那条用的是 REG_EXPAND_SZ、数据写成 %windir%\system32\SecurityHealthSystray.exe,正是前面说的「要用环境变量就换这个类型」。

三、任务计划程序——进阶推荐

Win + Rtaskschd.msc,创建任务,触发器选「登录时」。启动文件夹和 Run 键做不到的事,它都能做:

1、延迟启动。触发器里勾「延迟任务时间:30 秒」,避开开机那阵最挤的 IO,桌面能早点能用。

2、以最高权限运行,且不弹 UAC。这是让需要管理员权限的程序自启的标准解法,没有之一——常规选项卡里勾「使用最高权限运行」,UAC 提示就被绕过了。

3、附加条件:仅在接通电源时、仅在网络可用时、空闲时才跑,等等。

任务计划程序里 TrafficMonitor 的自启任务,触发器是「登录时」,操作是启动 TrafficMonitor.exe

上图是一个真实例子:TrafficMonitor(那个显示网速的小工具)在任务计划程序库里建了个自己的文件夹 TrafficMonitor,里面一个名为 Autorun for <用户名> 的任务,触发器写着「登录 <计算机名>\<用户名> 时」,操作选项卡里是「启动程序 → E:\Static\...\TrafficMonitor.exe」。它之所以不用注册表,正是因为要读网卡流量得有管理员权限——走 Run 键就得每次开机弹 UAC,走计划任务勾上「使用最高权限运行」就一声不响

这也顺带说明了排查时的一个现实:这台机器的注册表 Run 键里根本找不到 TrafficMonitor,只看注册表会以为它没自启。

命令行版本:

bash
schtasks /create /tn "MyApp" /tr "\"D:\Tools\myapp.exe\"" /sc onlogon /rl highest /delay 0000:30

这里有两个默认值会咬人:

[ 坑一:条件选项卡里「只有在计算机使用交流电源时才启动此任务」默认是勾上的。笔记本拔了电源,任务就不跑了,而且没有任何提示。 ]

[ 坑二:设置选项卡里「如果任务运行时间超过下列时间,停止任务」默认勾着,值是 3 天。常驻后台的程序会在开机第三天被计划程序悄悄杀掉。 ]

正经软件(某些驱动更新器、云盘客户端)和流氓软件都爱用这里,所以排查自启动时千万别漏了计划任务——它比注册表隐蔽得多,普通人不会想到来这看。

四、Windows 服务——开机就启,不等登录

services.msc 里那一屏。它和上面所有机制的根本区别是:服务在用户登录之前就已经跑起来了。数据库、同步工具、SSH 隧道、反代这类东西,不应该等到有人坐下来登录才启动。

另一个好处是崩溃自动重启:服务属性的「恢复」选项卡里可以配「第一次失败:重新启动服务」,相当于自带一个 supervisor。

普通 exe 不能直接注册成服务——服务要实现 Service Control Manager 的那套控制接口(响应启动/停止/暂停消息),普通程序没有,硬注册进去会在启动时超时报 1053 错误。所以需要包一层:

工具从哪来说明
sc create系统自带只适合本来就实现了服务接口的程序
NSSM(Non-Sucking Service Manager)nssm.cc最省事,nssm install MyService 弹图形界面填路径,能管标准输出重定向、崩溃自动重启
WinSW(Windows Service Wrapper)GitHub winsw/winsw单个 exe + 一个同名 XML 配置,适合跟着项目一起版本控制、随代码分发

sc create 的经典坑:等号后面必须有一个空格

bash
sc create MyService binPath= "D:\Tools\svc.exe" start= auto

写成 binPath="D:\..."(等号后无空格)会直接失败,这是 sc 那套老掉牙的参数解析留下的。

还有一条硬限制:服务不能显示界面。Vista 之后有 Session 0 隔离,服务跑在 session 0,用户桌面在 session 1+,服务弹出来的窗口你根本看不见,只会表现为「程序卡在那儿不动」。要有托盘图标的程序就别做成服务,或者做成「服务 + 一个用户态的托盘客户端」两件套。

五、设置里的「启动应用」——它不是一种机制

设置 → 应用 → 启动,或者任务管理器的「启动应用」标签页。

这里不是第五种自启动方式,而是前面几种的汇总管理界面。很多人以为它是个独立开关,其实它列出来的每一项,来源都是启动文件夹、注册表 Run 键或者 UWP 的 StartupTask。

值得知道的是「关闭」按钮干了什么:它不会删除原来的注册表值或快捷方式,而是在另一个地方记一笔「这项被禁用了」。完整的三对键(我在自己机器上逐个确认过都存在):

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder

Run32 对应的就是前面说的 32 位程序那套 WOW6432Node\...\Run。)

里面的值名和 Run 键里的值名一一对应,值是一段 12 字节的 REG_BINARY首字节 02/06 表示启用,03 表示禁用,后 8 字节是被禁用那一刻的时间戳(FILETIME)。

所以会出现这种情况:注册表 Run 键里明明有值,程序就是不启动——去 StartupApproved 对应的那条看看首字节,多半是被这里关掉了。反过来,你用脚本往 Run 键写了一条,用户在界面上禁用后,你的脚本重写多少次都没用。

那个「启动影响:高/中/低」的评级也来自这套机制,是系统统计的实际耗时,可以当作优化时的参考。

六、UWP / 商店应用的 StartupTask

微软商店装的应用不走注册表,而是在 Package.appxmanifest 里声明一个 StartupTask 扩展(windows.startupTask,指定 TaskIdExecutable),由系统统一调度。用户侧只能在「设置 → 应用 → 启动」那个界面里开关它,注册表 Run 键和启动文件夹里都看不到。

普通用户基本碰不到,但排查时要知道「有些自启项不在传统位置」这回事。

排查:一个 Autoruns 就够

上面列的还只是常见位置。完整的持久化点位远不止六个:WinlogonUserinit / Shell 值、AppInit_DLLs、映像劫持(IFEO 的 Debugger 项)、Active Setup、组策略的登录脚本、WMI 事件订阅、浏览器扩展、驱动、Codec……手工挨个翻不现实。

微软官方 Sysinternals 的 Autoruns 一个工具全覆盖,它就是干这个的:把所有已知的自启动位置一次列全,按类别分标签页。免费、免安装,解压就能跑(建议右键以管理员身份运行,否则看不全 HKLM 那半边):

https://download.sysinternals.com/files/Autoruns.zip

Autoruns 列出 Packaged Apps 分组下的 StartupTask 条目,未验证签名的行以粉红高亮

最后:怎么选

普通桌面程序 → 启动文件夹(可见、好删)

要延迟、要管理员权限、要附加条件 → 任务计划程序

无界面、不该等登录、崩了要自动拉起 → Windows 服务(NSSM / WinSW 包一层)

软件自己要实现「勾选自启」这个功能 → HKCU 的 Run 键(不需要管理员权限就能写)

写代码实现自启功能时特别提醒一句:HKCU 而不是 HKLM。前者当前用户就能写,后者需要管理员权限,为了一个自启选项去要提权,是很糟糕的产品决策。

评论

评论加载中……