# WCDB / SQLCipher 动态调试抓取 Key 方法整理 ## 1. 前言 > 基于当前最新 weixin 4.1.0.18版本的思路 * WCDB 基于 SQLite/SQLCipher 定制,数据库加密依赖 **cipher key**。 * 加密流程:上层调用 `setCipherKey` → 底层函数 `sub_183D75280` / `sub_183D750F0` → 调用 `sub_1828B11E0` 生成 codec context → 安装到 Pager (`sub_183D71450`),并设置页级解密函数 `sub_183D74E50`。 * 动态调试时,只要在 key 尚未进入派生/KDF 之前断点,就能直接 dump 出明文 key。 --- ## 2. 抓取 Key 的关键函数链 1. **应用配置 / 迁移函数** * `sub_1828B1B30`:尝试 `PRAGMA cipher_compatibility=3/2/1`,必要时 `ATTACH ... KEY` + `sqlcipher_export()`。 2. **设置密钥并发 PRAGMA** * `sub_1828B2230(db, keyPtr, keyLen, pragmaFmt, &userVer, &jmode)` * 调用 `sub_183D75280(db, keyPtr, keyLen)` 设置 key * 再执行 `PRAGMA cipher_*` *  3. **设置密钥的核心函数** * `sub_183D75280(db, keyPtr, keyLen)` → `sub_183D752A0` → `sub_183D750F0` * 之后下断点在这里即可获取keyPtr *  * 在 `sub_183D750F0` 内部调用 `sub_1828B11E0` 创建 codec\_ctx,再通过 `sub_183D71450` 安装 pager codec: * **xCodec** \= `sub_183D74E50` ← 页级解密函数 * **xCodecFree** \= `sub_183D75240` --- ## 3. 动态调试断点位置 ### A. 关键函数:`sub_183D75280` * **Windows x64 调用约定**: * `RCX = db handle` * `RDX = key 指针` * `R8D = key 长度` * 在函数入口下断 → 直接读取 `RDX` 指向的内存,长度 `R8D`。 * **这就是明文 key**。 ### B. 下游:`sub_183D750F0` * 仍然带有 `a3=keyPtr, a4=keyLen`,在 codec 创建前可抓取。 ### C. Codec 创建:`sub_1828B11E0` * 参数同样包含 keyPtr/keyLen,用于派生实际页加解密密钥。 * 理论上也可以在这里抓,之后方法失效可以尝试。 --- ## 4. 验证交叉点 * 在调试器中 dump `RDX` 地址的内存,按 `R8D` 长度保存。 * 常见情况: * **32 字节随机高熵的二进制** → 直接就是 AES256/ChaCha20 key; * **ASCII/UTF-8 字符串** → 是口令,后续会在 `sub_1828B11E0` 里做 PBKDF2 派生。 * 验证方法: * 后续进入页解密函数 `sub_183D74E50`,在 `sqlite3OsRead` 返回页后会被调用,对整页 buffer 解密;页头会从乱码变 `"SQLite format 3\0"`。 * 或者直接使用[DB Browser for SQLCipher](https://sqlitebrowser.org/) 浏览原始数据库文件。 --- ## 5. 抓取示例 > 注意:断点断在 dll基指+183D75280-18000000 其他地方也可以,具体看需求。 ### 调试时寄存器快照 ``` RDX = 00000237329BDB50 → key 指针 R8D = 0x20 → key 长度 32 ``` ### 内存 dump(32 字节) ``` B1 8A 40 98 xxxxx FD 2C 4E 1C 64 EB 26 D3 xxxxx DE 70 E4 74 ``` ### 格式化输出 * Hex: `b18a40xxxxxxx70e474`  ### 基于key获取rawkey  ### 使用 sqlcipher browser 浏览数据库  ### 成功访问  --- ## 6. 总之 1. 下断 `sub_183D75280` → 看 `RDX` / `R8D` → dump 内存 → 得到 key。 2. Key 会传递给 `sub_1828B11E0` → codec\_ctx → Pager (`sub_183D71450`)。 3. 页解密函数是 `sub_183D74E50`。 ## 7.最后 最后就可以通过hook的手段,通过插入int3然后强行读取rdx来然后取消hook就可以获取到key了,这里我就不展示了。