1

Тема: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Здравствуйте, товарищи.

Имеется задача: из bat-файла, включающего и команды и прилепленный бинарный файл, выгрузить отдельно этот самый бинарный файл.
Если более детально, имеется прилепленный к батнику cab-архив, который нужно сохранить как отдельный файл.

Ранее задачу решал связкой CMD+JS, но это слишком громоздко, хотелось бы без сторонних средств или преобразования файла в "текстовый hex", ибо увеличение размера в 2 раза.

В идеале "выгружающая" часть батника должна занимать 200-300 байт, если более - смысл теряется.

Есть ли способы решения ?

2

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

А почему Вы не хотите воспользоваться гибридами? Только я предлагаю использовать VBS - он лучше (в отличие от JS) справляется с бинарными данными. Потом преобразуйте VBS в гибрид CMD+VBS, как наприсано в CMD/BAT + JS/VBS/PS1/WSF/HTA/PL/PY/BASH: Два в одном. Потом


copy /b HEADER.vbs.bat+ARCHIVE.cab ARCHIVE.bat
( 2 * b ) || ! ( 2 * b )

3 (изменено: Himmler, 2018-02-17 11:39:50)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Связка CMD+JS меня не устраивает из-за своей громоздкости (в моём случае громоздкость - это сотни байт).
Нужно учесть некоторые моменты, типа русских символов в пути и разрядность ОС. У меня вот получилось более 300 байт. А при таком размере в моём случае смысл уже теряется.

Вот пример кода. Подсократить можно, но не радикально.

set src=%~F0
set src=%src:\=\\%

set cscript=cscript
if exist %SystemRoot%\syswow64 (set cscript=%SystemRoot%\syswow64\cscript)

echo FSO=new ActiveXObject('Scripting.FileSystemObject')>>%temp%\src.js
chcp 1251
echo src=FSO.GetFile('%src%').OpenAsTextStream(1).Read(65536)>>%temp%\src.js
chcp 866
echo FSO.CreateTextFile('%temp:\=\\%\\bin.cab').Write(src.slice(src.lastIndexOf('bin=')+4))>>%temp%\src.js
%cscript% %temp%\src.js>nul
del /f %temp%\src.js
cd/d %temp%

4 (изменено: Rumata, 2018-02-17 13:20:37)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Скрипт по приведенной ссылке делает достаточно компактные гибриды.

Но VBS не будет работать - прервется на стадии компиляции

( 2 * b ) || ! ( 2 * b )

5 (изменено: Himmler, 2018-02-18 16:47:47)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Не увидел там ничего, что дало бы возможность ужать "выгружающую" часть хотя бы до 200 байт.
Один только кусок

FSO.CreateTextFile('%temp:\=\\%\\bin.cab').Write(src.slice(src.lastIndexOf('bin=')+4))>>%temp%\src.js

это уже 100 байт. А выкинуть его вряд ли получится независимо от языка.

Поэтому и написал, что такие связки слишком громоздки, и хотелось бы узнать о других способах.
Например, если бы дефолтный виндовый expand прожёвывал бы cab-архивы также, как это делает WinRar (а именно находил бы сигнатуру начала тела архива независимо от того, что находится в начале подсунутого файла)

6 (изменено: Rumata, 2018-02-18 17:35:47)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Расскажите
1. почему ограничение в 300 байт критично
2. почему batch, а не более удобные средства (те же self-extracted archives)
3. и почему такое желание впихнуть невпихуемое

Не сочтите все сказанное за сарказм. Я тоже люблю безумное программирование в качестве гимнастики для ума, но ведь в практических целях есть более удобные средства.

( 2 * b ) || ! ( 2 * b )

7

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Упакованный даже самым крутым пакером exe-шник (пробовал с десяток разных, в том числе UPX, ASPask и др.) весит на 300 байт больше, чем тот же exe-шник, добавленный в cab-архив.
В Винде есть вмазанный expand. Вот и возникла идея ужать сильнее, чем PE-пакеры (ибо в них распаковывающая часть на таких малых объёмах - порядка 2-3 килобайт - даёт порядочные накладные расходы).
Размер exe-шника до сжатия - 4,5 Кб.
После сжатия лучшим пакером - 2,94 Кб.
Внутри архива - 2,63 Кб.

