Почему base64 превращается в Jase64
Грустно признавать, но сегодня под вечер будем ковыряться в кишочках и дебажить аномалию. Но оно того стоит!
читать первым в телеграм читать первым в макс
Выполняем команду:
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 = ограничение трассировки
- signal = не следим за сигналами
После запуска получаем:
[<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.
И чо? А то, что для драйвера терминала, этот символ означает начало управляемой последовательности. Вот это поворот!
Скучная теория
Драйвер терминала — это код, который распознает управляющие последовательности и интерпретирует их в действия. Например, перемещает курсор.
В моем случае, драйвер это эмулятор терминала (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.
И да, всем отличных предстоящих выходных, берегите себя.
