#03 — И целого байта мало… | 2B or not 2B

    Итак, друзья, 1-е апреля прошло, пора раскрывать карты, что же такое "2B or not 2B" на самом деле. Это совместный текст от автора работы jin_x и уже знакомого вам деда unbeliever

    image

    Обязательно скачайте архив с работой на Pouet и прочитайте вводную (первоапрельскую) статью, а так же комментарии к ней. Посмотрите первое видео с практической демонстрацией того, как работает код из «двух байт» на x86. И вот уже потом попробуйте осилить весь текст ниже.


    Да, 2B or not 2B — это действительно среда для запуска различных sizecoding-работ, очень простая и, пожалуй, самая маленькая из существующих. При этом она имеет свои требования и ограничения.

    Если кто-то ещё не уловил, тул 2b.com запускается из-под DOS (DOSBox, FreeDOS, MS-DOS) и осуществляет прыжок в область командной строки (по смещению $82* сегмента PSP), запуская на исполнение код, который передан в командной строке в двоичном виде. Собственно говоря, код этот вполне может иметь вид строки, которую можно набрать на клавиатуре (т.е. состоять из ASCII-символов с кодами от 33 до 126), но об этом немного позже.

    * шестнадцатеричные числа мы будем записывать в паскалевской нотации $XX, это удобно, и fasm позволяет это делать.

    Что важно знать?


    В-нулевых, в качестве компилятора мы рекомендуем использовать fasm, весь наш инструментарий написан именно под него.

    Во-первых, основной код может иметь максимальный размер 125 байт (таковы ограничения длины командной строки) и будет стартовать как обычная COM-программа, только со смещения $82, а не $100, как обычно. Сразу после основного кода будет автоматически добавлен символ возврата каретки (CR) с кодом 13 ($0D), а по адресу $100 будет находиться команда jmp short $82 ($EB, $80).

    Во-вторых, поскольку запуск предполагается из BAT-файла (ну или из интерпретатора командной строки), код не должен содержать некоторые символы. Прежде всего это символы перенаправления ввода-вывода ("<", ">" и "|"), а также символ подстановки параметров и переменных окружения ("%"). В некоторых системах (в том числе Windows, поддерживающих запуск DOS-программ из-под V86) специальное значение имеют ещё и символы "&", "^". Спец-символы с кодами до 32 поддерживаются не всеми DOS, а некоторые не поддерживаются никакими или почти никакими (особенно скудный набор имеет DOSBox), поэтому все эти символы исключаем тоже.

    В-третьих, стартовые значения всех регистров и флагов, такие же, как и при запуске COM-программы. В подавляющем большинстве DOS на старте будет: ax=bx=0 (почти всегда это так), cx=$FF, dx=cs=ds=es=ss, si=$100, di=sp=$FFFE (при достаточном количестве оперативной памяти), bp=$9XX (младший байт везде разный, но его старшая тетрада, то есть полубайт, обычно = 1), флаги cf=df=0. Использовать это или нет – дело ваше.

    Больше всего здесь смущает пункт «во-вторых», не так ли?
    Предположим, нам нужно написать:

       mov ah,0
       int $16
       cmp al,27
       je x

    А тут аж сразу 5 запретных символов: 0 в mov ah,0, $16 в int $16, $3C (символ "<") и 27 ($1B) в cmp al,27 и некоторое число с кодом < 32 в je x, если x находится где-то неподалёку дальше по коду.

    Что же делать? То, что можно заменить другими командами, заменяем:

    • вместо mov ah,0 пишем xor ah,ah или даже cbw (когда это возможно);
    • вместо cmp al,27 пишем not al + sub al,not 27 или xor al,not 27 + inc al, а ещё лучше (т.к. здесь нам нужно дождаться нажатия клавиши и сравнить полученный код с кодом клавиши ESC) – dec ah.

    С int $16 сложнее, но если подумать, то конструкцию xor ah,ah + int $16 можно заменить, например, на mov ah,$83 + ror ah,1 + int $21.

    Остаётся je $+10. Тут есть как минимум 2 пути: либо сделать прыжок назад (на достаточное расстояние), а оттуда – вперёд. Либо произвести замену байта в коде. Например, здесь можно написать z: je ($*2+3)-x, а где-то выше: not byte [si-($100-(z+1))].

    В итоге получим:

       not byte [si-($100-(z+1))]  ; замена 2-го байта (смещения прыжка) je при si=$100
       mov ah,$83
       rol ah,1  ; ah=7
       int $21  ; ожидаем нажатия клавиши, код будет в al
       not al
       sub al,not 27  ; cmp al,27
    z: je ($*2+3)-x  ; прыжок на метку x (после изменения второго байта)

    Альтернативные решения


    Конечно, в финальной intro на 100+ байт запретных символов может оказаться довольно много (например, 15-20 и даже больше), и каждый раз производить подобные манипуляции – занятие довольно муторное, к тому же, они нередко приводят к увеличению длины кода.

    Поэтому можно прибегнуть к шифрованию. Либо всего кода, либо отдельных мест. В примере 2b_life.asm мы шифруем весь код, добавляя к каждому байту значение $AC. После первого шифрования у нас осталось порядка 4-х запретных символов, которые мы смогли проработать заменой на другие команды. Конечно, выбор метода шифрования (add, sub, xor, not и пр.), а также ключа тоже требует времени, но это меньшее из всех зол. Код дешифровщика занимает всего 8 байт – это вполне приемлемо в данной ситуации. Шифрование же происходит автоматически с помощью директив repeat, load и store (т.е. мы получаем уже зашифрованный код).


    Отдельные места шифруются в примере 2b_note.asm. Здесь, опять же, с помощью repeat, load и store к некоторым байтам добавляется значение $3D, а список адресов этих байтов хранится отдельно (по 1 байту адреса на каждый такой байт). Итого мы шифруем 20 байт + 13 байт занимает дешифровщик. Да, первый способ был более экономичным :)

    В начале статьи мы обещали рассказать про код, который может иметь вид строки, состоящий из ASCII-символов с кодами от 33 до 126 (чтобы его можно было, например, без особых трудностей набрать на клавиатуре). Такое возможно, например, если зашифровать код с помощью 16-ричных символов или подобным образом. Да, это расточительно, но если шифровать методом BASE64, расход может оказаться ещё больше, ведь и декодер должен состоять только из таких символов.


    Инструменты


    Для удобства написания кода под "2B or not 2B" было создано 4 файла:

    • 2b.draft.asm – черновик для создания 128-байтовых интр в BAT-файле, в начало которого добавляются символы запуска программы 2b (однако тут шифрование, подмену или ещё какие-то трюки вам придётся выполнять самим, либо выдергивать из примеров). Читайте комменты, там всё довольно подробно описано.
    • 2b.draft44.asm – черновик для создания 44-байтовых интр, код которых будет полностью состоять из ASCII-символов с кодами от 33 до 126. Здесь каждый байт основного кода шифруется двумя псевдо-шестнадцатеричными символами: сначала младшая тетрада + «A» (диапазон букв «A»...«P»), затем старшая тетрада + «K» (диапазон букв от «K» до «Z»). Размер дешифровщика – 37 байт (+ 2 байта на команды pusha + popa, если вы хотите сохранить стартовые значения регистров). Итого на код остаётся: (125 — 37) / 2 = 44 байта (или 43, если добавить pusha + popa). Зато о шифровании можно вообще не париться :). Примеры, основанные на этом черновике – 2b_snow.asm и 2b_hello.asm
    • 2b.check.inc – include-файл для проверки кода. В случае наличия запретных символов, либо при превышении допустимого размера, при компиляции будет выведено сообщение (с указанием смещения в сегменте кода и смещения от начала BAT-файла).
    • 2b.debug.inc – include-файл для создания отладочных файлов (самостоятельно запускаемого COM-файла и BIN-файла с чистым кодом).

    Ну и к чему это всё?


    Набор существующих платформ, под которые пишутся интры, уже долгие годы практически не меняется, если не брать во внимание категорию Wild (АОН, паяльники, ватные палочки). Мы предлагаем вам… не то чтобы новую платформу, но по крайней мере, некоторое разнообразие, со своими ограничениями. Именно ограничения и их преодоление являются сутью демосцены как процесса. Было бы прикольно на ближайшем демопати узреть оком своим целый конкурс в рамках этой концепции, где разные авторы смогут попробовать свои силы и мастерство в «2B or not 2B compo» :)

    ---EOF---

    #FF — И целого байта мало… | Пилот)
    #00 — ИЦБМ… | Приглашение на Revision Online 2020
    #01 — ИЦБМ… | Какими бывают intro?
    #02 — ИЦБМ… | The Cross of Changes
    #03 — ИЦБМ… | 2B or not 2B
    #04 — ИЦБМ… | Берем БК за рога
    #05 — ИЦБМ… | Анимэ
    #06 — ИЦБМ… | Метеоризмы

    Развлекательный канал деда в Телеграм: teleg.run/bornded

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

    ТАК ПОБЕЖДАЛИ — ТАК ПОБЕДИМ!
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 14

      +3
      Истинное занятие для суровых челябинских тру-программистов, кмк. Обычно среди мужчин принято выбирать победителя по признаку «больше». Здесь тот редкий случай, когда «меньше» однозначно лучше.

      Чтобы два раза не вставать, напомню, мало-ли — пригодится: бинарные файлы из командной строки можно набрать следующим образом (цифры — на NumPad, ограничения — некоторые непечатаемые символы не вводятся, например, $00):

      copy con program.com
      Alt+205
      Alt+25
      Ctrl+Z

      За титаническую работу по впихиванию невпихуемого спасибо автору и группе.
      Удачи всем в этом благородном занятии. главное ничего не сломать и не создать нечаянно сингулярность

      ps. Когда у авторов управляющей программы спутника (не помню подробностей — чьего и когда) осталось что-то около 700 байт свободного ОЗУ, они впихнули программу стабилизации по звёздам. (Если кто-то помнит подробности, пожалуйста, поправьте).
        0
        подробностей не помним, но в самой нулевой статье (ссылка выше есть) я приводил пример как раз с перепрограммированием Вояджера
          0
          Спасибо за «тру», развлекаемся как можем :)
          copy con – штука хорошая, только не вбивать же каждый код раз руками при запуске. Да и как передать такой текст в качестве командной строки?

          Хотя, кстати, можно было бы устроить этакое реалтайм-компо: запускается батник с copy con intro.com, автор вбивает руками код (лучше по памяти) и потом этот код запускается :)))
            0
            Развлекаетесь и развлекаете, за что спасибо.
            copy con… может пригодиться тем, кто hex редактор в руках не держал. Понимаю, что это явно не про авторов демок.
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            и многие другие интересные фильмы :-)
            +3
            А-а, кажется я понял, резюмирую: весь «хак» заключается — в переносе исполняемого кода из .com файла в .bat файл, но за это пришлось — платить, тем что в байт-код'e не должно быть запрещённых ESC символов, и соответственно размером кода в 126 байт т.к. (0x100 — 0x82 = 0x7E=126)
            Я правильно понял!?
            Спасибо за статью, и за проработанный тулчейн: по сути это некий абфускатор и чекер — который «бьёт по рукам» за неправильные символы.
            А вообще, идея использовать 2B — как «новую» соревновательную платформу для sizecoding'а — довольно интересная, жалко только, что достаточно мудрёная. Было бы круто, если все тулы ( эмулятор DosBox, Fasm, 2B тулчейн и набор .bat 'ников ) были собраны в одном архиве, и вот если бы — это всё компилилось и запускалось бы — одной кнопкой…
            Вообще как вариант, простого соревновательного sizecoding'а, я бы предложил Shadertoy Sizecoding compo. Вот что-то подобное можно было бы провести на наших demoparty, например, на ближайшем Chaos Constructions.
              0
              да все верно
                +1
                Перенести код в BAT можно и по-другому, скажем, написав так:
                echo CodeCodeCode>intro.com
                intro.com
                Запрет на спецсимволы останется, но тогда код запустится всё же как обычная COM-программа (правда, в конец добавится ещё CR,LF) и не будет некоторых фишек: старта с адреса $82 (запуска из области командной строки), ограничения в 125 байт. А прикол (упор) как раз в запуске кода из области командной строки, а не просто из BAT-файла, для чего и нужна отдельная программа, размером 2 байта :)

                Ограничение размера всё же не 126, а 125 байт (а вместе с 2b+пробел получится красивое число 128), т.к. в конец ещё CR (13) добавляется (сам DOS добавляет).

                Все тулы поместить в один архив можно, вот только периодически выходят новые версии DOSBox и fasm, и либо в архиве будут лежать устаревающие версии, либо нужно следить и обновлять периодически. Но идея хорошая :)

                Shadertoy Sizecoding compo – тоже классная идея!
                +1
                И все же — а в чем преимущество такого подхода перед обычными COM-программами?
                Пока я вижу только недостаток — невозможность использовать некоторые байты в коде.
                  +1
                  пишите обычные 128 байт интро в .COM — у нас свободная страна!
                    0
                    «Набор существующих платформ, под которые пишутся интры, уже долгие годы практически не меняется, если не брать во внимание категорию Wild (АОН, паяльники, ватные палочки). Мы предлагаем вам… не то чтобы новую платформу, но по крайней мере, некоторое разнообразие, со своими ограничениями. Именно ограничения и их преодоление являются сутью демосцены как процесса».

                    Кто-то пишет демки с использованием Unity и Unreal Engine, а кто-то под ZX и АОНы.
                    Кому что нравится :)
                      +1
                      К примеру, этот код вероятно пройдёт мимо антивируса. Он не сохранится на диске. Не то чтобы преимущества, скорее особенности.
                      Впрочем, вирусов в подобных программах, скорее всего, искать бессмысленно, т.к. человек, способный разрабатывать подобные чудеса, сможет себя прокормить и без вирусов, а если приспичит, то кмк сможет позаботиться о том, чтобы антивирус ничего не нашёл.
                        0
                        это надо проверять… насколько мне известно, основная масса антивирусного ПО давно анализирует .BAT — вопрос лишь на каком уровне идет этот анализ.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое