郭立 (leeguoo)

# 微信ローカルデータの二つの道——wechat-use と wechat-decrypt の原理レベルの比較(実測による訂正付き)

二つの微信復号ツールはよく比較されますが、比較の方向がずれています。本当の違いは機能の多さではなく、SQLCipher の key をどう取得するかにあります。この記事では両者を原理レベルまで分解します——SQLCipher の per-DB 鍵モデル、メモリから key を取り出す二つの仕組み(LLDB+entitlement vs sudo+mach_vm)——さらに実際に自分のマシンで走らせ、初版を書いたときの二つの誤判定もついでに訂正しました。

2026年7月6日 · 記事 · 公開

このページの目次
1.まず{暗号化|あんごうか}モデルをきっちり説明する2.原理の核心:メモリから key をこじ開ける二つのルート3.ルート A:wechat-decrypt —— replace-mode 再署名 + sudo メモリスキャン4.ルート B:wechat-use —— key 読み取りは再署名なし(ログイン瞬間のデバッグインターフェース)5.真似する価値のある工夫:画像 key はメモリスキャンしない6.復号そのもの:両者はまったく同じ、手動でも数行7.データベース取得後:能力は分岐する8.それぞれの workflow9.wechat-use(送信 + 読み取り + リアルタイム)10.wechat-decrypt(復号 + エクスポート + MCP、macOS)11.組み合わせる(実際の場面:agent を WeChat に接続)12.見落とされがちな 2 つの点13.リスクと境界(これは復号ツールなので、明確にしておくべきこと)14.では、彼の実装は私たちのものより優れているのか15.選定結論

Snoopy が<ruby>鍵<rt>かぎ</rt></ruby>を持って、ロックされたチャット吹き出しに届こうとしている

微信 4.x はローカルのチャットデータベースを SQLCipher の中にロックしました。プログラムでチャット記録きろく画像がぞう音声おんせいを読ませたいなら、避けて通れないことがあります。まず復号ふくごう用の key を手に入れることです。

「key を取ってからデータベースを読む」ということをめぐって、コミュニティには成熟した二つの道があります。一つは wechat-use——macOS 上でメッセージ送信そうしん + データベースり + リアルタイム監視かんしを行うあの一式です。もう一つはオープンソースの wechat-decrypt——クロスプラットフォームの復号ふくごう + 一括いっかつエクスポート + 音声おんせいテキスト変換へんかん + ネイティブ MCP です。

この二つはよく比較されますが、比較の方向がずれています。本当の違いは「どちらの機能が多いか」ではなく、その key をどう取得するか、そして取得したあとに何ができるかにあります。この記事では両者を原理レベルまで分解します。書き終えたあと、自分のマシンで wechat-use を実際に走らせてみました——その結果、自分の二つの誤判定を訂正することになりました。後ほど説明します。

まず暗号化あんごうかモデルをきっちり説明する

WeChat 4.x のローカル DB は標準の SQLCipher 4暗号化あんごうかされた SQLite で、場所はここです:

macOS: ~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/<wxid>/db_storage

SQLCipher 復号モデル:ファイルヘッダー salt + メモリ raw key → SQLCipher 4 → 平文

SQLCipher の鍵モデルには、先に理解しておくべきポイントが三つあります。ここを押さえないと、この後二つのツールが何をこじ開けているのか分かりません:

  • 1 DB 1 key。 グローバルな鍵が一本あるわけではありません。contact.dbmessage_*.dbsns.db…それぞれの DB が自分専用の 32 バイト raw key を持っています。私のマシンで wechat-use が保存した ~/.wx-rs/keys.json を見ると、ちょうど 19 個の per-DB エントリがあり、各行は {key_hex: 64 文字, salt_hex: 32 文字} でした。
  • salt はファイルヘッダーに隠れている。暗号化あんごうか DB ファイルの先頭 16 バイトはランダム salt です。暗号化あんごうかされた SQLite を開いても SQLite format 3 で始まらないのはそのためです——その 16 バイトが salt に占有されています。wechat-decrypt の C コードにも memcmp(header, "SQLite format 3", 15) == 0 → return -1 という一文があります。平文 DB に当たったらスキップするわけです。
  • raw key はメモリ内にしかない。 32 バイトの raw key はディスクには落ちません。WeChat 実行時にだけ派生され、プロセスのメモリ内に保持されます。形式は x'<64桁 key hex><32桁 salt hex>' です(つまり SQLCipher PRAGMA key の raw 形式で、96 個の 16 進文字)。

つまり一つの DB を復号するには、二つをそろえる必要があります: raw key(プロセスメモリから抜き出す)+ salt(DB ファイルヘッダー先頭 16 バイトから読む)wechat-decryptfind_all_keys_macos.c にある HEX_PATTERN_LEN 96key_hex[65]salt_hex[33] という定数は、まさにこのモデルに対応しています。

「salt はファイルヘッダーにある」というのは私の当てずっぽうではありません。hexdump 一回で検証できます。 私のマシンの contact.db の先頭 16 バイトはこうでした:

$ xxd -l 16 contact/contact.db
00000000: 90f7 c71d 17b5 bfe6 24b1 76ac df40 df7f

平文 SQLite なら 53 51 4c 69 74 65 …"SQLite format 3\000")で始まるはずです。ここではランダムなバイト列になっています——この 16 バイトこそ salt で、暗号化あんごうかによってファイルヘッダーが置き換わっています。wechat-decrypt が「すでに平文 DB かどうか」を判断するのに使っているのも、この memcmp(header, "SQLite format 3", 15) です。

1 アカウントにつき一式の DB があり、単一ファイルではありません。 WeChat は用途ごとに 20 個近い暗号化あんごうか DB に分割しており、それぞれ別々の key を持っています。私のマシンの ~/.wx-rs/keys.json には 19 件あり、対応関係は次の通りです:

DB入っているもの
message/message_0.dbmessage_1.dbチャット本文(容量に応じた分割)
message/message_fts.dbチャット全文検索インデックス
message/media_0.dbmessage_resource.dbメッセージ内のメディア/リソース
message/biz_message_0.db公式アカウント/サービスアカウントのメッセージ
session/session.db会話リスト(左側のあの欄)
contact/contact.dbcontact_fts.db連絡先 + 連絡先検索
sns/sns.dbモーメンツ
favorite/favorite.dbfavorite_fts.dbお気に入り
emoticon/emoticon.db / head_image/head_image.dbスタンプ / アバター
hardlink/hardlink.dbファイルのハードリンクインデックス
bizchatgeneralsolitaireweclaw企業アカウント会話 / 汎用設定 / 接龍 / など

難しいのは復号そのものではありません——SQLCipher 標準ライブラリは、正しい key を渡せば開けます——難しいのは、その key をメモリから抜き出すことです。 二つのツールの本当の差が出るのは、まさにこのステップです。

原理の核心:メモリから key をこじ開ける二つのルート

二つの key 抜き出しフロー:wechat-use はログイン瞬間のデバッグインターフェース経由(再署名なし・sudo なし・直行);wechat-decrypt は先に再署名 + sudo + メモリスキャンで key 探索

WeChat プロセスのメモリを読むには、macOS 側に一つ壁があります。WeChat は hardened runtime 付きで署名されており、システムはデフォルトで他のプロセスが task_for_pid で attach することを許しません——たとえ root でも。二つのツールがこの壁を越える方法こそが最大の違いであり、私がこの記事の修正でいちばん無様に迷走した部分でもあります(下で、どう二回間違えたかを書きます)。

ルート A:wechat-decrypt —— replace-mode 再署名 + sudo メモリスキャン

$ bash
sudo codesign --force --deep --sign - /Applications/WeChat.app   # ① replace-mode で ad-hoc に再署名
cc -O2 -o find_all_keys_macos find_all_keys_macos.c -framework Foundation
sudo ./find_all_keys_macos                                        # ② root でメモリスキャン

codesign --force --deep --sign -replace-mode(置換式) の再署名です。WeChat 全体を ad-hoc 署名に変更し、hardened runtime だけでなく WeChat 自身の entitlements もまとめて消し去り、その後 root 権限mach_vm_read を使ってメモリをブロック単位(1 ブロック 2MB)でスキャンし、96 桁 hex の key+salt パターンを正規表現で探し、ヒットしたものを all_keys.json に書き込みます。salt は別途 DB ファイルヘッダから読みます。考え方は単純明快で、リバースエンジニアリング界隈では標準的なやり方であり、完全にオープンソースで監査可能です。代償は、root が必要なこと、そして置換式の再署名によって WeChat の元の権限まで一緒に消してしまうことです(極端なケースでは WeChat 自身の TCC に影響する可能性があります)。

ルート B:wechat-use —— key 読み取りは再署名なし(ログイン瞬間のデバッグインターフェース)

ここは私が最初にいちばんねじれて理解していたところなので、二層に分けて説明します。

key の読み取り(init)自体は再署名も sudo も不要です。 これは WeChat にログインするその瞬間に、macOS の公開デバッグインターフェースを使って復号材料を一度だけ取得し、取得後すぐ detach します——codesign --force --deep はせず、root も使いません。ルート A とは完全に別物です。ルート A は、先に置換式で再署名してから root で力ずくのメモリスキャンをします。

メッセージ送信が必要な場合だけ再署名が必要で、しかも merge-mode です。 送信用の slot_send アダプタには WeChat の get-task-allow=true が必要で、そのために一度 merge-mode(合併式) の再署名を走らせます——追加する entitlement は get-task-allow ただ一つで、WeChat が元から持つすべての権限は保持します(SKILL では replace-mode を使うなと明確に警告しています。なぜなら、それを使うと WeChat の TCC 権限が消えるからです)。ここがルート A より抑制的な点です。同じく署名を触るにしても、こちらは外科手術的な合併であって、一刀両断の置換ではありません。

私のローカルでの wechat-use doctor の出力です。

✓ wechat_get_task_allow   get-task-allow = true (LLDB が attach 可能)
✓ wechat_codesigned       signer: ad-hoc (ローカル再署名済み、get-task-allow 含む)
✓ wechat_dylib_fingerprint SHA-256=fc162db6…  raw_key ✓ (keymap)
✓ wechat_update_guard     ✓ disabled (Tencent が MacUpdate に書き込めない)

ここで罠にはまらないよう注意してください(私ははまりました)。ここに「ローカル再署名済み」と表示されているのは、このマシンでは送信機能を設定済みで、その merge-mode 再署名を実行したことがあるからです。これは key 読み取りの必須条件ではありません——読み取りだけで送信しないなら、init のログイン瞬間インターフェースだけで十分です。もう二つ細かい点があります。wechat_dylib_fingerprint は、WeChat の dylib フィンガープリントに応じて keymap を選び raw key の位置を特定していることを示します(固定 pattern を力ずくでスキャンするよりアップグレード耐性があります)。wechat_update_guard: disabled は、MacUpdate ディレクトリを書き込み不能にして、再署名がアップデートで上書きされないようにしていることを示します。

key 抜き出しを一言で比較すると、wechat-decrypt の key 読み取り = replace-mode 再署名 + root でメモリ総当たりスキャン;wechat-use の key 読み取り = ログイン瞬間のデバッグインターフェース、再署名なし・sudo なし(メッセージ送信時だけ別途 merge-mode 再署名) です。key 読み取りというステップでは、wechat-use のほうが明らかに軽く、抑制的です。一方で wechat-decrypt は粗いものの、完全オープンソースで、クロスプラットフォームに一貫しています。

真似する価値のある工夫:画像 key はメモリスキャンしない

wechat-decrypt には、なかなか賢い細部があります。画像の key はプロセスメモリから取らず、ディスク上の kvcomm キャッシュから派生しますfind_image_key_macos.py)。WeChat は画像 .dat の key をディスクキャッシュに残しているため、画像を復号するだけならプロセスに attach する必要も、root も不要です。自分で画像復号を作るなら、このルートはクリーンなので、そのまま真似できます。

復号そのもの:両者はまったく同じ、手動でも数行

raw key を抜き出し、ファイルヘッダの salt と組み合わせれば、その後に謎はありません——どちらも標準 SQLCipher 4 です。key を手に入れた後、手動で DB を一つ復号するなら、たったこれだけです(key は伏せ字)。

$ sql
$ sqlcipher contact/contact.db
sqlite> PRAGMA key = "x'<64桁 key hex…>'";   -- メモリから抜いた raw key
sqlite> PRAGMA cipher_compatibility = 4;      -- SQLCipher 4 を指定
sqlite> .tables                               -- テーブルが列挙できれば復号成功

key が正しければ、.tables で連絡先テーブルが表示されます。key が間違っていれば、即座に file is not a database と出ます。salt を手動で渡す必要はありません——ファイルヘッダ先頭 16 バイトにあり、SQLCipher が自分で読みます。二つのツールがやっているのは、この数行の自動化です。前段の再署名 / attach / メモリスキャンといったすべての労力は、あの x'…' を手に入れるためだけにあります。どちらにも独自の復号アルゴリズムなどありません。「どちらの復号が強いか」は偽の問いであり、違いはすべて前段の key 抜き出しにあります。

データベース取得後:能力は分岐する

メッセージを届ける郵便配達員 vs データを掘る探偵

key の抽出は入場券であり、手に入れた後、二つのツールは別々の方向へ進みます。この表は、私が実測して訂正した後の版です(バツが付いている箇所は、第一版で書き間違えたところです)。

観点wechat-usewechat-decrypt
メッセージ送信バックグラウンドで UI の点滅なし、配信状態付き送信不可、読み取り専用
DB 読み取り(会話 / 連絡先 / 履歴 / 画像)対応(手元の実測では sessions が正常に返る)全量を平文 SQLite に復号
音声文字起こし✅ 対応(v1.17: silk-decoder + whisper-cli + ggml-medium)✅ SILK→WAV→テキスト
key 抽出方式(macOS)key 読み取り:ログイン直後のデバッグインターフェイス、再署名なし・sudo なし(メッセージ送信時のみ別途 merge-mode 再署名)replace-mode 再署名 + sudo mach_vm ハードスキャン
アップグレード追従dylib フィンガープリント keymap + probe-build による自適応固定メモリ pattern、作者の更新待ち
プラットフォームmacOS のみWin / Mac / Linux
企業微信なし5.x は Windows のみ実測
Claude 用(MCP)HTTP/gRPC 経由、自分でラップが必要ネイティブ MCP、1 行で接続、20+ ツール
リアルタイム監視daemon RPC → SSEWeb UI SSE / CLI / MCP
リモート接続Cloudflare Tunnel で REST を公開基本はローカル
コスト有料アクティベーションコード無料・オープンソース

ここにこそ実測の価値があり、しかも私は一度ならず間違えました。 第一版ではドキュメントを読んだだけで「wechat-use は再署名せず、音声文字起こしもしない」と書きました。実際に動かすと doctor に「ローカル再署名」と表示されたため、間違いを見つけたと思い、「両方とも再署名しないと読めない」と直しました——しかし結果的に行き過ぎた訂正でした。その時の再署名はメッセージ送信用の設定であり、key 読み取り自体は再署名しません。SKILL 全体とソースコードを読んで、ようやく正確につながりました。key 読み取りは再署名なし、メッセージ送信時だけ merge-mode 再署名。文字起こしは v1.17 の時点で元からあります。 ドキュメント読みで一度間違え、半端な証拠を見てまた間違え、ツールを動かし切り、ドキュメントとソースを読み切って、ようやく結論を出せました。

この表を一言で読むなら、こうです。wechat-use は「送信 + 読み取り + 文字起こし + リアルタイム」の万能 bot プラットフォーム(有料・クローズドソース・macOS のみ)であり、wechat-decrypt は「復号 + エクスポート + 文字起こし + ネイティブ MCP」のオープンソースデータツール(無料・クロスプラットフォーム・企業微信あり、ただしメッセージ送信不可)です。

それぞれの workflow

wechat-use(送信 + 読み取り + リアルタイム)

① TG チャンネルを購読してアクティベーションコードを取得
② wechat-use init          # ログイン瞬間にデバッグインターフェースで key を抜く。再署名なし、sudo なし(送信時だけ別途 merge-mode 再署名)。daemon は必要時に起動
   ├─ 読み取り: wechat-use sessions / history / contacts / search / image  (YAML、低 token)
   ├─ 送信: wechat-use send "テキスト" <名前またはwxid>                         (バックグラウンドでゼロ点滅)
   └─ リアルタイム: wechat-use listen  または  wechat-bridge → HTTP /messages/stream?since=
③ リモート: wechat-use tunnel setup → Cloudflare Tunnel で REST を公開
④ アップグレード: wechat-use probe-build → 新 build に適配

wechat-decrypt(復号 + エクスポート + MCP、macOS)

① python venv + pip install -r requirements.txt + brew install whisper-cpp
② WeChat を終了 → sudo codesign で再署名 → cc でコンパイル → sudo ./find_all_keys_macos   # root でメモリをスキャンして key を抜く
③ python decrypt_db.py            # 全量復号 → 平文 SQLite(-i で増分)
   ├─ エクスポート: python export_all_chats.py -t  # JSON/CSV/HTML + 音声文字起こし + 増分/日付ウィンドウ
   ├─ リアルタイム: python main.py                 # Web UI localhost:5678 (SSE)
   └─ Claude へ: claude mcp add wechat -- python mcp_server.py   # 20+ 個の検索ツール

mcp_server.py は 1 行で Claude に接続でき、get_chat_history / search_messages / decode_voice で直接検索できます。モーメンツなどの XML を解析する際には XXE 防護DOCTYPE/ENTITY の拒否 + サイズ上限)も入っており、この点は多くの同類より厳密です。

組み合わせる(実際の場面:agent を WeChat に接続)

メッセージ送信 / リアルタイム応答 / グループ @ bot ──────────→  wechat-use   (成熟、実運用済み)
履歴の一括取得 / 企業微信 / Claude でネイティブ検索 ──→  wechat-decrypt MCP  (MCP + クロスプラットフォームを補完)

両者は競合しませんが、同時に走らせないでください——どちらも WeChat に attach し、署名と DB を触るため、1 台のマシンで 2 つ同時に上げると、署名と attach が衝突します。

見落とされがちな 2 つの点

音声をどうテキスト化するかは、どちらも同じです。 WeChat の音声は mp3 ではなく、SILK(Skype がオープンソース化した狭帯域音声 codec)です。そのため、テキスト化は 3 段のパイプラインになります:SILK → WAV にデコード → whisper に渡して文字起こしwechat-decrypt は silk decoder + whisper.cpp/OpenAI を内蔵しています。wechat-use v1.17 も silk-decoder + whisper-cli + ggml-medium を同梱しており、グループ履歴を読む際に音声を自動で文字起こしします。この流れは、TTS の歓迎メッセージとはちょうど逆です——あちらはテキスト→音声、こちらは音声→テキストですが、どちらも whisper に依存しています。

個人 WeChat と企業微信は同じ鍵ではありません。 個人 WeChat 4.x は SQLCipher 4(前述の仕組み:ファイルヘッダーの salt + メモリ上の raw key)を使います。企業微信 5.x は別物で、wxSQLite3 の AES-128 です。暗号化パラメータも key の位置も異なるため、wechat-decryptfind_wxwork_keys.py を別途用意しており、しかも実測済みなのは Windows のみです。wechat-use はそもそも企業微信には触れません。企業微信を復号したい場合、現時点では wechat-decrypt + Windows という選択肢しかありません。

リスクと境界(これは復号ツールなので、明確にしておくべきこと)

  • どちらも WeChat の署名を変更する可能性がありますが、方法とタイミングが異なります。 wechat-decrypt は key を読むために replace-mode で再署名(WeChat 既存の権限をまとめて消す)+ root を行います。wechat-use は key の読み取りでは再署名せず、メッセージ送信時だけ merge-mode で get-task-allow を追加(既存の権限は保持)し、それを維持するために自動更新を能動的に無効化します。いずれにせよ、ad-hoc 再署名によって WeChat は公式署名からローカル署名に変わるため、理論上は検出されたり、Gatekeeper の警告が出たりする可能性があります。手を動かす前に、自分が何を変更しているのか理解しておきましょう。
  • 自分のアカウント、自分のデータだけを復号してください。 この種のツールの正当な用途は、自分のチャットをエクスポート/バックアップすることであり、他人のものを読むことではありません。
  • key とチャット内容を外部に漏らさないでください。 raw key が一度漏れれば、誰でもあなたのデータベースを復号できます。この記事内の実際の key やチャット内容はすべてマスキングしています——この種の記事を書いたり、ログを貼ったりするときは、必ずそうするべきです。
  • Developer Mode / attach 権限が必要です。 wechat-use には macOS Developer Mode + get-task-allow が必要で、wechat-decrypt には root が必要です。どちらも「任意のプロセスのメモリを読める」レベルの権限なので、付与する前によく考えてください。

