Совместимость с JPG + Zip файлом с Zip-форматом

Надеюсь, вы слышали о опрятном взломе, который позволяет объединить JPG и Zip файл в один файл, и он действителен ( или, по крайней мере, читаемый) файл для обоих форматов. Ну, я понял, что, поскольку JPG позволяет произвольным материалам в конце, а ZIP в начале, вы можете вставить еще один формат там - посередине. Для целей этого вопроса предположим, что средние данные - это произвольные двоичные данные, которые не конфликтуют с форматами 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 КБ. Он не работает в 7-Zip. Однако, когда я кот один меньше "двойной" (так вместо 13 файлов и 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

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

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

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

Ответ 1

На самом деле это ответ на две части действительно:)

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

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

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

Ответ 2

Я загрузил источник для 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". Затем в папке "Один" запустите "7za t nonworking.jpg", и вы увидите "Все нормально".

Ответ 3

Итак, для кого-то еще, найдя этот вопрос, вот история:

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

tyranid однако получил меня решение.

  • Во-первых, добавление небольшого байта в JPG, как он предлагает, позволит 7-Zip открыть его. Тем не менее, он слегка отходит от действительного фрагмента JPG, он должен быть FFEF00 07 504B030400 - длина отключена на 2 байта.
  • Это позволяет 7-Zip открыть его, но не извлекать файлы, он терпит неудачу. Это связано с тем, что записи в центральном каталоге имеют внутренние указатели/смещения, указывающие на запись файла. Поскольку перед этим вы добавляете кучу вещей, вам нужно исправить все эти указатели!
  • Чтобы открыть zip с помощью Windows, основанной на поддержке zip, вам нужно, по словам тиранида, исправить "смещение начала центрального каталога относительно номера исходного диска". Вот питон script, чтобы сделать последние два, хотя это фрагмент, а не 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 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 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()

Ответ 4

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