node.js で PC 上の WeChat アプレット パッケージを復号化するための処理アイデア

node.js で PC 上の WeChat アプレット パッケージを復号化するための処理アイデア

元々はNuggetsに投稿されていたので、こちらに移動しました。

WeChat ミニプログラムは、PC に暗号化された形式で保存されます。直接開いても、役立つ情報は表示されません。パッケージの具体的な内容を確認するには、暗号化を解除する必要があります。この記事では、Node.js を使用して復号化アルゴリズムを実装します。主に、crypto、commander、chalk の 3 つのパッケージを使用します。

アプレットのソースコードはどこにありますか?

PCで開かれたミニプログラムは、ローカルWeChatファイルのデフォルトの保存場所にキャッシュされます。WeChat PC => その他 => 設定から表示できます。

デフォルトの保存場所にある /WeChat Files/WeChat Files/Applet フォルダーに入ると、プレフィックスが wx の一連のファイルが表示されます (ファイル名は実際にはミニプログラムの appid です)。これらは、開いたミニプログラムです。

ミニプログラムの 1 つのフォルダーに入ると、数字の文字列で名前が付けられたフォルダーが表示されます。このフォルダをクリックすると、アプレットに対応するコードである __APP__.wxapkg ファイルが表示されます。

しかし、ファイルを開くと、次の内容が見つかりました。

WTF はこの🔨からわかります。当然ながら、このファイルは暗号化されており、見たい内容を表示するには復号化する必要があります。

PC ミニプログラムはどのように暗号化されますか?

これは、ある大物によって Go 言語で書かれた PC 側の wxapkg 復号化コードへの参照です。整理すると、暗号化プロセスは次のようになります。

まず、平文コードは 1024 バイト目で 2 つに分割されます。前半は CBC モードの AES を使用して暗号化され、後半は直接 XOR されます。最後に、暗号化された 2 つのセクションを連結し、先頭に固定文字列「V1MMWX」を書き込みます。

したがって、__APP__.wxapkg ファイルを開くと、暗号化されたコードが表示されます。これを復元するには、後ろから前へ段階的に押し戻す必要があります。

復号化のアイデア

前処理

デコードプログラムを書くにはnode.jsを使います。上記の暗号化プロセスに従って、まず暗号化されたファイルを読み取り、最初の 6 バイトの固定文字列を削除します。 AES 暗号化と XOR の前後のビット数は同じなので、暗号化された 1024 バイトのヘッダーと暗号化されたテールを取得できます。

const fs = require('fs').promises;
...
 
const buf = await fs.readFile(pkgsrc); // 元のバッファを読み込む
bufHead = buf.slice(6, 1024 + 6);
bufTail = buf.slice(1024 + 6);

暗号化されたヘッダー

この 1024 バイトの平文を取得するには、AES 暗号化の初期ベクトル iv と 32 ビットのキーを知る必要があります。 16 バイトの初期ベクトル iv が文字列「the iv: 16 bytes」であることがわかっているので、次に pbkdf2 アルゴリズムによって導出された 32 ビット キーを計算する必要があります。

pbkdf2 (パスワードベースのキー導出関数) は、キーを生成するために使用される関数です。疑似ランダム関数を使用し、元のパスワードとソルトを入力として受け取り、継続的な反復を通じてキーを取得します。暗号ライブラリでは、pbkdf2 関数は次のようになります。

const crypto = require('crypto');
...
 
crypto.pbkdf2(パスワード、ソルト、反復、キー長、ダイジェスト、コールバック)

パラメータは、元のパスワード、ソルト値、反復回数、キーの長さ、ハッシュ アルゴリズム、およびコールバック関数です。ソルトは「saltiest」、元のパスワードはWeChatアプレットのID(つまり、wxで始まるフォルダ名)、反復回数は1000、ハッシュアルゴリズムはsha1であることがわかっています。したがって、キーを計算するコードを記述できます。

