Skip to content

About

声纹解锁 · 用一句话解锁 Windows 锁屏(零界面占用 + 票据门控;不联网、不上传音频)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

VoiceUnlock · 声纹解锁

用一句话解锁 Windows 锁屏:正常说一句 25 秒的话,210 秒后自动进桌面 —— 而锁屏界面一个像素都不改。

English summary: VoiceUnlock is a Windows lock-screen unlocker driven by a speaker-verification (voiceprint) model, not by speech recognition. A resident agent listens only while the workstation is locked; when the voiceprint matches it mints a short-lived unlock ticket, and a Credential Provider inside LogonUI submits the logon credential. The lock screen itself is left completely untouched — no tile, no status text, no theming: with no valid ticket the provider enumerates zero credentials, so Windows renders its own, stock UI.


⚠ 先读免责声明:DISCLAIMER.md

  • 没有后门:不联网、不上传音频、无遥测、无自动更新;DISCLAIMER.md §1 给了你自己核验的命令。
  • 但凭据有被解出的风险:它必须替你保管登录密码(DPAPI 用户作用域加密)。 防得住"拿到磁盘文件的人"和"别的用户",防不住已能以你身份运行代码的人、本机管理员, 也防不住离线爆破 —— DPAPI 主密钥由你的 Windows 口令派生,口令越弱越危险。
  • 声纹可以被冒用:没有活体检测;在真实麦克风旁播录音、或用语音克隆,仍可能骗过判定。 请把它当"便捷因素",不要当唯一防线。

本软件按"现状"提供、不提供任何担保,使用风险自负;只用于你自己拥有或已获授权的设备。


目录


它解决什么问题

笔记本 / 台式机在工位或家里,锁了屏要敲一长串密码,很烦;但换成"人脸"又常常要求专用摄像头。 这个项目把声纹当便捷因素:你说一句话就能进桌面,密码框一直都在、随时可用。

两个设计底线,贯穿全部实现:

  1. 锁屏界面一点不改。 不画磁贴、不加状态文字、不动外观 —— 用户看到的永远是 Windows 自己的登录界面。
  2. 绝不把用户锁在门外。 自动提交凭据之前,一定先用 Windows 自己验证一次;不对就一次都不提交。

它长什么样(用户视角)

Win+L 锁屏
   ↓
对着麦克风正常说一句 2~5 秒(不用喊)
   ↓
2~10 秒后自动回到桌面
  • 锁屏上没有任何我们的元素,也不会有提示音 —— 所以请稍等几秒,别连按回车。
  • 想手输密码:随时输,Ctrl+Alt+Del 也一直可用。
  • 只在"锁屏"这一种场景工作:开机/注销后那个还没有用户会话的登录界面,本项目不参与 (原因见已知限制:那要求把凭据降级成机器级保护,本项目选择不降级)。

前置条件

项 要求
系统 Windows 10 x64(19041+,本项目在 19045 上实测)。Windows 11 可用但未签名凭据提供者可能不被加载,见已知限制
账户 必须有密码。空密码账户没有可提交的凭据,声纹解锁没有意义
麦克风 任意可用录音设备;虚拟混音总线默认不参与解锁(见隐私与安全)
权限 安装需要管理员(要注册锁屏凭据提供者、写 HKLM)
网络 完全不需要。程序不联网、不上传任何音频

安装

  1. 从 Releases 下载 VoiceUnlock-Setup-<版本>.exe。

  2. 在目标机器上运行(安装界面中英文自动)。

  3. 勾选项:

    勾选项 默认 说明
    登录时自动启动声纹监听 勾 注册计划任务 + 最高权限,见原理 §3
    安装完成后立即设置登录凭据 勾 弹一个窗口要你输 Windows 登录密码
    安装完成后立即登记声纹 勾 需要麦克风,按屏幕提示念一句
  4. 装完立刻核对(30 秒):

    & "C:\Program Files\VoiceUnlock\VoiceUnlock.exe" paths

    重点看这几行:

    credential    = ...credential.bin
    credential    = OK (decryptable in this session; user=<你> password length=<n>)
    profiles      = 1 以上
    autostart     = scheduled task VoiceUnlockAgent (highest privileges)
    --- CP registration state ---
      => both keys present - it should appear on the lock screen
    

    非中文 Windows 上这些标签是英文(程序按系统语言自动切换输出,避免 437 代码页下中文变乱码)。 想强制:set VOICEUNLOCK_LANG=zh(或 en)。


