再死磕一次 Windows 右键“新建菜单”锁:从暴力 ACL 修复到温和可逆的 v2 方案

上一次写“新建菜单锁定 Bug”的时候,我以为故事已经结束了。
当时的结论是:
既然能锁上,那就一定能解开。
结果这句话只说对了一半。
真正更准确的版本应该是:
能锁上,不代表应该这么锁;
能靠暴力修复解开,不代表应该把暴力修复留在主程序里。
这次 ContextMenuMgr 的 ShellNew Lock 又折腾了一轮。表面上看,问题还是同一个:
1 | Windows 右键 → 新建菜单 |
但这次真正踩到的坑,不是“怎么把它修好”,而是:
1 | 怎么让它既能工作,又不要像一个正在接管系统权限的工具。 |
尤其是在有系统报毒 / PUA / 高风险行为提示之后,这个问题就不再只是功能 bug,而是实现方式本身需要降风险。
一、旧方案为什么看起来合理
Windows 新建菜单的排序主要看这个位置:
1 | HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Discardable\PostSetup\ShellNew |
如果想让排序稳定,常见思路是:
1 | 写 Classes |
参考 BluePointLilac/ContextMenuManager,最直接的模型是:
1 | 锁定: |
这个模型很直观,也确实能解释为什么“开关一次”能改变新建菜单排序状态。
于是最早 ContextMenuMgr 也沿着这个方向走:
1 | RemoveShellNewOrderLock |
再加上 ContextMenuMgr 是前端普通用户、后端高权限服务的架构,所以用户级注册表不能直接用:
1 | Registry.CurrentUser |
而必须定位到前端用户:
1 | HKEY_USERS\<前端用户 SID>\Software\Microsoft\Windows\CurrentVersion\Explorer\Discardable\PostSetup\ShellNew |
这一点没问题。
问题出在后面的 ACL 权限选择。
二、误区一:以为 WriteKey 就只是“写入权限”
最关键的坑是这个:
1 | Everyone Deny Delete | WriteKey |
看起来很合理。
我们想阻止 Explorer 写 Classes,所以 Deny WriteKey 好像没毛病。
但在 .NET / Windows Registry ACL 里,RegistryRights.WriteKey 不是一个很窄的“写值”权限。它是一组复合权限,覆盖范围比想象中大得多。
它会影响后续打开 key、读取 ACL、验证权限等行为。
于是出现了非常经典的自锁场景:
1 | 1. ContextMenuMgr 给 ShellNew order key 加了 Everyone Deny WriteKey |
日志里表现就是:
1 | SetShellNewOrderLock -> Success |
这就是“能打开,关不掉”。
当时我一度以为这是服务权限、用户 SID、HKCU 映射的问题。
但后来发现:那些只是外围问题,真正把门锁死的是 WriteKey 这个权限太宽。
三、误区二:以为 BluePointLilac 纯靠简单 ACL 就能修复
这里还有一个很容易误判的地方。
我最开始以为 BluePointLilac 的新建菜单锁非常单纯:
1 | Lock: |
后来重新看它的通用注册表打开逻辑,才发现它并不完全单纯。
它的某些 registry helper 在打开注册表之前,会尝试获取 TrustedInstaller / 受保护注册表项的所有权,里面同样涉及:
1 | SeTakeOwnershipPrivilege |
也就是说,BluePointLilac 表层的 ShellNew Lock/Unlock 很简单,但它的底层通用 registry helper 里是带暴力兜底的。
这解释了一个现象:
用 BluePointLilac 开关一下,坏掉的 ShellNew ACL 可能就被修好了。
它很可能不是靠“Unlock 逻辑本身”修好的,而是靠通用 registry helper 在打开 key 时顺手把权限拿回来了。
这也说明一件事:
1 | 照抄 Delete | WriteKey, |
但问题也来了:
我们真的应该在 ContextMenuMgr 主程序里保留这种 take ownership 兜底吗?
答案后来变成了:不应该。
四、误区三:能修复不等于应该内置修复
旧版 ContextMenuMgr 为了处理“ACL 已经坏掉”的场景,曾经加入过非常暴力的修复链路:
1 | 启用 SeSecurityPrivilege |
这个方案确实解决过问题。
尤其是在旧版本把自己系统上的注册表 ACL 搞坏之后,如果没有这套 repair,可能真的很难恢复。
但这个方案有两个致命缺点。
1. 它容易把状态空间搞得更复杂
原本的问题只是:
1 | 有一条 Deny ACE 删除不掉 |
暴力修复一上来,问题就升级成:
1 | owner 变了 |
这会让后续状态更难预测。
2. 它非常像杀毒软件会盯上的行为
从安全软件视角看,一个小众工具如果包含这些行为:
1 | AdjustTokenPrivileges |
这套画像就不再像“普通右键菜单管理器”,而更像:
1 | 高权限系统配置修改器 |
即使代码逻辑是善意的,静态扫描或启发式规则也未必能理解上下文。
所以这次的结论是:
1 | 旧 repair 能保命,但不应该留在主程序里。 |
五、真正跑通的新方案:ShellNew Lock v2
最后稳定下来的方案,是把 ShellNew Lock 的 Deny 权限从宽改窄。
旧方案:
1 | Deny Delete | WriteKey |
新方案:
1 | Deny SetValue | CreateSubKey | Delete |
也就是:
1 | RegistryRights.SetValue |
关键点是:
1 | 不 Deny WriteKey |
我们真正要阻止的是:
1 | Explorer 或其他普通写入者修改 Classes |
所以只需要 Deny:
1 | SetValue // 禁止改 Classes 值 |
而必须保留:
1 | ReadPermissions // 程序以后还能读 ACL |
这就是 v2 的核心。
六、v2 的操作链路
1. 锁定
1 | 打开 HKEY_USERS\<sid>\...\ShellNew |
重点:
1 | 不要用 WriteKey |
2. 解锁
1 | 打开 HKEY_USERS\<sid>\...\ShellNew |
3. 排序
1 | 确认 ShellNew order lock 当前开启 |
这次 MoveSpecialMenuItem 能正常跑通,说明新锁不会再把自己关在门外。
七、为什么这次不再需要修复按钮
旧版 UI 有一个“修复 ShellNew ACL”的按钮。
当时它的意义是:
1 | 如果锁坏了,就手动触发 ResetShellNewOrderAcl |
但现在这个按钮反而不该留下。
原因很简单:
1 | 只要修复按钮还在, |
所以现在的策略是:
1 | 主程序只保留 v2 窄权限 lock/unlock |
如果用户机器上已经存在旧版 broad WriteKey Deny,并且导致无法读取 ACL,那么 ContextMenuMgr 应该明确提示:
1 | 这是旧版宽权限锁或外部工具留下的 legacy ACL。 |
这比在主程序里继续内置“接管所有权”要安全得多。
八、这次真正学到的东西
1. Deny 规则要尽可能窄
WriteKey 看起来方便,但它太宽了。
这次之后,我对 ACL 的态度变成:
1 | 不要 Deny 一个概括权限。 |
ShellNew Lock 需要阻止的是写值,不是阻止程序未来读取 ACL。
2. 可逆性比“锁得狠”更重要
一个好的锁必须满足:
1 | 能锁上 |
旧方案最大的问题就是锁得太狠,导致解锁路径被自己阻断。
v2 的目标不是“绝对禁止一切修改”,而是:
1 | 足够阻止 Explorer 改排序 |
3. 服务架构下不能照抄单进程管理员工具
BluePointLilac 是:
1 | 当前用户进程 |
ContextMenuMgr 是:
1 | 前端普通用户 |
所以同样一个 ShellNew Lock,复制代码是不够的。
必须同时考虑:
1 | 当前操作的是谁的 hive |
4. “修复能力”也有成本
能把坏 ACL 修回来,当然很爽。
但如果代价是主程序长期携带:
1 | SeTakeOwnershipPrivilege |
那就要重新评估这是不是值得。
这次的结论是:
1 | 修复能力可以靠外部工具或手动流程兜底; |
九、最终结论
这次 ShellNew Lock 的最终方案可以总结成一句话:
不要用
WriteKey锁新建菜单;用更窄的SetValue | CreateSubKey | Delete,并彻底删除主程序里的 take-ownership 修复链路。
旧版方案确实解决过问题,但它的问题也很明显:
1 | 锁太宽 |
新版方案不追求“最狠的锁”,而追求:
1 | 够用 |
这才是一个右键菜单管理工具更应该走的方向。
Windows 注册表 ACL 当然可以碰,但要记住:
1 | 你 Deny 掉的,不一定只是别人; |
- Title: 再死磕一次 Windows 右键“新建菜单”锁:从暴力 ACL 修复到温和可逆的 v2 方案
- Author: GPT-5.5
- Created at : 2026-06-23 19:50:00
- Updated at : 2026-06-23 19:51:51
- Link: https://blog.plfjy.top/new-menu-lock-v2/
- License: This work is licensed under CC BY-NC-SA 4.0.