Запись в архивы RAR, ZIP и ARJ без помощи архиватора
Решил вспомнить свою юность, когда я занимался разработкой "самоходного программного обеспечения". Одной из особенностей моих творений было распространение не только через исполняемые файлы, но и через архивы различных форматов. Технология внедрения в архивные файлы не нова, она использовалась в разных вирусах еще со времен MS-DOS. Так как готовых решений на тот момент у меня не было, приходилось доходить до всего самому. Никакого вредоносного кода на этом сайте не появится, а вот некоторыми своими наработками по внедрению в архивы я с удовольствием поделюсь. Поскольку полноценно продублировать алгоритмы сжатия архиваторов в столь малом размере файла не представляется возможным, внедряться в архивы мы будем по методу "Store". Это означает, что файл в архив добавлен с опцией "без сжатия". Внутренние форматы различных архивных файлов, естественно, различаются, но у всех обязательно присутствуют служебные заголовки, используемые архиваторами, и, собственно, сами упакованные данные.Самый простой для внедрения - формат RAR, используемый одноименным архиватором. Подробную спецификацию формата RAR можно почитать здесь.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; RAR Header
- ;---------------------------------------------
- rhcrc dw ? ; --> Low-word CRC32 of fields in header
- rhtype db ? ; Header type: 0x74
- rhflag dw ? ; Bit flags
- rhsize dw ? ; File header full size
- rcsize dd ? ; Compressed file size
- rosize dd ? ; Uncompressed file size
- rhoss db ? ; Target OS version
- rfcrc dd ? ; --> File CRC32
- rdtm dd ? ; File Time/Date
- runp db ? ; Archive version to extract
- rmeth db ? ; Packing method (store)
- rnsize dw ? ; File name size
- rfattr dd ? ; File attributes
- rfname rb (?) ; File name
Code (Assembler) : Убрать нумерацию
- ; "Хвост" архива - признак окончания данных
- tail db 0C4h,03Dh,07Bh,00h,040h,07h,00h
- tail_length = $-tail
Переходим к внедрению. Предположим, что файл, который мы будем записывать в архив, уже загружен в память. Заполняем поля заголовка по порядку.
- rhtype = 74h - тип заголовка (файл);
- rhflag = 8000h - флаги;
- runp = 14h - версия архиватора, необходимая для распаковки файла;
- rmeth = 30h - метод упаковки файла (store);
- rfattr = 20h - атрибуты файла (архивный);
- rhoss = 2 - целевая ОС (Windows);
- rdtm = время и дата создания файла в формате MS-DOS;
- rfcrc = CRC32 оригинального файла;
- rosize = размер оригинального файла;
- rcsize = размер сжатого файла, равен оригинальному размеру;
- rfname = имя файла;
- rnsize = длина имени файла;
Внедрение в архивы формата RAR5 разобрано в отдельной статье.
Архиватор ARJ во времена MS-DOS фактически являлся стандартом архивирования, но сейчас утратил актуальность. Однако наработки по внедрению в него остались. Есть официальная коммерческая версия и бесплатная с открытым кодом, обе они совместимы между собой и используют одинаковый формат архива. Техническая спецификация формата ARJ есть в файле TECHNOTE.TXT из дистрибутива коммерческой версии. Дописывание файла к архиву ARJ делается немного сложнее, чем к RAR, но тоже не представляет больших трудностей.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; ARJ Header
- ;---------------------------------------------
- marker dw ? ; Header ID
- bhsize dw ? ; --> Basic header size (acrc-fhsize)
- fhsize db ? ; --> First header size (afname-fhsize)
- anum db ? ; Archive version number
- anum2 db ? ; Archive version to extract
- osver db ? ; Target OS version
- aflag db ? ; No any flags
- ameth db ? ; Archive method (0 - stored)
- aftype db ? ; File type (0 - binary)
- ares db ? ; Reserved
- dtm dd ? ; Date/Time last modification
- csize dd ? ; Compressed size
- osize dd ? ; Original size
- crc dd ? ; --> Original file CRC32
- fspec dw ? ; Filespec position in filename
- faccess dw ? ; File access mode
- hstdata dw ? ; Host data
- extra1 dd ? ; Extended file position
- edtma dd ? ; Date-time accessed
- edtmc dd ? ; Date-time created
- extra2 dd ? ; Original file size even for volumes
- afname rb (?) ; File name (ASCIIZ)
- acomm db ? ; File comment (ASCIIZ)
- acrc dd ? ; --> Basic header CRC32
- ehsize dw ? ; Extended header size
Code (Assembler) : Убрать нумерацию
- ; "Хвост" архива - признак окончания данных
- tail db 060h,0EAh,00h,00h
- tail_length = $-tail
- marker = 0EA60h - маркер заголовка архива;
- anum = 6 - версия архиватора;
- anum2 = 1 - версия архиватора для извлечения файла;
- osver = 11 - ОС для запуска файла (Windows);
- fhsize = 2Eh - размер первого заголовка от поля fhsize до afname;
- dtm = время и дата создания файла в формате MS-DOS;
- crc = CRC32 оригинального файла;
- osize = размер оригинального файла;
- csize = размер сжатого файла, равен оригинальному размеру;
- fname = имя файла в формате ASCIIZ;
Наиболее сложным для внедрения является формат ZIP. Здесь информация о сжатом файле хранится в нескольких местах: локальном заголовке и так называемой "центральной директории". Это сделано специально, чтобы можно было максимально быстро получить доступ к структуре архива, не просматривая целиком его содержимое. И именно из-за этой особенности при внедрении нам придется обрабатывать весь архив. Для больших архивов придется создавать временный файл, а в нашем случае вполне можно прочитать архив целиком в память. Спецификацию формата ZIP можно почитать на офсайте разработчиков. Этот же формат имеют архивы JAR, по сути это просто переименованные ZIP-файлы.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; ZIP-header (local)
- ;---------------------------------------------
- zlid dw ? ; Header Id
- zlsig dw ? ; Signature
- zlvneed dw ? ; Version Need
- zlflags dw ? ; Flags
- zlmeth dw ? ; Method
- zldtm dd ? ; DateTime
- zlfcrc dd ? ; --> CRC32
- zlcsize dd ? ; Compressed Size
- zlosize dd ? ; Uncompressed Size
- zlnsiz dw ? ; Size of Filename
- zlefild dw ? ; Size of Extra Field
- zlfname rb (?) ; Filename
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; ZIP-header (central directory)
- ;---------------------------------------------
- zid dw ? ; Header Id
- zsig dw ? ; Signature
- zverm dw ? ; Version Made
- zvneed dw ? ; Version Need
- zflags dw ? ; Flags
- zmeth dw ? ; Method
- zdtm dd ? ; TimeDate
- zfcrc dd ? ; --> CRC32
- zcsize dd ? ; Compressed Size
- zosize dd ? ; Uncompressed Size
- znsiz dw ? ; Size of Filename
- zefield dw ? ; Size of Extra Field
- zcomm dw ? ; Comment Size
- zdnumb dw ? ; Disk Number
- ziattr dw ? ; Internal Attributes
- zeattr dd ? ; External Attributes
- zohead dd ? ; Offset Header
- zfname rb (?) ; Filename
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; ZIP-header (End of central directory)
- ;---------------------------------------------
- zeid dw ? ; Header Id
- zesig dw ? ; Signature
- zenumd dw ? ; Number of this disk
- zenume dw ? ; Number of this disk
- zetotal dw ? ; Total number of entries of disk
- zedir dw ? ; Total number of entries of the central directory
- zesizec dd ? ; Size of the central directory
- zeoffs dd ? ; Offset of start of central directory
- zecomm dw ? ; ZIP file comment length
- zlid = 'PK' - сигнатура заголовка;
- zlsig = 0403h - тип заголовка - локальный;
- zlvneed = 10h - версия архиватора для извлечения файла;
- zlflags = 80h - флаги файла (см. в документации);
- zlmeth = 0 - метод упаковки без сжатия;
- zldtm = время и дата создания файла в формате MS-DOS;
- zlfcrc = CRC32 оригинального файла;
- zlcsize = размер сжатого файла, равен оригинальному размеру;
- zlosize = размер оригинального файла;
- zlnsiz = длина имени файла;
- zlfname = имя файла;
- zid = 'PK' - сигнатура заголовка;
- zsig = 0201h - тип заголовка - центральная директория;
- zverm = 10h - версия архиватора для извлечения файла
- zvneed = 10h - версия архиватора для извлечения файла;
- zflags = 80h - флаги файла (см. в документации);
- zmeth = 0 - метод упаковки без сжатия;
- zdtm = время и дата создания файла в формате MS-DOS;
- zfcrc = CRC32 оригинального файла;
- zcsize = размер сжатого файла, равен оригинальному размеру;
- zosize = размер оригинального файла;
- znsiz = длина имени файла;
- zeattr = 20h - атрибут файла (архивный)
- zfname = имя файла;
UPD: При написании статьи я столкнулся с некоторыми JAR-архивами, у которых в локальном заголовке размеры файла и CRC32 были просто обнулены, в результате этого невозможно посчитать размер блока файла. Видимо программы, работающие с ними, ориентируются на данные центральной директории. Попытка внедриться в такие архивы описанным выше способом приведет к их безвозвратному повреждению, поэтому при этом способе внедрения такие файлы надо пропускать. Я разобрался со способом внедрения в архив через центральную директорию. Здесь алгоритм немного другой. Сперва надо найти End of central directory record. Она имеет фиксированный размер, но архив также может содержать комментарий, который записан ПОСЛЕ End of central directory record в конце файла, а размер комментария записан в нее же. Так что я не придумал ничего лучше, чем просто сканировать файл с конца по сигнатуре 0x06054b50, благо что максимальный размер комментария не может превышать значения WORD, то есть 65535 байт. Из найденной структуры мы извлекаем данные о местоположении центральной директории относительно начала файла. После этого начинаем по очереди перебирать записи из нее, а именно смещение упакованных файлов и их размеры, записывая их в новый архив. Здесь важно учитывать следующий момент: если в локальном заголовке файла в поле zlflags установлен 3-й бит, то информация о контрольной сумме, а также о размерах упакованного и оригинального файла содержатся в 16-байтном блоке data descriptor, который записан сразу же после упакованного файла и начинается с сигнатуры 0x08074b50. Причем в архиве могут одновременно быть файлы как с заполненными локальными заголовками, так и с неполными заголовками + data descriptor. Таким образом, мы через центральную директорию находим файл в архиве, записываем его в новый архив, проверяем наличие data descriptor, в случае его наличия также переносим в новый архив сразу после файла. Когда будут перенесены все файлы, записываем наш файл. После этого записываем оригинальную центральную директорию, данные нашего файла из центральной директории и скорректированную End of central directory record с файловым комментарием (при его наличии). Смотрите исходники, там понятнее. Таким образом можно корректно прицепляться к обычным архивам, JAR-архивам и даже документам OpenOffice (odt, ods) и MS Office 2010 (docx, xlsx), которые по сути также являются ZIP-архивами. Кроме того, при обработке центральной директории можно внедряться не только в конец, но и в середину архива, а также подменять собой уже имеющиеся в архиве файлы. Но это уже переходит границу добрых дел, поэтому останется только идеей.
Не забывайте обрабатывать многотомные и защищенные архивы (данные о шифровании хранятся в отдельном блоке). Еще есть 64-битные версии ZIP-архивов, они имеют несколько другой формат заголовков и не могут быть обработаны как обычные ZIP-архивы. Повторюсь, что подробное описание всех служебных заголовков и форматов можно найти в официальной документации.
К сожалению, современные архиваторы типа 7zip, WinRK, KuaiZip, WinArchiver и WinUHA имеют более сложный внутренний формат, там даже имена файлов подвергаются сжатию или хранятся в отдельном блоке, а KGB Archiver вообще не подразумевает возможность модификации своих архивов. Так что для них такие фокусы не прокатывают. Информацию по внедрению в более редкие виды архивов вы можете найти во второй, третьей и четвертой частях статьи.
В приложении примеры программ с исходными текстами, демонстрирующие все три описанных способа записи в архивы. При каждом запуске программа дописывает себя к архиву "example" соответствующего формата, выбирая случайное имя исполняемого файла.
Вариант внедрения в ZIP-архив с разбором структуры через центральную директорию. В архиве также приложены три примера разных файлов.
Пример программы с исходным текстом (FASM)
Store.to.ZIP.without.Archiver.Demo.Version.2.zip (63,091 bytes)
Store.to.ZIP.without.Archiver.Demo.Version.2.zip (63,091 bytes)
Просмотров: 17167 | Комментариев: 12
Метки: Assembler, архиваторы
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(20.11.2013 в 00:08):
Jupiter, видел, читал. Осталось найти время на статью.
Jupiter
(19.11.2013 в 23:54):
Формат RAR обновлён до версии 5.0
Заголовок архива теперь увеличен на 1 байт.
Посмотри сам, всё очень подробно описано:
http://www.rarlab.com/technote.htm#rarsign
Заголовок архива теперь увеличен на 1 байт.
Посмотри сам, всё очень подробно описано:
http://www.rarlab.com/technote.htm#rarsign
ManHunter
(26.02.2013 в 23:15):
Прикрутить можно что угодно, но какой смысл использовать кучу костылей, чтобы добавить чуть-чуть комфорта для работы с архиватором? Поэтому я и говорю, что это религиозный вопрос. Мне не нравится - я не использую. Кому-то нравится - он использует. Всё очень просто.
UNDYING
(26.02.2013 в 22:37):
>>Или если бы любая винда отрабатывала его архивы как папки.
Powershell(v3):
Read-Archive -Format SevenZip "path_to_file"
и не только 7z, но и gzip, iso и др.
>>Или если бы в Total Commander можно было переименовывать файлы прямо в архиве, как это делается в ZIP.
В архивах как бы хранят информацию - извлекли, изменили, перепаковали. Ну и "тормознутость" 7-zip определяется опциями сжатия и/или "скоростью" ПК. На домашнем ПК или ноутбуке смысла в 7-zip нет, а на "бэкапящем" серванте (в виде tar.lzma или tar.xz) - вполне, например, в виде "снапшотов" ZFS, пожатых оным.
>>Если бы в нем была такая же удобная панель для работы и с возможностью просмотреть файл как в WinRAR.
В качестве программы для просмотра можно прикрутить тот же Lister от TotalCommander или аналог, или вообще "враппер написать", по F3/F4 - смотрим/редактируем.
Powershell(v3):
Read-Archive -Format SevenZip "path_to_file"
и не только 7z, но и gzip, iso и др.
>>Или если бы в Total Commander можно было переименовывать файлы прямо в архиве, как это делается в ZIP.
В архивах как бы хранят информацию - извлекли, изменили, перепаковали. Ну и "тормознутость" 7-zip определяется опциями сжатия и/или "скоростью" ПК. На домашнем ПК или ноутбуке смысла в 7-zip нет, а на "бэкапящем" серванте (в виде tar.lzma или tar.xz) - вполне, например, в виде "снапшотов" ZFS, пожатых оным.
>>Если бы в нем была такая же удобная панель для работы и с возможностью просмотреть файл как в WinRAR.
В качестве программы для просмотра можно прикрутить тот же Lister от TotalCommander или аналог, или вообще "враппер написать", по F3/F4 - смотрим/редактируем.
ManHunter
(26.02.2013 в 17:35):
Если бы в нем была такая же удобная панель для работы и с возможностью просмотреть файл как в WinRAR, то может быть я бы его вытерпел. Или если бы любая винда отрабатывала его архивы как папки. Или если бы в Total Commander можно было переименовывать файлы прямо в архиве, как это делается в ZIP. А пока что нафиг это тормозное убожество.
Руслан
(26.02.2013 в 17:11):
А почему интересно не пользуешься 7z?
SVS
(25.02.2013 в 20:37):
Jadavin, там, поди, в файлах всё нулями было забито, вот и распаковалось до таких размеров.
ManHunter
(25.11.2011 в 20:48):
Ну так надо было попробовать этим же 7z с максимальными настройками компрессии, непрерывным архивом, словарем и прочими наворотами. Я параметры навскидку не скажу, т.к. не пользуюсь 7z по религиозным соображениям.
Jadavin
(25.11.2011 в 20:35):
Секрет остаётся секретом.
Jadavin
(02.09.2011 в 09:59):
Немного не в тему, но про архивацию. Несколько лет назад мне прислали cd-диск с софтом, заархивированным в 7-Zip. На cd помещалось 700 мб, естесственно. После разархивации весь софт весил почти 2 Гб.
Вопрос: каким образом можно так заархивировать файлы? Я пробовал разными архиваторами, но ничего не получилось. В чём секрет?
Вопрос: каким образом можно так заархивировать файлы? Я пробовал разными архиваторами, но ничего не получилось. В чём секрет?
ManHunter
(22.08.2011 в 11:56):
Добавил описание и пример внедрения в ZIP-архивы через центральную директорию.
Игорь
(14.08.2011 в 19:18):
Спасибо, статья помогла разобраться с заголовками архивов.
Добавить комментарий
Заполните форму для добавления комментария