日常使用

想做的事 怎么做
登记声纹 开始菜单「登记声纹」,或 VoiceUnlock.exe enroll(照着屏幕上的句子念)
登记多支麦克风 VoiceUnlock.exe enroll --all;只登记一支:--device <序号>
查看状态 开始菜单「查看状态(排障)」,或 VoiceUnlock.exe paths / agent status
换密码之后 必须重新设置凭据:VoiceUnlock.exe setup-password(DPAPI 绑定当前会话)

声纹登记有几个要点(都踩过坑,写在代码注释里):

  • 屏幕会给出要念的句子和倒计时 —— 内容不影响识别(声纹与文本无关),给句子只是为了让你知道"现在该说话、说多久"。
  • 登记前会做质检:先判"有没有人在说话"(活跃比例),静音、噪声、几乎没人声都会拒绝建档并说明原因。噪声也能过的那种判据会写出害人的阈值。
  • 登记完会按解锁时的判定路径复核一遍,必要时下调阈值。
  • 不登记也能用:任何一次成功解锁之后,用到的设备会被自动建档。

排障

第一条命令永远是:

& "C:\Program Files\VoiceUnlock\VoiceUnlock.exe" paths

日志(每次判定、分数、阈值、耗时都在里面):

%LOCALAPPDATA%\VoiceUnlock\logs\agent.log

常见症状 → 往哪看:

症状 看什么
说了一句话但完全没反应 agent.log 里有没有 [listen] 电平触发。没有 ⇒ 麦克风没被采到,或 agent 没在锁屏状态听
声纹通过但锁屏不动 有没有 [agent] 常驻判定(...):通过 之后的票据记录;再看 CP 是否注册(cp-status)
日志里几百行 拒绝对端(映像='?'),收到请求 一条没有 agent 不是以最高权限运行 —— 见原理 §3
凭据可读性 = ✗ 本会话解不开 会话建立后改过密码。在当前登录会话里重跑一次 setup-password
锁屏上出现"未登记自启"之类提示 旧版本的 paths 只看 HKCU Run;新版显示的是计划任务

紧急关闭与卸载

场景 做法
已经进了桌面、想立刻关掉 双击桌面「★ 紧急关闭声纹解锁」(会弹一次 UAC),或管理员运行 VoiceUnlock.exe panic —— 它做三件事:注销凭据提供者、停常驻监听、移除自启
正常卸载 控制面板 → 程序和功能。卸载会先注销凭据提供者再删文件,并清掉凭据与声纹数据
连桌面都进不去 WinRE → 命令提示符 → 删掉两处注册项(具体命令随包 使用说明.txt 里有整段可复制)

卸载顺序不是随便定的:必须先注销凭据提供者,再删 DLL。反了就会变成"注册表里指着一个不存在的 DLL", 那是会把锁屏弄坏的形态。


工作原理(完整链路)

┌──────────────────────── 用户会话(登录之后) ────────────────────────┐
│  VoiceUnlockAgent.exe(计划任务启动,最高权限)                        │
│    ├─ 只在【确认锁屏】时才开麦(session.is_locked(),按输入桌面判读)  │
│    ├─ 电平触发:每设备自适应门限 = max(该设备近期中位数×6, 绝对下限)   │
│    ├─ 录一段 → 声纹 embedding → 与该设备档案比 cos → 过阈值            │
│    └─ 通过 ⇒ 备一张 40 秒有效的"解锁票据"(单次使用)                  │
│                          ↕  命名管道 \\.\pipe\VoiceUnlock              │
│                             SDDL 收紧 + 对端映像校验(只认 logonui.exe)│
└──────────────────────────────────────────────────────────────────────┘
                                  ↕
┌────────────────── 安全桌面:LogonUI.exe(SYSTEM) ───────────────────┐
│  我们的 Credential Provider(DLL,随安装包注册)                      │
│    ├─ 每 200ms ping 问票据                                            │
│    ├─ ticket=false ⇒ 【枚举 0 个凭据】→ 界面上完全没有我们            │
│    └─ ticket=true  ⇒ CredentialsChanged(经 GIT 封送,跨线程回调 STA)│
│                      → 凭据出现 → pbAutoLogonWithDefault              │
│                      → LogonUI 自动提交 → 解锁                        │
└──────────────────────────────────────────────────────────────────────┘

1. 零界面占用是怎么做到的

