浏览器扩展禁用后又被启用怎么办:少写一个字段

上周三傍晚六点,我坐在电脑前盯着 360 安全浏览器的扩展页。

那个「办公助手」又回来了。

我前一天已经把它停用掉了。脚本还打印了一行「7 项停用,校验通过」。

结果关掉浏览器再开,五个内置扩展齐刷刷回到启用状态。

一个都没停住。

那天晚上到十二点,我一直在找一个不存在的敌人。

我先怪的是 360 的加密配置

我以为问题出在浏览器有份加密配置,启动时会刷掉我的改动。停用状态写在用户目录的 Preferences 里,那是纯 JSON,路径是:

%APPDATA%\360se6\User Data\Default\Preferences
  → extensions.settings → 每个扩展一个条目

我改了里面 5 个扩展的 state,写成 0(Chromium 里 0=停用,1=启用)。写完读回来,值确实变了,脚本自检全绿。

可浏览器一起,这些扩展就齐刷刷活过来。

我第一反应是 360 有个叫 ext_360_cfg 的加密配置,启动时拿它重建白名单,把我改的值又刷回去。

这个说法听着挺顺。

但我从来没验证过它。

alt:时间线画出当晚三次「脚本报成功、实际没生效」的时刻,17点51分改34项开关被内存回写覆盖,17点58分停用7个扩展因缺字段全部回弹,18点54分改资源包因死代码写入块不可达
alt:时间线画出当晚三次「脚本报成功、实际没生效」的时刻,17点51分改34项开关被内存回写覆盖,17点58分停用7个扩展因缺字段全部回弹,18点54分改资源包因死代码写入块不可达

那天晚上我栽了三次。

三次都是脚本说成功、实际没生效。这张时间线是我后来补的,当时忙着修,没顾上记。

真相比我想的小得多

第二天我把那个真正被我停用成功的扩展调出来对照。这一步我前一天就该做。

真正停用成功的(刷新插入JS):
  "state": 0
  "disable_reasons": 1

我写停用失败的(办公助手):
  "state": 0
                        ← 这个字段根本没写

Chromium 判断一个扩展能不能处于停用状态,看的是 disable_reasons 这个位掩码。state 只是结果,位掩码才是「凭什么」。它的含义是「为什么被停用」,其中 1 对应 DISABLE_USER_ACTION,意思是「用户主动停用」。

我只写了「它是停用的」,没写「谁让它停的」。

对 Chromium 来说这是个状态不一致。它认为这是脏数据,启动时纠正回默认值。

alt:字段对照图,左栏只写state等于0的状态不一致被判为无效,右栏state等于0配disable_reasons等于1才被认定合法停用,底部标注Chromium按位掩码校验停用合法性
alt:字段对照图,左栏只写state等于0的状态不一致被判为无效,右栏state等于0配disable_reasons等于1才被认定合法停用,底部标注Chromium按位掩码校验停用合法性

补上这个字段,问题当场消失。

这个铁律在 Chrome、Edge、360 系上通用。

顺便推翻了我自己的一个「加固方案」

改对字段之前,我还想了个加固办法:把 Preferences 文件设成只读。逻辑上很硬——浏览器改不回来,配置就锁死了。

结果浏览器一启动,自己把只读属性解除了,然后回写。

这个防护等于零。

后来我把它理成一条判据:判断能不能动某个配置文件,不能只看「文件能不能打开」。实测浏览器在跑的时候,open('r+b') 能成功,os.rename 也能成功,写进去看着也没报错,但关浏览器那一下全没了。

正确判据得两条同时成立:主进程真的退出,并且文件可写。托盘图标右键「退出」才算退出,关窗口不算。

我那天就是被这点坑了。张工说「我关了」,我去看 sesvc.exe 还在跑就放行了,其实是十个 360se.exe 还活着。

# 进程判断别用 tasklist | grep,Git Bash 下对 tasklist 输出做 grep 不可靠,
# 我实测它返回 0,误判浏览器已关,Python subprocess 是准的
import subprocess
out = subprocess.run(["tasklist"], capture_output=True, text=True).stdout
print("360se.exe 存活" if "360se.exe" in out else "已退出")

那三个更新组件我是这么摘掉的。它们不常驻,只在触发更新时才启动,所以不用关浏览器:

grep -c "360se.exe" tasklist.txt
ls Application/16.3.1038.64/360seupdate.exe
mv 360seupdate.exe 360seupdate.exe.disabled

同一晚上还有第三个坑,形态完全不同。我改浏览器安装目录里的 newpages.zip 往新标签页注入跳转,脚本报成功、退出码 0,zip 却一个字节没变。原因是写 zip 的函数里,写入那几行代码前面有一句提前 return,永远走不到。

那次的伪装最彻底:我的校验只查了上一次已经注入过的那个 html 文件,全绿。而 file_size 还停在 909309,才把问题露出来。

三次都是同一个病——脚本自己报成功不算数。

我现在校验只信两件事。

写入动作之后读回来,断言值等于期望值。再抽查一次文件字节数是不是真的变了。

一个副产品:把 ID 认错了

排查过程中我发现自己脚本里有个 ID 标错了。iidoeablkpgmldojpceilmnacecmjlco 我标成「找券券」,实际是「阅读模式」。

一个有用的功能,被我写进了强制停用名单。找券券真正的 ID 是 gcckmgpflcgmojmpblplppomkmcdjlan。

名字是我从别处抄来的,抄错了。

这一晚上顺手还清了 1.4 GB 空间——旧版本内核目录 420 MB、离线安装包 120 MB、浏览器自带的运行时组件 256 MB。功能本体我一个字节都没删,只是把这些用不上的搬走了。

浏览器目录下的具体组件 ID 和位数我没深究,各版本可能不一样,你按自己机器上的实际值对。

发表评论