Выполняем команду:
echo Shs | base64 -d
Jase64: invalid input
И наблюдаем аномалию в названии утилиты: Jase64.
Из команды понятно, что утилита base64 при декодировании обнаружила ошибку в данных. Ошибка — не корректная длина закодированного текста (invalid input).
Но чо за херня с Jase64? Новая попа-группа Юры Шильникова-Томатного? Должно же быть Base64?
Если что-то пошло попесде, нам поможет «страус». Расчехляем strace.
Давай посмотрим что и куда пишет base64. Перенаправляем стандартные потоки вывода и ошибки в устройство
/dev/null. Дополнительно говорим «страусу», чтобы выводил системные вызовы write.Запускаем кишку:
с запущенной кишкой, обычно к практологу
LC_ALL=C strace -Yqqqyfe write -P /dev/null --signal=none bash -c 'echo Shs | base64 -d &>/dev/null'
Кратенько по ключам:
LC_ALL = страхуемся
Y = чекаем вызовы ввода-вывода
qqq = меньше мусора
y = показываем пути к файлам
f = чекаем дочерние процессы
e = выбираем вызовы write
P = ограничение трассировки
signals = не следим за сигналами
После запуска получаем:
[<base64>] write(1</dev/null>, "J\33", 2) = 2
[<base64>] write(2</dev/null>, "base64: ", 8) = 8
[<base64>] write(2</dev/null>, "invalid input", 13) = 13
[<base64>] write(2</dev/null>, "\n", 1) = 1
Уже поинтереснее. Разбираем.
- В первой строке происходит запись части декодированного текста в стандартный вывод (дескриптор 1).
- В следующих двух строках, в стандартный поток ошибок пишется имя утилиты и ошибка (дескриптор 2).
- Ну и последняя строка, запись новой строки.
Теперь снова смотрим первую строку и видим что системный вызов write пишет букву «J» и какой-то магический символ 33.
✔️ «Страус» по умолчанию выводит числовые значения непечатных символов в восьмеричной системе счисления. То есть 033 будет соответствовать символу ESC в кодировке ASCII.
И чо? А то, что для драйвера терминала, этот символ означает начало управляемой последовательности. Вот это поворот!
Скучная теория
Драйвер терминала это код, который распознает управляющие последовательности и интерпретирует их в действия. Например, перемещает курсор.
В моем случае, драйвер это эмулятор терминала (Terminal). А еще есть код в пространстве ядра, который реализует специальные устройства. И этот код может модифицировать данные проходящие через эту линию связи.
Схема терминала:
Bash write stdout -> /dev/pts/number Kernel_Space <- /dev/ptmx Terminal read
Terminal write -> /dev/ptmx Kernel_Space <- /dev/pts/number Bash read
Bash read это чтение команды, или сочетания клавиш например для редактирования строки.
Возвращаемся к Jase64
Для начала исключаем пространство ядра (Kernel_Space) и проводим проверку данных, которые поступили на устройство читаемое эмулятором терминала.
Видим что на устройстве эмулятора Terminal, данные пришли без искажений. Подозреваемым остается драйвер терминала.
write(30</dev/ptmx>, "\33[200~echo Shs | base64 -d \33[201"..., 33) = 33
Как это 👆 получить, я рассказал утром в этом посте.
И происходит следующее. Эмулятор терминала получив символ «J» отображает его, но потом натыкается на специальный символ ESC начало управляющей последовательности и слово base64.
Символ «b» распознаётся как часть управляющей последовательности и этот символ не отображается.
Многие терминалы не имеют никаких действий связанных с данной последовательностью «ESCb» и она просто отбрасывается.
Ниже пару примеров, когда часть последующего ввода распознаётся как завершение управляющей последовательности.
echo -ne 'J\033' ; sleep 3 ; echo base64 >&2
echo -ne 'J\033[64' ; sleep 3 ; echo bashdays >&2
Вот такие вот приколы, вот такие вот кишочки. Нельзя просто так взять и насыпать в терминал всё что тебе захочется.
Если после каких-то действий, ты начал замечать аномалии, выполни команду reset и всё встанет на свои места.
Хорошая статья: The TTY demystified.
И да, всем отличных предстоящих выходных, берегите себя ❤️
Не забываем подписываться на пиздатые тулзы.
tags: #linux #bash #debug
—
🔔 @bashdays