*fuck

SectorCFuck

Очень интересно устроен разбор файла программы в компиляторе SectorC, который я ковыряю вечерами из любви к ненормальному программированию. Для тех, кто успел позабыть, напомню — этот компилятор занимает один сектор (512 байт) и способен выполнять программу на подмножестве Си.

Когда смотришь на код такого маленького объёма, сразу возникает вопрос — каким чудом удалось туда запихнуть грамматику языка Си, даже если какое-то подмножество? Ответ — хеширование.

Автор использует реализацию функции atoi, которая превращает любые строки в шестнадцатибитное числа:

unsigned short sectorc_atoi(const char *s)
{
    unsigned short n = 0;
    for (;;) {
        char c = *s++;
        if (!c) break;

        n = 10 * n + (c - '0');
    }

    return n;
}

Все токены, которые встречаются в программе, обязательно разделяются пробелами (за исключением ;, для него есть специальная обработка). Это позволяет довольно просто парсить программу — любой токен скармливаем atoi, получаем число и по таблице смотрим с чем имеем дело.

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

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

Если линтер выключить, можно достигнуть интересного эффекта. Для каждого токена можно вычислить коллизию позаковыристей и написать всю программу без букв и цифр. SectorC как будто бы «из коробки» предназначен для запуска обфусцированного кода.

Ниже программа, выводящая «Hello» (её надо запускать без линтера):

<**^ ')'|] /=]
    -_.@" -[ )~( /@< $^+>\() /<'
    -_.@" -[ /<'' /@< $^+>\() /<'
    -_.@" -[ ,[~ /@< $^+>\() /<'
    -_.@" -[ +^^` /@< $^+>\() /<'
    -_.@" -[ /<(' /@< $^+>\() /<'