凭据提供者的枚举结果直接决定登录界面渲染什么。所以只要在"没有有效票据"时返回 0 个凭据, LogonUI 就没有任何东西可画 —— 我们不需要去隐藏、去适配主题、去猜缩放,Windows 自己的界面就是它自己的样子。

反过来"先画上去再想办法藏"是做不到的:CP 一旦枚举出凭据,LogonUI 就会给它分配磁贴/选中框, 那是系统画的,改不了外观。

这条设计顺带解决了与任何主题登录界面共存的问题:我们不在锁屏上画东西,就没有"适配"这回事。

2. 两条解锁路径

路径 适用机器 CP 做什么 实测
提交凭据 有密码(主场景) 提交 DPAPI 解出的 Windows 密码 已实测自动解锁
注入回车 空密码账户(兼容) 在安全桌面 SendInput 注入回车 = Windows 自己的"按回车即登录"路径,不提交任何凭据,不会触发账户锁定 未在真机实测

3. 为什么登录自启必须是"计划任务 + 最高权限"

这是实测出来的产品缺陷(另一台机器上排障得到),不是设计洁癖:

  • 管道的对端校验要读客户端进程的映像名,而锁屏那边的客户端是 LogonUI.exe(SYSTEM 身份)。
  • 普通权限的 agent 调 OpenProcess 会被系统拒绝(err=5)→ 映像名取不到 → 判 peer-not-allowed → 所有锁屏请求都被回绝。
  • 症状极具误导性:agent.log 里几百行 拒绝对端(映像='?'),而 收到请求 一条都没有; 用户看到的只是"声纹不灵"。

所以自启是计划任务 VoiceUnlockAgent,RunLevel=HighestAvailable(不弹 UAC), 并且带 IgnoreNew(防两个实例抢管道)、ExecutionTimeLimit=PT0S(默认 72 小时会掐掉常驻进程)、 电池上不停(笔记本拔电源就停服等于功能消失)、失败重启 3 次。

4. 声纹判定(三层:本设备 / 跨设备 / 自动登记)

  • 每设备一份档案 + 每设备一个阈值:不同麦克风的频响差别很大,共用一个阈值必然误判。
  • 本设备命中:直接比阈值,最快(快判 ~3 秒)。
  • 跨设备兜底:新插入/未登记的设备走"多段全部通过"的严格路径。
  • 自动登记:任何一次成功解锁之后,用到的设备被自动建档,下次它就是"一等公民"。
  • 同一物理源的重复计票去重:否则一个混音总线会冒充多个"独立证据"。
  • 可信 / 不可信白名单:混音总线、回环、虚拟设备默认不参与解锁(本机最大的安全风险点就在这里)。

推理侧不依赖 torch/funasr:CAM++ 说话人模型导出为 ONNX,只用 onnxruntime + kaldi_native_fbank 跑, 冻结版与源码版的向量一致性有 parity 自检(cos = 1.00000000 才算过)。

5. 防锁死设计

自动提交密码之前,一定先用 Windows 自己(LogonUser)真登一次:

  • 登录成功 → 才允许提交;
  • 登录失败(比如用户改过密码)→ 一次都不提交,并把原因写清楚到日志。

理由很实在:本机账户策略是"10 次密码错锁 10 分钟"。如果程序闷头提交旧密码, 用户会被自己的程序锁在门外。代价是每 30 秒最多一次 LogonUser(几毫秒)。

另外:常驻程序不在、凭据读不出来、票据过期 —— 任何一种情况下 CP 都是"什么凭据都不枚举", 手输密码不受任何影响。


从源码构建

需要:Windows 10/11 x64、Python 3.12、Inno Setup 6、MSVC(编译凭据提供者 DLL)。

git clone <this repo> ; cd voiceunlock
python -m venv venv
venv\Scripts\pip install -r requirements.txt      # numpy / onnxruntime / kaldi-native-fbank / soundcard

# 1) 取声纹模型(不随仓库分发)
#    a) 从上游下载 CAM++ 权重仓库(iic/speech_campplus_sv_zh-cn_16k-common),
#       把整个目录放到 models\campplus_sv\(里面应能看到 campplus_cn_common.bin)
#    b) 用一个【带 torch + funasr 的解释器】导出 ONNX(不是本项目的 venv ——
#       export_onnx.py 要 import torch/funasr,只做一次性离线转换):
#          <带torch的解释器>\python.exe tools\export_onnx.py
#       产物:models\campplus.onnx(约 27 MB)
#       路径可用环境变量覆盖:VOICEUNLOCK_SV_DIR / VOICEUNLOCK_MODELS