8

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Не скажу, что Ваша затея мне очень нравится. Скажем так, это была гимнастика для ума, мне было интересно "размяться".

header.bat (3 строки)


@set "W=%TEMP%\%RANDOM%"
@echo:o="%~dpn0":z=214:WScript.StdIn.Skip(z):s=WScript.StdIn.Read(%~z0-z):WScript.StdOut.Write(s)>"%W%"
@cscript //nologo //e:vbscript "%W%" < "%~dpnx0" >"%~dpn0"&del /q "%W%"&@goto:EOF

Описание
1. Склеиваем header.bat и какой-то другой файл в еще один файл

copy /y /b header.bat+FILENAME.1.EXT FILENAME.2.EXT.bat

2. Запускаем полученный скрипт - FILENAME.2.EXT.bat производит файл FILENAME.2.EXT

FILENAME.2.EXT.bat

3. Файлы FILENAME.1.EXT (оригинальный) и FILENAME.2.EXT (полученный) сравнивались.

Имена файла могли быть как в латинице так и в кириллице.
Не удалось автоматизировать автоматическое определение размера пролога.
И где-то там можно съэкономить 5-10 байт :-)

( 2 * b ) || ! ( 2 * b )

9

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

А вот так получше будет:
-- увеличили эффективность
-- включили локальность переменных
-- установка размера пролога вынесена в самое начало файла

По-прежнему, файл FILENAME.EXT.bat производит файл FILENAME.EXT


@setlocal&set "W=%TEMP%\%RANDOM%.vbs"&set Z=210
@echo:z=%Z%:WScript.StdIn.Skip(z):s=WScript.StdIn.Read(%~z0-z):WScript.StdOut.Write(s)>"%W%"
@cscript //nologo "%W%"<"%~dpnx0">"%~dpn0"&del /q "%W%"&@goto:EOF

Можно дать более звучное чем "Z заглавная" имя переменной, задающей размер пролога.

( 2 * b ) || ! ( 2 * b )

10

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Как вариант можно CAB поместить в ZIP (или сразу EXE сжать в ZIP), добавить к ZIP пролог и сохранить с расширением командного файла. Пролог должен делать следующее:
1) создавать временную копию всего файла с расширением ZIP;
2) создавать временный сценарий WSH извлекающий из копии с расширением ZIP её содержимое с помощью методов Shell.Application.

Вот только сомневаюсь, что получится короче. Но отпадает необходимость знать или вычислять длину пролога.

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

11 (изменено: Rumata, 2018-02-20 12:23:33)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Если забить на те маленькие плюсы, провозглашенные в моем сообщении #9, и сосредоточиться только на минимизации (хотя там тоже есть место этому), то мы придем к пределу в 173 байта. Дальше "не квантуется".


@set "W=%TEMP%\%~n0.vbs"
@echo:Z=173:WScript.StdIn.Skip Z:WScript.StdOut.Write WScript.StdIn.Read(%~z0-Z)>"%W%"
@cscript//nologo "%W%"<"%~f0">"%~dpn0"&del/q "%W%"&exit/b
( 2 * b ) || ! ( 2 * b )

12 (изменено: Rumata, 2018-02-20 12:24:30)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Продолжаю играть в оптимизацию:
1. локализация переменных (люблю setlocal и не люблю засорять глобальную область видимости)
2. отпала необходимость в определении размера пролога (используются регулярные выражения для вырезания 3 строк пролога из входного потока)
3. минимизирован код (естественно ценой ухудшения читабельности)
4. учтен случай юниксовых концов строк в прологе (маловероятно, но возможно)


@setlocal&set "W=%TEMP%\%~n0.vbs"
@echo:Set R=New RegExp:R.Pattern="(.+\r?\n){3}":WScript.StdOut.Write R.Replace(WScript.StdIn.Read(%~z0),E)>"%W%"
@cscript//nologo "%W%"<"%~f0">"%~dpn0"&del/q "%W%"&exit/b

208 байт. С такими играми -- меньше некуда.

( 2 * b ) || ! ( 2 * b )

