1つのバグの履歴またはパスワードの圧縮方法

これは、個人的な経験、つまり他の誰かの古いサポートされていないコードのバグを見つける経験の物語です。

それはすべていつものように始まりましたが、私より前に簡単なタスクがありました:現在のフォルダー内のファイルをC ++ / Qtの特定のパスワードでZIPアーカイブにパックするには、もっと簡単に思えますか?

当然、最初のアシスタントはGoogleであり、 ZIPアーカイブを操作するための2つのQtライブラリがあることを提案しました。

QuaZIPおよびOSDab ZIPは 、とりわけ、 Qt自体がパッケージング用のqCompressおよびqDecompressメソッドをサポートしています。

この方法は、ストリーム、すべてのヘッダー、および開発者の良心の暗号化しか取得できないため、私にはあまり向いていないことがわかりました。 このパスは長すぎたため、すぐに拒否し、ライブラリに注意を向けました。

OSDaB ZIPはすぐに削除する必要がありました。これは優れたライブラリであるにもかかわらず、そのコードはGPLライセンスの下でのみ配布されますが、独自のアプリケーションに機能を組み込む必要がありました。 幸いなことに、 QuaZIPは2つのGPLライセンスLGPLライセンスを取得しました 。 止まった。 特に彼のデバイスを詳しく調べることなく、私は彼と一緒に作業するための最も簡単なクラスをスケッチし、テストを開始しました。





これが問題の始まりです。アーカイブはこのライブラリによって美しく作成され暗号化され、それもうまく解凍されましたが、それは悪いことです。他のアーカイバは正しいパスワードを認識せず、それが正しくないと言いました。 最初に、圧縮解除アルゴリズムをチェックし、このように考えました。エラーが対称的である場合、作成されたファイルおよび別のアーカイバによって保護されたパスワードを圧縮解除できませんが、ライブラリはこれをうまく行いました。 問題は暗号化アルゴリズムのみにあることが明らかになりましたが、それを見つける方法は?



はじめに、コード、コードに関するコメント、および作成者に関する情報を調べました。 QuaZIPクラス自体は、純粋なCで記述されたMiniZipライブラリのQtラッパーであることがわかりました。QuaZIPラッパーが無関係であることをすぐに確認して、MiniZipコードの調査を開始しました。 一見、すべてが完璧に機能しました。 それで、私は再びGoogleで、そしてまさにこのライブラリの開発者のWebサイトで質問をすることにしました。 そこで、2007年に発生した同じ質問を持つ2つの未報告のバグレポートを見つけました。



憂鬱に。



さらに、問題の調査に取り掛かりました。 もちろん、疑いのある最初のファイルは、暗号化アルゴリズム自体が実装されているcrypt.hファイルでした。

このファイルはInfo-ZIPソフトウェアパッケージのcrypt.hファイルであり、BSDライセンス(これはシステム内のすべてのLinuxシステムと同じzip / unzipです)の下でわずかに適合および書き換えられていることを発見しました。



次に、お気に入りのOktetaで あるPKWARE WebサイトZIP形式の説明を取得し、ライブラリを使用してアーカイブを作成しました。同様に標準のZIPを使用してアーカイブを作成し、それらを比較し始めました。

写真は非常に興味深いことが判明しました。ここにあります:





暗号化されたZIPファイルの構造はかなり単純です。

ファイル全体は、直接コンテンツを含むセクションとCentral headerというセクションの2つのセクションに分かれています。

中央ヘッダーには、ファイル、サイズ、作成日、ファイル属性などに関する情報が含まれています。これは、アーカイバプログラムがアーカイブファイル全体を解析せずにコンテンツに関する情報をすばやく読み取り、ユーザーに表示できるようにするために必要です。

ファイルセクションは次のように構築されます。

[ローカルファイルヘッダー1]

[ヘッダー1を暗号化する12バイト]

[ファイルデータ1]

[データ記述子1]







[ローカルファイルヘッダーn]

[12バイトの暗号化ヘッダーn]

[ファイルデータn]

[データ記述子n]




ローカルファイルヘッダーを見るのは興味深いです。

ローカルファイルヘッダー署名4バイト(0x04034b50)

2バイトを抽出するために必要なバージョン

汎用ビットフラグ2バイト

圧縮方式2バイト

最後のmodファイル時間2バイト

最後のmodファイルの日付2バイト

crc-32 4バイト

圧縮サイズ4バイト

非圧縮サイズ4バイト

ファイル名の長さ2バイト

追加フィールド長2バイト

ファイル名(可変サイズ)

追加フィールド(可変サイズ)


また、ヘッダーブロックは4つの「マジック」バイト「PK ..」で始まり、最後の2バイトは開始したブロックに応じて変化することに注意してください。



そのため、スクリーンショットからわかるように、オリジナルとの不一致は1つだけです。完全なコンプライアンスを達成するために、フラグビットを修正しました。



そしてさらに調べてみると、当然、12バイトの暗号化ヘッダーは常に異なります。これは、生成時に乱数ジェネレーターが使用されるためです。 ファイルの内容の長さは一致しました-これは良い兆候ですが、ライブラリによって生成されたデータにはデータ記述子が完全に欠けていました。 追加することにしました。 結局のところ、それは完全にオプションでした。



その結果、ヘッダーフィールドやその他のサポート情報に関して、すべてのファイルが完全に同一になりました。



アルゴリズムをテストするために、ランダム性を削除し、ライブラリとInfo-Zipプログラムセット内の静的な値に置き換え、両方のプログラムをコンパイルしてアーカイブを作成し、これらのアーカイブが機能しないことを保証しますが、アルゴリズムが一致する場合は同一に見えるはずです



文字列を置き換える

c = (rand() >> 7) & 0xff;
      
      





crypt.hファイルと両方のプログラムで、同様の方法で作成された2つの新しいアーカイブを受け取り、Oktetaでそれらを調べ始めました。







ファイルは完全に異なっていることがわかりましたが、新しい共通部分、つまりローカルヘッダーの後ろの最初の10バイトが取得されました。 つまり 暗号化ヘッダーの12バイトのうち10バイトが一致しますが、2つは一致しません。

crypt.hの内容を見ると、最後の2バイトが特定のcrcForCryptingキーを使用して特別な方法で生成されていることがわかります。



  buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
      
      





両方のプログラムでハードコードされたcrcForCryptingを置き換えると、ファイルが同じ(!)であることがわかりましたつまり、まるで同じファイルであるかのように、まったく同じでした。

真実は近くにあると感じたので、 crcForCrypting変数の値がどのように取得されるかを研究することにしましたか? そして、 Info-ZIPではこの変数はファイル作成時間の16ビット左シフトによって取得されるのに対し、 MiniZIPではこの変数は常にゼロであることが判明しました。



これが問題の解決策であることが判明しました。 この変数を埋めるために必要なコードを追加し、ランダムに戻すことを忘れずに、ライブラリを使用してアーカイブを再度生成し、それをarkで正常に解凍しました。



結論として、私は掘削プロセス中にQuaZIPの著者に連絡したと言います。ところで、彼は私たちの同胞ですが、残念ながら、彼は私を助けることができませんでした。 しかし、私の研究が完了すると、彼はパッチを受け入れ、近い将来にQuaZIPの新しいバージョンをリリースすることを約束しました。

とりあえず、QuaZIPの更新はありません。現在のバージョンのパッチを含む記事にdiffを適用します。



All Articles