Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wechat-key-macos

Extract SQLCipher database keys from WeChat for Mac 4.x (verified on 4.1.13), so you can decrypt and query your own local chat data — when older tools return 0 keys / cannot decrypt.

This repo is primarily an agent-generic skill (SKILL.md) plus a self-contained script, and it also bundles a patched wechat-cli so you get a complete working solution end-to-end.

⚠️ For your own local WeChat account on your own Mac only. All processing is local; nothing is uploaded. See Disclaimer.


TL;DR

# 1) deps (mind the version pins!)
python3 -m pip install 'frida<17' pycryptodome

# 2) one-time: make WeChat debuggable (see "task_for_pid" below), then fully
#    quit & reopen WeChat, keep it logged in.

# 3) extract keys (run as root; click around WeChat while it runs)
sudo python3 scripts/extract_key.py --output keys.json

# 4) (optional) also write decrypted, plaintext SQLite copies
sudo python3 scripts/extract_key.py --decrypt ./decrypted

keys.json{ "message/message_0.db": { "enc_key": "<hex>", "salt": "<hex>", "size_mb": N }, ... }.


Why old tools fail on WeChat 4.x

WeChat encrypts its SQLite databases with SQLCipher (AES-256-CBC).

  • WeChat ≤ 4.1.8 kept the raw key in memory as an ASCII PRAGMA string x'<64 hex key><32 hex salt>'. Tools (wechat-cli, wechat-decrypt, pywxdump, …) scanned process memory for that pattern. Works great — on old builds.

  • WeChat 4.x (e.g. 4.1.13) no longer keeps that string resident. Memory scans find 0 matches. The derived keys are also not reliably recoverable as aligned raw bytes. And each database has a different key:

    enc_key_i = PBKDF2-HMAC-SHA512(password, salt_i, 256000, 32)
    

    salt_i = first 16 bytes of each .db; password (the account master key) is shared across all databases.

So on 4.x you can't just grep memory. You need to observe the key as WeChat actually uses it.

The method used here

Attach Frida to the running WeChat process and hook the macOS CommonCrypto functions that SQLCipher calls:

Hook Why
CCCrypt, CCCryptorCreate, CCCryptorCreateWithMode SQLCipher AES-decrypts every DB page through these, passing the 32-byte derived key. WeChat reads its DBs constantly, so keys stream in. Each captured key is verified against every DB's page-1 HMAC to map key → database.
CCKeyDerivationPBKDF If a key derivation happens during capture, this yields the password directly → derive all DB keys at once.

No process restart, no fragile memory layout assumptions, no reliance on x'...' strings. It degrades gracefully: you get keys for whatever databases WeChat touches during the capture window (click around to cover more).

Full technical write-up: reference/method.md.

SQLCipher parameters (WeChat 4.x)

Parameter Value
Cipher AES-256-CBC
Page size 4096 bytes
Reserve / page 80 bytes (16-byte IV + 64-byte HMAC-SHA512)
KDF PBKDF2-HMAC-SHA512, 256000 iterations
HMAC subkey PBKDF2-HMAC-SHA512(enc_key, salt XOR 0x3a, 2, 32)
Salt first 16 bytes of each database file
Key usage enc_key is the final AES key (used directly, no extra derivation)

Verified environment / version matrix

Component Verified Notes
WeChat for Mac 4.1.13 expected to work across 4.0–4.1.x
macOS 26.6.2 13+ should be fine
CPU Apple Silicon (arm64) Intel (x86_64) supported by the same APIs
Python 3.14.3 any 3.10+
frida 16.7.19 MUST be <17. frida 17 removed built-in JS bridges / changed behavior this relies on.
pycryptodome 3.23.0 any recent 3.x
numpy (not required here) only the bundled wechat-cli's legacy memory scanner uses it

Prerequisites

  1. Full Disk Access for your terminal: System Settings → Privacy & Security → Full Disk Access → add Terminal/iTerm → restart the terminal.

  2. WeChat running and logged in.

  3. Make WeChat debuggable (once). If frida attach or task_for_pid fails, ad-hoc re-sign WeChat with get-task-allow:

    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

    Then fully quit WeChat (⌘Q) and reopen it. Re-signing is local, reversible (reinstall WeChat to revert), and does not affect the account.

Usage

# auto-detect db_storage, capture for 240s (default)
sudo python3 scripts/extract_key.py

# explicit paths / longer window
sudo python3 scripts/extract_key.py \
  --db-dir "$HOME/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/<wxid>/db_storage" \
  --output keys.json --wait 300

# also decrypt matched DBs into ./decrypted as plaintext SQLite
sudo python3 scripts/extract_key.py --decrypt ./decrypted

While it runs, click around WeChat: open several chats, Contacts, Favorites, Moments, stickers, and use search. Each feature reads its database, revealing that DB's key. Databases not read during the window are simply skipped — re-run after opening them.

Querying your data (bundled wechat-cli)

wechat-cli/ is a fork of huohuoer/wechat-cli patched to work on WeChat 4.x macOS (adds init --method frida, a ctypes memory scanner, and the CommonCrypto extraction above). It reads keys from ~/.wechat-cli/all_keys.json (same format as keys.json).

cd wechat-cli
python3 -m venv .venv && . .venv/bin/activate
pip install 'frida<17' -e .

# extract keys straight into ~/.wechat-cli (frida method for 4.x)
sudo .venv/bin/wechat-cli init --force --method frida
sudo chown -R "$(whoami)" ~/.wechat-cli   # init runs as root; give the files back to you

# then query (no sudo)
wechat-cli sessions --limit 10
wechat-cli history "name" --limit 20 --format text
wechat-cli search "keyword"
wechat-cli contacts --query "name"

Troubleshooting

Symptom Fix
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; raise --wait.
frida ObjC undefined / attach weirdness You're on frida ≥ 17 — pip install 'frida<17'.
task_for_pid failed Do the re-sign step, then fully quit & reopen WeChat.
pip TLS errors behind a filtering proxy pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org 'frida<17' pycryptodome.
macOS git/cc say "agree to the Xcode license" sudo xcodebuild -license once (unrelated to this tool).
keys extract but decryption looks wrong Confirm page 4096 / reserve 80; a future WeChat could change these (see reference/method.md).

Credits

Disclaimer

For personal use on your own device and account only. This tool reads locally stored data; it does not send, modify, or delete messages, and nothing leaves your machine. You are responsible for complying with applicable laws and WeChat's terms. Provided as-is, no warranty.

License

Apache-2.0 (matches the bundled wechat-cli).

About

Extract SQLCipher keys from WeChat for Mac 4.x (tested 4.1.13) to decrypt local chat DBs — agent-generic skill + Frida CommonCrypto method + patched wechat-cli

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages