# WeChatを起動せずにメッセージを送受信する：独立した通信コアの復元記録

WeChatのチャット{履歴|りれき}をツールに{移行|いこう}すると、ごく{自然|しぜん}に一つの{疑問|ぎもん}が{生|う}まれます。ツールはメッセージを{読|よ}み{取|と}り、{画像|がぞう}を{復号|ふくごう}できるのに、なぜ{送信|そうしん}するときにはパソコン{上|じょう}のWeChatに{依存|いぞん}しなければならないのでしょうか。ネットワーククライアントを{呼|よ}び{出|だ}すように、{自|みずか}らログインし、リクエストを{送信|そうしん}し、{結果|けっか}を{受信|じゅしん}することはできないのでしょうか。

{今回|こんかい}の{調査|ちょうさ}は、ここから{始|はじ}まりました。{目標|もくひょう}を{検証可能|けんしょうかのう}な{範囲|はんい}に{絞|しぼ}りました。**デスクトップ{版|ばん}WeChatを{起動|きどう}せず、QRコードによるログイン、{初期化|しょきか}、テキストの{送受信|そうじゅしん}を{独立|どくりつ}して{完了|かんりょう}し、{状態|じょうたい}を{保存|ほぞん}する。{最後|さいご}にツール{専用|せんよう}のWeChatを{削除|さくじょ}し、もう{一度|いちど}{双方向|そうほうこう}の{送受信|そうじゅしん}を{行|おこな}う。**

{現在|げんざい}、この{一連|いちれん}の{処理|しょり}は、{自分|じぶん}のアカウントとファイル{転送|てんそう}アシスタントを{使|つか}った{実機検証|じっきけんしょう}に{成功|せいこう}しています。スマートフォンでは{独立|どくりつ}したプログラムから{送|おく}られた{全文|ぜんぶん}を{確認|かくにん}でき、{独立|どくりつ}したプログラムでもスマートフォンからの{返信|へんしん}を{受信|じゅしん}できました。{本文|ほんぶん}はバイト{単位|たんい}で{完全|かんぜん}に{一致|いっち}しています。{以下|いか}では、{実際|じっさい}に{学|まな}んだ{順序|じゅんじょ}に{沿|そ}って、その{仕組|しく}みと{復元過程|ふくげんかてい}を{改|あらた}めて{説明|せつめい}します。

これは、{特定|とくてい}のバージョンと{特定|とくてい}のプロトコル{経路|けいろ}を{対象|たいしょう}とした{調査記録|ちょうさきろく}です。コードはプライベートリポジトリで{個別|こべつ}に{開発|かいはつ}されており、WeChat Useには{同梱|どうこん}されません。{本文|ほんぶん}には、{実在|じつざい}するアカウントの{認証情報|にんしょうじょうほう}、ログイン{用|よう}QRコード、チャットの{原文|げんぶん}は{含|ふく}まれていません。

## 一、{目標|もくひょう}：アプリから{何|なに}を{切|き}り{離|はな}すのか？

まず、WeChatで「こんにちは」と{送信|そうしん}する{場面|ばめん}を{想像|そうぞう}してください。ウィンドウは、{連絡先|れんらくさき}の{選択|せんたく}、テキストの{入力|にゅうりょく}、{送信|そうしん}ボタンのクリックを{担当|たんとう}します。しかし、その{背後|はいご}では、アカウント{認証|にんしょう}、シリアライズ、{暗号化|あんごうか}、ネットワーク{接続|せつぞく}、サーバーからの{応答|おうとう}、ローカル{状態|じょうたい}の{管理|かんり}も{必要|ひつよう}です。

{独立|どくりつ}して{実装|じっそう}したいのは、この{後半部分|こうはんぶぶん}です。

ここでいう「コア」とは、{通信|つうしん}プロトコルとステートマシンを{指|さ}し、OSカーネルのことではありません。また、WeChatの{全機能|ぜんきのう}を{一|いち}から{作|つく}り{直|なお}すという{意味|いみ}でもありません。{最小限|さいしょうげん}の{独立|どくりつ}したテキストクライアントであれば、まずは{明示的|めいじてき}な{受信者|じゅしんしゃ}IDだけを{受|う}け{付|つ}けるようにし、{完全|かんぜん}な{連絡先一覧|れんらくさきいちらん}、グループメンバー、モーメンツ、メディアシステムまで{最初|さいしょ}から{実装|じっそう}する{必要|ひつよう}はありません。

これにより、{検収目標|けんしゅうもくひょう}が{具体的|ぐたいてき}になります。

1. {新|あたら}しいセッションは、スマートフォンでQRコードをスキャンして{確認|かくにん}し、{確立|かくりつ}しなければなりません。デスクトップ{版|ばん}WeChatの{実行中|じっこうちゅう}プロセスが{保持|ほじ}するログイン{状態|じょうたい}を{流用|りゅうよう}してはなりません。
2. {送信|そうしん}するアプリケーションリクエストは、{独自|どくじ}のプログラムでエンコード、{暗号化|あんごうか}、{転送|てんそう}しなければなりません。
3. {受信時|じゅしんじ}には、{独自|どくじ}の{同期状態|どうきじょうたい}を{更新|こうしん}し、{独自|どくじ}のメッセージデータベースへ{書|か}き{込|こ}まなければなりません。
4. プログラムの{終了後|しゅうりょうご}も、{新|あたら}しいプロセスが{状態|じょうたい}を{読|よ}み{戻|もど}せなければなりません。{終了|しゅうりょう}していないプロセスを{常駐|じょうちゅう}させ、{見|み}せかけの{状態|じょうたい}を{維持|いじ}するものではありません。
5. デスクトップ{版|ばん}WeChatが{起動|きどう}しておらず、ツール{専用|せんよう}コピーを{削除|さくじょ}した{後|あと}でも、{双方向|そうほうこう}にメッセージを{送受信|そうじゅしん}できなければなりません。

{前回|ぜんかい}の[WeChatの{画像|がぞう}はどのように{暗号化|あんごうか}され、私たちはそれをどのように{段階的|だんかいてき}に{復元|ふくげん}したのか](https://blog.leeguoo.com/posts/wechat-419-image-encryption-and-decryption/)では、{画像|がぞう}オブジェクトとキャッシュの{復元|ふくげん}について{調査|ちょうさ}しました。{今回|こんかい}は、そこからさらに{一段階|いちだんかい}{手前|てまえ}へ{進|すす}みます。メッセージは、ローカルのチャット{履歴|りれき}になる{前|まえ}に、どのようにネットワークからクライアントへ{届|とど}くのでしょうか？

## 二、まず三つを区別する：伝送、業務、保存

「WeChatはAESを{使|つか}っている」と{言|い}うだけでは、まったく{不十分|ふじゅうぶん}です。なぜなら、{同|おな}じ{内容|ないよう}でも、{置|お}かれている{場所|ばしょ}によって{異|こと}なる{形式|けいしき}で{封入|ふうにゅう}され、{使用|しよう}される{鍵|かぎ}や{状態|じょうたい}も{異|こと}なるからです。

{今回|こんかい}は、それを三つの{層|そう}に{分|わ}けます。

| {層|そう} | {解決|かいけつ}する{問題|もんだい} | {今回|こんかい}の{調査|ちょうさ}における{対象|たいしょう} |
| --- | --- | --- |
| {伝送保護|でんそうほご} | この{接続|せつぞく}で、{信頼|しんらい}できる{暗号化通信路|あんごうかつうしんろ}をどのように{確立|かくりつ}するか、レコードが{改|かい}ざんされていないか | MMTLSハンドシェイク、レコード{認証|にんしょう}、{短時間接続|たんじかんせつぞく}の{再開|さいかい} |
| {業務封入|ぎょうむふうにゅう} | アカウントがどのように{本人性|ほんにんせい}を{証明|しょうめい}し、1{回|かい}の{送信|そうしん}または{同期要求|どうきようきゅう}をどのようにエンコードするか | ログインプロトコル、{業務暗号化|ぎょうむあんごうか}、リクエストヘッダー、メッセージ{本文|ほんぶん} |
| ローカル{永続化|えいぞくか} | {受信|じゅしん}したメッセージと{次回|じかい}の{同期位置|どうきいち}をどのように{確実|かくじつ}に{保存|ほぞん}するか | {独自|どくじ}の{受信|じゅしん}データベース、トランザクション、カーソル、{操作記録|そうさきろく} |

{画像|がぞう}DATの{復号|ふくごう}は、{通信|つうしん}セッションの{解読|かいどく}と{同|おな}じではありません。{伝送|でんそう}ハンドシェイクの{成功|せいこう}も、アカウントへのログイン{成功|せいこう}を{意味|いみ}するわけではありません。さらに、アカウントへのログイン{後|ご}も{同期状態|どうきじょうたい}を{初期化|しょきか}して、はじめて{差分|さぶん}メッセージを{正|ただ}しく{受信|じゅしん}できます。

まずは、{処理全体|しょりぜんたい}の{流|なが}れを{次|つぎ}のように{捉|とら}えてください。

```mermaid
flowchart TD
    A[独自クライアント] --> B[認証済み伝送チャネルの確立]
    B --> C[ログイン用QRコードの取得]
    C --> D[スマートフォンでスキャンして確認]
    D --> E[アカウント認証の完了]
    E --> F[初回初期化と同期]
    F --> G[独自のアカウント状態とメッセージDBへ保存]
    G --> H[テキスト送信リクエスト]
    G --> I[メッセージの増分同期]
    H --> J[サーバー応答とスマートフォンでの照合]
    I --> K[メッセージと新しいカーソルを同一トランザクションでコミット]
```

この{図|ず}は、「curlで{送信|そうしん}するだけ」が{見|み}た{目|め}ほど{単純|たんじゅん}ではない{理由|りゆう}も{示|しめ}しています。HTTPリクエストは、あくまで{外殻|がいかく}の一つにすぎません。{真|しん}に{独立|どくりつ}させるべきなのは、**リクエストの{前|まえ}にどのような{状態|じょうたい}を{準備|じゅんび}するのか、パケット{内|ない}のバイト{列|れつ}をどのように{生成|せいせい}するのか、そしてレスポンスの{後|あと}にどの{状態|じょうたい}の{変更|へんこう}を{許可|きょか}するのか**という{点|てん}です。

## 三、プロセス：まずチャネルを{確立|かくりつ}し、その{後|あと}にアカウントを{扱|あつか}う

### 1. {暗号化接続|あんごうかせつぞく}の{要点|ようてん}は、{相手|あいて}が{誰|だれ}であるかを{証明|しょうめい}すること

この{転送実装|てんそうじっそう}は、{新|あたら}しい{楕円曲線|だえんきょくせん}の{一時鍵|いちじかぎ}から{開始|かいし}し、サーバー{署名|しょめい}を{検証|けんしょう}して、ハンドシェイク{完了段階|かんりょうだんかい}の{検証値|けんしょうち}を{確認|かくにん}します。その{後|あと}ではじめて、{送信側|そうしんがわ}と{受信側|じゅしんがわ}それぞれの{鍵|かぎ}とシーケンス{番号|ばんごう}を{確立|かくりつ}します。

ここでは、二つの{概念|がいねん}を{分|わ}けて{考|かんが}える{必要|ひつよう}があります。

{鍵交換|かぎこうかん}によって{双方|そうほう}は{共通|きょうつう}の{秘密|ひみつ}を{導出|どうしゅつ}できます。一方、{身元認証|みもとにんしょう}によってクライアントは、{秘密|ひみつ}を{交換|こうかん}した{相手|あいて}が{確|たし}かに{想定|そうてい}したサーバーであることを{確認|かくにん}できます。{前者|ぜんしゃ}だけでは、データを{復号|ふくごう}できることが、その{接続|せつぞく}を{信頼|しんらい}できることを{自動的|じどうてき}に{意味|いみ}するわけではありません。

レコードのシーケンス{番号|ばんごう}も{同様|どうよう}に{重要|じゅうよう}です。これは一つの{接続|せつぞく}における{暗号化状態|あんごうかじょうたい}の{管理|かんり}に{関与|かんよ}するため、{送信方向|そうしんほうこう}と{受信方向|じゅしんほうこう}で{共用|きょうよう}することはできず、{失敗|しっぱい}したコンテキストを{流用|りゅうよう}して{次|つぎ}のパケットを{組|く}み{立|た}てることもできません。この{実装|じっそう}では、シーケンス{番号|ばんごう}がオーバーフローした{場合|ばあい}は{直|ただ}ちに{拒否|きょひ}し、{失敗|しっぱい}したコンテキストの{再利用|さいりよう}を{停止|ていし}します。

この{経路|けいろ}では P-256 などの{暗号要素|あんごうようそ}を{使用|しよう}しますが、MMTLS には{独自|どくじ}のメッセージと{状態|じょうたい}に{関|かん}する{規約|きやく}があります。これをそのまま「{標準|ひょうじゅん} TLS 1.3」と{呼|よ}び、{汎用|はんよう} TLS ライブラリがプロトコル{全体|ぜんたい}を{実装済|じっそうず}みだと{仮定|かてい}することはできません。

{短時間接続|たんじかんせつぞく}では、{認証済|にんしょうず}みのセッションチケットと{再開鍵|さいかいかぎ}の{導出|どうしゅつ}も{関係|かんけい}します。その{目的|もくてき}は、{接続|せつぞく}を{再確立|さいかくりつ}するたびにかかるコストを{削減|さくげん}することです。{前回|ぜんかい}の{一時秘密鍵|いちじひみつかぎ}と{暗号化|あんごうか}コンテキスト{一式|いっしき}をディスクに{保存|ほぞん}し、そのまま{繰|く}り{返|かえ}し{使用|しよう}するという{意味|いみ}ではありません。

### 2. QRコードはログインフローへの{入口|いりぐち}

チャネルが{確立|かくりつ}されると、クライアントはQRコードをリクエストできます。しかし、{有効|ゆうこう}なPNGを{取得|しゅとく}できたことは、{画像|がぞう}を{取得|しゅとく}したことを{証明|しょうめい}するだけで、アカウントへのログインが{完了|かんりょう}したことを{証明|しょうめい}するものではありません。

{復元|ふくげん}したログインフローは、{次|つぎ}のように{要約|ようやく}できます。

```text
Get QR → QRコードを表示 → スマートフォンでスキャンして確認
       → Checkで同一QRコードの状態を確認
       → 最終アカウント認証 → 認証済みアカウント情報を保存
```

このバージョンの{経路|けいろ}では、{取得|しゅとく}と{確認|かくにん}はそれぞれ Get 502 と Check 503 に、{最終認証|さいしゅうにんしょう}は Manual 252 に{対応|たいおう}します。これらの{番号|ばんごう}は、このプロトコル{経路|けいろ}の{分析|ぶんせき}で{使用|しよう}した{操作識別子|そうさしきべつし}であり、{外部向|がいぶむ}けの{安定|あんてい}したAPIではありません。

ここで{最|もっと}も{見落|みお}としやすいのは、「{同一|どういつ}のQRコード」であることです。{確認応答|かくにんおうとう}は、{元|もと}のリクエストの{識別情報|しきべつじょうほう}およびQRコードの{発行元|はっこうもと}と{結|むす}び{付|つ}ける{必要|ひつよう}があります。{古|ふる}いQRコードの{確認結果|かくにんけっか}を{使|つか}って{新|あたら}しい{認証処理|にんしょうしょり}を{完了|かんりょう}させることはできません。また、ユーザーが「スキャンした」と{言|い}っただけで、サーバーの{状態確認|じょうたいかくにん}を{省略|しょうりゃく}することもできません。

スマートフォンでのスキャン{確認|かくにん}は、{認可操作|にんかそうさ}です。プログラムができるのはプロトコルフローを{完了|かんりょう}させることであり、ユーザーによるアカウントログインの{確認|かくにん}を{代行|だいこう}することではありません。

### 3. ログイン{内部|ないぶ}には、さらにもう一{段階|だんかい}の{鍵合意|かぎごうい}がある

{最終認証|さいしゅうにんしょう}の{内部|ないぶ}で{行|おこな}われるのは、単に「スキャン{結果|けっか}を{返送|へんそう}する」ことだけではありません。このバージョンの{経路|けいろ}では、ネストされた secp224r1 の{鍵合意|かぎごうい}を{復元|ふくげん}しました。{非圧縮公開鍵|ひあっしゅくこうかいかぎ}は57バイト、{得|え}られる{共有|きょうゆう} X {座標|ざひょう}は28バイトで、その{後|あと}、{観測|かんそく}したアルゴリズムに{従|したが}ってパッケージの{復号|ふくごう}に{使用|しよう}する{鍵|かぎ}を{導出|どうしゅつ}します。

これは、{前述|ぜんじゅつ}の P-256 とは{役割|やくわり}が{異|こと}なります。P-256 は{転送|てんそう}ハンドシェイクの{経路|けいろ}に、secp224r1 はこのログイン{処理|しょり}の{経路|けいろ}に{現|あらわ}れます。どちらも単に「ECDH{鍵|かぎ}」と{略記|りゃっき}すると、{実装時|じっそうじ}に{入力|にゅうりょく}を{誤接続|ごせつぞく}しやすくなります。

このログイン{分岐|ぶんき}では、{共有|きょうゆう} X の MD5 {結果|けっか}が CBC ラッピングに{使用|しよう}され、key と IV に{同一|どういつ}のバイト{列|れつ}が{用|もち}いられることを{確認|かくにん}しました。これは{既存|きそん}プロトコルとの{互換性|ごかんせい}のために{復元|ふくげん}したものであり、{新規|しんき}システムに{推奨|すいしょう}する{暗号設計|あんごうせっけい}ではありません。

{同様|どうよう}に、ここでの MD5 をサーバーの{身元認証|みもとにんしょう}と{見|み}なすことはできません。ハッシュ、{業務上|ぎょうむじょう}の{検証値|けんしょうち}、ハンドシェイク{署名|しょめい}は、それぞれ{異|こと}なる{役割|やくわり}を{担|にな}っています。いずれかが{欠|か}けている{場合|ばあい}に、{別|べつ}のものが「こちらも{検証|けんしょう}しているように{見|み}える」という{理由|りゆう}で{代用|だいよう}することはできません。

## 四、ログイン{後|ご}、なぜすぐに{通常|つうじょう}のメッセージ{受信|じゅしん}を{開始|かいし}できないのか？

{新|あたら}しい{入館|にゅうかん}カードを{受|う}け{取|と}ったと{想像|そうぞう}してください。そのカードは{入館|にゅうかん}できることを{示|しめ}しますが、{前回|ぜんかい}どのメッセージまで{読|よ}んだのかは{教|おし}えてくれません。

{独立|どくりつ}クライアントも、{初回|しょかい}ログイン{後|ご}に{初期|しょき}{同期|どうき}{状態|じょうたい}を{取得|しゅとく}する{必要|ひつよう}があります。{今回|こんかい}のフローでは、まず NewInit 139 を{実行|じっこう}し、レスポンスに{含|ふく}まれるバインド{済|ず}みの{初期化|しょきか}フィールドを{用|もち}いて{自身|じしん}の{同期|どうき}{開始点|かいしてん}を{確立|かくりつ}した{後|あと}、{最初|さいしょ}の NewSync を{実行|じっこう}します。

ここでは、ある{細部|さいぶ}を{明確|めいかく}に{確認|かくにん}しました。{使用|しよう}するのは Init レスポンスの f2 フィールドです。あるサンプルで f2 と f3 の{内容|ないよう}がたまたま{同一|どういつ}だったとしても、それを{理由|りゆう}にフィールドの{識別|しきべつ}を{無視|むし}することはできません。{偶然|ぐうぜん}の{一致|いっち}は、{相互|そうご}{交換|こうかん}{可能|かのう}なプロトコル{契約|けいやく}を{意味|いみ}しません。

{最初|さいしょ}の{同期|どうき}が{完了|かんりょう}してから、{自身|じしん}の{受信|じゅしん}データベースをコミットし、{実行|じっこう}{構成|こうせい}を{生成|せいせい}します。これにより、「{初期化|しょきか}{完了|かんりょう}」とは、ある{関数|かんすう}が 0 を{返|かえ}したことではなく、{永続化|えいぞくか}され、{新|あたら}しいプロセスから{再読|さいどく}み{込|こ}みできる{一連|いちれん}の{状態|じょうたい}を{意味|いみ}します。

### {同期|どうき}カーソルは「{最後|さいご}のメッセージ ID」ではない

チャットソフトウェアの{差分|さぶん}{状態|じょうたい}は、{通常|つうじょう}タイムスタンプよりも{複雑|ふくざつ}です。{解析|かいせき}した SyncKey は、{識別子|しきべつし}を{持|も}つ{状態値|じょうたいち}の{集合|しゅうごう}であり、プロトコルのルールに{従|したが}ってマージする{必要|ひつよう}があります。{最後|さいご}のメッセージ ID で{安易|あんい}に{代用|だいよう}することはできません。

1 {回|かい}のレスポンスに{含|ふく}まれるのも、1 {件|けん}のテキストだけではありません。そこにはコマンドのリストが{含|ふく}まれ、コマンドごとに{異|こと}なる{変化|へんか}を{表|あらわ}します。メッセージコマンドについては、ネストされた{文字列|もじれつ}、{元|もと}のフィールド、{完全|かんぜん}な{整数値|せいすうち}を{保持|ほじ}しています。{認識|にんしき}できないコマンドについても{生|なま}データを{保持|ほじ}し、{暗黙|あんもく}のうちに{破棄|はき}したにもかかわらず、{同期|どうき}が{完全|かんぜん}であるかのように{扱|あつか}うことを{避|さ}けています。

メッセージ ID は JavaScript の `Number` が{安全|あんぜん}に{表現|ひょうげん}できる{整数|せいすう}{範囲|はんい}を{超|こ}えることもあります。ストレージとインターフェースでは、{完全|かんぜん}な 64 ビット{値|ち}を{保持|ほじ}しなければなりません。JSON を{経由|けいゆ}する{箇所|かしょ}では、いったん{浮動|ふどう}{小数点数|しょうすうてんすう}に{変換|へんかん}してから{戻|もど}すのではなく、{正確|せいかく}な 10 {進数|しんすう}{文字列|もじれつ}を{使用|しよう}する{必要|ひつよう}があります。

### なぜメッセージとカーソルを{同時|どうじ}にコミットする{必要|ひつよう}があるのか？

メッセージのバッチ M と、{新|あたら}しいカーソル K を{受信|じゅしん}したと{仮定|かてい}します。

K を{先|さき}に{保存|ほぞん}し、その{後|あと}プロセスがクラッシュすると、M は{保存|ほぞん}されません。すると{次回|じかい}、サーバーは K より{後|あと}から{処理|しょり}を{続行|ぞっこう}し、メッセージの{欠落|けつらく}が{発生|はっせい}する{可能性|かのうせい}があります。{反対|はんたい}に M を{先|さき}に{保存|ほぞん}し、K を{保存|ほぞん}しなかった{場合|ばあい}は、{次回|じかい}に{同|おな}じメッセージバッチを{再度|さいど}{受信|じゅしん}する{可能性|かのうせい}があります。

この{実装|じっそう}では、{元|もと}のレスポンスと{取得元|しゅとくもと}の{要約|ようやく}を{保持|ほじ}したうえで、{独自|どくじ}のデータベーストランザクション{内|ない}で、メッセージのバージョン、バッチの{情報|じょうほう}、および{新|あたら}しいカーソルを{同時|どうじ}にコミットします。

```text
バッチレスポンス全体を検証
    ↓
追跡可能な元のレスポンスを保存
    ↓
BEGIN
    メッセージのバージョンを書き込む
    バッチの重複排除情報を書き込む
    同期カーソルを更新
COMMIT
```

いずれかの{検証|けんしょう}に{失敗|しっぱい}した{場合|ばあい}、カーソルは{進|すす}めません。この{設計|せっけい}では、デスクトップ{版|ばん} WeChat のオンラインデータベースを{読|よ}み{取|と}ったり{変更|へんこう}したりする{必要|ひつよう}はありません。{管理|かんり}するのは、{独立|どくりつ}クライアント{自身|じしん}のデータです。

## 5. 1{件|けん}のテキストは、どのようにネットワークリクエストになるのか？

ここまでで、アカウント{状態|じょうたい}と{同期|どうき}の{開始点|かいしてん}がそろいました。ここでようやく「こんにちは」を{送信|そうしん}できます。

まず、{受信者|じゅしんしゃ}、{本文|ほんぶん}、および{関連|かんれん}するビジネスフィールドを protobuf バイト{列|れつ}にエンコードします。protobuf が{担|にな}うのは{構造化|こうぞうか}エンコーディングだけであり、{暗号化|あんごうか}は{行|おこな}いません。{続|つづ}いて、ビジネス{層|そう}が{選択|せんたく}されたモードに{応|おう}じて{圧縮|あっしゅく}と{暗号化|あんごうか}を{処理|しょり}し、ビジネスヘッダーとチェック{値|ち}を{加|くわ}え、{最後|さいご}にトランスポート{層|そう}のレコードへ{格納|かくのう}して{送信|そうしん}します。

```mermaid
flowchart LR
    A[受信者とテキスト] --> B[protobuf ビジネスペイロード]
    B --> C[モードに応じた圧縮と暗号化]
    C --> D[ビジネスヘッダーとチェック値]
    D --> E[トランスポート暗号化レコード]
    E --> F[サーバー]
```

### {圧縮|あっしゅく}と{暗号化|あんごうか}の{順序|じゅんじょ}は{逆|ぎゃく}にできない

{今回|こんかい}の{静的|せいてき}な{復元|ふくげん}では、ordinary mode 5 と mode 13 という 2 つのビジネスペイロードのエンコード{分岐|ぶんき}を{対象|たいしょう}としています。

mode 5 では、zlib {圧縮|あっしゅく}に{効果|こうか}があるかを{判定|はんてい}してから、AES-128-CBC を{使用|しよう}します。パケットヘッダーにある{元|もと}の{長|なが}さと、{選択|せんたく}された{暗号化前|あんごうかまえ}の{長|なが}さには、それぞれ{異|こと}なる{意味|いみ}があるため、すべてを{暗号文|あんごうぶん}の{長|なが}さとして{記録|きろく}することはできません。

一方、mode 13 では{強制的|きょうせいてき}な{圧縮|あっしゅく}と AES-GCM を{採用|さいよう}し、{観測|かんそく}されたフォーマットには、{暗号文|あんごうぶん}、12 バイトの nonce、16 バイトの tag が{含|ふく}まれています。AEAD の tag は、{暗号文|あんごうぶん}と{対応|たいおう}する{入力|にゅうりょく}の{完全性|かんぜんせい}を{検証|けんしょう}するために{使用|しよう}され、nonce の{生成|せいせい}に{関|かん}する{制約|せいやく}もプロトコルの{正当性|せいとうせい}の{一部|いちぶ}です。[RFC 5116](https://www.rfc-editor.org/rfc/rfc5116) には、このような{認証付|にんしょうつ}き{暗号|あんごう}インターフェースと nonce の{要件|ようけん}についての{一般的|いっぱんてき}な{説明|せつめい}がありますが、WeChat プロトコルの{仕様書|しようしょ}ではありません。

{今回|こんかい}、{実在|じつざい}するアカウントでの{送受信|そうじゅしん}に{使用|しよう}されたのは、{確認済|かくにんず}みの{通常|つうじょう}の CBC5 {経路|けいろ}です。もう{一方|いっぽう}のエンコード{分岐|ぶんき}についてオフラインテストを{実装|じっそう}したからといって、{実際|じっさい}のすべての{送受信|そうじゅしん}が GCM を{経由|けいゆ}するわけでも、{未識別|みしきべつ}のすべてのモードに{自動的|じどうてき}に{対応|たいおう}できるわけでもありません。

### そのまま{移植|いしょく}するのが{最|もっと}も{難|むずか}しいのは、プロトコル{固有|こゆう}の{計算|けいさん}ロジック

{標準的|ひょうじゅんてき}な AES、{楕円曲線|だえんきょくせん}、zlib には、いずれも{成熟|せいじゅく}した{実装|じっそう}があります。しかし、プロトコル{固有|こゆう}のチェック{値|ち}は、「よく{使|つか}われるアルゴリズムを 1 つ{選|えら}ぶ」だけでは{再現|さいげん}できません。

ここでは、ある{固定|こてい}モジュール{内|ない}の{計算|けいさん}ロジックに{遭遇|そうぐう}しました。デスクトップ{版|ばん} WeChat の{関数|かんすう}をそのまま{呼|よ}び{出|だ}し{続|つづ}けると、「{独立|どくりつ}クライアント」でありながら、アプリケーション{本体|ほんたい}への{依存|いぞん}が{残|のこ}ります。そこで、これをデータとして{解析|かいせき}し、{実際|じっさい}に{使用|しよう}される{命令|めいれい}をデコードして、{制限付|せいげんつ}きインタープリターを{実装|じっそう}しました。

インタープリターがアクセスするのは、{自身|じしん}がマッピングしたメモリだけです。{出所|でどころ}の{不明|ふめい}なアドレスをホストポインターとして{扱|あつか}わず、{任意|にんい}の{外部|がいぶ}コールも{許可|きょか}せず、{命令|めいれい}ステップ{数|すう}の{上限|じょうげん}も{設定|せってい}しています。{現在|げんざい}は、{実際|じっさい}に{使用|しよう}される 55 {種類|しゅるい}の{命令|めいれい}をカバーしており、モジュールのバイト{列|れつ}にも{固定|こてい}のダイジェスト{制約|せいやく}を{設|もう}けています。

この{経路|けいろ}では、{暗号化後|あんごうかご}のビジネスペイロードから{入力|にゅうりょく}を{生成|せいせい}し、それをモジュールに{渡|わた}して{計算|けいさん}させ、{最終的|さいしゅうてき}に{規定|きてい}された{長|なが}さの{結果|けっか}を{取得|しゅとく}します。{戻|もど}り{値|ち}、{出力長|しゅつりょくちょう}、および{実際|じっさい}の{書|か}き{込|こ}みを{検証|けんしょう}し、いずれか 1 つでも{条件|じょうけん}を{満|み}たさなければ、パケットの{組|く}み{立|た}てを{続行|ぞっこう}しません。

ここで{得|え}られた{重要|じゅうよう}な{教訓|きょうくん}は、**プロトコルの{復元|ふくげん}とは、{単|たん}に{鍵|かぎ}を{探|さが}すことではなく、{正当|せいとう}なリクエストの{生成|せいせい}に{必要|ひつよう}な{計算|けいさん}と{状態|じょうたい}も{復元|ふくげん}することである**、ということです。{局所的|きょくしょてき}なチェック{値|ち}を{計算|けいさん}できたとしても、それで{証明|しょうめい}できるのは{局所|きょくしょ}の{関数|かんすう}が{出力|しゅつりょく}を{返|かえ}したことだけです。{最終的|さいしゅうてき}には、{完全|かんぜん}なリクエストと{実際|じっさい}のレスポンスを{通|つう}じて{検証|けんしょう}する{必要|ひつよう}があります。

## 6．{受信|じゅしん}は{逆方向|ぎゃくほうこう}のアンパックだが、{送信|そうしん}コードを{逆|ぎゃく}に{実行|じっこう}することではない

サーバーからレスポンスを{受信|じゅしん}したら、まず{転送|てんそう}レコードを{検証|けんしょう}し、{対応|たいおう}するビジネス{層|そう}のカプセル{化|か}を{解除|かいじょ}し、コマンドとメッセージを{解析|かいせき}して、{最後|さいご}に{同期|どうき}トランザクションへ{進|すす}みます。

これは、{方向|ほうこう}は{反対|はんたい}でも{役割|やくわり}は{近|ちか}い{経路|けいろ}ですが、{送信側|そうしんがわ}のすべての{前提|ぜんてい}を{機械的|きかいてき}に{再利用|さいりよう}することはできません。たとえば、あるエンコーダーが{自作|じさく}データを{暗号化|あんごうか}し、さらに{復号|ふくごう}して{元|もと}に{戻|もど}せたとしても、それで{証明|しょうめい}できるのは{内部的|ないぶてき}な{整合性|せいごうせい}だけです。{双方|そうほう}がフィールドの{意味|いみ}を{同|おな}じように{誤|あやま}って{実装|じっそう}していれば、ラウンドトリップテストはそれでも{成功|せいこう}する{可能性|かのうせい}があります。

そのため、{固定|こてい}サンプルのフィールドと{境界|きょうかい}、{改変|かいへん}されたデータを{拒否|きょひ}する{動作|どうさ}、{実際|じっさい}のサーバーレスポンス、そしてスマートフォン{側|がわ}の{完全|かんぜん}な{本文|ほんぶん}との{対応関係|たいおうかんけい}を、それぞれ{分|わ}けて{検証|けんしょう}します。これらの{証拠|しょうこ}が{一連|いちれん}につながってはじめて、その{受信経路|じゅしんけいろ}が{正常|せいじょう}に{動作|どうさ}したと{判断|はんだん}できます。

### メッセージの{受信|じゅしん}には{継続的|けいぞくてき}なポーリングが{必須|ひっす}なのか？

ここでは、{公式|こうしき}のネットワークコンポーネントと、{現在|げんざい}の{私|わたし}たちの{実装|じっそう}を{区別|くべつ}する{必要|ひつよう}があります。

Tencent が{公開|こうかい}している [Mars](https://github.com/Tencent/mars) は、ネットワーク{通信|つうしん}コンポーネントを{提供|ていきょう}しており、STN には{長短|ちょうたん}コネクションやハートビートなどの{仕組|しく}みが{含|ふく}まれています。これはネットワーク{全体|ぜんたい}のアーキテクチャを{理解|りかい}するうえで{役立|やくだ}ちますが、{公開|こうかい}されているネットワークコンポーネントが、WeChat のアカウント、ログイン、ビジネスプロトコルを{含|ふく}む{完全|かんぜん}な{実装|じっそう}であるわけではありません。

{私|わたし}たちの{独立|どくりつ}ツールでは、{現在|げんざい}、{明示的|めいじてき}な{差分取得|さぶんしゅとく}と、ハートビートの{通知後|つうちご}に{同期|どうき}する{経路|けいろ}までが{接続済|せつぞくず}みです。{長時間接続|ちょうじかんせつぞく}によるプッシュ{通知|つうち}はまだ{完全|かんぜん}には{接続|せつぞく}されていません。そのため、{現在|げんざい}のツールが{公式|こうしき}のプッシュ{機構|きこう}を{完全|かんぜん}に{再現|さいげん}しているとはいえませんし、{逆|ぎゃく}に WeChat {公式|こうしき}が{固定間隔|こていかんかく}のポーリングだけに{依存|いぞん}しているともいえません。

ツールに{設定|せってい}されている{待機間隔|たいきかんかく}とタイムアウトは、{私|わたし}たち{独自|どくじ}の{運用方針|うんようほうしん}です。プロトコル{内|ない}に{待機時間|たいきじかん}らしき{数値|すうち}があっても、その{単位|たんい}がまだ{明確|めいかく}に{特定|とくてい}されていなければ、{数値|すうち}の{大|おお}きさだけを{根拠|こんきょ}に「{何秒|なんびょう}」と{解釈|かいしゃく}することはできません。

## 七、デスクトップ版 WeChat から本当に{独立|どくりつ}していることを、どう{証明|しょうめい}するのか？

ウィンドウを{閉|と}じるだけでは{不十分|ふじゅうぶん}です。アプリケーションがバックグラウンドで{動作|どうさ}しているかもしれませんし、{私|わたし}たちのプログラムが{密|ひそ}かに{元|もと}のアプリケーション{内|ない}のリソースを{読|よ}み{取|と}ったり、{以前|いぜん}のバックグラウンドサービスを{流用|りゅうよう}したりしている{可能性|かのうせい}もあります。

そのため、{最終検証|さいしゅうけんしょう}では「コマンドの{終了コード|しゅうりょうコード}が 0」であることに{加|くわ}え、さらにいくつかの{確認|かくにん}を{行|おこな}いました。

まず、{独立|どくりつ}した QR コードのスキャンで{作成|さくせい}した{新|あたら}しいアカウント{情報|じょうほう}を{使|つか}い、{初期化|しょきか}と{独自|どくじ}のメッセージ{保存|ほぞん}を{完了|かんりょう}させました。その{後|ご}、{新|あたら}しいプロセスをソースコードのディレクトリ{外|がい}から{起動|きどう}し、ネットワークを{禁止|きんし}した{状態|じょうたい}で{受信箱|じゅしんばこ}とカーソルを{読|よ}み{戻|もど}しました。これは、{状態|じょうたい}が{確実|かくじつ}に{保存|ほぞん}されたことを{証明|しょうめい}します。ただし、オフラインの `status` コマンドがサーバーへ{再度|さいど}ログインしたことを{証明|しょうめい}するものではありません。

{次|つぎ}に、{現在|げんざい}のコンピューターでツール{専用|せんよう}の WeChat を{通常|つうじょう}どおり{終了|しゅうりょう}し、このコピーをゴミ{箱|ばこ}へ{移動|いどう}しました。チャットデータは{削除|さくじょ}しておらず、アプリケーションは{復元|ふくげん}できます。メインの WeChat は{残|のこ}しましたが、{起動|きどう}していません。

「{起動|きどう}していなくても、アプリケーションから{密|ひそ}かに{何|なに}かを{取得|しゅとく}しているのではないか」という{疑問|ぎもん}を{避|さ}けるため、プロトコルのランタイムにはシステムのサンドボックス{制限|せいげん}を{適用|てきよう}しました。メインの WeChat とゴミ{箱|ばこ}にある{専用|せんよう}コピーのどちらに{対|たい}しても{読|よ}み{取|と}りを{拒否|きょひ}し、{関連|かんれん}する{元|もと}のアプリケーションのデータディレクトリとサービスへのアクセスも{制限|せいげん}しました。{実際|じっさい}に{読|よ}み{取|と}りを{試行|しこう}すると、アクセス{権限|けんげん}の{拒否|きょひ}が{返|かえ}されました。

{最後|さいご}に、この{条件下|じょうけんか}で{送信|そうしん}と{受信|じゅしん}をそれぞれ 1 {回|かい}ずつ{実行|じっこう}しました。

| {確認対象|かくにんたいしょう} | {実際|じっさい}の{結果|けっか} | {何|なに}を{示|しめ}すか |
| --- | --- | --- |
| テキスト{送信|そうしん} | サーバーが{受理|じゅり}し、スマートフォンにテスト{本文|ほんぶん}が{完全|かんぜん}に{表示|ひょうじ}された | {独立|どくりつ}した{送信経路|そうしんけいろ}がスマートフォンまで{到達|とうたつ}した |
| テキスト{受信|じゅしん} | {新着|しんちゃく}メッセージの{本文|ほんぶん}がスマートフォンからの{返信|へんしん}とバイト{単位|たんい}で{一致|いっち}し、サーバーのメッセージ ID が 0 ではなかった | {独立|どくりつ}した{同期|どうき}によって{該当|がいとう}メッセージを{取得|しゅとく}できた |
| {同期状態|どうきじょうたい} | シーケンスが 2 から 3 に{進|すす}み、{累計|るいけい} 130 {件|けん}のメッセージバージョンを{保存|ほぞん}した | {受信|じゅしん}トランザクションと{状態|じょうたい}の{永続化|えいぞくか}が{実際|じっさい}に{行|おこな}われた |
| {実行環境|じっこうかんきょう} | {送受信中|そうじゅしんちゅう}のサンプリングでは、いずれもデスクトップ版 WeChat のプロセスが{検出|けんしゅつ}されなかった | この 2 {回|かい}の{操作|そうさ}は、{実行中|じっこうちゅう}のデスクトップ版 WeChat に{依存|いぞん}していなかった |
| アプリケーションへの{依存|いぞん} | {専用|せんよう}コピーの{元|もと}のパスは{存在|そんざい}せず、メインアプリケーションとゴミ{箱|ばこ}のコピーへの{読|よ}み{取|と}りはサンドボックスによって{拒否|きょひ}された | ランタイムは、この 2 つのアプリケーションを{読|よ}み{取|と}って{操作|そうさ}を{完了|かんりょう}することができない |
| ライフサイクル | {元|もと}の{子|こ}プロセスの{終了記録|しゅうりょうきろく}、{結果|けっか}とリクエストの{対応関係|たいおうかんけい}をすべて{照合|しょうごう}した | {成功|せいこう}が{別|べつ}のリクエストや{残存|ざんそん}プロセスによるものではない |

ここでいう 130 は、{保存|ほぞん}されたメッセージバージョンの{件数|けんすう}であり、130 {件|けん}の{独立|どくりつ}した{新規|しんき}チャットを{受信|じゅしん}したと{主張|しゅちょう}するものではありません。{最後|さいご}の{差分受信|さぶんじゅしん}では 2 {件|けん}のメッセージバージョンが{追加|ついか}され、そのうち 1 {件|けん}の{新規|しんき}メッセージの{本文|ほんぶん}が、スマートフォンからのテスト{返信|へんしん}と{完全|かんぜん}に{一致|いっち}しました。

より{正確|せいかく}にいうと、{結論|けつろん}は{次|つぎ}のとおりです。**ツール{専用|せんよう}の WeChat を{移動|いどう}し、メインの WeChat を{起動|きどう}せず、さらにプロトコルプロセスから{読|よ}み{取|と}れない{状態|じょうたい}で、{独自|どくじ}のテキスト{双方向送受信|そうほうこうそうじゅしん}を{完了|かんりょう}しました。**コンピューター{上|じょう}のすべての WeChat ファイルを{削除|さくじょ}したという{意味|いみ}ではありません。

## 八、なぜ{操作記録|そうさきろく}も{通信|つうしん}の{中核|ちゅうかく}に{含|ふく}まれるのか？

{送信要求|そうしんようきゅう}では、サーバーはすでに{処理|しょり}したものの、クライアントが{応答|おうとう}を{受|う}け{取|と}れないという{厄介|やっかい}な{状況|じょうきょう}が{起|お}こりえます。このとき{自動的|じどうてき}にもう{一度|いちど}{送信|そうしん}すると、{重複配信|ちょうふくはいしん}になるおそれがあります。

そのため、まず{送信意図|そうしんいと}を{永続化|えいぞくか}してから、{実行|じっこう}を{担|にな}う{具体的|ぐたいてき}な{子|こ}プロセスを{生成|せいせい}します。{要求|ようきゅう}、パラメーターの{要約|ようやく}、プロセスの{識別情報|しきべつじょうほう}、{結果|けっか}、{終了記録|しゅうりょうきろく}を、すべて{同一|どういつ}の{操作単位|そうさたんい}に{関連付|かんれんづ}けます。{結果|けっか}が{不明|ふめい}な{場合|ばあい}は{記録|きろく}を{保持|ほじ}して{自動再試行|じどうさいしこう}を{停止|ていし}し、ファイルが{一時的|いちじてき}に{現|あらわ}れないからといって、{未送信|みそうしん}だったとみなしてはなりません。

{受信|じゅしん}にも{同様|どうよう}の{問題|もんだい}がありますが、こちらはバッチ{単位|たんい}の{重複排除|ちょうふくはいじょ}とアトミックコミットによって、ローカルでの{重複|ちょうふく}を{制御|せいぎょ}できます。これに{対|たい}して、{送信側|そうしんがわ}の{状態|じょうたい}が{不明|ふめい}な{場合|ばあい}、ローカルの{重複排除|ちょうふくはいじょ}だけでは、リモート{側|がわ}で{実行|じっこう}されていないことを{証明|しょうめい}できません。

これが、{今回|こんかい}のコンピューター{間|かん}{移行|いこう}で、ログイン{情報|じょうほう}だけをコピーするのでは{不十分|ふじゅうぶん}だった{理由|りゆう}でもあります。{整合性|せいごうせい}のある{受信|じゅしん}データベースと{過去|かこ}の{操作記録|そうさきろく}もコピーし、さらに{旧|きゅう}コンピューターが{同|おな}じ{実行状態|じっこうじょうたい}を{使|つか}い{続|つづ}けないようにする{必要|ひつよう}があります。そうしなければ、2 つのクライアントが{同時|どうじ}にカーソルを{進|すす}めたり、{結果不明|けっかふめい}の{操作|そうさ}について{互|たが}いに{矛盾|むじゅん}する{判断|はんだん}をしたりするおそれがあります。

セッションファイルを 1 つ{保存|ほぞん}するのは{簡単|かんたん}です。しかし、「これまでに{何|なに}を{行|おこな}い、{次|つぎ}に{何|なに}ができるのか」を{説明|せつめい}できるクライアント{状態|じょうたい}を{保存|ほぞん}してこそ、{永続化|えいぞくか}の{完全|かんぜん}な{意味|いみ}になります。

## 九、{結論|けつろん}：{何|なに}ができ、{何|なに}がまだできていないのか？

{今回|こんかい}{完成|かんせい}したのは、{独立|どくりつ}したテキスト{通信|つうしん}コアです。スマートフォンによる QR コード{認証|にんしょう}、アカウント{認証|にんしょう}、{初回|しょかい}{初期化|しょきか}、{独自|どくじ}のメッセージデータベース、{差分同期|さぶんどうき}、テキスト{送信|そうしん}、そして{新|あたら}しいプロセスによる{状態|じょうたい}の{復元|ふくげん}に{対応|たいおう}しています。ツール{専用|せんよう}のデスクトップ{版|ばん} WeChat を{削除|さくじょ}したあとも、テキストの{双方向|そうほうこう}{送受信|そうじゅしん}について、スマートフォンとローカルの{記録|きろく}が{相互|そうご}に{対応|たいおう}していることを{実測|じっそく}で{確認|かくにん}できました。

ただし、これはまだ{完全|かんぜん}な WeChat クライアントではありません。{完全|かんぜん}な{連絡先|れんらくさき}リストとグループメンバーのマッピング、{任意|にんい}の{送信先|そうしんさき}への{対応|たいおう}、メディアの{送受信|そうじゅしん}、{完全|かんぜん}な{長時間接続|ちょうじかんせつぞく}によるプッシュ{通知|つうち}、{長時間稼働|ちょうじかんかどう}の{安定性|あんていせい}については、この 2 {回|かい}のテキスト{検証|けんしょう}から{保証|ほしょう}することはできません。{以前|いぜん}{実現|じつげん}した{画像|がぞう}の{復号|ふくごう}も、{自動的|じどうてき}にこの{新|あたら}しいコアのメディア{機能|きのう}になるわけではなく、{別途|べっと}{接続|せつぞく}して{検証|けんしょう}する{必要|ひつよう}があります。

この{研究|けんきゅう}を{通|とお}して、もっとも{伝|つた}えたい{学習方法|がくしゅうほうほう}は、{大規模|だいきぼ}なアプリケーションを、{入力|にゅうりょく}・{出力|しゅつりょく}・{状態|じょうたい}を{持|も}ついくつかの{境界|きょうかい}に{分解|ぶんかい}し、それぞれの{境界|きょうかい}に{対応|たいおう}する{証拠|しょうこ}を{探|さが}すことです。

QR コードが{表示|ひょうじ}されたら、アカウント{認証|にんしょう}まで{完了|かんりょう}したかをさらに{確認|かくにん}する。レスポンスを{解析|かいせき}できたら、メッセージとカーソルが{一緒|いっしょ}にコミットされているかをさらに{確認|かくにん}する。サーバーから{成功|せいこう}が{返|かえ}されたら、スマートフォンでも{結果|けっか}を{照合|しょうごう}する。{新|あたら}しいプロセスが{起動|きどう}できたら、{元|もと}のアプリケーションを{流用|りゅうよう}していないかをさらに{確認|かくにん}する。

このように{一層|いっそう}ずつ{仕組|しく}みを{復元|ふくげん}していくことで、「アプリケーションから{独立|どくりつ}してリクエストを{送|おく}る」という{発想|はっそう}が、{独立|どくりつ}して{動作|どうさ}し、{再検証|さいけんしょう}できる{通信|つうしん}プログラムへと{変|か}わったのです。

## {復習|ふくしゅう}では、この7つの{問|と}いに{沿|そ}って{確認|かくにん}する

1. **ウィンドウを{閉|と}じた{後|あと}、{通信|つうしん}しているのは{誰|だれ}か？** アプリケーションのUI、プロトコルクライアント、サーバーを{区別|くべつ}する。
2. **なぜAES{鍵|かぎ}を1つだけ{探|さが}さないのか？** {転送|てんそう}、ビジネスロジック、ストレージでは、{鍵|かぎ}と{状態|じょうたい}の{用途|ようと}が{異|こと}なる。
3. **QRコードが{表示|ひょうじ}されたらログイン{完了|かんりょう}なのか？** さらにスマートフォンでの{確認|かくにん}、{同一生成元|どういつせいせいげん}の{状態確認|じょうたいかくにん}、{最終的|さいしゅうてき}なアカウント{認証|にんしょう}が{必要|ひつよう}となる。
4. **{最初|さいしょ}のメッセージ{受信|じゅしん}はどこから{始|はじ}めるのか？** まず{初期化|しょきか}し、その{後|あと}、{正確|せいかく}な{取得元|しゅとくもと}の{同期状態|どうきじょうたい}を{使用|しよう}する。
5. **なぜカーソルを{先|さき}に{保存|ほぞん}してはいけないのか？** メッセージとカーソルは、{同|おな}じローカルトランザクションでコミットしなければならない。
6. **{送信成功|そうしんせいこう}は{何|なに}で{判断|はんだん}するのか？** リクエストに{対応|たいおう}する{有効|ゆうこう}なレスポンスに{加|くわ}え、{受信側|じゅしんがわ}で{本文全体|ほんぶんぜんたい}を{照合|しょうごう}する。{結果|けっか}が{不明|ふめい}な{場合|ばあい}は{自動再試行|じどうさいしこう}しない。
7. **{独立性|どくりつせい}をどう{証明|しょうめい}するのか？** {元|もと}のアプリケーションを{実行|じっこう}せず、{依存情報|いぞんじょうほう}の{読|よ}み{取|と}りを{制限|せいげん}し、{元|もと}のプロセスの{証拠|しょうこ}を{保存|ほぞん}したうえで、{実環境|じつかんきょう}における{双方向|そうほうこう}の{受入試験|うけいれしけん}を{行|おこな}う。

{本稿|ほんこう}のWeChat{固有|こゆう}のフィールドと{検収結果|けんしゅうけっか}は、{今回|こんかい}の{非公開実装|ひこうかいじっそう}および{保存|ほぞん}された{原始記録|げんしきろく}に{基|もと}づく。{外部|がいぶ}リンクはネットワークアーキテクチャと{暗号技術|あんごうぎじゅつ}の{背景|はいけい}を{補足|ほそく}するためだけに{用|もち}い、これらの{実測結果|じっそくけっか}に{代|か}わる{証拠|しょうこ}とはしない。
