11

JPG と Zip ファイルを 1 つのファイルに結合し、両方の形式で有効な (または少なくとも読み取り可能な) ファイルにすることができる巧妙なハックについて聞いたことがあると思います。JPG は最後に任意のものを入れ、最初に ZIP を入れることができるので、もう 1 つのフォーマットをそこに入れることができることに気付きました。この質問では、中間データは任意のバイナリ データであり、JPG または ZIP 形式と競合しないことが保証されていると仮定します (つまり、魔法の zip ヘッダー 0x04034b50 が含まれていません)。図:

0xFFD8 <- start jpg data end -> 0xFFD9 ... ARBITRARY BINARY DATA ... 0x04034b50 <- start zip file ... EOF

私はこのように猫を飼っています:

cat "mss_1600.jpg" filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb "null.bytes" "randomzipfile.zip" > temp.zip

これにより、6,318 KB のファイルが生成されます。7-Zipでは開きません。ただし、「ダブル」を 1 つ減らすと (13 個の filea と b の代わりに 12 個になります):

cat "mss_1600.jpg" filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb filea fileb "null.bytes" "randomzipfile.zip" > temp.zip

7-Zip で開く5,996 KB のファイルが生成されます。

したがって、私の任意のバイナリ データには、それを台無しにする魔法の Zip ファイル ヘッダーがないことがわかっています。動作する jpg+data+zipと動作しないjpg+data+zipの参照ファイルがあります(ブラウザーがイメージと見なすため、名前を付けて保存し、zip 拡張子を自分で追加します)。

13 の組み合わせでは失敗し、12 の組み合わせでは失敗する理由を知りたいです。ボーナス ポイントについては、どうにかしてこれを回避する必要があります。

4

4 に答える 4

22

7-Zip のソースをダウンロードし、何が原因であるかを突き止めました。

CPP/7zip/UI/Common/OpenArchive.cpp には、次のように表示されます。

// Static-SFX (for Linux) can be big.
const UInt64 kMaxCheckStartPosition = 1 << 22;

つまり、ファイルの最初の 4194304 バイトのみがヘッダーとして検索されます。そこに見つからない場合、7-Zip はそれを無効なファイルと見なします。

1 << 22に変更することで、その制限を 2 倍にすることができます1 << 23。7-Zip を再構築してその変更をテストしたところ、動作しました。

編集: この問題を回避するには、ソースをダウンロードし、上記の変更を加えてビルドします。VS 2008 を使用してビルドしました。VS コマンド プロンプトを開き、extracted-source-location \CPP\7zip\Bundles に移動して、「nmake」と入力します。次に、Alone ディレクトリで '7za t nonworking.jpg' を実行すると、'Everything is Ok' と表示されます。

于 2009-12-03T01:55:03.820 に答える
10

実際には、それは本当に2つの部分の答えです:)

まず第一に、人々が何と言おうと、技術的にzipファイルをファイルの最後にそのまま置くことはできません。中央ディレクトリ レコードの末尾には、現在のディスクの先頭からのバイト オフセットを示す値があります (.zip ファイルが 1 つしかない場合は、現在のファイルを意味します)。現在、多くのプロセッサはこれを無視していますが、Windows の zip フォルダーは無視していないため、Windows エクスプローラーで機能するようにその値を修正する必要があります (気にする必要はありません ;P)ファイル形式に関する情報については、Zip APPNOTEを参照してください。基本的に、「開始ディスク番号に対する中央ディレクトリの開始のオフセット」値を見つけるために、16進エディターで見つけます(またはツールを作成します)。次に、最初の「中央ファイル ヘッダー署名」 (504b0102 の 16 進数) を見つけて、値をそのオフセットに設定します。

残念ながら 7zip は修正されませんが、これは 7zip がファイル形式を推測しようとする方法によるものです。基本的に、バイナリ シーケンス 504b0304 の最初の ~4MiB のみを検索します。見つからない場合は、Zip ではないと想定し、他のアーカイブ形式を試します。これは明らかに、ファイルをもう 1 つ追加すると問題が発生する理由であり、検索の制限を超えてしまいます。

それを修正するには、その 16 進文字列を壊さずに jpeg に追加する必要があります。これを行う 1 つの方法は、FFD8 JPEG SOI ヘッダーの直後に次の 16 進データ FFEF0005504B030400 を追加することです。これにより、シーケンスにカスタム ブロックが追加されますが、これは正しいため、jpeg ヘッダーはそれを無視する必要があります。

于 2009-12-03T01:58:56.200 に答える
4

