Проблема комбинирования файлов в формате JPG + Zip с форматом Zip - PullRequest
11 голосов
/ 30 ноября 2009

Надеюсь, вы слышали о аккуратном хаке , который позволяет объединить JPG и Zip-файл в один файл, и это допустимый (или хотя бы читаемый) файл для обоих форматов. Что ж, я понял, что, поскольку JPG допускает произвольные вещи в конце, а ZIP в начале, вы можете вставить туда еще один формат - посередине. Для целей этого вопроса предположим, что промежуточные данные - это произвольные двоичные данные, гарантирующие, что они не будут конфликтовать с форматами JPG или ZIP (то есть они не содержат магический zip-заголовок 0x04034b50). Иллюстрация:

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

Я катаюсь так:

filea fileb 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

Создает файл 6,318 КБ. не открывается в 7-Zip. Тем не менее, когда я на одну кошку меньше «двойной» (вместо 13 filea и b's, 12):

кошка "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

Создает файл размером 5,996 КБ, который открывает в 7-Zip.

Так что я знаю, что в моих произвольных двоичных данных нет волшебного заголовка Zip-файла, который мог бы испортить его. У меня есть справочные файлы с рабочим jpg + data + zip и нерабочим jpg + data + zip (сохраните как, потому что браузер считает их изображениями, и добавьте расширение zip самостоятельно).

Я хочу знать, почему он терпит неудачу с 13 комбинациями, а не с 12. Для получения бонусных баллов мне нужно как-то обойти это.

Ответы [ 4 ]

21 голосов
/ 03 декабря 2009

Я скачал исходник для 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 на 1 << 23. Я проверил это изменение, перестроив 7-Zip, и он работает.

РЕДАКТИРОВАТЬ : Чтобы обойти эту проблему, вы можете скачать исходный код , внести вышеуказанные изменения и собрать его. Я построил его с использованием VS 2008. Откройте командную строку VS, перейдите к extract-source-location \ CPP \ 7zip \ Bundles и введите 'nmake'. Затем в каталоге Alone запустите «7za t nonworking.jpg», и вы должны увидеть «Все в порядке».

10 голосов
/ 03 декабря 2009

На самом деле это ответ из двух частей :)

Во-первых, что бы люди ни говорили, zip-файлы технически не могут 1004 * дословно ставиться в конце файлов. Конец записи центрального каталога имеет значение, которое указывает смещение в байтах от начала текущего диска (если у вас есть только один файл .zip, это означает текущий файл). Сейчас многие процессоры игнорируют это, хотя папка zip в Windows этого не делает, поэтому вам нужно исправить это значение, чтобы оно работало в проводнике Windows (не то, чтобы вы заботились об этом; P). См. Zip APPNOTE для получения информации о формат файла. Обычно вы находите в шестнадцатеричном редакторе (или пишете инструмент) значение «смещения начала центрального каталога относительно номера начального диска». Затем найдите первую «центральную подпись заголовка файла» (шестнадцатеричное 504b0102) и установите значение для этого смещения.

Теперь, увы, это не исправляет 7zip, но это происходит из-за того, как 7zip пытается угадать формат файла. По сути, он будет искать только первые 4MiB двоичной последовательности 504b0304, если он не найдет ее, то предположит, что это не Zip, и попробует другие форматы архивов. Именно поэтому добавление еще одного файла ломает вещи, оно выдвигает его за предел для поиска.

Теперь, чтобы исправить это, вам нужно добавить шестнадцатеричную строку в jpeg, не нарушая ее. Один из способов сделать это - добавить сразу после заголовка FFD8 JPEG SOI следующие шестнадцатеричные данные, FFEF0005504B030400. Это добавляет пользовательский блок с вашей последовательностью и является правильным, поэтому заголовки jpeg должны просто игнорировать его.

4 голосов
/ 08 декабря 2009

Так что для любого, кто найдет этот вопрос, вот история:

Да, Энди в буквальном смысле прав, почему 7-Zip не работает с файлом, но это не помогает моей проблеме, поскольку я не могу заставить людей использовать МОЮ версию 7-Zip.

Тиранид, однако, дал мне решение.

  • Прежде всего, добавив небольшую байтовую строку в JPG, как он предлагает, 7-Zip откроет ее. Однако он немного отличается от допустимого фрагмента JPG, он должен быть FFEF00 07 504B030400 - длина была отключена на 2 байта.
  • Это позволяет 7-Zip открывать его, но не извлекать файлы, он молча завершается неудачей. Это связано с тем, что записи в центральном каталоге имеют внутренние указатели / смещения, которые указывают на запись файла. Так как вы положили кучу вещей до этого, вам нужно исправить все эти указатели!
  • Чтобы открыть zip с помощью встроенной поддержки zip в Windows, вам необходимо, как говорит tyranid, исправить «смещение начала центрального каталога относительно номера начального диска». Вот скрипт Python для выполнения двух последних, хотя это фрагмент, а не copypasta-ready-to-use

#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()
2 голосов
/ 03 марта 2010

Вы можете создавать гибридные файлы JPG + ZIP, используя DotNetZip . 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 в этот поток. Все смещения правильно рассчитаны, если вы сделаете это таким образом.

Другое дело - что касается одного из комментариев в другом посте ... ZIP может иметь произвольные данные в начале и в конце файла. Насколько я знаю, требования нет, поскольку центральный каталог zip должен находиться в конце файла, хотя это типично.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...