Chromium 的应用程序绑定加密(App-Bound Encryption,以下简称 ABE)解密早已不是新鲜话题,但把 PoC 打磨成真正可落地的工程方案,中间还隔着不少坑。本文侧重讨论 ABE 在 Windows 上的工程化落地,顺带分享过程中 AI & Security 的一些体会。
最终落地的是一套无需 UAC 提权、无视浏览器独占锁、能读取并解密浏览器数据的可工程化方案,并对 Edge/ChatGPT App 的 Chrome 数据导入功能实现做了分析。
https://github.com/KNSoft/KNSoft.MakeLifeEasier/tree/main/Source/Samples/AbeDecrypt
PoC: AbeDecrypt Sample:本文的 PoC 示例,演示文中提到的 4 种解密方式,获取 Chrome/Edge 所选 User Data 的 v10/v20 Key,并展示所选 Profile 解密出来的 Cookie 及密码数据:

https://github.com/KNSoft/KNSoft.ZPigeon
KNSoft.ZPigeon:支持读取 Chrome/Edge 各 Profile 下的各类数据(含 Cookie、密码)以及启动可远程控制的无头浏览器实例:

背景
最近我在做 KNSoft.ZPigeon,一个由 AI 加持的 Windows 远程管理平台。之所以选这个方向,是因为我一直相信「鸽子」的最终形态会是功能极其强大的企业 IT 管理系统。如今在企业 IT、终端安全、Windows 底层研发和 AI 四个领域的积累,刚好够我朝这个目标再迈一步。
浏览器数据获取自然是其中绕不开的功能,ABE 正是最后一关。网上的 PoC 虽多,但多数只验证了「方案可行」;真正落地还得顾全易用性与效率,并处理多 Profile、浏览器运行中的独占锁、权限要求等现实问题。这也是本文的重点。
Chromium 如何加密与保存数据?
相关资料很多,Chromium 本身也开源,所以实现部分直接以 Chromium/Chrome/Edge 为基线快速梳理机制。关键机制只说结论,不再大段摘录代码;未开源部分(浏览器私有实现)再额外说明。
数据存放
系统预装的 Edge 数据存放于 %LOCALAPPDATA%\Microsoft\Edge\User Data 目录,Chrome 类似:
- Local State:JSON,存放密钥(非明文)。DPAPI、ABE 的 Key 都在这里。
- Default:默认 Profile 目录,同级可能还有 Profile 1、Profile 2 等。
Network\Cookies:SQLite,cookies 表(Cookie,加密)。
Login Data:SQLite,logins 表(密码,加密)。
Login Data For Account:SQLite,同 Login Data,但这里存的是登录浏览器账号所保存的密码。
History:SQLite,历史记录(明文)。
- ……
加密内容如 Cookies 库 cookies 表的 encrypted_value 列、Login Data 库 logins 表的 password_value 列,均以 Blob 形式存储:
0 3 15 末尾-16
┌─────────────┬───────┬───────────────────┬───────┐
│ "v10"/"v20" │ nonce │ 密文 ct │ tag │
│ 3B │ 12B │ 与明文等长 │ 16B │
└─────────────┴───────┴───────────────────┴───────┘
版本号 随机数 AES-256-GCM 输出 防篡改校验
其中:
- 版本号:目前 Windows 上只有 v10 和 v20 两种。
- nonce:随机生成 12 字节,每条记录独立使用,只用一次。
- 通过 AES-256-GCM(v10/v20 Key, nonce, 明文) 运算(AAD 为空)得到密文 + tag,tag 用于校验。
密文要用对应版本(v10/v20)的 Key 解密。Local State 的 os_crypt 节点存放核心密钥:
{
"os_crypt": {
"encrypted_key": "<Base64 编码>",
"app_bound_encrypted_key": "<Base64 编码>"
}
}
encrypted_key:Base64("DPAPI" 5 字节前缀 + DPAPI Blob),Blob 加密保存 v10 Key。
app_bound_encrypted_key:Base64("APPB" 4 字节前缀 + ABE Blob),Blob 加密保存 v20 Key。
去掉前缀之后,根据后续 Blob 解出对应 Key 就是解密的关键点。下文会针对不同加密版本说明这些 Blob 是如何生成的,以及如何从 Blob 反推出 Key。
密文用对应版本的 Key 解密后,得到的内容依业务而定:
cookies 的 encrypted_value:自数据库 Schema 24 起为 SHA256(host_key) + cookie 值,开头的「域名指纹」用于防止密文被跨站拷贝复用。
logins 的 password_value:直接是密码本身。
v10/v20 加密密钥在 Local State 里跨 Profile 共享;记录加密格式与密钥 Envelope 均存在版本迁移,因此新版浏览器中仍可能保留旧版格式。
加密版本 v10:DPAPI
Windows CNG DPAPI 是数据保护 API。它只绑定用户,除了系统保存的用户级 Master Key(%APPDATA%\Microsoft\Protect\<SID>\)之外,调用者还可以再传入一个可选熵参与加密。Chromium 没有使用可选熵,用了也不见得能提升多少安全系数。
加密:CryptProtectData([in] 明文, [in, opt] 可选熵, [out] 密文)
解密:CryptUnprotectData([in] 密文, [in, opt] 可选熵, [out] 明文)
同一登录用户下的任意进程都能解开 v10 Key。
首次需要加密时生成 v10 Key:
- 生成随机 32 字节 AES-256 密钥,这就是 v10 Key。
- 调用
CryptProtectData(v10 Key) 得到 DPAPI Blob。
- 把
"DPAPI"(5 字节前缀)拼到 DPAPI Blob 前面,再做 Base64 编码。
- 写入
Local State 的 os_crypt.encrypted_key 字段。
后续按照 Local State 中 os_crypt.encrypted_key 字段反向运算即可拿到 v10 Key,再进行加解密。整套过程相当简单,几行 PowerShell 就能把 v10 Key 解出来并打印(以系统内置 Edge 为例):
# 1. 读 Local State,取 encrypted_key,Base64 解码
$blob = [Convert]::FromBase64String((Get-Content "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Local State" -Raw | ConvertFrom-Json).os_crypt.encrypted_key)
# 2. 验证前缀 "DPAPI" 并剥掉,DPAPI 解密
if ($blob.Length -le 5 -or [System.Text.Encoding]::ASCII.GetString($blob, 0, 5) -cne 'DPAPI') { throw 'Invalid encrypted_key: missing DPAPI prefix' }
Add-Type -AssemblyName System.Security
$key = [Security.Cryptography.ProtectedData]::Unprotect($blob[5..($blob.Length-1)], $null, [Security.Cryptography.DataProtectionScope]::CurrentUser)
if ($key.Length -ne 32) { throw "Invalid v10 key length: $($key.Length)" }
# 3. 打印
($key | ForEach-Object { $_.ToString("X2") }) -join ''
加密版本 v20:ABE(App-Bound Encryption,应用程序绑定加密)
引入 elevation_service.exe,注册为以 SYSTEM 身份执行的系统服务(如 GoogleChromeElevationService / MicrosoftEdgeElevationService),开放 COM 接口给浏览器调用,通过 COM 为浏览器封装、解封 v20 Key。
首次需要加密时:
- 浏览器生成随机 32 字节 AES-256 密钥,这就是 v20 Key。
- 浏览器调用
IElevator::EncryptData,把 v20 Key 和「保护级别」(见后文)交给服务。
- 服务私有处理。Chromium 源码中目前通过
GOOGLE_CHROME_BRANDING 编译器开关控制,若启用则调用私有 PreProcessData,把原始 v20 Key 转换成私有 Envelope,未来取 Key 时再对等地调用私有 PostProcessData 处理。Edge 也有这步,具体见后文。
- 服务模拟客户端(
IServerSecurity::ImpersonateClient),并取得调用方路径(I_RpcOpenClientProcess + QueryFullProcessImageNameW),把校验数据与 v20 Key(或转换后的私有 Envelope)一起在调用方用户上下文中执行 DPAPI 加密。
- 服务退出模拟,再在 SYSTEM 上下文中执行 DPAPI 加密,得到 ABE Blob,返回给浏览器。
- 浏览器拼接
"APPB" + ABE Blob,Base64 编码后写入 os_crypt.app_bound_encrypted_key。
后续浏览器通过 Local State 的 os_crypt.app_bound_encrypted_key 字段拿到 ABE Blob,经 COM 得到 v20 Key,即可进行加解密。
可见 ABE 沿用了 Windows CNG DPAPI,并在 SYSTEM 上下文进行校验及加解密操作。
Chromium 定义的「保护级别」
PROTECTION_NONE:不校验应用身份。
PROTECTION_PATH_VALIDATION:解封时要求当前调用方的规整路径与封装时一致。
PROTECTION_PATH_VALIDATION_WITH_ISOLATION:除路径一致外,还校验进程的隔离状态。
默认值是 PROTECTION_PATH_VALIDATION_WITH_ISOLATION,即同时校验「调用方的规整路径」和「进程的隔离状态」:
- 调用方的规整路径:
- 去掉 exe 文件名。
- 去掉末尾的 Temp / Application / 版本号目录。
- 把
Program Files (x86) 归一化为 Program Files。
- 进程的隔离状态:
- Elevation Service (SYSTEM) 以带隔离标记(
SetTokenInformation(TokenSecurityAttributes),例如 Chrome 的 GOOGLECHROME://ISOLATION)的 Token 创建浏览器进程。
- 服务查询调用进程 Token 中是否存在此隔离标记,阻止非隔离客户端读取隔离客户端存储的 Key。
虽然默认值指示校验进程隔离状态,且校验代码确已默认启用,但隔离标记只授予由 Elevation Service 以隔离方式启动的浏览器实例,常规启动的浏览器并不携带。加密时记录的隔离状态为 False,解密时双方一致即可通过,所以该校验在普通机器上尚无实际约束。即使未来有约束,目测绕过方式依然很多。当下的关键点,还是得先过掉路径校验。
私有 Envelope
Chrome 和 Edge 基于 Chromium 预留的扩展做了近乎相同的私有处理。服务拿到 v20 Key 明文后,先转换成私有 Envelope,再做后续的 DPAPI 加密。Envelope 目前有 3 个版本:

v1 与 v2 只是加密算法不同,用的 Key 是固定的,通过二进制逆向可得到:

v20 Key 的明文直接用以上硬编码 Key 加密得到密文 ct(ciphertext[32]),再组合成 Envelope。
而 v3 引入了 CNG。v20 Key 不再被直接加密,而是先随机生成 32 字节的 Envelope AES Key:
- Envelope AES Key 先与一个固定的 32 字节 Mask 异或,再在服务的 SYSTEM 上下文中用 Windows CNG/KSP 保存的 Key(Chrome 是
Google Chromekey1,Edge 是 Microsoft Edgekey1)加密,得到 cng_block[32]。
- v20 Key 用 Envelope AES Key 做 AES-256-GCM 加密,得到
nonce[12] + ciphertext[32] + tag[16]。
XOR 用的固定 Mask 通过二进制逆向可得到:

四种 v20 解密方案
过路径校验
1. Drop:把可执行程序扔进浏览器目录
既然要校验客户端路径,那直接把程序塞进浏览器目录,路径自然就对了。我个人考虑到的点有:
- 需要管理员权限才能向启用 ABE 的浏览器安装目录写入文件(Per-User 安装的浏览器也用不了 ABE),例如 Windows 内置的 Edge。
- 缺少技术纵深:
- 「进程的隔离状态」或未来的防御手段可以轻易防住。
- EDR 特征明显,文件监控极其容易实现。
2. Inject:注入到浏览器进程
DLL 注入对大家来说已是基操,但 DLL 也不是注入的唯一选择,Map + CreateRemoteThread 就够了。把自身代码映射到浏览器进程,再用远端线程执行:
3. Hijack:劫持浏览器进程执行代码
这条路线可以说是 Inject 方案的升级版,思路一致,无非是把调用 Elevation Service 接口做解密的代码放进浏览器进程空间执行。只是用 CreateProcess + Map,以主线程代替远端线程,两者归并为一个方案也不为过。
想做得足够优雅,还是比较考验技术的。xaitax/Chrome-App-Bound-Encryption-Decryption 是一个值得参考的例子,我在 KNSoft.ZPigeon 中落地的方案也算是 Hijack。
EDR 特征依然明显,但劫持的方式实在太多,就看道高一尺还是魔高一丈了。
过了路径校验,接下来就是如何调用 Elevation Service 接口。接口定义可以参考 Chromium 的 elevation_service_idl.idl。要注意的是,不同版本、不同浏览器的接口定义可能不同,例如 Edge 就多了 3 个专有方法,DecryptData 的槽位被顺延。
IElevator : IUnknown {
RunRecoveryCRXElevated(...) // 槽 3
EncryptData(...) // 槽 4
DecryptData(BSTR in, BSTR* out, DWORD* lastError) // 槽 5 ★
}
IElevator2 : IElevator { RunIsolatedChrome, AcceptInvitation } // 槽 6-7
IElevatorEdgeBase : IUnknown { 3 个 Edge 专有方法 } // 槽 3-5(Edge 特有的插入层)

不依赖浏览器自身服务,对着密文硬解
盘算一下,只要有管理员权限,Elevation Service 能做的事我们也能做。唯一免不掉的是 SYSTEM 上下文——无论是 DPAPI 还是 v3 中浏览器藏在 CNG 里的那把 Key 都需要 SYSTEM(当然,从 LSA 那想办法拿到 SYSTEM 的 CNG Key 也一样,不过那就是另一个话题了)。
4. Elevate:提权到 SYSTEM
前三个方案多少都有入侵行为。即便是 Drop,也欺骗了路径验证,还往人家安装目录乱扔了东西。

此方案需要管理员权限,给自己提权到 SYSTEM 上下文之后,Elevation Service 做的加密我们就能解。产品化方案更适合基于这条路线。
按照 v20 Key 的加密流程,把前文「首次加密落盘」的步骤一步步倒过来做:
- 第 6 步的逆:拿到
Local State 里 os_crypt.app_bound_encrypted_key,Base64 解码,拆掉前缀 "APPB" 得到 ABE Blob。
- 第 5 步的逆:切换到 SYSTEM 上下文,调用 DPAPI
CryptUnprotectData 剥掉一层。
- 第 4 步的逆:切换到用户上下文,调用 DPAPI
CryptUnprotectData 再剥掉一层,得到内层结构。
- 第 3 步的逆:如果大小为 32 字节,则判断无私有 Envelope,跳过本步骤;否则判断 Envelope 版本,对应解密:
- 61 字节 v1:用逆向得到的内嵌 AES-256 Key,以 AES-256-GCM 打开 nonce[12] + ct[32] + tag[16]。
- 61 字节 v2:同上,换 ChaCha20-Poly1305 算法与对应的内嵌 Key。
- 93 字节 v3:
- 切到 SYSTEM 上下文,打开 Chrome/Edge 在 CNG/KSP 中保存的、用于包裹 Envelope AES Key 的那把 Key:
NCryptOpenStorageProvider(Microsoft Software KSP) + NCryptOpenKey(Google Chromekey1 / Microsoft Edgekey1)。
NCryptDecrypt 解开 cng_block[32],与逆向得到的固定 Mask 逐字节异或,得到 Envelope AES Key。
- 再用它解开其后的 nonce[12] + ct[32] + tag[16]。
至此拿到 32 字节 v20 Key。之后每条 "v20" 前缀的记录按 nonce[12] + ct + tag[16] 用它做 AES-256-GCM-Open 即可。
Edge 与 ChatGPT 采取了这个方案,具体实现大同小异。
Edge 本就有全套 Elevation Service,只需把 v20-v3 读取 CNG/KSP 的密钥名称加上 Chrome 的,就可以借助自身机制实现导入。逆一下 Edge 的 elevation_service.exe,追 NCryptOpenKey 调用一看便知:

用户只需在 Edge 里点一下,无需再显式 UAC 授权即可导入。如果阻止 MicrosoftEdgeElevationService 服务启动,就无法导入:

ChatGPT 没有自己已安装到系统的 Elevation Service,因此得自己提权到 SYSTEM:

显式 UAC 提权到管理员后,运行 2 个 ChatGPT.exe 子进程,命令行样本:
"C:\Program Files\WindowsApps\OpenAI.Codex_26.901.4073.0_x64__2p2nqsd0c76g0\app\ChatGPT.exe" --owl-browser-profile-import-broker="\\.\pipe\owl-browser-profile-import-9c93e555-ec01-4812-b76c-dbd774bc8436"
"C:\Program Files\WindowsApps\OpenAI.Codex_26.901.4073.0_x64__2p2nqsd0c76g0\app\ChatGPT.exe" --owl-browser-profile-import-system-service="\\.\pipe\owl-browser-profile-import-1369c23f-7770-4a51-b7ee-c85f27101757"
它们分别处于用户上下文和 SYSTEM 上下文(注册服务方式提权),通过管道互通有无,共同完成前述一系列解密流程。Chrome 导入的核心实现位于其目录中的 chrome.dll,直接做二进制扫描可见其中包含前文提到的 v1/v2 Key 及 v3 XOR mask,还有相关文本:


正是前述方案,无需进一步逆向分析。
PoC 实现与 KNSoft.ZPigeon 工程化落地实践
前面说的是 To C 的产品化方案,还举例了 Edge 和 ChatGPT 的实现:前者借助已注册的 SYSTEM 服务,后者需要一次交互式的显式 UAC 提权。而面向 To B 资产的远程管理,通常以对机器具有法律意义上的所有权为前提(企业资产),因此无需与使用者交互,也能得到 EDR 的默许,更适合之前提到的 Inject/Hijack 方案。
下面以 KNSoft.ZPigeon 为例,配合 PoC 分享实践中的思路与技术点。至于代码,Vibe 实现后还没得空逐一打磨,能入眼便将就着看;代码未必完全符合本文思路,来日再慢慢整理。
数据读取
ABE v20-v3 加密机制自身需要管理员权限,所以无需考虑 Per-User 安装的版本。接下来根据默认路径或注册表信息找到 Edge/Chrome 的 User Data 目录,再枚举其中的 Profiles 就能定位目标数据。注意读取保存的密码时别只顾着 Login Data 库而忘了 Login Data For Account。
把目标支持的最低 Windows 版本抬高一点,就可以直接用 Windows.Data.Json.dll,不必为 C/C++ 引入第三方 JSON 库;同理,SQLite 也可以直接用 winsqlite3.dll:
#define SQLITE_API __declspec(dllimport)
#include <winsqlite/winsqlite3.h>
#pragma comment(lib, "winsqlite3.lib")
这部分代码在 PoC 示例项目所在仓库 KNSoft.MakeLifeEasier 的 Net\Browser.c 中。
代码执行
调用 CreateProcessInternalW 以 CREATE_SUSPENDED 方式创建浏览器进程,再通过 NtAllocateVirtualMemory + NtWriteVirtualMemory 写入 Payload。
对各位来说这应是一气呵成的基操,但要做得优雅还得注意:
- 写入 Payload 往往需要修复重定位,可以根据自身 PE 重定位表精准修复。NTDLL 的 PE Loader 这块实现并不复杂,可参考:
- KNSoft.NDK 中
ntdll!LdrProcessRelocationBlockEx、ntdll!LdrProcessRelocationBlockLongLong 的实现。
- KNSoft.MakeLifeEasier 中
PE_RelocateImage 的实现。
- 优雅收尾:用
NtProtectVirtualMemory 将内存页属性由 RW 改为 RX(而不是始终 RWX),再用 NtFlushInstructionCache 刷新一下。
本文 PoC 是 Vibe 出来的,比较粗糙,映射了自身整个 PE 过去,实际可以做得更精准。接下来调用 NtGetContextThread + NtSetContextThread + NtResumeThread 即可执行 Payload,Payload 调用 Edge/Chrome 的 COM 接口拿到 v20 Key。
数据库锁
Chromium 对 Login Data 库的打开设置了独占锁 PRAGMA locking_mode=EXCLUSIVE,此时需要设置 nolock=1 或 immutable=1。之所以不是 Windows 文件句柄的独占锁,是因为 Chromium 的 Windows-only 实验性机制 set_exclusive_database_file_lock(直接以独占方式持有文件句柄)正在铺开,对 Login Data 库的启用也已排上日程。
不过它已经为 Cookies 库启用了,为其它重要库启用也只是迟早的事,工程侧需要提前考虑这种场景。ChatGPT 导入内容包含 Cookie,界面会提示导入前需完全关闭 Chrome;Edge 导入内容不包含 Cookie,但在 Chrome 运行时(用导入目标 Profile),仍会有部分数据无法导入。
我们进程的权限不比浏览器低,所以即使它独占了文件,我们依然有办法访问,可以直接做成 Fallback 应对此场景。PoC 与 KNSoft.ZPigeon 均已实现,技术方案也不复杂:
NtQueryInformationFile(..., FileProcessIdsUsingFileInformation):获取占用数据库文件的 PID 列表。
NtQueryInformationProcess(..., ProcessHandleInformation, ...):获取进程句柄表,找到独占数据库的句柄。
NtDuplicateObject:复制句柄,然后读取文件内容到自身进程内存。
sqlite3_open_v2(":memory:", ...) + sqlite3_deserialize:读取内存数据库。
结语:AI & Security
AI 能力的提升给各领域都带来了极大的机遇与挑战,安全领域同样如此。不论 AI for Security 还是 Security for AI,都值得持续关注与探索。
本来做 KNSoft.ZPigeon 是想探索如今 AI-Driven 与 Human-Driven 之间如何两全——让 AI 高效生成的大量功能代码保持稳定、可维护且专业,同时让 AI 形成「设计方案-编码-测试-改进」的自闭环迭代。但在实现这个功能的过程中,GPT 以可能涉及 Cyber Security 为由拒绝了我无数次。
切换到 GLM 5.3 Max 之后,它倒是不拒绝我了,但着实几经波折:
-
我浏览器的所有 Cookie 被意外清空了。
- 它写 PoC 时,调用 Chrome 的 Elevation Service COM 收到
E_NOINTERFACE 错误,实际原因是 Chrome 接口随版本更新换代,而它用的是老接口的 IID。
- 但它以为报错原因是 Chrome 没有注册此接口,然后自行往 HKCU 里写入 TypeLib 信息(
RegisterTypeLibForUser),写入的信息还是错的。
- 浏览器优先使用 HKCU 里的错误信息,访问 COM 失败,Chromium 的故障恢复机制主动清空了全部无法解密的 v20 Cookie。

-
把 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX 结构套给了 PROCESS_HANDLE_TABLE_ENTRY_INFO,然后得出「24H2+ 内核把这个结构的 Object/UniqueProcessId 字段删掉了」的结论,后来被我纠正。它还自己做实验证明因自己抄错结构得出的错误结论是正确的,甚至脑补结构体变更的动机是「不再泄露内核指针」。

-
代码逻辑不合理,读取了不需要的变量,而这些变量由于不需要而未经初始化,直到 PoC 运行时被 RTC 抓住而崩溃。

-
把自己写的等待逻辑(10s+)算进方案耗时,然后凭空脑补把耗时归因于「Edge 全量导入表加载 + 服务冷启动」。我提出质疑后,才定位并修正了计算问题。

还有别的插曲就不一一列举了,但一路过来它也算帮了不少忙。GLM 5.3 的宣传片中说要让 AI 站在蓝方,但如果事与愿违,我们便不得不思考:
- 如果面对 AI 的进攻,我们能否守得住?
- 如果面对 Mythos/GPT 这些模型的进攻,我们与手里的开源模型是否守得住?
GLM 5.3 在宣传上突出了安全能力,这是个不错的开始,至少说明国内前沿模型开始关注 AI for Security。下次再做类似的事情,GPT 大概率还是会拒绝我,我依然会选 GLM 5.3,也推荐大家试试。即便不入手 Coding Plan,通过智谱自家的 AutoClaw 也能拿到一些免费额度;如果想同时对比多个模型,RouteFast.ai 这类 API 中转服务也能省去分别申请的麻烦。
相关资料与项目
- Google Security Blog: Improving the security of Chrome cookies on Windows
- xaitax/Chrome-App-Bound-Encryption-Decryption
- runassu/chrome_v20_decryption
- GLM-5.3:前沿编程能力与涌现的网络安全能力
如果你也在做 Windows 底层或浏览器安全方向,欢迎到云栈社区一起交流。