# 2) 编译锁屏凭据提供者(需要 VS 的 VC.Tools 组件 + CMake)
pwsh -File provider\build.ps1
#    产物:provider\build\Release\VoiceCredentialProvider.dll

# 3) 一键打包(冻结 + 自检 + 出安装包)
pwsh -File packaging\build_installer.ps1
#    产物:dist\VoiceUnlock-Setup-<版本>.exe 与免安装形态 dist\VoiceUnlock\

打包脚本内置三道硬门槛,任何一道不过就中止:

  1. 生成源码侧 parity 参考向量;
  2. PyInstaller 冻结(控制台 exe + 无窗口 agent exe,共享一份 _internal);
  3. 冻结版自检:parity cos 必须 1.00000000,且判定逻辑自测通过。

出门前再跑两条自检:

venv\Scripts\python.exe tools\i18n_audit.py --all           # 输出语言层五项审计
venv\Scripts\python.exe tools\scan_package_sensitive.py     # 包里有没有本机痕迹

tools\scan_package_sensitive.py 值得单独说:PyInstaller 的 PYZ 是压缩的, 直接按字节 grep exe 什么都查不到(连源码里的字面量都不在可见字节里), 所以它会把模块解出来、反序列化、遍历字符串常量;另外源码里的 # 注释不进包, 而 docstring 会进包。这两个坑都真踩过。


验证与证据

这个项目是"证据优先"的:每个结论都对应一条可复跑的命令或日志。

项 判据
推理一致性 冻结版 parity --ref logs\parity_ref.npy → cos = 1.00000000
运行时内核 VoiceUnlock.exe parity / python -m src.selftest → SELFTEST OK
判定逻辑 VoiceUnlock.exe selftest-decision → DECISION SELFTEST OK(覆盖本设备/跨设备/自动登记三层)
装机 安静安装退出码 0;paths 里 CP 两处键 present;计划任务 RunLevel=HighestAvailable
锁屏界面 抓图对比:登录界面只有系统自己的账户名 + 密码框
对端校验修复 计划任务启动的 agent + 锁屏 30 秒:收到请求 87 次 / 拒绝对端 0 次(修复前是"几百次拒绝、0 次请求")
卸载干净 两个 CP 注册键、计划任务、旧的 HKCU 自启项、用户数据全部清除
输出语言 中文系统逐行零回归;英文系统全 ASCII(437 代码页不乱码)—— tools\i18n_audit.py --all 五项断言

隐私与安全

完整的风险清单、可自行核验的"无后门"证据、以及责任限制,都在 DISCLAIMER.md。 下面是摘要。

  • 不联网、不上传音频。 没有任何网络代码;所有处理在本机。
  • 只做声纹,不做语音识别。 你说什么内容不影响结果,也不会有转写。
  • 凭据用 Windows 自带的 DPAPI(用户作用域)加密存本机,只有你自己的登录会话能解开; 不会以明文出现在任何文件或日志里。 ⚠ 但这个保护不防已能以你身份运行代码的人、本机管理员/SYSTEM, 也不防离线爆破(主密钥由你的 Windows 口令派生 → 口令越弱越危险)。
  • 声纹可以被冒用:没有活体检测;录音回放/语音克隆可能骗过判定。它只是"便捷因素"。
  • 声纹是生物特征,泄露不可撤销 —— 别把 profiles.json 上传或分享。
  • 虚拟设备默认不信任:混音总线/回环设备不参与解锁(防止自建音色 + 混音总线自我欺骗)。
  • 常驻 agent 以最高权限运行(原因见上)—— 这是为了让管道的对端校验可用;它只做"听声纹 → 备票据"这一件事。
  • 仓库里不包含任何用户数据(凭据、声纹档案、日志)也不包含第三方主题登录界面的文件。

