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の優先順位について詳しく説明します

推薦する

TypeScript ジェネリックを簡単に説明する方法

目次概要ジェネリック医薬品とはビルドシステムジェネリック医薬品の一般的な理解ジェネリッククラスジェネ...

Dockerを使用してNextCloudネットワークディスクを展開する方法

NextCloud コンピュータ上の任意のファイルやフォルダを共有し、NextCloud サーバーと...

アダプティブ Web デザインを実現する方法に関する関連知識ポイントの要約

「アダプティブ Web デザイン」はどのように機能しますか?実はそれほど難しいことではありません。 ...

MySQL 5.7.30 のインストールとアップグレードの問題に関する詳細なチュートリアル

くさびコンピュータにインストールされている MySQL のバージョンが比較的古く、おそらくバージョン...

MySQLの自動増分IDについて知っておくべきこと

はじめに: MySQL を使用してテーブルを作成する場合、通常は自動インクリメント フィールド (A...

CentOS 7 で MySQL 5.7.23 をアップグレードする際の落とし穴と解決策

序文最近、CentOS 7 で MySQL 5.7.23 をアップグレードする際に落とし穴を発見しま...

Tomcat は親の委任メカニズムを破壊して Web アプリケーションの分離を実現します。

目次Tomcat クラスローダー階層WebAppクラスローダー共有クラスローダーカタリナクラスローダ...

データベースを削除して逃げる?xtraback を使用して MySQL データベースをバックアップする方法

1. mysqldump バックアップ方法では論理バックアップが使用されます。最大の欠点は、バック...

異なるデータベースで DROP TABLE を書く方法

異なるデータベースで DROP TABLE を書く方法1.MySQL 存在する場合はテーブルを削除 ...

CSSを使用して中央に固定された2つの列と適応型列を実現する方法

1. 絶対位置とマージンを使用するこの方法の原則は、左側と右側をドキュメントの流れから外れるように配...

HTML テキストフォーマットの簡単な例 (詳細な説明)

1. テキストの書式設定: この例では、HTML ファイル内のテキストを書式設定する方法を示します...

VMware ESXi6.7 の簡単なセットアップ(画像とテキスト付き)

1. VMware vSphere の概要VMware vSphere は、業界をリードする最も信...

MySql テーブル、データベース、シャーディング、パーティショニングの知識ポイントの紹介

1. はじめにデータベース内のデータ量が一定レベルに達すると、システムパフォーマンスのボトルネックを...

MySQLデータのバックアップ方法の選択と考え方

目次1. rsync、cpでファイルをコピーする2. xxxをoutfile構文に選択する3. 遅延...

MySQL トランザクション同時実行問題の解決

開発中にこのような問題に遭遇しましたビデオ視聴記録が 100 に更新されると、視聴されたことを意味し...