,[_

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

Терминал показывает выполнение скрипта ./run_raw.sh с предупреждением о неопределённом формате образа и выводом «Hello».
Пример запуска обфусцированной программы без линтера

Обфусцированный таким образом листинг, запускающий файл и программу для поиска коллизий выложил на «Гитхаб», можно посмотреть подробности там.

Кстати, такое развлечение, когда мы не используем в программе алфавитно-цифровые символы, называется ЧтоНибудьFuck, уж так повелось, — FuckJS, FuckPHP и так далее. Отсюда и название заметки.

Оставить комментарий

PHPFuck для PHP8

Немного про программирование.

Один из читателей написал, что программа PHPFuck (это способ писать программы без алфавитно-цифровых символов), которую я писал несколько лет назад, не работает с восьмой версией интерпретатора ПХП. Он попробовал починить её самостоятельно, но не преуспел.

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

<?=[[$_=@([].[]),$___=-!![],$$_=![]+![],$$_=$$_*$$_,$__=$$_+![],$$__=$$_+$__,
$____=$_[-![]-![]],$$___="{$$__}"^$____,$$___=($$___^"$___"). ($$___^"{$$__}"^"$___"^"{$$_}").
($$$_=$$___^"{$$_}"). $$$_.($_____="{$$__}"^"{$$_}"^++$____).("$___"&$____).
([$$___=$_[+![]],"{$$_}"|++$$___][+![]]).$_____.$_[+![]].$$$_. ("$__"^$_[-![]-![]]^"$___")
],$$___][+![]];

Выше новая версия.

Кстати, она компактнее, но при этом выводит больше текста — «hello world», вместо «Hello!» предыдущего варианта. Ниже я немного расскажу откуда взялась экономия и с чем ещё пришлось столкнуться.

Принцип формирования текста остался, в общих чертах, прежним — корнем всего является комбинация @([].[]), которая даёт слово ArrayArray из-за неявного преобразования массива в текст — это такая особенность ПХП (символ @ нужен тут, чтобы подавить предупреждение, которое выдаёт интерпретатор).

Из этой строки потом рождаются все остальные символы. Дело в том, что в ПХП над символами можно производить некоторые бинарные и математические операции, что иногда позволяет получить другие символы. Например, первая буква — h в слове hello получается из сочетания '9' ^ 'a' ^ '0' (где ^ — бинарная операция «исключающего или»).

При этом цифры я получаю довольно простым способом — 0 как +!![] (преобразованием false в число), «девятку»,  — набирая нужные цифры по единицам (из +![]), а a — как предпоследнюю букву из полученной ранее строки ArrayArray, то есть @([].[])[-2] (аналогично, -2 получается тут из двух замаскированных единиц со знаком «минус»).

По сравнению с предыдущим вариантом в новом коде сделаны следующие изменения.

Во-первых, вместо того, чтобы составлять из получаемых букв printf (функцию, которую я использовал в прошлый раз для вывода текста), тут я пользуюсь выводом через короткую конструкцию <?= — в этом основная экономия. Я выбрал этот вариант, так как конструкция <?, которую я использовал ранее, больше не работает, а заменившая её <?php содержит буквы.

В этой связи весь код теперь — большое выражение, все переменные в нём лежат внутри массива. Если давать им нормальные имена, то основной принцип выглядит как-то так: <?=[ [ $v1 = …, $v2 = …, $result = $v1 . $v2 . … ], $result ][1]; То есть они вычисляются внутри массива, последний элемент которого, содержащий вычисленное выражение, выводится.

Во-вторых, в ПХП стало больше строгости и числа, которые я использую для алхимии с буквами, теперь приходится преобразовывать в строки — раньше это происходило неявно, а сейчас интерпретатор останавливается с фатальной ошибкой. То же с не заданными константами — раньше они преобразовывались в строки (чем я пользовался, используя несуществующую константу, чтобы получить символ _ для дальнейших преобразований), теперь так делать нельзя.

Кроме того, мне надоело подбирать буквы вручную, вместо этого я написал небольшую программу, которая делает это за меня. Я не стал придумывать алгоритм, который бы анализировал какие биты нужно изменить, а решил задачу полным перебором.

Ниже приведен листинг этой программы, вдруг кому-то интересно:

<?php
declare(strict_types=1);

// Доступный алфавит
$abc = str_split(count_chars("Array0123456789", 3));

// буквы, которые надо получить
$need = str_split(count_chars("hello world", 3));

function gen(array $chrs): Generator
{
    // чем операции длиннее, тем ближе к концу
    // это нужно, чтобы более короткие конструкции выигрывали
    foreach ($chrs as $ch) {
        yield $ch => [$ch, $ch];
    }

    // возврат: из чего делаем, [как выглядит, значение]
    foreach ($chrs as $ch) {
        yield $ch => ["~$ch", ~$ch];
    }

    // для символа инкремент работает, а декремент — нет (особенность ПХП)
    foreach ($chrs as $ch) {
        $value = $ch;
        yield $ch => ["++$ch", (string)++$value];
    }
}

function eval_op(string $op, string $a, string $b): string
{
    return ([
        '|' => fn($a, $b) => $a | $b,
        '&' => fn($a, $b) => $a & $b,
        '^' => fn($a, $b) => $a ^ $b,
    ][$op])($a, $b);
}

function generate(array $set): Generator
{
    foreach (gen($set) as $src1 => [$p1, $ch1]) {
        foreach (gen($set) as $src2 => [$p2, $ch2]) {
            foreach (['|', '&', '^'] as $op) {
                $result = eval_op($op, $ch1, $ch2);

                if ($result !== $ch1 && $result !== $ch2) {
                    yield [
                        $src1,
                        $src2,
                        "$p1 $op $p2",
                        $result,
                    ];
                }
            }
        }
    }
}

function out(string $str): string
{
    return preg_replace_callback(
        '/[\x00-\x1F\x80-]/s',
        fn($m) => sprintf("\\x%02X", ord($m[0])),
        $str
    );
}

$set = $abc;

do {
    foreach (generate($set) as [$src1, $src2, $str, $res]) {
        $idx = array_search($res, $need, true);

        if (!isset($rules[$res])) {
            $rules[$res] = $str;
        }

        if ($idx !== false) {
            if (!in_array($src1, $abc, true)) {
                echo out("{$rules[$src1]} = '$src1'"), "\n";
            }

            if (!in_array($src2, $abc, true)) {
                echo out("{$rules[$src2]} = '$src2'"), "\n";
            }

            echo out("$str = '$res'"), "\n\n";

            unset($need[$idx]);
        }

        $set[] = $res;
    }

    $set = array_unique($set);
} while ($need);

Это ни в коем случае не инструмент для оптимизации программ в такой технике, а набросок, позволяющий ускорить подбор букв.

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

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

2 комментария

PythonFuck: разбор

Как я писал в конце прошлого года, есть такое развлечение — не используя алфовитно-цифровые символы, писать осмысленные программы на разных языках программирования. Для конкретного языка это называется «имя языка»+«Fuck». В прошлый раз я попробовал провернуть это на «Пайтоне». Я не в курсе, пытался ли кто-то такое сделать раньше, даже не гуглил, хотелось избежать влияния чужих наработок.

Получилась такая программа, работающая только из консоли интерпретатора и только под вторым «Пайтоном»:

_=(''<())+(''<());__=_**_**_
___='%'+('%'+`'±'`[_])%(__-_-_)

___*(_*_*_-_)%((
(__+_)*_*_,
`{_}`[_/_],
)+(`(''<'')`[_],)*_+\
(
__*_*_*_-__-_/_,
__*_+_/_,
))

Пришло время разобрать как она работает. Я собирался сделать написать разбор через неделю после её написания, но заботы конца года не позволили.

С самого начала было очевидно, что в указанных ограничениях Пайтон не позволит вызвать какой-либо метод по имени, которое записано в переменной. Если попробовать, получится что-то похожее вот на это:

module, method = "__builtin__", "print"
vars(vars()[module])[method]("Hello")

Есть варианты, но без использования букв — никак.

Так как в силу вышеизложенного функцию печати не вызовешь, пришлось довольствоваться выражением в консоли интерпретатора — там результат выводится на экран автоматически. В остальном, как будет видно ниже, удалось обойтись теми конструкциями, которые есть в языке.

Посмотрим в код. Первая строка:

_=(''<())+(''<());__=_**_**_

Тут два разных выражения, разделённых точкой с запятой. В первом в скобках я дважды сравниваю строку и кортеж, получая два True. Второй Пайтон позволяет сравнивать разные встроенные типы, возвращая результат сравнения их названий, записанных как строки. Так как строка str меньше tuple, то результат — истина.

Два True в арифметической сумме дают двойку, неявно преобразовываясь к числу в этой операции. В принципе тут можно использовать унарный минус и тильду, но тогда мне это в голову не пришло:

_=-~(''<()) # число «2» можно получить и так тоже

Результат мы кладём в переменную с именем «подчёркивание». В той же строке мы получаем «16» возводя два в степень два в степени два, результат помещается в переменную «два подчёркивания».

Вторая строка:

___='%'+('%'+`'±'`[_])%(__-_-_)

Тут используется символ «±», он не алфавитно-цифровой, хоть и за пределами однобайтной таблицы, так что правила не нарушаются.

Этот символ вставлен внутрь конструкции «обратный апостроф», которая является во втором Пайтоне эквивалентом repr — функции, возвращающей строковое представление объекта, в данном случае это будет «'\\xc2\\xb1'». Нам отсюда нужен второй символ (нумерация с нуля) — «x». Цифра два для индекса у нас уже есть — в переменной _.

Дальше используется оператор «процент» — форматированный вывод. Полученное «x» используется как шаблон для «процента», чтобы преобразовать число в шестнадцатеричный вид. Из выражения __-_-_ (16−2−2) получаем «12» или, после преобразования, «c».

Вообще можно было обойтись без этого шага, получив эту же букву из строкового представления «±» («'\\xc2\\xb1'»), но в процессе продумывания я себе выписал ряд трюков и мне хотелось использовать их все.

В итоге, в переменную «три подчёркивания» попадает строка «%с», её мы будем использовать для оператора форматированного вывода («%»), чтобы получать символ по его коду.

Дальше символы получаются следующим образом:

(__+_)*_*_ # (16+2)*2*2 = 72, код символа «H», через шаблон «%c» получим отсюда букву
`{_}`[_/_] # первая часть даёт строку «set([2])», отсюда берём символ «e»
`(''<'')`[_]*_ # получаем строку «False», оттуда символ «l», удваиваем его умножением на два
__*_*_*_-__-_/_ # 16*2*2*2-16-2/2 = 111, код символа «о»
__*_+_/_ # 16*2+2/2 = 33, код символа «!»

Не требующее преобразования оставляем как есть, а из кодов символы получаем шаблоном «%с». В результате складывается строка «Hello!».

Второй, а не третий Пайтон нужен, в основном, из-за двух вещей — оператора «обратный апостроф» и двух типов строк, в одном из них юникодные символы отображаются в кодированном представлении, в третьей версии языка ото всего этого избавились.

Оставить комментарий

PythonFuck ★

Давайте вспомним, что у меня блог ещё и про программирование.

Скриншот терминала bash с выполнением Python-кода через /dev/stdin, выводящего 'Hello!', и командой exit().

Есть такое развлечение — не используя алфовитно-цифровые символы, писать осмысленные программы на разных языках программирования. Для конкретного языка это называется «имя языка»+«Fuck». Меня это занятие не то чтобы захватило, но иногда интересно поразмяться и попробовать себя в этой забаве.

Для некоторых языков такого ещё никто не делал, приходится изобретать собственные приёмы, в конечном счете это интереснее всего. Я уже делал PHPFuck, BashFuck (и даже не один раз) и совмещал JavaScriptFuck с языком программирования Brainfuck.

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

Долго мне казалось, что с Пайтоном ничего такого не сделать и я даже пытался, но не получилось. А вчера, пока делал для студентов задачу-завлекалку на день карьеры, решил сделать очередной «подход к снаряду».

_=(''<())+(''<());__=_**_**_
___='%'+('%'+`'±'`[_])%(__-_-_)

___*(_*_*_-_)%((
(__+_)*_*_,
`{_}`[_/_],
)+(`(''<'')`[_],)*_+\
(
__*_*_*_-__-_/_,
__*_+_/_,
))

Это запускается и работает (выводит строку «Hello!»), но только из командной строки и только во второй версии Пайтона. Когда напишу небольшой разбор кода, станет понятным почему.

Оставить комментарий

F★ckJS на поло

Белая футболка с надписью «ПРАКТИКА» и кодом var phone = ... на фоне стола с зелёным держателем для ручек.
Поло с программой на ДжаваСкрипте, написанной четыре года назад

Очень в тему к предыдущему посту на работе нашёлся мешок с поло размера XXL (из-за размера и сохранился, остальные разобрали).

На поло программа на ДжаваСкрипте, которую я писал к какому-то событию, должно быть готовились к какой-то конфереции — если её запустить в консоли браузера, появляется надпись «prihodite rabotat 88002005221». У меня в «исходящих» сохранилось письмо от 11 марта 2013 года, где я высылаю оригинал этой программы нашему дизайнеру, четыре года уже прошло, надо же.

2 комментария

Bashfuck-3

Hello world (69.01КиБ)
Очередной «Хеллоу ворлд» на «башфаке» — способе программирования на «баше» без алфавитно-цифровых символов

Вчера весь день болела голова и я, чтобы хоть как-то отвлечься, написал, на ночь глядя, ещё один «Хеллоу ворлд» на «баше» в стиле «Башфак» — три года назад эти увлёкся, тогда остались кое-какие нереализованные идеи.

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

В свежем варианте самое главное новшевство — использование функций.

В «баше» с именами переменных всё чересчур строго, если не использовать буквы и цифры остаются доллар и подчёркивание, смотрится однообразно (вообще-то можно ещё использовать массивы, там квадратные скобки, это уже поинтереснее, но вот индексы, вычисляемые без помощи чисел чересчур тяжеловесны и с ними та же проблема — программа смотрится однообразно).

А вот с функциями веселее, я использовал в именах точки и тире, и закодировал при помощи азбуки Морзе результат работы каждой функции. Например функция, которая генерирует букву «w» называется «.__» (то есть «точка»-«тире»-«тире»).

В этот раз я использовал более разнообразный набор свежевыдуманных трюков для получения букв. Большинство букв набрал из вывода команды df (показывает наличие места на смонтированных устройствах), первая строка вывода содержит заголовки полей (и разнообразных букв) и мало отличается от системы к системе.

Имя этой команды легко получается из конструкции перенаправления ввода-вывода в специальный файла вида /dev/fd/XX. Получаем имя этого файла, берём буквы «f», «d» и запускаем полученную команду.

Ещё немного помогла специальная переменная «минус» (да, у неё имя такое), в ней отображаются установленные флаги, в скриптах там всегда две буквы — «hB», их я тоже использовал. Так же мне нужны были цифры, чтобы получать буквы из строк по индексу (благо инструкции работы со строками делаются специальными значками, а не именованными функциями, как в большинстве языков).

С цифрами особых проблем не было. Так, например измерением длины значения переменной «минус» (длина измеряется символом «решётка») получается цифра два, очень удобно. Цифра «один» получается измерением длины значения другой переменной — кода возврата последней команды (её имя — «вопрос»), остальные нужные цифры получались комбинацией этих двух.

Из всего вышеописанного получились все буквы, кроме «r» и «w». Первую я получил, запустив на выполнение ls /bin/?m — по маске гарантированно находится файл команды удаления /bin/rm, её имя содержит искомую букву.

Для получения второй воспользовался командой «tr» и идеей, похожей на реализацию ROT13 на «баше» — подобрал диапазоны из имеющихся в моём распоряжении букв так, чтобы одна подходящая буква сдвинулась нужным мне образом — стала буквой «w».

1 комментарий

Как работает Bashfuck

Думаю, по горячим следам надо описать как работают мои Bashfuck и Bashfuck-2. Пока ещё сам детально помню.

Поскольку буквы и цифры использовать запрещено, все имена переменных состоят исключительно из подчёркиваний — это единственный вариант в «Баше» в таком случае. Чтобы что-то напечатать, сначала надо получить какие-то буквы.

К счастью, переменная с именем, состоящим из одного подчёркивания содержит полный путь к интерпретатору, которым был запущен наш скрипт. Поскольку путь может отличаться, а название интерпретатора — нет, путь надо удалить, оставив только слово «bash». Это делается в обоих случая в первой строке:

# Удаляем из переменной $_ всё до последнего слеша
__=${_##*/}

Теперь у нас есть четыре буквы «b», «a», «s» и «h». Из имеющегося надо как-то получить недостающее. Тут нам помогает следующий факт: можно запускать что угодно, по имени, которое содержится в переменной.

Далее оба скрипта пытаются составить в одной из переменных команду «base64», чтобы передав ей на вход строку, получить недостающие буквы. В самом деле, передавая на вход разные строки и пропуская их по нескольку раз через «base64» команду, можно получить весь английский алфавит:

# bash тут — строка, которую мы уже получили
$ base64<<<bash
YmFzaAo=
$ base64<<<bash | base64
WW1GemFBbz0K

Например, в полученных строках некоторые буквы нам вполне интересны: «Hello world». Но как получить слово base64? С цифрами проще: имея строки разной длины и арифметические операции можно получить нужное:

# Переменная __ содержит слово bash
__=bash
# Очень просто получить цифру четыре: надо измерить длину строки:
$ echo ${#__}
4
# Любым похожим путём получаем двойку, прибавляем к четвёртке и у нас есть 6
$ echo $((${#__} + 2))
6

Поскольку типы переменных «Баш» не особо-то различает, склеить всё остальное труда не представляет. Но нужно ещё откуда-то получить букву «e». Тут дорожки этих двух скриптов расходятся. Первый работает так: составляя из имеющихся букв команду «hash», запускает её и получает следующую строку:

$ hash
hash: hash table empty

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

Если использовать такой синтаксис с любой командой, которая принимает не файл, а строку, то строку и получим (цифры будут меняться от запуска к запуску, остальная строка останется неизменной):

# пробуем вывести на печать
$ echo <(:)
/dev/fd/63

Нам это значение нужно в переменной и с присвоением этот трюк тоже сработет.

В том и другом скрипте нужную букву мы получили и можем собрать нужное имя без проблем. Для этого воспользуемся следующей конструкцией, которая позволяет выбирать подстроки из переменных:

# переменная теперь содержит что-то вроде /dev/fd/xx
___=<(:)
# выбираем третий символ (отсчёт с нуля):
echo ${___:2:1}

Вместо двойки и единицы можно подставить переменные, которые эти числа содержат, либо вычислить их по ходу дела. Во втором примере применяется ещё и другая техника — поскольку некоторые числа из подручных переменных вычислять сложно, я срезаю некоторое количество букв слева и справа, чтобы добраться до нужной буквы:

# переменная, где нужная мне буква в середине слова
v=sed
# так можно срезать последнюю букву
echo ${v%?}
# а так —  первую
echo ${v#?}

По большому счёту, это всё. Используется ещё несколько приёмов, но для понимания происходящего осталось пояснить только ещё одну вещь. В четвёртом «Баше», который требуется для работы обоих скриптов, есть ещё такой специальный синтаксис, который позволяет менять регистр букв. Это позвояет не заморачиваться с тем, в каком регистре получаются отдельные буквы и првести всё к нужному ближе к концу:

v='heLLo WORld'
# всё к нижнему (будет hello world)
echo ${v,,}
# только первую —  к верхнему (будет HeLLo WORld)
echo ${v^}

Собранную строку осталось вывести на экран. Для этого в том же коде я попутно собираю из получающихся букв ещё и строку «cat» (что на одну букву меньше «echo», кода получается меньше) и с помощью этой команды получаю нужный результат:

c=cat
v='Hello world'
# Выведет Hello world
$c<<<$v

Можно, кстати, попробовать ещё задействовать функции, благо их объявление алфавитных символов не требует, а их имена могут подержать не только подчёркивания (но и, например, «собаку», что даёт бо́льшую свободу), но третий вариант я уже делать не буду, надоело.

2 комментария

Bashfuck-2

Попробовал несколько иной способ сбора строки «Hello world» в Bashfuck. Получилось, работает. В предыдущей версии один из читателей жаловался, что у него не запустилось, я тут подумал, что возможно версия «Баша» не та — требуется четвёртая, разумеется.

Вот другой вариант сбора строки:

__=${_##*/} ___=${#__} ______=${__::$(($___-${#___}))} ____=$((${#___}+${#___}))
_____=<(:) ___=$______${_____:$____:${#___}}$(($___+$____))$___
__=${__:(-${#____})}${_____:$____:${#____}} ____=`$___<<<$___|$___`
__=$__${____:$((${#______}+${#__})):$((${#______}^${#__}))} __=$__${__#??}
_______=${____:(-${#__})}
______=`$___<<<$__` __=$__${______#??????} __="${__%?} ${____::$((${#______}/${#__}))}"${__#????}
______=${#___} ____=`$___<<<$____|$___` __=${__%?}${____:$((${#___}+${#___})):$______}
__=${__%????}${_____:${#______}:${#______}} __=${__,,}
_______=${_______::${#______}}${___:${#______}:${#______}}$($___<<<$______|$___)
_______=${_______::$((${#____}/${#__}))};${_______,,}<<<${__^}

Надо будет выбрать время, написать как оно работает.

1 комментарий

Bashfuck

«…fuck» — это не ругательство, а развлечение, навеянное знаменитым языком программирования «Brainfuck». Практического смысла никакого, правила просты — написать программу на каком-либо языке программирования, не используя буквы и цифры. Уровень повышенной сложности — использовать для программы не все доступные закорюки, а сильно ограниченное количество, например, только пять любых на выбор.

JSFuck у меня в блоге был, PHPFuck — тоже, был даже совмещённый в одной программе JSFuck и Brainfuck, почему бы не попробовать сделать то же на «Баше»?

__=${_##*/} ___=${#__}
__=($__ $(${__:$(($___-${#___}))}${__:${#___}:$___}) $__)
____=${__::$((${#__}-${#___}))}${__[$___]::${#___}}${#__[@]}${#__}
_____=$($____<<<${__[@]}) ______=${_____:${#__[${#__}]}:${#___}}
______=${______,}${__[${#_}]:${#___}:${#___}}${__[$(($___-${#___}))]:${#_}:${#___}}
_______=$($____<<<$_____)
____=${__[${#______}]:${#______}:${#___}} ____=$____$____
__=${__[${#_}]:${#______}}${__[$___]:${#_}:${#___}}$____${_____:$((${#__[@]}+${#__[$___]})):${#___}}
__=$__' '${_______::${#___}}${__:$___}${_____:$((${#_____}>>${#___})):${#___}}
__=$__${____:${#___}}${_______:$(($___+${#______})):${#___}} __=${__,,}
$______<<<${__^}

Сказано — сделано, собственно. Программа на «Баше», что выше, выводит фразу «Hello world», её исходники можно свободно скачать с «ГитХаба».

9 комментариев

PHPFuck

JSFuck у нас уже был (это намёк на язык Брейнфак, а не ругательство), почему бы не изобрести PHPFuck?

Часа полтора потратил на скрипт, который выводит слово «Hello!»:

<?
$__=$_[$_[]++].=$_;$_[!_]+=++$_[!_];@$$_=($_[$$_][$_]&$_[$$_][$_[!_]]).$_[$$_][$_];
$_[]=&$$_;@$$_.=$_[!!_][$__]^$__[$_[!_]]|$__[!_];$___=$__[!_]|_&~$__[!!_];
@$$_.=++$___.($__[$_]^_^$__[$_[!_]]).($_[!!_][!$_]^$_[_==_][$_[!_]>>!!_]^_);
$___&=$__[$_[!_]];$___.=${$_}[!_]^${$_}[$_[!_]]|$__[!_].($$$_=$__[!_]^_^$__[!!_]).
$$$_.($_[_]=$__[$_[!_]]^_^${$_}[$_[!_]>>!!_]);$$_($___.
(~${$_}[~-$_[!_]]&$___[$_[!_]]).($_[_]&~$___[!!_]));

Это настоящий код на PHP, он работает и выполняет заявленное. За вдохновение спасибо неизвестному автору бэкдора, как только я увидел код, понял, что так написать можно что угодно.

Что вкратце происходит у меня. Я создаю массив, привожу его к строке, получается слово «Array» (это такая особенность PHP, если кто не знает), потом при помощи бинарных операций над буквами (с буквами можно использовать «~», «&», «|», «^», а так же операцию инкремента, получая другие буквы), собираю из этого слова слово «printf» (точнее «prINTf», функции в PHP регистронезависимые), которое в дальнейшем будет использовано в качестве имени функции. Потом из двух слов — «Array», «prINTf» и знака подчёркивания теми же бинарными преобразованиями получаю «Hello!» с переводом строки.

Например, буква «N» получается операцией «A» | «_» | ~«r» с последующим инкрементом результата, а «l» (латинская «эл») — «A» ^ «r» ^ «_». Подчёркивание можно использовать без кавычек — все незакавыченные идентификаторы PHP рассматривает как строки. Кое-где там используются похожим образом получаемые цифры, но это уж совсем не интересно рассказывать.

В конце просто вызываю функцию, передавая ей полученную строку.

12 комментариев

JavaScript, совмещённый с Brainf*ck: «hello friends» ★

Помните моё поздравление с Новым годом, написанное на запутанном Джаваскрипте? Такой же принцип я использовал для привлекающего внимание баннера нашей компании.

Я давно уже заметил, что это сильно похоже на язык Брейнфак. Давно хотелось попробовать смешать эти два языка в одном листинге. Со стороны Брейнфака всё довольно гладко — надо только балансировать квадратные скобки (но Джаваскрипт требует того же) и избегать вечных циклов (тут уже сложнее, так как листинги на запутанном Джаваскрипте часто требуют пустых циклов), незнакомые символы этот язык пропускает. В Джаваскрипте синтаксис построже, там нужно было помучаться больше.

Получилась следующая программа (используется расцветка для Брейнфака, чтобы было видно какие части программы он пропускает):

($=!{}[/-/+/\+@+@+!+/)-!{$:/>/+/!>/+/</}+
(_=!-{}-$+/>/)-~(/</)+
(_$=-$-{}-$+/>/)[$$=-(~-~$+~-~$-!+{$:/!</})+
_[$$+=~-~{$:/]/}+
_$-~{}+
_$[$$+~{_:-$>/]/}+
$-$$]

Оба интерпретатора выполняют её с разным результатом, Брейнфак выводит «hello», а Джаваскрипт — «friends»:

bolk@Bolk ~$ ./brainfuck <<<'($=!{}+[/-/]+/\+@+@+!+/)[-!{$:/>/+/!+>/+/<</}]+(_=!-{}+[-$]+/>/)[-~(/>[->+>+<<]>>[-<+<+>>]<<</)]+(_$=[-$][-{}]+[-$]+/>>/)
[$$=-(~-~$+~-~$-!+{$:/!+>+>+>+>+>+<<<<</})]+_[$$+=~-~{$:/[->++++>+>++++++++>++++++++>+++++++++++[<]]/}]+_$[-~{}]+_$[$$+~{_:-$>/[.>]/}]+$[-+-$$]'

hello

bolk@Bolk ~$ v8 -e 'print(($=!{}+[/-/]+/+@+@+!+/)[-!{$:/>/+/!+>/+/<</}]+(_=!-{}+[-$]+/>/)[-~(/>[->+>+<<]>>[-<+<+>>]<<</)]+(_$=[-$][-{}]+[-$]+/>>/)
[$$=-(~-~$+~-~$-!+{$:/!+>+>+>+>+>+<<<<</})]+_[$$+=~-~{$:/[->++++>+>++++++++>++++++++>+++++++++++[<]]/}]+_$[-~{}]+_$[$$+~{_:-$>/[.>]/}]+$[-+-$$])'

friends

Повозиться пришлось прилично, если честно, некоторые конструкции ДжЭс делит с Брейфаком (большинство квадратных скобок), а кое-где торчат чистые инструкции Брейнфака, засунутые в те места программы, где они не могут помешать своему соседу. Пустые циклы, которые образуются квадратными скобками я старался сводить к операции зануления ячейки („[-]“), сильно мешали плюсы, используемые в Джаваскрипте для объединения букв — они увеличивали содержимое ячейки в Брейнфаке на единицу, это приходилось учитывать.

Слово «friends» я выбрал потому что его легко записать:

"false"[0]+"true"[1]+"undefined"[5]+"true"[3]+"undefined"[1]+"undefined"[2]+"false"[3]

Принцип я когда-то более-менее подробно разбирал.

3 комментария

С Новым годом, что ли?

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

[][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+
!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+
([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+
!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+
([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
(([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])[+!![]]+
([]+[][{}])[-~[]]+([]+{})[~[]+(+!![]<<!![]+!![]+
!![])]+([]+![])[!![]+!![]+!![]]+([]+![])[!![]<<!![]+
!![]]+([]+![])[!![]+!![]]+([]+![])[-[]])()
[([{}-{}]+[])[-~[]]+([]+![])[!![]+!![]]+([]+![])
[!![]<<!![]+!![]]+([]+!![])[-~[]]+([]+!![])[+[]]](
[][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+
!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+
([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+
([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
(([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])
[+!![]]+([]+[][{}])[-~[]]+([]+{})[~[]+(+!![]<<!![]+
!![]+!![])]+([]+![])[!![]+!![]+!![]]+([]+![])
[!![]<<!![]+!![]]+([]+![])[!![]+!![]]+([]+![])
[-[]])()[([]+![])[-~[]]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+{})[!![]+!![]]](([]+![])[-~[]]+
([][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])
[+![]]+([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+
([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+
([]+{})[-~[]]+ ([]+!![])[+!![]]]+[])[(-~[]+[+![]])-!![]])+
([]+![])[-~[]]+
(/!/[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+([]+[][{}])
[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+!![])[+!![]]]+[])[+!![]+[!![]<<!![]-~[]]]+
(/!/[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+([]+[][{}])
[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+!![])[+!![]]]+[])[+!![]+[!![]<<!![]-~[]]]+
([]+[~[]/[]])[[[!![]+!![]]*[!![]+!![]]]*[!![]+!![]]]+
([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+
([]+[][{}])[-~[]]+
([]+{})[-~[]<<[-~[]+-~[]]]+
([][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])
[+![]]+([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+
(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+
([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+
(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]](([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])[+!![]]+([]+[][{}])
[-~[]]+([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+([]+![])
[!![]+!![]+!![]]+([]+![])[!![]<<!![]+!![]]+([]+![])
[!![]+!![]]+([]+![])[-[]])()+[])[([]+![])[!![]+!![]+
!![]]+([]+![])[!![]+!![]]+([]+[][{}])[!![]+(!![]<<!![]+!![])]+
([]+{})[!![]+(!![]<<!![]+!![])]+([]+!![])[!![]+!![]+!![]]](~[]+~[])[-[]]+
([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+
(~[]/[]+[])[-~[]<<-(~[]+~[]+~[])]+
([~[]<[]]+[])[-[~[]+~[]+~[]]]+
([{}-{}]+[])[-~[]]+
 ([]+!![])[+!![]]+
(/!/+[])[-~[]])

Запускать лучше всего в консоли браузера (в Firebug, Dragonfly и так далее). Цели сократить код у меня не было, более того, кое-где для разнообразия (буквы-то повторяются) я придумывал разные конструкции, чтобы не так скучной было. С Новым годом!

Если кого-то поразило это буйство скобочек, то намекаю ещё раз — это полноценная программа на Яваскрипте.

24 комментария

Невероятный JavaScript

Насколько запутанный JavaScript вы можете написать? Хотя JS-игра, которую мы сокращали на пару с LO, выглядит довольно запутанно, но можно писать куда более непонятно, если ваша цель не сократить объём, а сделать так, чтобы у читающих листинг вашего кода выпали глаза.

В блоге Shadowflux образчик хорошо запутанного кода с объяснением работы:

($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+
($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_)

Выглядит довольно страшно, но, если разобраться, ничего сложного. Напоминает старый добрый Perl.

7 комментариев