これは、個人的な経験、つまり他の誰かの古いサポートされていないコードのバグを見つける経験の物語です。
それはすべていつものように始まりましたが、私より前に簡単なタスクがありました:現在のフォルダー内のファイルを
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を適用します。