*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» (её надо запускать без линтера):
<**^ ')'|] /=]
-_.@" -[ )~( /@< $^+>\() /<'
-_.@" -[ /<'' /@< $^+>\() /<'
-_.@" -[ ,[~ /@< $^+>\() /<'
-_.@" -[ +^^` /@< $^+>\() /<'
-_.@" -[ /<(' /@< $^+>\() /<'
,[_Я поленился и не стал рассчитывать разные значения для одинаковых токенов, которые упоминаются несколько раз, но можно сделать и это, тогда восстановление исходного текста будет безумно затратной задачей.
Обфусцированный таким образом листинг, запускающий файл и программу для поиска коллизий выложил на «Гитхаб», можно посмотреть подробности там.
Кстати, такое развлечение, когда мы не используем в программе
PHPFuck для PHP8
Немного про программирование.
Один из читателей написал, что программа PHPFuck (это способ писать программы
В самом деле, восьмая версия стала куда строже к программисту,
<?=[[$_=@([].[]),$___=-!![],$$_=![]+![],$$_=$$_*$$_,$__=$$_+![],$$__=$$_+$__,
$____=$_[-![]-![]],$$___="{$$__}"^$____,$$___=($$___^"$___"). ($$___^"{$$__}"^"$___"^"{$$_}").
($$$_=$$___^"{$$_}"). $$$_.($_____="{$$__}"^"{$$_}"^++$____).("$___"&$____).
([$$___=$_[+![]],"{$$_}"|++$$___][+![]]).$_____.$_[+![]].$$$_. ("$__"^$_[-![]-![]]^"$___")
],$$___][+![]];Выше новая версия.
Кстати, она компактнее, но при этом выводит больше текста — «hello world», вместо «Hello!» предыдущего варианта. Ниже я немного расскажу откуда взялась экономия и с чем ещё пришлось столкнуться.
Принцип формирования текста остался, в общих чертах, прежним — корнем всего является комбинация @([].[]), которая даёт слово ArrayArray
Из этой строки потом рождаются все остальные символы. Дело в том, что в ПХП над символами можно производить некоторые бинарные и математические операции, что иногда позволяет получить другие символы. Например, первая буква — h в слове hello получается из сочетания '9' ^ 'a' ^ '0' (где ^ — бинарная операция «исключающего или»).
При этом цифры я получаю довольно простым способом — 0 как +!![] (преобразованием false в число), «девятку», — набирая нужные цифры по единицам (из +![]), а a — как предпоследнюю букву из полученной ранее строки ArrayArray, то есть @([].[])[-2] (аналогично, -2 получается тут из двух замаскированных единиц со знаком «минус»).
По сравнению с предыдущим вариантом в новом коде сделаны следующие изменения.
В этой связи весь код теперь — большое выражение, все переменные в нём лежат внутри массива. Если давать им нормальные имена, то основной принцип выглядит
Кроме того, мне надоело подбирать буквы вручную, вместо этого я написал небольшую программу, которая делает это за меня. Я не стал придумывать алгоритм, который бы анализировал какие биты нужно изменить, а решил задачу полным перебором.
Ниже приведен листинг этой программы, вдруг
<?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);Это ни в коем случае не инструмент для оптимизации программ в такой технике, а набросок, позволяющий ускорить подбор букв.
Для более глубоких оптимизаций нужно научить эту программу дробить выражения, которые уже вычислялись, на куски и эффективно использовать их, а так же подбирать частоиспользуемым кускам имена переменных меньшей длины.
У меня дробления нет, я использую предыдущие выражения целиком, а имена переменным моя программа вообще не подбирает.
PythonFuck: разбор
Как я писал в конце прошлого года, есть такое развлечение — не используя
Получилась такая программа, работающая только из консоли интерпретатора и только под вторым «Пайтоном»:
_=(''<())+(''<());__=_**_**_
___='%'+('%'+`'±'`[_])%(__-_-_)
___*(_*_*_-_)%((
(__+_)*_*_,
`{_}`[_/_],
)+(`(''<'')`[_],)*_+\
(
__*_*_*_-__-_/_,
__*_+_/_,
))Пришло время разобрать как она работает. Я собирался сделать написать разбор через неделю после её написания, но заботы конца года не позволили.
С самого начала было очевидно, что в указанных ограничениях Пайтон не позволит вызвать
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 ★
Давайте вспомним, что у меня блог ещё и про программирование.
Есть такое развлечение — не используя
Для некоторых языков такого ещё никто не делал, приходится изобретать собственные приёмы, в конечном счете это интереснее всего. Я уже делал PHPFuck, BashFuck (и даже не один раз) и совмещал JavaScriptFuck с языком программирования Brainfuck.
Как правило ещё стараются использовать как можно меньше различных символов, но я этим не заморачиваюсь, мне гораздо интереснее соблюсти запрет на буквы и цифры.
Долго мне казалось, что с Пайтоном ничего такого не сделать и я даже пытался, но не получилось. А вчера, пока делал для студентов
_=(''<())+(''<());__=_**_**_
___='%'+('%'+`'±'`[_])%(__-_-_)
___*(_*_*_-_)%((
(__+_)*_*_,
`{_}`[_/_],
)+(`(''<'')`[_],)*_+\
(
__*_*_*_-__-_/_,
__*_+_/_,
))Это запускается и работает (выводит строку «Hello!»), но только из командной строки и только во второй версии Пайтона. Когда напишу небольшой разбор кода, станет понятным почему.
F★ckJS на поло
Очень в тему к предыдущему посту на работе нашёлся мешок с поло размера XXL
На поло программа на ДжаваСкрипте, которую я писал
Bashfuck-3
Вчера весь день болела голова и я, чтобы хоть
Цель, напомню, написать программу, которая делает
В свежем варианте самое главное новшевство — использование функций.
В «баше» с именами переменных всё чересчур строго, если не использовать буквы и цифры остаются доллар и подчёркивание, смотрится однообразно
А вот с функциями веселее, я использовал в именах точки и тире, и закодировал при помощи азбуки Морзе результат работы каждой функции. Например функция, которая генерирует букву «w» называется «.__» (то есть
В этот раз я использовал более разнообразный набор свежевыдуманных трюков для получения букв. Большинство букв набрал из вывода команды df (показывает наличие места на смонтированных устройствах), первая строка вывода содержит заголовки полей (и разнообразных букв) и мало отличается от системы к системе.
Имя этой команды легко получается из конструкции перенаправления
Ещё немного помогла специальная переменная «минус» (да, у неё имя такое), в ней отображаются установленные флаги, в скриптах там всегда две буквы — «hB», их я тоже использовал. Так же мне нужны были цифры, чтобы получать буквы из строк по индексу (благо инструкции работы со строками делаются специальными значками, а не именованными функциями, как в большинстве языков).
С цифрами особых проблем не было. Так, например измерением длины значения переменной «минус» (длина измеряется символом «решётка») получается цифра два, очень удобно. Цифра «один» получается измерением длины значения другой переменной — кода возврата последней команды (её имя — «вопрос»), остальные нужные цифры получались комбинацией этих двух.
Из всего вышеописанного получились все буквы, кроме «r» и «w». Первую я получил, запустив на выполнение ls /bin/?m — по маске гарантированно находится файл команды удаления /bin/rm, её имя содержит искомую букву.
Для получения второй воспользовался командой «tr» и идеей, похожей на реализацию ROT13 на «баше» — подобрал диапазоны из имеющихся в моём распоряжении букв так, чтобы одна подходящая буква сдвинулась нужным мне образом — стала буквой «w».
Как работает Bashfuck
Думаю, по горячим следам надо описать как работают мои Bashfuck и
Поскольку буквы и цифры использовать запрещено, все имена переменных состоят исключительно из подчёркиваний — это единственный вариант в «Баше» в таком случае. Чтобы
К счастью, переменная с именем, состоящим из одного подчёркивания содержит полный путь к интерпретатору, которым был запущен наш скрипт. Поскольку путь может отличаться, а название интерпретатора — нет, путь надо удалить, оставив только слово «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Поскольку типы переменных «Баш»
$ 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Можно, кстати, попробовать ещё задействовать функции, благо их объявление алфавитных символов не требует, а их имена могут подержать не только подчёркивания (но и, например, «собаку», что даёт бо́льшую свободу), но третий вариант я уже делать не буду, надоело.
Bashfuck-2
Попробовал несколько иной способ сбора строки «Hello world» в Bashfuck. Получилось, работает. В предыдущей версии один из читателей жаловался, что у него не запустилось, я тут подумал, что возможно версия «Баша» не та — требуется четвёртая, разумеется.
Вот другой вариант сбора строки:
__=${_##*/} ___=${#__} ______=${__::$(($___-${#___}))} ____=$((${#___}+${#___}))
_____=<(:) ___=$______${_____:$____:${#___}}$(($___+$____))$___
__=${__:(-${#____})}${_____:$____:${#____}} ____=`$___<<<$___|$___`
__=$__${____:$((${#______}+${#__})):$((${#______}^${#__}))} __=$__${__#??}
_______=${____:(-${#__})}
______=`$___<<<$__` __=$__${______#??????} __="${__%?} ${____::$((${#______}/${#__}))}"${__#????}
______=${#___} ____=`$___<<<$____|$___` __=${__%?}${____:$((${#___}+${#___})):$______}
__=${__%????}${_____:${#______}:${#______}} __=${__,,}
_______=${_______::${#______}}${___:${#______}:${#______}}$($___<<<$______|$___)
_______=${_______::$((${#____}/${#__}))};${_______,,}<<<${__^}Надо будет выбрать время, написать как оно работает.
Bashfuck
«…fuck» — это не ругательство, а развлечение, навеянное знаменитым языком программирования «Brainfuck». Практического смысла никакого, правила просты — написать программу
JSFuck у меня в блоге был, PHPFuck — тоже, был даже совмещённый в одной программе JSFuck и Brainfuck, почему бы не попробовать сделать то же на «Баше»?
__=${_##*/} ___=${#__}
__=($__ $(${__:$(($___-${#___}))}${__:${#___}:$___}) $__)
____=${__::$((${#__}-${#___}))}${__[$___]::${#___}}${#__[@]}${#__}
_____=$($____<<<${__[@]}) ______=${_____:${#__[${#__}]}:${#___}}
______=${______,}${__[${#_}]:${#___}:${#___}}${__[$(($___-${#___}))]:${#_}:${#___}}
_______=$($____<<<$_____)
____=${__[${#______}]:${#______}:${#___}} ____=$____$____
__=${__[${#_}]:${#______}}${__[$___]:${#_}:${#___}}$____${_____:$((${#__[@]}+${#__[$___]})):${#___}}
__=$__' '${_______::${#___}}${__:$___}${_____:$((${#_____}>>${#___})):${#___}}
__=$__${____:${#___}}${_______:$(($___+${#______})):${#___}} __=${__,,}
$______<<<${__^}Сказано — сделано, собственно. Программа на «Баше», что выше, выводит фразу «Hello world», её исходники можно свободно скачать с «ГитХаба».
PHPFuck
JSFuck у нас уже был (это намёк на язык Брейнфак, а не ругательство), почему бы не изобрести PHPFuck?
Часа полтора потратил на скрипт, который выводит слово «Hello!»:
<?
$__=$_[$_[]++].=$_;$_[!_]+=++$_[!_];@$$_=($_[$$_][$_]&$_[$$_][$_[!_]]).$_[$$_][$_];
$_[]=&$$_;@$$_.=$_[!!_][$__]^$__[$_[!_]]|$__[!_];$___=$__[!_]|_&~$__[!!_];
@$$_.=++$___.($__[$_]^_^$__[$_[!_]]).($_[!!_][!$_]^$_[_==_][$_[!_]>>!!_]^_);
$___&=$__[$_[!_]];$___.=${$_}[!_]^${$_}[$_[!_]]|$__[!_].($$$_=$__[!_]^_^$__[!!_]).
$$$_.($_[_]=$__[$_[!_]]^_^${$_}[$_[!_]>>!!_]);$$_($___.
(~${$_}[~-$_[!_]]&$___[$_[!_]]).($_[_]&~$___[!!_]));Это настоящий код на PHP, он работает и выполняет заявленное. За вдохновение спасибо неизвестному автору бэкдора, как только я увидел код, понял, что так написать можно что угодно.
Что вкратце происходит у меня. Я создаю массив, привожу его к строке, получается слово «Array» (это такая особенность PHP, если кто не знает), потом при помощи бинарных операций над буквами (с буквами можно использовать «~», «&», «|», «^», а так же операцию инкремента, получая другие буквы), собираю из этого слова слово «printf» (точнее «prINTf», функции в PHP регистронезависимые), которое в дальнейшем будет использовано в качестве имени функции. Потом из двух слов — «Array», «prINTf» и знака подчёркивания теми же бинарными преобразованиями получаю «Hello!» с переводом строки.
Например, буква «N» получается операцией «A» | «_» | ~«r» с последующим инкрементом результата, а «l» (латинская «эл») — «A» ^ «r» ^ «_». Подчёркивание можно использовать без кавычек — все незакавыченные идентификаторы PHP рассматривает как строки.
В конце просто вызываю функцию, передавая ей полученную строку.
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]Принцип я
С Новым годом, что ли?
Я тут, чтобы мозг совсем не расслаблялся на праздниках, убил около часа и вручную сделал на Яваскрипте небольшое поздравление с Новым годом:
[][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+
!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+
([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+
!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+
([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
(([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])[+!![]]+
([]+[][{}])[-~[]]+([]+{})[~[]+(+!![]<<!![]+!![]+
!![])]+([]+![])[!![]+!![]+!![]]+([]+![])[!![]<<!![]+
!![]]+([]+![])[!![]+!![]]+([]+![])[-[]])()
[([{}-{}]+[])[-~[]]+([]+![])[!![]+!![]]+([]+![])
[!![]<<!![]+!![]]+([]+!![])[-~[]]+([]+!![])[+[]]](
[][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+
!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+
([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+
([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+
([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])[+!![]]]
(([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])
[+!![]]+([]+[][{}])[-~[]]+([]+{})[~[]+(+!![]<<!![]+
!![]+!![])]+([]+![])[!![]+!![]+!![]]+([]+![])
[!![]<<!![]+!![]]+([]+![])[!![]+!![]]+([]+![])
[-[]])()[([]+![])[-~[]]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+{})[!![]+!![]]](([]+![])[-~[]]+
([][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])
[+![]]+([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+
([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+
([]+{})[-~[]]+ ([]+!![])[+!![]]]+[])[(-~[]+[+![]])-!![]])+
([]+![])[-~[]]+
(/!/[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+([]+[][{}])
[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+!![])[+!![]]]+[])[+!![]+[!![]<<!![]-~[]]]+
(/!/[([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+([]+[][{}])
[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])
[-[~[]+~[]]]+([]+{})[-~[]+(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})
[-~[]]+([]+!![])[+!![]]]+[])[+!![]+[!![]<<!![]-~[]]]+
([]+[~[]/[]])[[[!![]+!![]]*[!![]+!![]]]*[!![]+!![]]]+
([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+
([]+[][{}])[-~[]]+
([]+{})[-~[]<<[-~[]+-~[]]]+
([][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})
[-~[]]+([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])
[+![]]+([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+
(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]][([]+{})[-~[]+(!![]<<!![]+!![])]+([]+{})[-~[]]+
([]+[][{}])[-~[]]+([]+!{})[!![]+!![]+!![]]+([]+!![])[+![]]+
([]+!![])[+!![]]+([]+!![])[-[~[]+~[]]]+([]+{})[-~[]+
(!![]<<!![]+!![])]+([]+!![])[+![]]+([]+{})[-~[]]+([]+!![])
[+!![]]](([]+!![])[+!![]]+([]+{})[-~[]<<[-~[]+-~[]]]+
([]+!![])[-[]]+([]+[][{}])[-[]]+([]+!![])[+!![]]+([]+[][{}])
[-~[]]+([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+([]+![])
[!![]+!![]+!![]]+([]+![])[!![]<<!![]+!![]]+([]+![])
[!![]+!![]]+([]+![])[-[]])()+[])[([]+![])[!![]+!![]+
!![]]+([]+![])[!![]+!![]]+([]+[][{}])[!![]+(!![]<<!![]+!![])]+
([]+{})[!![]+(!![]<<!![]+!![])]+([]+!![])[!![]+!![]+!![]]](~[]+~[])[-[]]+
([]+{})[~[]+(+!![]<<!![]+!![]+!![])]+
(~[]/[]+[])[-~[]<<-(~[]+~[]+~[])]+
([~[]<[]]+[])[-[~[]+~[]+~[]]]+
([{}-{}]+[])[-~[]]+
([]+!![])[+!![]]+
(/!/+[])[-~[]])Запускать лучше всего в консоли браузера (в Firebug, Dragonfly и так далее). Цели сократить код у меня не было, более того,
Если
Невероятный JavaScript
Насколько запутанный JavaScript вы можете написать? Хотя
В блоге Shadowflux образчик хорошо запутанного кода с объяснением работы:
($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+
($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_)Выглядит довольно страшно, но, если разобраться, ничего сложного. Напоминает старый добрый Perl.