13 (изменено: Himmler, 2018-02-20 20:48:43)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Я, конечно, понимал, что результат будет извращением, но чтобы настолько...
А вообще, например в коде

@set "W=%TEMP%\%~n0.vbs"
@echo:Z=173:WScript.StdIn.Skip Z:WScript.StdOut.Write WScript.StdIn.Read(%~z0-Z)>"%W%"
@cscript//nologo "%W%"<"%~f0">"%~dpn0"&del/q "%W%"&exit/b

Можно добавить что-то вроде

set s=WScript.Std

И получить


@set "W=%TEMP%\%~n0.vbs"
@echo:Z=173:%s%In.Skip Z:%s%Out.Write %s%In.Read(%~z0-Z)>"%W%"
@cscript//nologo "%W%"<"%~f0">"%~dpn0"&del/q "%W%"&exit/b

И, кстати, насчёт Shell.Application идея тоже неплохая - возможно, zip с прицепленными вначале "левыми" данными прожуётся нормально.

А ещё если удастся из ntdll вызвать RtlDecompressBuffer, то можно подсунуть прямо из скрипта сырой предварительно пожатый бинарный поток и получить на выходе разжатый файл.

14

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Himmler пишет:

Я, конечно, понимал, что результат будет извращением, но чтобы настолько..

Кстати, да. Так будет еще короче - 167! Кажется, дальше некуда.


@set S=WScript.Std&set "W=%TEMP%\%~n0.vbs"
@echo:Z=167:%S%In.Skip Z:%S%Out.Write %S%In.Read(%~z0-Z)>"%W%"
@cscript//nologo "%W%"<"%~f0">"%~dpn0"&del/q "%W%"&exit/b
( 2 * b ) || ! ( 2 * b )

15

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

А вот извольте-ка посмотреть

109 байт - выгружатор


cd/d %tmp%&echo WScript.StdOut.Write(WScript.StdIn.Read(9999).slice(109))>t.js&cscript//nologo t.js<"%~f0">t

136 байт - выгружатор+разжиматор+запускатор


cd/d %tmp%&echo WScript.StdOut.Write(WScript.StdIn.Read(9999).slice(136))>t.js&cscript//nologo t.js<"%~f0">t&expand t 1.exe&start 1.exe

В конце строки - символ 0xA (одного его достаточно)
Файлы до 9999 байт (именно на таких нужна экономия в 100-200 байт).

Да, пришлось выкинуть кусок с удалением временных файлов, плюс все имена теперь фиксированные.


Добавление в ZIP вместо CAB в моём случае даёт ещё примерно 120 байт выигрыша, но распаковщик из ZIP через Shell.Application, скорее всего, съест всё преимущество.

16 (изменено: Rumata, 2018-02-21 21:51:49)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Ага! Вы ухватили идею. Но!
1. %tmp% надо в кавычки, потому что могут быть пробелы и другие символы.
2. отсутствует код выхода exit/b
3. В командной строке перенесет в каталог %tmp%, что может быть нежелательно.
4. Неудобно - результирующий файл не имеет осмысленное имя.

Интересная у Вас трава. Не отпускает. Но я бы остановился на размере в 131 байт:


@pushd "%TMP%"&echo:W=WScript;W.StdOut.Write(W.StdIn.Read(%~z0).slice(131))>t.js&cscript//nologo t.js<"%~f0">"%~dpn0"&popd&exit/b

А это его создавалка. Способ использования в подсказке:


@echo off

if "%~1" == "" (
	echo:Usage %~n0 FILE [/C]
	echo:/C means to create a copy
	goto :EOF
)

setlocal
set "ZC="
if /i "%~2" == "/c" set "ZC=-copy"
set "ZI=%~f1"
set "ZO=%~dpn1%ZC%%~x1.bat"
call :create-prolog "000" >"%ZO%"
for %%f in ( "%ZO%" ) do call :create-prolog %%~zf >"%ZO%"
copy /y /b "%ZO%"+"%ZI%" "%ZO%"
goto :EOF

:create-prolog
echo:@pushd "%%TMP%%"^&echo:W=WScript;W.StdOut.Write(W.StdIn.Read(%%~z0).slice(%~1))^>t.js^&cscript//nologo t.js^<"%%~f0"^>"%%~dpn0"^&popd^&exit/b
goto :EOF
( 2 * b ) || ! ( 2 * b )

