chore: 导入旧记忆备份供恢复

This commit is contained in:
2026-09-23 22:38:40 +08:00
parent 5313fcee7a
commit 6a6a895eaf
638 changed files with 29816 additions and 1 deletions
@@ -0,0 +1,64 @@
---
title: Checkpoint 2026-08-20 02:00:50 UTC [d8e18b60e5]
type: checkpoint
permalink: main/projects/ea207d36-693c-4349-b3c8-7c40c8c889f5/checkpoint-2026-08-20-02-00-50-utc-d8e18b60e5
stable_id: 715e4eb8-48b7-4540-9a75-5be72eeb0c7e
scope: project
memory_type: checkpoint
project_id: ea207d36-693c-4349-b3c8-7c40c8c889f5
usage_profile_id: 7aee455b-24cc-4008-bdaf-443721e6f939
status: active
revision: 1
request_id: winunlock-impl-complete-2026-08-20
created_at: '2026-08-20T02:00:50.601098+00:00'
updated_at: '2026-08-20T02:00:50.601098+00:00'
tags:
- 实现完成
- credential-provider
- rust
- 编译通过
- 待VM验证
---
# WinUnLock 完整工程实现完成(编译通过 + 导出验证 + 首次提交)
## 完成内容
在 E:\code\WinUnLock 从零落地了完整可编译工程,30 个文件、3701 行,首次提交 3e4d09b(分支 main)。
### 四个 crate(包名即产物名)
- **common**(rlib):protocol.rs 管道协议 v1 编解码(magic 0x554E4C4B/version 1,单字段上限 64KiB)、config.rs(serde+toml)、dpapi.rs(CryptProtectData/CryptUnprotectData,LocalMachine 作用域 + 固定 entropy "WinUnLock::DPAPI::v1")、account.rs(域\用户 解析)、paths.rs(%PROGRAMDATA%\WinUnLock)、error.rs(thiserror)
- **provider**(cdylib → provider.dll):lib.rs(导出+类工厂)、provider.rs(ICredentialProvider)、credential.rs(ICredentialProviderCredential + CredPack + LSA Negotiate 包查询)、pipe.rs(管道服务端)、registration.rs(自注册)、logging.rs(轻量文件日志)、shared.rs
- **client**(bin → client.exe):读 config+cred.bin,WaitNamedPipeW+CreateFileW+WriteFile 发送,带重试
- **setup**(bin → setup.exe):交互式设置,用 SetConsoleMode 关闭 ENABLE_ECHO_INPUT 实现密码不回显(未引入 rpassword 依赖)
## 关键实现决策
- **有意不设 panic="abort"**:provider.dll 跑在 LogonUI 内,abort 会拖垮登录界面。改为在 DllMain/导出函数/管道线程用 catch_unwind 兜底,满足「确保 FFI 边界不展开」。已在根 Cargo.toml 注释说明理由。
- **只在 CPUS_UNLOCK_WORKSTATION 返回 S_OK**,其余场景(含 CPUS_LOGON)返回 E_NOTIMPL,彻底不干扰正常登录/改密。
- **不抢默认磁贴**:凭据未送达时 GetCredentialCount 把 pdwdefault 设为 0xFFFFFFFF 且 autologon=false,保证用户仍能手动解锁;凭据送达后才置 0 + true。
- 管道:PIPE_ACCESS_INBOUND|FILE_FLAG_FIRST_PIPE_INSTANCE,PIPE_TYPE_MESSAGE|READMODE_MESSAGE|WAIT|PIPE_REJECT_REMOTE_CLIENTS,nMaxInstances=1,SDDL `D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;AU)(D;;GA;;;AN)(D;;GA;;;NU)`(沿用 hodor 思路)。Drop 时靠「自连接 CreateFileW(GENERIC_WRITE)」唤醒阻塞的 ConnectNamedPipe。
- GetSerialization 成功后立即 zeroize 共享密码并把 ready 置回 false(一次性凭据)。
## 编译期踩到并已修复的 windows 0.62.2 API 差异(重要经验)
1. `GetLocalTime()` **无出参**,直接返回 SYSTEMTIME(不是 C 风格 `GetLocalTime(&mut st)`)。
2. `WaitNamedPipeW` 返回 **BOOL**(用 `.as_bool()`),不是 Result。
3. DPAPI 签名验证正确:`CryptProtectData(&in_blob, PCWSTR::null(), Some(&entropy), None, None, u32_flags, &mut out)`;`CryptUnprotectData` 第 2 参是 `Option<*mut PWSTR>` 传 None。dwflags 是裸 u32。输出 blob 需 LocalFree。
4. `RegCreateKeyExW`/`RegSetValueExW` 的 reserved 参数在 0.62.2 里是 **Option 类型**(传 None,不是 0)。
5. `GetModuleFileNameW(Some(hmodule), &mut buf)` 第一参是 Option<HMODULE>。
## 验证结果
- `cargo build --release --target x86_64-pc-windows-msvc` **通过,零警告**。产物:provider.dll 586752 B、client.exe 518656 B、setup.exe 647680 B。
- `cargo test` **14 个测试全过**(协议往返/错误magic/错误version/超长字段/截断数据/Debug不泄露密码/账户解析×4/配置往返/DPAPI往返/GUID格式化)。DPAPI 往返实测通过,证明 LocalMachine+entropy 组合可用。
- dumpbin /exports 确认 provider.dll 正确导出 5 个函数:DllCanUnloadNow、DllGetClassObject、DllMain、DllRegisterServer、DllUnregisterServer(cdylib 自动导出,无需 .def,已在 .cargo/config.toml 注释说明)。
## 遇到的坑与解决
- **rust-toolchain.toml 写死 channel="1.94.1" 会触发 rustup 联网重装整条工具链**,本机 1.94.1 目录处于「部分安装」状态,导致 rename 失败(os error 145 目录不是空的)并反复重试。已改为 `channel = "stable"`(本机 stable 即 1.94.1)。该文件在 rustup 回滚过程中被清掉过一次,重写后已确认存在并进入提交。
- 昨天中断留下的 3 个占位 stub(winunlock-provider/client/setup,引用不存在的 winunlock-common)导致 cargo 无法解析 workspace,已全部用真实实现覆盖,包名统一为 common/provider/client/setup,使产物直接叫 provider.dll/client.exe/setup.exe,与 install.ps1 和 provider.reg 对齐。
## 边界遵守情况(已实测确认)
全程只做代码编写 + cargo build/test + dumpbin。已验证:`%PROGRAMDATA%\WinUnLock` 不存在、注册表 CLSID 项不存在、计划任务不存在。未执行任何 regsvr32/改注册表/建计划任务/复制文件到 ProgramData。
## 下一步 / 给下个会话
1. **端到端真机流程尚未验证**,必须由用户在虚拟机里测:先拍快照 → 管理员跑 install.ps1(可用 -UnlockTime 定在 1~2 分钟后)→ 跑 bin\setup.exe 设账户密码 → Win+L 锁屏 → 观察是否自动解锁 → 看 logs\provider.log 与 client.log。
2. 若 VM 测试失败,优先怀疑三处:(a) GetSerialization 的 ulAuthenticationPackage(Negotiate 包 ID 查询失败会退回 0);(b) 是否真的走到 GetCredentialCount 的 autologon 分支(看 provider.log 有无「凭据已就绪,请求自动登录」);(c) 域账户时 full_username 拼接是否符合 LSA 期望。
3. 故障恢复要点已写进 README 第 8 节:删 `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{E9C67B30-B120-4838-A5A3-FBC2DE03C532}`(决定性的一个)与 `HKLM\SOFTWARE\Classes\CLSID\{同上}`;含安全模式方案与 WinRE 下 `reg load HKLM\WULOCK` 离线删除方案(注意挂载后路径里没有 SOFTWARE\ 这一层)。
4. 已知限制:仅支持账户文本密码(Windows Hello PIN 不适用);睡眠/休眠下不会唤醒机器;client 退出码 10 = 管道不存在(通常只是当时没锁屏,属正常)。