原文链接:985K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2e0W2y4G2k6Y4c8Q4x3V1k6w2e0W2y4G2k6Y4c8Q4x3X3g2y4j5h3E0W2e0r3W2X3k6f1g2S2M7$3W2W2M7W2)9J5c8X3u0D9L8$3u0Q4x3V1k6E0j5h3W2F1i4K6u0r3f1$3!0#2M7X3y4W2i4K6u0r3f1$3q4E0M7r3I4W2M7#2)9J5c8V1q4T1k6f1c8W2j5%4u0&6M7s2c8Q4x3V1k6d9c8f1q4p5e0f1g2Q4x3X3g2E0k6l9`.`.
Chromium 应用程序绑定加密(App-Bound Encryption,以下简称ABE)的解密已不是新鲜事,本文将侧重于讨论其在工程上的落地实践,以及分享过程中 AI & Security 的体会。
最终落地一个无需 UAC 提权、无视浏览器独占锁,读取并解密浏览器数据(Cookie、密码为例)的可工程化方案,以及对 Edge/ChatGPT App 的 Chrome 数据导入功能实现分析。
af9K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2e0W2y4G2k6Y4c8Q4x3V1k6w2e0W2y4G2k6Y4c8Q4x3X3g2y4j5h3E0W2e0r3W2X3k6f1g2S2M7$3W2W2M7W2)9J5c8Y4c8J5k6h3g2Q4x3V1k6E0j5h3W2F1i4K6u0r3f1$3!0#2M7X3y4W2i4K6u0r3f1$3q4E0M7r3I4W2M7#2)9J5c8V1q4T1k6f1c8W2j5%4u0&6M7s2b7`.
PoC: AbeDecrypt Sample: 本文 PoC 示例,演示本文例举的 4 种解密方式,获得 Chrome/Edge 里所选 User Data 的 v10/v20 Key,并显示所选 Profile 解密出来的 Cookie 及密码数据:

ba7K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6w2e0W2y4G2k6Y4c8Q4x3V1k6w2e0W2y4G2k6Y4c8Q4x3X3g2K9f1r3W2Y4k6h3!0F1
KNSoft.ZPigeon: 支持读取 Chrome/Edge 浏览器各 Profiles 下的各类数据(含 Cookie、密码)及启动可远程控制的无头浏览器实例:

最近在做 KNSoft.ZPigeon,一个 AI 加持的 Windows 远程管理平台。之所以想做,是因为我一直相信鸽子的最终形态会是功能极其强大的企业 IT 管理系统,而今在企业 IT、终端安全、Windows 底层研发与 AI 四个领域的积淀够让我离这个目标更进一步了。
其中功能自然包含浏览器数据的获取,ABE 是最后一关。PoC 虽多,但只是证明“方案可行”,真正落地还要兼顾易用性和效率,并处理多 Profile、浏览器运行中的独占锁、权限要求等实际问题,这也是本文重点。
相关资料很多,Chromium 本身也开源,所以实现部分以 Chromium/Chrome/Edge 为基线快速梳理机制;关键机制直接说结论,不再大段摘录代码,未开源部分(浏览器私有实现)再额外说明。
系统预装 Edge 数据存放于 %LOCALAPPDATA%\Microsoft\Edge\User Data 目录,Chrome 类似:
加密内容如 Cookies 库 cookies 表的 encrypted_value 列、Login Data 库 logins 表的 password_value 列,以 Blob 形式存储:
其中:
其中密文要用对应加密版本(v10/v20)的 Key 解密,Local State 的 os_crypt 节点存放核心密钥:
去掉开头的前缀后,根据后面的 Blob 解出对应的 Key 是解密的关键点,下文将对不同加密版本说明这些 Blob 是如何得出的、如何从 Blob 解出 Key。而密文用对应加密版本的 Key 解密后,得到的内容则根据业务而定,例如:
v10/v20 加密密钥在 Local State 里跨 Profile 共享;记录加密格式与密钥 Envelope 均存在版本迁移,因此新版浏览器中仍可能保留旧版格式。
Windows CNG DPAPI 是数据保护 API。它只绑定用户,除了系统保存的用户级 Master Key(%APPDATA%\Microsoft\Protect\<SID>\),调用者还可以再传入一个可选熵参与加密(Chromium 没有使用可选熵,用了也不见得提升安全系数 ┐( ̄ヮ ̄)┌)。
同一登录用户下的任意进程都能解开 v10 Key。
首次需要加密时生成 v10 Key:
后续根据 Local State 的 os_crypt.encrypted_key 字段反向运算即可拿到 v10 Key,然后进行加解密。十分简单,几行 PowerShell 就能把 v10 Key 解密并打印出来(以系统内置 Edge 为例):
引入 elevation_service.exe,注册为以 SYSTEM 身份执行的系统服务(如 GoogleChromeElevationService / MicrosoftEdgeElevationService),开放 COM 接口供浏览器调用,通过 COM 为浏览器封装、解封 v20 Key。
首次需要加密时:
后续浏览器通过 Local State 的 os_crypt.app_bound_encrypted_key 字段拿到 ABE Blob,通过 COM 得到 v20 Key,即可进行加解密。
可见沿用了 Windows CNG DPAPI,并在 SYSTEM 上下文进行验证及加解密操作。
默认值是 PROTECTION_PATH_VALIDATION_WITH_ISOLATION,即同时校验“调用方的规整路径”和“进程的隔离状态”:
虽然默认值指示校验进程的隔离状态,且校验代码确已默认启用,但隔离标记只授予经 Elevation Service 以隔离方式启动的浏览器实例,常规启动的浏览器并不携带。加密时记录的隔离状态为 False,解密时双方一致即可通过,故该校验对普通机器尚无实际约束。即使未来有约束,目测绕过的方式依然很多。当下的关键点,便是过掉路径校验。
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:
XOR 用的固定 Mask 通过二进制逆向可得到:
十分简单直接,既然验证客户端路径,那把程序扔进浏览器目录里路径就对了。我想到要考虑的有:
DLL 注入对大家来说已是基操,但 DLL 也不是注入必需的,Map + CreateRemoteThread 就够了。将自身代码映射到浏览器进程,然后用远端线程执行它。
这条路线可以说是 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 的槽位被顺延)。
盘算一下,只要有管理员权限,浏览器 Elevation Service 能做的事情我们也能做,只有 SYSTEM 上下文免不了,不论是 DPAPI 还是 v3 用的、浏览器在 CNG 里藏的那把 Key 都需要 SYSTEM(当然,从 LSA 那想办法拿到 SYSTEM 的 CNG Key 也一样,但这就是另一个话题了)。
之前的 3 个方案多少有入侵行为。即使是 Drop,也欺骗了路径验证,还往人家的安装目录乱扔了东西。