crypto.pbkdf2(wxid, salt, 1000, 32, 'sha1', (err, dk) => {
    もし(エラー){
        // 間違い}
    // dk は計算されたキーです})

キーと初期ベクトル iv を取得したら、暗号文の復号化を開始できます。 AES 暗号化アルゴリズムは非対称暗号化アルゴリズムです。そのキーは、ユーザーのみが知っている公開キーと秘密キーに分かれています。誰でも公開キーを使用して暗号化できますが、平文を復号化できるのは秘密キーを持っている人だけです。

アプレットが使用する暗号化アルゴリズムは、CBC (Cipher Block Chaining) モードの AES です。つまり、暗号化の際には、まず平文をブロックに分割し、各ブロックを前のブロックの暗号化された暗号文と XOR し、次に公開鍵を使用して暗号化して各ブロックの暗号文を取得します。最初の平文ブロックは、前の平文ブロックには存在しないため、初期ベクトル iv と XOR 演算され、公開鍵で暗号化されます。実装する際には、crypto が提供する復号化関数を呼び出すだけで済みます。

AES アルゴリズムには、キーの長さに応じて AES128、AES192、AES256 が含まれることがわかっています。振り返ってみると、キーは 32 バイト、つまり 256 ビットなので、明らかに AES256 を使用する必要があります。要約すると、復号化コードは次のように記述できます。

const decipher = crypto.createDecipheriv('aes-256-cbc', dk, iv);
const originalHead = Buffer.alloc(1024, decipher.update(bufHead));

このうち、originalHead は必要なプレーンテキストの最初の 1024 バイトです。印刷して確認してみましょう:

うーん...それはちょっと面白いですね。

暗号化された尾部

この部分は簡単です。 XOR 演算は再帰的であるため、アプレット ID の桁数を判定して XOR xorKey を取得し、それを暗号文と XOR して元のテキストを取得するだけです。

const xorKey = wxid.length < 2 ? 0x66 : wxid.charCodeAt(wxid.length - 2);
末尾 = [];
for(let i = 0; i < bufTail.length; ++i){
    tail.push(xorKey ^ bufTail[i]);
}
Buffer から tail を取得します。

ヘッダー部分のプレーンテキストとテール部分のプレーンテキストを連結し、バイナリ形式でファイルに書き込んで最終的なプレーンテキストを取得します。

もっと美しく

上記の説明に基づいて、復号化プロセス全体をブラック ボックスにカプセル化できます。

指揮官

コマンダー ライブラリを使用すると、プログラムがコマンド ラインから直接アプレットの ID と暗号テキスト パッケージを読み取ることができるようになります。 Commander は、独自の CLI コマンドを簡単に定義できる Node.js コマンドライン インターフェイス ソリューションです。たとえば、次のコードの場合:

const プログラム = require('commander');
...
プログラム
    .command('decry <wxid> <src> [dst]')
    .description('PC WeChat アプレット パッケージのデコード')
    .action((wxid, src, dst) => {
        wxmd(wxid, src, dst);
    })
 
プログラム.バージョン('1.0.0')
    .usage("decry <wxid> <src> [dst]")
    .parse(プロセス.argv);

コマンド「decry <wxid> <src> [dst]」を定義しました。山括弧は必須パラメータを表し、角括弧はオプション パラメータを表します。説明にはこのコマンドについての説明テキストが含まれ、アクションはこのコマンドを実行することです。ノードを使用してコンソールでコードを実行すると、次のインターフェースが表示されます。

次に、プロンプトに従って復号化のパラメータを入力します。 commander.js の中国語ドキュメントはここにあります。

チョーク

コンソールにちょっとした色を加えるために、chalk.js を使用して出力を美しくすることができます。チョークの基本的な使い方も比較的簡単です。

const チョーク = require('チョーク');
...
 
console.log(chalk.green('green'))

このようにして、白黒のコンソールに緑のタッチを加え、パンダの夢を実現することができます。

さらに、es6 文字列タグ テンプレートを使用して、チョークをより便利に使用することもできます。詳細については、Chalk の公式ドキュメントを参照してください。

ソースコード

コードはgithubとgiteeに公開されているので参考にしてください〜

githubはこちら、giteeはこちら

これで、node.js での PC 上の WeChat アプレット パッケージの復号化に関するこの記事は終了です。より関連性の高い nodejs WeChat アプレットの復号化コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • 携帯電話番号を取得するためのWeChatアプレットJavaScript復号化サンプルコードの詳細な説明
  • WeChat アプレットのログインデータの復号化と状態維持の例

<<:  純粋な CSS でマークダウンの自動番号付けを実装するサンプル コード

>>:  ウェブページのCSSの優先順位について詳しく説明します

推薦する

yum インストールエラーの問題を解決する 保護されたマルチライブラリバージョン

現在、クラウドサーバーに nginx をインストールする際、最初に zlib などの依存ライブラリを...

Spring Boot + jar パッケージングのデプロイメント Tomcat 404 エラーの問題を解決する

1. Spring Boot は jsp jar パッケージをサポートしていません。jsp は wa...

uni-app WeChatアプレット認証ログイン実装手順

目次1. appIDの申請と設定1. appidの取得方法2. AppIDの設定2. 基本的なユーザ...

Vue テンプレート構成と Webstorm コード形式仕様設定

目次1. コンパイラコードフォーマット仕様設定2. Vueテンプレートの設定1. コンパイラコードフ...

webpack -v エラー解決

背景webpackのバージョンを確認したいのですが、webpack -vを実行するとエラーが報告され...

Ubuntu Server でのワイヤレス ネットワーク カードの詳細な設定

1. ワイヤレス ネットワーク カードを挿入し、コマンドiwconfigを使用してワイヤレス ネット...

JSはreduce()メソッドを使用してツリー構造データを処理します

目次意味文法例1. 初期値initが渡されない2. 初期値を渡す場合3. アレイの重複排除4. Re...

Vue3 でマークダウン エディター コンポーネントを使用する方法

目次インストールコンポーネントのインポート基本的な使い方保存したマークダウンまたは HTML テキス...

プロフェッショナルなMySQL開発設計仕様とSQL記述仕様

チーム開発のプロセスでは、プロジェクトの安定性、コードの効率性、管理の利便性のために、内部開発および...

伝説的な VUE 構文シュガーは何をするのでしょうか?

目次1. 糖衣構文とは何ですか? 2. VUE の構文糖とは何ですか? 1. 最も一般的な構文シュガ...

vite を使用して vue3 アプリケーションを構築する方法

1. インストールヒント: 現在、VUE3.0 の公式翻訳ドキュメントはありません。しかし、すでに誰...

Docker に共通コンポーネント (mysql、redis) をインストールする方法

Dockerはmysqlをインストールします docker search mysql 検索 dock...

Linuxはiftopを使用してネットワークカードのトラフィックをリアルタイムで監視します

Linux は iftop を使用してネットワーク カードのトラフィックをリアルタイムで監視します。...

MySQL 8.0.12 winx64 詳細なインストールチュートリアル

この記事では、MySQL 8.0.12のインストールチュートリアルを参考までに紹介します。具体的な内...