Как работает 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
Можно, кстати, попробовать ещё задействовать функции, благо их объявление алфавитных символов не требует, а их имена могут подержать не только подчёркивания (но и, например, «собаку», что даёт бо́льшую свободу), но третий вариант я уже делать не буду, надоело.
Евгений!
А как вы используете команду echo, не получив букву «e» ?
Комментарий для Горбунов Олег:
Это пример на понимание, сейчас перепишу это место.