17 (изменено: Himmler, 2018-02-23 01:12:25)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Кое-что поправил, кое-что убрал, в итоге остановился на этом:
-Убрал имя js-файла (оно необязательно, а вот расширение нужно, для exe - то же самое).
-%tmp% действительно нужно в кавычки.
-Размер теперь неограничен.

109 байт - выгружатор


cd/d "%tmp%"&echo WScript.StdOut.Write(WScript.StdIn.Read(%~z0).slice(109))>.js&cscript//nologo .js<"%~f0">t

134 байта - выгружатор+разжиматор+запускатор


cd/d "%tmp%"&echo WScript.StdOut.Write(WScript.StdIn.Read(%~z0).slice(134))>.js&cscript//nologo .js<"%~f0">t&expand t .exe&start .exe

Кроме того, обнаружил, что создание CAB архива по алгоритму LZX позволяет вплотную приблизиться к уровню сжатия 7z и zip (разница в моём случае около 40 байт).

В итоге я выиграл у лучшего пакера больше 200 байт (получилось 2,72 Кб), а это отличный результат.
Очень благодарен товарищу Rumata за идею с StdIn/StdOut.

18

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Himmler & Rumata, господа, очень захотелось "покурить", то что курили вы, но к своему стыду, не смог понять как этим воспользоваться.
Если не очень затруднительно, можете мануальчик набросать ?

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

19

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Xameleon, у меня немного другое видение проблемы. Оно чуть отличается от того как видит ее Himmler.

Есть некие данные (бинарный файл), который надо "завернуть" в пакетный файл, который при запуске вытаскивает бинарные данные и при необходимости распаковывает их. По всей видимости, на системах товарища Himmler этого достаточно. Есть важное ограничение - размер пакетного распаковщика не должна превышать 300 байт (я его плохо понял, но поиграть при таких жестких условиях было интересно). Мне кажется, есть некоторая проблема распространения таких файлов - многие почтовые серверы "режут" вложения с расширением ".cmd" или ".bat".

Есть интересное ответвление или дальнейшее развитие этой идеи: забыть о существующем ограничении (оно не требуется в начале, но о нем можно подумать позже) и создать эмулятор tar-файлов.

Касательно моих изысков. Мое предыдущее сообщение содержит скрипт, который при запуске подхватывает имя некоторого файла, копирует его в другой, добавляя в начало пролог. При запуске результирующий файл вытаскивает исходный файл.

( 2 * b ) || ! ( 2 * b )

20

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Rumata, так общую идею то я уловил. Я не понял как использовать bat файлы.
1) Создал a.bat c содержимым:


cd/d "%tmp%"&echo WScript.StdOut.Write(WScript.StdIn.Read(%~z0).slice(109))>.js&cscript//nologo .js<"%~f0">t

2) Создал b.bat c содержимым:


cd/d "%tmp%"&echo WScript.StdOut.Write(WScript.StdIn.Read(%~z0).slice(134))>.js&cscript//nologo .js<"%~f0">t&expand t .exe&start .exe

А дальше как планируется это использовать ?

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

21 (изменено: Himmler, 2018-03-01 01:16:33)

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

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

copy/b a.bat+123.bin drop.bat

в результате даст батник, запуск которого приводит к созданию отдельно лежащего файла, содержимое которого полностью совпадает с 123.bin.

Либо же для добавленного в CAB-архив exe-шника (123.exe лежит внутри 123.cab)

copy/b b.bat+123.cab drop.bat

в результате даст батник, запуск которого приводит к созданию отдельно лежащего файла, содержимое которого полностью совпадает с 123.exe c последующим его запуском

22

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Himmler, хм... Вроде бы сделал как Вы сказали. Сложил в один каталог всё, что описано.

В итоге в папке имею:
a.bat (с тем самым содержимым, как и было описано выше)
b.bat (с тем самым содержимым, как и было описано выше)
123.exe (переименованный для чистоты эксперимента notepad.exe)
test.bat с содержимым:

copy/b a.bat+123.exe drop.bat

Запускаю test.bat.
Вижу вот такой вывод:

a.bat
123.exe
Скопировано файлов:         1.

