# Wechat Key Macos

> Extract SQLCipher database keys from WeChat for Mac 4.x (tested 4.1.13) so its local chat databases can be decrypted and queried. Use when a user on macOS wants to read/decrypt/export their own local WeChat data and existing tools (e.g. wechat-cli, wechat-decrypt) fail with 0 keys / "cannot decrypt" because they only support WeChat <= 4.1.8. Runtime: macOS (Apple Silicon or Intel), Python 3.10+, frida < 17, pycryptodome; requires sudo and a running, logged-in WeChat that has been ad-hoc re-signed with get-task-allow.

- Skill: `3351666087/wechat-key-macos` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add 3351666087/wechat-key-macos`
- Raw SKILL.md: https://api.skillmd.com/api/skills/3351666087/wechat-key-macos/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- License: Apache-2.0
- Author: 3351666087 (https://skillmd.com/u/3351666087)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/3351666087/wechat-key-macos

---


# wechat-key-macos

Extract per-database SQLCipher keys from a **running WeChat for Mac 4.x** and use
them to decrypt/query the local databases. This is agent-agnostic: any coding
agent (or a human) can follow it.

## When to use

Use this skill when, on macOS, the user wants to read/query/export **their own**
local WeChat data and a key-extraction step fails on a modern WeChat build —
symptoms: `提取到 0 个密钥` / "extracted 0 keys", `无法解密 session.db` / "cannot
decrypt", or a memory scan that finds no `x'...'` pattern. Those tools assume
WeChat ≤ 4.1.8; this skill handles 4.x (verified on **4.1.13**).

Only operate on the machine's own logged-in account, at the user's request.

## Why the old method fails on 4.x (one paragraph)

WeChat ≤ 4.1.8 left the SQLCipher raw key in memory as an ASCII PRAGMA string
`x'<64 hex key><32 hex salt>'`, so tools scanned for it. WeChat 4.x no longer
keeps that string resident, and the derived keys are not reliably recoverable as
aligned raw bytes either. **Each database now has its own key**
`enc_key_i = PBKDF2-HMAC-SHA512(password, salt_i, 256000, 32)` (salt = first 16
bytes of each file; the account `password` is shared across all DBs). The
reliable, version-robust way to obtain the keys is to hook the system crypto
functions WeChat calls at runtime — see `reference/method.md`.

## Method (what the script does)

Hook macOS **CommonCrypto** with Frida on the live WeChat process:

- `CCCrypt`, `CCCryptorCreate`, `CCCryptorCreateWithMode` — SQLCipher calls these
  to AES-decrypt **every database page**, passing the 32-byte derived key. WeChat
  reads its DBs constantly, so keys stream in with no special trigger. Each
  captured key is verified against every database's page-1 HMAC-SHA512 to map it
  to a database.
- `CCKeyDerivationPBKDF` — if a key derivation happens (e.g. a DB is opened during
  capture), this yields the **password** directly, letting us derive keys for all
  databases at once.

SQLCipher parameters (WeChat 4.x): AES-256-CBC, page size **4096**, reserve **80**
(= 16-byte IV + 64-byte HMAC-SHA512), KDF **PBKDF2-HMAC-SHA512 × 256000**, HMAC
subkey via `PBKDF2(enc_key, salt XOR 0x3a, 2)`, per-database salt.

## Prerequisites (do these first)

1. **Full Disk Access** for the terminal: System Settings → Privacy & Security →
   Full Disk Access → add Terminal/iTerm → restart it.
2. **WeChat running and logged in.**
3. **Make WeChat debuggable (once).** If attaching or `task_for_pid` fails,
   ad-hoc re-sign WeChat with `get-task-allow`, then fully quit & reopen WeChat:
   ```bash
   sudo codesign --force --sign - \
     --entitlements /dev/stdin /Applications/WeChat.app <<'EOF'
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
   <plist version="1.0"><dict>
     <key>com.apple.security.get-task-allow</key><true/>
   </dict></plist>
   EOF
   ```
   Re-signing is local and reversible (reinstall WeChat to revert). It does not
   affect the account.
4. **Install deps** (note the pins):
   ```bash
   python3 -m pip install 'frida<17' pycryptodome
   ```

## Steps

1. Ensure prerequisites above.
2. Run the extractor as root:
   ```bash
   sudo python3 scripts/extract_key.py            # auto-detects db_storage
   # or: sudo python3 scripts/extract_key.py --db-dir "<...>/db_storage" --wait 240
   ```
3. **While it runs, interact with WeChat** — open several chats, Contacts,
   Favorites, Moments, the sticker panel, and use search. Each feature reads its
   database, which makes SQLCipher decrypt pages and thus reveals that DB's key.
   You'll see `[FOUND] <db> enc_key=...` lines.
4. It writes `keys.json` = `{ "rel/path.db": { "enc_key": "<hex>", "salt": "<hex>", "size_mb": N }, ... }`.
   Databases not read during the window won't be covered — re-run after opening
   those features to fill them in.
5. To also produce decrypted, plaintext SQLite files:
   ```bash
   sudo python3 scripts/extract_key.py --decrypt ./decrypted
   ```
   Then open `./decrypted/message/message_0.db` etc. with any SQLite tool, or use
   the bundled `wechat-cli/` to query (it consumes the same key format at
   `~/.wechat-cli/all_keys.json`).

## Verifying a key without reading private data

Decrypt only page 1 and check the SQLite header — no chat content is exposed:
```python
from scripts.extract_key import decrypt_page  # header should be b"SQLite format 3\x00"
```

## Output contract

`keys.json`: object keyed by database path relative to `db_storage`, each value
`{ "enc_key": <64 hex = 32-byte AES-256 key, used directly>, "salt": <32 hex>,
"size_mb": <float> }`. `enc_key` is the final AES key (no further derivation).

## Troubleshooting

- **`aes_keys_seen` stays 0** → WeChat didn't call CommonCrypto during capture, or
  attach didn't take. Confirm sudo + re-signed WeChat; keep clicking in WeChat.
- **frida `ObjC` / bridge / attach oddities** → you're on frida ≥ 17. Pin `frida<17`.
- **`task_for_pid failed`** → do the re-sign step, then fully quit & reopen WeChat.
- **pip TLS errors behind a filtering proxy** → add
  `--trusted-host pypi.org --trusted-host files.pythonhosted.org`.
- **Keys extract but decryption looks wrong** → confirm page size 4096 / reserve 80;
  a future WeChat could change these (see `reference/method.md`).

## Safety / scope

Local, read-only with respect to WeChat's data. Operate only on the user's own
machine and account, at their explicit request. Never upload databases or keys.
See `README.md` disclaimer.