したがって、この質問を見つけた他の人のために、ここに話があります:

はい、ファイルで 7-Zip が失敗する理由について、Andy は文字通り正しいですが、人々に MY バージョンの 7-Zip を正確に使用してもらうことができないため、問題の解決にはなりません。

しかし、ティラニッドは私に解決策をもたらしました。

  • まず、彼が提案するように JPG に小さなバイト文字列を追加すると、7-Zip で開くことができます。ただし、有効な JPG フラグメントからわずかにずれています。FFEF00 07 504B030400 である必要があります。長さは 2 バイトずれています。
  • これにより、7-Zip で開くことができますが、ファイルを抽出することはできず、黙って失敗します。これは、中央ディレクトリのエントリに、ファイルのエントリを指す内部ポインタ/オフセットがあるためです。その前にたくさんのものを置いたので、それらすべてのポインタを修正する必要があります!
  • Windowsに組み込まれたzipサポートでzipを開くには、tyranidが言うように、「開始ディスク番号に対する中央ディレクトリの開始のオフセット」を修正する必要があります。最後の 2 つを実行する Python スクリプトを次に示しますが、これはフラグメントであり、copypasta ですぐに使用できるものではありません。

#Now we need to read the file and rewrite all the zip headers.  Fun!
torewrite = open(magicfilename, 'rb')
magicdata = torewrite.read()
torewrite.close()

#Change the Central Repository's Offset
offsetOfCentralRepro = magicdata.find('\x50\x4B\x01\x02') #this is the beginning of the central repo
start = len(magicdata) - 6 #it so happens, that on my files, the point is stored 2 bytes from the end.  so datadatadatdaata OF FS ET !! 00 00 EOF where OFFSET!! is the 4 bytes 00 00 are the last two bytes, then EOF
magicdata = magicdata[:start] + pack('I', offsetOfCentralRepro) + magicdata[start+4:]

#Now change the individual offsets in the central directory files
startOfCentralDirectoryEntry = magicdata.find('\x50\x4B\x01\x02', 0) #find the first central directory entry
startOfFileDirectoryEntry = magicdata.find('\x50\x4B\x03\x04', 10) #find the first file entry (we start at 10 because we have to skip past the first fake entry in the jpg)
while startOfCentralDirectoryEntry > 0:
    #Now I move a magic number of bytes past the entry (really! It's 42!)
    startOfCentralDirectoryEntry = startOfCentralDirectoryEntry + 42

    #get the current offset just to output something to the terminal
    (oldoffset,) = unpack('I', magicdata[startOfCentralDirectoryEntry : startOfCentralDirectoryEntry+4])
    print "Old Offset: ", oldoffset, " New Offset: ", startOfFileDirectoryEntry , " at ", startOfCentralDirectoryEntry
    #now replace it
    magicdata = magicdata[:startOfCentralDirectoryEntry] + pack('I', startOfFileDirectoryEntry) + magicdata[startOfCentralDirectoryEntry+4:]

    #now I move to the next central directory entry, and the next file entry
    startOfCentralDirectoryEntry = magicdata.find('\x50\x4B\x01\x02', startOfCentralDirectoryEntry)
    startOfFileDirectoryEntry = magicdata.find('\x50\x4B\x03\x04', startOfFileDirectoryEntry+1)

#Finally write the rewritten headers' data
towrite = open(magicfilename, 'wb')
towrite.write(magicdata)
towrite.close()
于 2009-12-08T15:04:03.517 に答える
2

DotNetZipを使用してハイブリッドJPG+ZIPファイルを作成できます。DotNetZipはストリームに保存でき、zipコンテンツの書き込みを開始する前に、既存のストリームの元のオフセットを認識するのに十分インテリジェントです。したがって、擬似コードでは、次の方法でJPG+ZIPを取得できます。

 open stream on an existing JPG file for update
 seek to the end of that stream
 open or create a zip file
 call ZipFile.Save to write zip content to the JPG stream
 close

すべてのオフセットが正しく計算されます。同じ手法を使用して、自己解凍型アーカイブを作成します。EXEでストリームを開き、最後までシークして、そのストリームにZIPコンテンツを書き込むことができます。このようにすると、すべてのオフセットが正しく計算されます。

別のこと-別の投稿のコメントの1つに関して...ZIPは、ファイルの最初最後に任意のデータを含めることができます。zip中央ディレクトリがファイルの最後にある必要があることを私が知っている限り、要件はありませんが、それは一般的です。

于 2010-03-03T20:35:54.543 に答える