此方案需要管理员权限,然后给自己提权到 SYSTEM 上下文,进而 Elevation Service 做的加密我们就可以解密,产品化方案更适合基于此路线。
根据 v20 Key 的加密流程,我们照着前文所写的首次加密落盘步骤一步步反过来做:
至此拿到 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 子进程,命令行样本:
它们分别处于用户上下文和 SYSTEM 上下文(注册服务方式提权),通过管道互通有无,共同完成上述一系列解密流程。Chrome 导入核心实现位于其目录中的 chrome.dll,直接二进制扫描可见其中包含前文提到的 v1/v2 Key 及 v3 XOR mask,还有相关文本:


正是前述方案,无需进一步逆向分析。
刚才说了 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:
这部分代码在 PoC 示例项目所在仓库 KNSoft.MakeLifeEasier 的 Net\Browser.c 。
调用 CreateProcessInternalW 以 CREATE_SUSPENDED 方式创建浏览器进程,调用 NtAllocateVirtualMemory + NtWriteVirtualMemory 写入 Payload。对各位来说应该已是一气呵成的基操,但要做得优雅还得注意:
本文 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 均已实现,技术方案不复杂:
可见,浏览器自身的挣扎几近徒劳:徒增解密成本、抬高产品化门槛,却没防住真正的攻击者。大家都在同一个权限平面上,谁防得住谁?也许得借助更高层次的保护,如 Credential Guard、VBS Enclave、TPM;也许该把保护机制交给 AV/EDR/HIDS 这些角色来做。比起这些,我想得更多的是 AI & Security。
AI 能力的提升对各领域都带来了极大的机遇与挑战,安全同样如此。不论 AI for Security 还是 Security for AI 都亟待关注与探索。
本来做 KNSoft.ZPigeon 是想探索如今 AI-Driven 与 Human-Driven 之间如何两全,让 AI 高效生成的大量功能代码稳定高效、可维护、专业度高,以及 AI 自闭环地实现“设计方案-编码-测试-改进”自迭代。但在实现此功能的过程中,GPT 以可能涉及 Cyber Security 为由拒绝了我无数次。
切换到 GLM 5.3 Max,虽然它不拒绝我了,但几经波折:
还有别的就不一一例举了,但一路过来也算是帮了不少忙。GLM 5.3 的宣传片中说让 AI 站在蓝方,但如果事与愿违,便不得不考虑:
GLM 5.3 在宣传上突出了安全方面的能力,是个很好的开始,至少说明国内前沿的模型开始关注 AI for Security。下次再做类似的事情时,GPT 还是会拒绝我,我还是会用 GLM 5.3,也推荐大家试试。即使不入手 Coding Plan,通过智谱自家的 AutoClaw 也能获取一些免费使用额度。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 (CC BY-NC-SA 4.0) 进行许可,如有错漏欢迎指出。
Ratin <ratin@knsoft.org>
中国国家认证系统架构设计师
ReactOS贡献者
0 3 15 末尾-16
┌─────────────┬───────┬───────────────────┬───────┐
│ "v10"/"v20" │ nonce │ 密文 ct │ tag │
│ 3 B │ 12 B │ 与明文等长 │ 16 B │
└─────────────┴───────┴───────────────────┴───────┘
版本号 随机数 AES-256-GCM 输出 防篡改校验
{
"os_crypt": {
"encrypted_key": "<Base64 编码>",
"app_bound_encrypted_key": "<Base64 编码>"
}
}
加密:CryptProtectData([in] 明文, [in, opt] 可选熵, [out] 密文)
解密:CryptUnprotectData([in] 密文, [in, opt] 可选熵, [out] 明文)
# 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 ''
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 特有的插入层)
"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"
#define SQLITE_API __declspec(dllimport)
#include <winsqlite/winsqlite3.h>
#pragma comment(lib, "winsqlite3.lib")
| 版本 |
总长度 |
Envelope 布局 |
AEAD |
| v1 |
61 |
version[1] + nonce[12] + ciphertext[32] + tag[16] |
AES-256-GCM |
| v2 |
61 |
version[1] + nonce[12] + ciphertext[32] + tag[16] |
ChaCha20-Poly1305 |
| v3 |
93 |
version[1] + cng_block[32] + nonce[12] + ciphertext[32] + tag[16] |
AES-256-GCM |
| 常量 |
值 |
| v1 AES-256-GCM Key |
B3 1C 6E 24 1A C8 46 72 8D A9 C1 FA C4 93 66 51 CF FB 94 4D 14 3A B8 16 27 6B CC 6D A0 28 47 87 |
| v2 ChaCha20-Poly1305 Key |
E9 8F 37 D7 F4 E1 FA 43 3D 19 30 4D C2 25 80 42 09 0E 2D 1D 7E EA 76 70 D4 1F 73 8D 08 72 96 60 |
| 常量 |
值 |
| v3 XOR Mask |
CC F8 A1 CE C5 66 05 B8 51 75 52 BA 1A 2D 06 1C 03 A2 9E 90 27 4F B2 FC F5 9B A4 B7 5C 39 23 90 |
| 浏览器/版本 |
CLSID(elevation_service 类) |
IID |
DecryptData 槽位 |
| Edge |
{1FCBE96C-1697-43AF-9140-2897C7C69767} |
IElevatorEdge {C9C2B807-7731-4F34-81B7-44FF7779522B}(= EdgeBase(3) + IElevator) |
8 |
| Chrome 127–151 |
{708860E0-F641-4611-8895-7D867DD3675B} |
IElevatorChrome {463ABECF-410D-407F-8AF5-0DF35A005CC8} |
5 |
| Chrome 152+ ★ |
同上 |
IElevator2Chrome {1BF5208B-295F-4992-B5F4-3A9BB6494838}(= IElevator2 + IElevator) |
5 |
- 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,历史记录(明文)
- ...
- Network\Cookies:SQLite,cookies 表(Cookie,加密)
- Login Data:SQLite,logins 表(密码,加密)
- Login Data For Account: SQLite, 同
Login Data,但这包含登录浏览器账号存储的密码
- History:SQLite,历史记录(明文)
- ...
- 版本号:目前 Windows 上只有 v10 和 v20 两种加密方式
- nonce:随机生成 12 字节,每条记录独立、只用一次
- AES-256-GCM(v10/v20 Key, nonce, 明文) 运算(AAD 为空)得到密文 + tag(用于校验)
encrypted_key:Base64("DPAPI" 5 字节前缀 + DPAPI Blob),Blob 加密保存 v10 Key
app_bound_encrypted_key:Base64("APPB" 4 字节前缀 + ABE Blob),Blob 加密保存 v20 Key
cookies 的 encrypted_value:从数据库 Schema 24 起为 SHA256(host_key) + cookie 值,开头的“域名指纹”防止密文被跨站拷贝复用
logins 的 password_value:直接是密码本身
PROTECTION_NONE:不校验应用身份
PROTECTION_PATH_VALIDATION:解封时要求当前调用方的规整路径与封装时一致
PROTECTION_PATH_VALIDATION_WITH_ISOLATION:除路径一致外,还校验进程的隔离状态
- 调用方的规整路径:
- 去掉 exe 文件名
- 去掉末尾的 Temp / Application / 版本号目录
- 把
Program Files (x86) 归一化为 Program Files
- 进程的隔离状态:
- Elevation Service (SYSTEM) 以带有隔离标记(
SetTokenInformation(TokenSecurityAttributes),例如 Chrome 的 GOOGLECHROME://ISOLATION)的 Token 创建浏览器进程
- 服务查询调用进程 Token 中是否存在此隔离标记,阻止非隔离客户端读取隔离客户端存储的 Key
- 去掉 exe 文件名
- 去掉末尾的 Temp / Application / 版本号目录
- 把
Program Files (x86) 归一化为 Program Files