では、彼の実装は私たちのものより優れているのか

分けて見ると:

  • key を抜く手法:wechat-use のほうが軽く、控えめです。 key を読むだけならそもそも再署名しません(ログイン直後にデバッグインターフェースを呼ぶだけで、sudo も不要)。メッセージ送信が必要なときだけ merge-mode で再署名します(get-task-allow を追加するだけで、WeChat の元の権限は保持)。一方 wechat-decrypt は key を読むだけでも replace-mode で再署名 + root で力技スキャンが必要で、より荒く、WeChat の既存権限まで一緒に消してしまいます。ただし、完全にオープンソースで、監査可能で、クロスプラットフォームで一式そろっています
  • 復号自体:同じです。 どちらも標準の SQLCipher 4 で、誰かが黒魔術を持っているわけではありません。
  • 機能範囲:それぞれ得意分野があります。 wechat-use は「メッセージ送信」を独占し、wechat-decrypt は「ネイティブ MCP + クロスプラットフォーム + 企業 WeChat」を独占しています。音声文字起こしは両方にあります(この点は最初、私が勘違いしていました)。

正確に言えば、これは「どちらの実装が優れているか」ではなく、2つの路線です。 key 抽出のスマートさと「メッセージを送れる」点では wechat-use の勝ち。オープンソース、無料、クロスプラットフォーム、ネイティブ MCP では wechat-decrypt の勝ちです。

選定結論

  • メッセージを送るなら、wechat-use は不可欠です。 wechat-decrypt はそもそも送信できないので、代替にはなりません。
  • wechat-decrypt を導入する価値は、補完する2点にあります。 無料のネイティブ MCP(Claude から1行で接続し、チャット履歴を直接検索できること)、そしてクロスプラットフォーム + 企業 WeChat 対応です。音声文字起こしは、もはや独自の売りではありません——両方にあります。
  • 実運用では、wechat-use は引き続き送信 + リアルタイム + DB 読み取りの主力として使い、「Claude からネイティブに WeChat データを検索したい」または「企業 WeChat を読む / Linux で動かす」必要があるときに、wechat-decrypt の MCP を追加導入するのがよいです。同じマシンで2セットを同時に起動しないこと。

技術的には、相手側から2つ真似できます。画像 key をディスク上の kvcomm キャッシュから派生すること(プロセスに attach しなくてよい)、そして x'key+salt' という 96-hex のメモリ形式を keymap と組み合わせて位置特定すること(pattern を力技でスキャンするよりアップグレードに強い)。いつか自前実装や強化をするなら、この2つの trick はそのまま使えます。

最後に方法論を一言。この文章で最も価値があるのは、あの比較表ではなく、私が2回間違えた過程です——ドキュメントを読んで一度書き間違え、doctor の再署名という半分の証拠を見てまた反対方向に行きすぎ、ツールを最後まで走らせ、SKILL とソースコードを読み切って、ようやく結論を出せました。比較を書くときに一番危ないのは、読んでいないことではなく、半分だけ読んで書き始めることです——そして誤解の源は、まさにツール自身のドキュメントにあった、「key を読む」と「メッセージを送る」を区別していない no re-signing という一文でした。

次の記事 →
Claudeのアカウント停止で見るべき3点:地域、IPタイプ、リクエスト指紋、そして自己チェック方法

コメント

コメントは即時公開されますが、ポリシー違反時は非表示になる場合があります。

最大 1000 文字。