Статья Упаковка бинарных ресурсов в батник ("CBI method" by Dragokas)

Тема в разделе "Пакетные файлы CMD, BAT", создана пользователем Dragokas, 10 авг 2014.

  1. Dragokas
    Оффлайн

    Dragokas Very kind Developer Команда форума Супер-Модератор Разработчик Клуб переводчиков

    Сообщения:
    4.476
    Симпатии:
    4.305
    Cabinet's Batch inline. "CBI method" by Dragokas

    Метод предоставляет возможность встраивать бинарную информацию (любые файлы)
    в качестве ресурсов BAT-файла, получая на выходе 1 комбинированный файл
    с расширением BAT или CMD.
    Батник затем сможет распаковать свой ресурс при необходимости его использования.

    В статье представлены описание метода и программная реализация.

    Описание метода

    Оригинальная идея принадлежит человеку с ником: Somebody.

    Она звучала так:
    Данный способ не подходит для случаев упаковки бинарных ресурсов различного содержания,
    т.к. некоторые комбинации символов для формата CMD являются недоспустимым и
    приводят к его "падению" прежде, чем BAT-файл начнет исполнение своей части кода
    из конца Cabinet архива.

    К счастью структура формат CAB предоставляет возможность резервирования произвольного
    количества байт (начиная со смещения 0x28) для хранения цифровой подписи.

    Эту область мы задействуем под свой нужны, расположив в ней основной код
    и команду распаковки.

    Техническая часть

    Код батника будет содержать команду для распаковки себя же:
    Код (Text):

    @extrac32.exe /E /Y /L "папка, куда распаковуем" "%~f0"
     
    /e - распаковать все
    /y - без подтверждения при замене

    Структура формата Cabinet архива описана в статье базы знаний: MSKB417343

    Она разделена на 4 блока:

    • CFHEADER
    • CFFOLDER
    • CFFILE
    • CFDATA

    Описание первого блока:

    Код (C++):

    // Размер | имя параметра | смещене | описание параметра

    struct CFHEADER
    {
      u1  signature[4]  // 0x00 - сигнатура архива "MSCF"
      u4  reserved1     // 0x04 - зарезервировано
      u4  cbCabinet     // 0x08 - размер архива в байтах
      u4  reserved2     // 0x0С - зарезервировано
      u4  coffFiles     // 0x10 - смещение первого вхождения блока CFFILE
      u4  reserved3     // 0x14 - зарезервировано

      u1  versionMinor  // 0x18 - версия формата архива (minor)
      u1  versionMajor  // 0x19 - версия формата архива (major)

      u2  cFolders      // 0x1A - количество блоков CFFOLDER
      u2  cFiles        // 0x1C - количество блоков CFFILE
      u2  flags         /* 0x1E - флаги наличия или отсутствия зарезервированной областей
                                  abReserve[], szCabinetPrev[], szDiskPrev[], szCabinetNext[], szDiskNext[]   */

      u2  setID         // 0x20 - должен быть одинаковый для всех частей составного архива
      u2  iCabinet;     // 0x22 - порядковий номер этого файла в составном архиве
      u2  cbCFHeader;   // 0x24 - (опционально) размер зарезервированной под ЭЦП области архива

      u1  cbCFFolder;       // 0x26 - (опционально) размер зарезервированной области каждой папки
      u1  cbCFData;         // 0x27 - (опционально) размер зарезервированной области каждого блока данных
      u1  abReserve[];      // 0x28 - (опционально) область, зарезервированная под ЭЦП (сюда будем записывать батник)
      u1  szCabinetPrev[];  // (опционально) имя предыдущего файла CAB
      u1  szDiskPrev[];     // (опционально) имя предыдущего диска
      u1  szCabinetNext[];  // (опционально) имя следующего файла CAB
      u1  szDiskNext[];     // (опционально) имя следующего диска
    };
     
    Чтобы создать скомбинированный файл, сперва нужен сам архив.
    Наиболее родной способ - воспользоваться встроенной в систему утилитой makecab.
    Синтаксис у нее непростой, поэтому лучше воспользоваться готовой реализацией:

    Код (DOS):

    @echo off
    SetLocal
    :: Имя для CAB-архива
    set "CabinetName=test.cab"
    :: Путь к папке, которую упаковуем
    set src=C:\Users\Alex\Desktop\gardening\gardening

    chcp 1251 >NUL
    For /F "delims=" %%a in ('dir /B /a-d "%src%\*.*"') do echo "%src%\%%a">>"%Temp%\list.txt"
    chcp 866 >NUL

    set .=
    set .=%.% /D FolderSizeThreshold=50000000
    set .=%.% /D DestinationDir=.
    set .=%.% /D MaxDiskSize=0
    set .=%.% /D cabinet=on
    set .=%.% /D compression=on
    set .=%.% /D CabinetDir=\.
    set .=%.% /D CabinetNameTemplate="%CabinetName%"
    set .=%.% /D CabinetName="%CabinetName%"
    set .=%.% /D InfFileName=NUL
    set .=%.% /D RptFileName=NUL
    set .=%.% /D CompressionMemory=21
    set .=%.% /D DiskDirectoryTemplate=.
    set .=%.% /D UniqueFiles=Off
    set .=%.% /D CompressionType=LZX

    makecab %.% /V1 /F "%Temp%\list.txt"

    del /F "%Temp%\list.txt"
    pause
     

    Тем не менее, у Microsoft есть и другая утилита из состава Resource Kit
    под названием CabArc.

    Создать архив CAB можно такой простой командой:
    Код (Text):

    cabarc N test.cab *.*
     
    Упакует все файлы в текущей папке в архив test.cab

    Чтобы вставить код Batch в такой архив, нам нужно зарезервировать область
    соответствующего размера, и здесь Cabarc будет как нельзя кстати.

    Синтаксис:
    Код (Text):

    CABARC [опции] команда cabfile [@список] [файлы] [папка назначения\]
     
    Основных каманды всего три:

    L - просмотр
    X - разархивировать
    N - архивировать

    Код (Text):

    Опции:
      -c   подтверждать операции с файлами
      -o   во время распаковки не запрашивать подтверждения при замене
      -m   алгоритм сжатия [LZX:<15..21>|MSZIP|NONE], (по-умолчанию - MSZIP)
      -p   сохранять пути файлов (абсолютные пути не поддерживаются)
      -P   урезать указанный префикс из файлов при добавлении в архив
      -r   рекурсивно с подкаталогами при добавлени в архив (см. также -p)
      -s   зарезервировать место под цифровую подпись (например, -s 6144 резервирует 6 КБ.)
      -i   Устанавливает ID архива при создании (по-умолчанию - 0)
      -d   установить размер частей (по-умолчанию, размер не определен, т.е. создается единый архив)
     

    Ключ -s позволяет установить произвольный размер зарезервированного под ЭЦП блока
    (от 1 до 65535 байт (0xFFFF)).

    Теперь смотрим, какой размер у батника, к которому мы хотим добавить ресурс.
    Пусть будет 250 байт (0xFA).

    Прибавляем 6 байт (ниже узнаете зачем).
    250 + 6 = 256 (0x100).

    Создаем архив с резервной областью в 256 байт:

    Код (Text):

    cabarc -s 256 N test.cab *.*
     
    Принцип слияния кода батника с архивом такой:

    Берем смещение архива 0x28 (блок abReserve[]).
    Добавляем туда 2 переноса строки (0x0D, 0x0A, 0x0D, 0x0A) - 4 байта.

    Далее вставляем сам код и еще + перенос строки (0x0D, 0x0A) - 2 байта.

    4 + 2 = 6 байт, о которых мы говорили выше.

    В коде батника должна быть предусмотрена команда распаковки архива из себя
    и очистка сообщения о неверной команде (когда батник попытается исполнить хидер CAB-архива):

    Код (Text):

    @cls & @extrac32.exe /E /Y /L .\ "%~f0"
     
    Комбинированный CBI-файл готов.
    Нужно переименовать его расширение в CMD.

    Правка упакованного батника напрямую

    Также можно выделить резервную область путем правки определенных байт CAB-архива:

    1. В структуре CFHEADER:

    Код (C++):

      u4  cbCabinet     // 0x08 - размер архива в байтах
      u4  coffFiles     // 0x10 - смещение первого вхождения блока CFFILE
      u2  flags         // 0x1E - флаги наличия или отсутствия зарезервированной областей
      u2  cbCFHeader;   // 0x24 - (опционально) размер зарезервированной под ЭЦП области архива
     
    flags должен содержать константу:
    Код (C++):

    #define cfhdrRESERVE_PRESENT    0x0004
     
    cbCFHeader - для архива с флагом = 0 (нет резервных областей) имеет смещение 0x2C.

    2. В каждой из структур CFFOLDER:

    Код (C++):

    u4  coffCabStart;   // смещение первого вхождения блока CFDATA для этой папки
     
    Программная реализация

    В дополнение к статье готовая программа для прикрепления к батнику ресурса из указанных файлов и/или каталогов.

    cabpacker.png
     

    Вложения:

    akok и Kиpилл нравится это.

Поделиться этой страницей