Казалось бы всё отлично.

На выхлопе получаю drop.bat со включением кода распаковки:

+ открыть спойлер

cd/d "%tmp%"&echo WScript.StdOut.Write(WScript.StdIn.Read(%~z0).slice(109))>.js&cscript//nologo .js<"%~f0">tMZђ       яя  ё       @                                   ш   є ґ Н!ёLН!This program cannot be run in DOS mode.

$       ?—ЗЈ{ц©р{ц©р{ц©рrЋ:рeц©р”Єсxц©р”сhц©р”¬сц©р”Ёс`ц©р{цЁрmч©р”Ўсfц©р”Vрzц©р”«сzц©рRich{ц©р                        PE  d† «ОД         р " 
ђ  R     а“       @        
   
   
            {¬  `Б                                          Шф D   ` ањ  P Ь               pг T                           PҐ            P¦ x                           .text   ОЏ     ђ                   `.rdata  v      x   ”             @  @.data   -                       @  А.pdata  Ь   P 
                @  @.rsrc   ањ  `  ћ  "             @  @.reloc           А             @  B                                                                                                                                                                                                                                                                                HѓмHLЌB‹КHЌ·У E3Й+ИЉЇЧ ¶H‹T$xБа БH‰T$(‰D$0A· ‰D$4I‹@IѓА
H‰D$8H‹J H‰H‹@ ·L‰B‰JЗB    A· E3А‰B‹D$pЗB   HЌT$0H‹
& ‰D$ я– HѓДHГМММММММ@SHѓм@L‹”$Ђ   H‹ЩM…Тt\L‹\$p‹В…Тt#ѓшu"A¶Иё   яБM‰JE„АM‰ZEБA‰лAѓ"
.....

Но при запуске drop.bat моргнуло окно консоли и тишина. Решил посмотреть что этот зверь пишет в консоль...

Запустил cmd, перешёл в каталог где лежат файлы через cd

Стартанул...

+ открыть спойлер

C:\test>cd/d "C:\Users\User\AppData\Local\Temp"  & echo WScript.StdOut.Write(WScript.StdIn.Read(246892).slice(109)) 1>.js  & cscript//nologo .js 0<"C:\test\drop.bat" 1>tMZР$$4A╕Ь
Синтаксическая ошибка в имени файла, имени папки или метке тома.

ЧЯДНТ ? O_o

P.S У меня Win 10 Pro, если что.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

23

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Xameleon пишет:

ЧЯДНТ ?

Движок форума убирает переводы строки в конце кода. А он нужен для отделения пролога от прилепленного содержимого.

24

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Xameleon, попробуйте создавалку из сообщения №16

( 2 * b ) || ! ( 2 * b )

25

Re: CMD/BAT: Хранение файлов внутри bat-файла и их выгрузка

Товарищи, в очередной раз убеждаюсь, что всё уже было придумано до нас.
Ещё в начале 2000-х этот трюк использовался в демосценах 1к и 4к, сейчас он уступил место оптимизирующему линкеру/пакеру crinkler (которым лично я в конце концов и воспользовался вместо изначальной задумки).
Так вот трюк в том, что внутри CAB-файла c 60-го байта идёт имя помещённого внутрь файла. Его-то и можно заменить на последовательность batch-команд, перед которыми идут байты 0A0D0A
Длину имени добавляемого файла следует выбирать равной суммарной длине batch-команд + 3 байта на 0A0D0A


Как это работает:
-Требуемый файл помещается в CAB-архив (для лучшего сжатия применется алгоритм LZX)
-С 60-го байта пишется последовательность 0A0D0A и сами команды, например expand %0 1.txt&start 1.txt
-Архив переименовывается в .bat и запускается

После запуска парсер командной строки находит заголовок CAB-архива MSCF, идущую за ним последовательность сырых байт, и упирается в написанный нами 0A0D0A.
Для него это признак конца команды, он пытается её исполнить и говорит, что не знает что такое "MSCF", затем переходит к следующей команде.
А там лежат expand %0 1.txt&start 1.txt, которые он и выполняет.
Далее для приличия можно дописать exit, распаковку во временную папку, удаление распакованного и прочее, но это уже детали.