已知限制

  • 只支持"锁屏",不支持"开机/注销后的登录界面"。 这是有意的边界,不是漏做: 锁屏时用户会话还在,常驻 agent 能在该会话里听声纹、并用 DPAPI 用户作用域解开凭据; 而开机/注销后的登录界面还没有任何用户会话 —— 要支持它,就得让一个 SYSTEM 常驻进程 在"无用户会话"的上下文里解密凭据,凭据必须从"只有你的登录会话能解开"降级为 "管理员/SYSTEM 能解开"(还不确定那个上下文能否采到麦克风)。本项目选择不降级: 登录界面就交给 Windows 原样处理(我们的凭据提供者那时拿不到票据,枚举 0 个凭据, 界面上看不到我们)。
  • 凭据提供者未做代码签名:Windows 11 对未签名 CP 的加载策略更严,可能不加载; Win10 实测正常。给其他机器用之前请先在目标机器上验一遍锁屏。
  • "注入回车"路径未在真机实测(只在有密码的机器上验过提交凭据那条路)。
  • 声学验证过的机器有限:作者本机(有真实麦克风)已验证真实语音自动解锁; 虚拟机里可以用"主机侧假麦克风"(往虚拟音频线里灌 wav)做端到端,但那只验证链路,不验证真实声学。
  • 登记/解锁需要安静环境;嘈杂环境下会走更保守的判定路径(更慢,但不容易误放行)。
  • 仅 Windows:锁屏凭据提供者是 Windows 专有机制。

许可与第三方组件

  • 本项目代码:见仓库根 LICENSE。
  • 随包分发的声纹模型 campplus.onnx 是上游 CAM++ 说话人声纹权重 (iic/speech_campplus_sv_zh-cn_16k-common,版权归阿里巴巴 / DAMO 达摩院,Apache License 2.0) 的格式转换件:由本项目用 torch.onnx.export 从原始 PyTorch 权重转换而来, 权重数值未做任何改动,没有再训练、微调、剪枝或量化。 为满足 Apache-2.0 §4 的义务,安装包里随附 LICENSE-Apache-2.0.txt 与 THIRD-PARTY-NOTICES.txt; 上游模型仓库自身不发 LICENSE/NOTICE,那份许可正文由本项目补入。 §6 不授予商标权:本项目与阿里巴巴集团无隶属或背书关系。
  • 不包含 Shittim Logon(蔚蓝档案主题登录界面)的任何文件 —— 它的授权禁止再分发与二次打包。 本项目的凭据提供者在无票据时枚举 0 个凭据、锁屏上不画任何东西,因此与任何主题登录界面 互不影响、也不需要适配;想用主题界面就自行安装它自己的发行版。
  • 仓库不分发上游模型权重:models\*.onnx 在 .gitignore 里,按上面的步骤自行导出/获取。

目录结构

src/                    运行时(独立推理内核 + 判定 + 管道 + 凭据 + 安装动作 + 输出语言层)
  sv.py                 CAM++ ONNX 推理(fbank → TDNN → seg_pooling → 192 维 embedding)
  verify.py             判定:快判/退回、挑窗、阈值、三层覆盖
  agent.py              常驻 agent:锁屏才开麦、电平触发、备票据
  win32pipe.py          命名管道服务端(SDDL + 对端映像校验 + 日志限流)
  session.py            锁屏判读(以输入桌面名为准)
  dpapi.py              凭据的 DPAPI 用户作用域加解密
  enroll.py             声纹登记(含质检与判定路径复核)
  install.py            凭据提供者注册 / 计划任务自启 / 紧急关闭
  i18n.py + i18n_catalog.py   输出语言自动切换(中文系统纯 no-op)
provider/               锁屏凭据提供者(C++ / COM,在 LogonUI 内加载)
packaging/              PyInstaller spec、Inno Setup 脚本、一键打包、给用户的使用说明
tools/                  构建 / 验证 / 自动化工具
  export_onnx.py        把上游 CAM++ 权重导出成 ONNX
  i18n_audit.py         输出语言层五项审计
  scan_package_sensitive.py  交付前隐私扫描(含"解 PYZ 查字符串常量")
  feed_audio.py         往主机虚拟音频线灌 wav(给虚拟机当假麦克风)
声纹解锁-施工方案.md      设计记录:每个决定的理由 + 全部失败路径与反例(最值得读的一份)
全流程说明.md            打包 → 装机 → 凭据 → 登记 → 使用 → 验证 → 应急,逐步带判据
HANDOFF.md              开发/交接记录:已完成与未完成、踩过的坑清单(30+ 条)

设计文档里记了大量失败路径与反例(哪个判据看似合理但会误判、哪个"修复"其实引入了新 bug)。 如果你要改判定阈值、加设备类型、动锁屏交互,先读一遍,能省几轮。

About

声纹解锁 · 用一句话解锁 Windows 锁屏(零界面占用 + 票据门控;不联网、不上传音频)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages