При обратном проектировании исполняемого файла часто используются несколько инструментов.
В большинстве случаев, которые я видел, код приходилось переписывать с нуля, поддерживать как программу на языке ассемблера или восстанавливать путем повторного применения запросов на изменение к более старой версии.
Когда целесообразно использовать?
Когда аргументы eval правильно заключены в кавычки (два уровня) и значения переменных, расширенные eval при первом синтаксическом анализе, не станут исполняемыми командами при втором синтаксическом анализе .
Почему использование eval - плохая идея?
Это не IMO (и этот вопрос требует мнений), если вы действительно знаете, что делаете.
Во-первых: проверьте, что (eval) того, что вы написали, соответствует вашим ожиданиям, заменив eval
на echo
и Во-вторых: хорошенько подумайте, какие переменные устанавливаются локально и какие значения они могут иметь.
Существуют ли какие-либо правила, согласно которым его можно использовать с полной и полной безопасностью, или это всегда и неизбежно дыра в безопасности?
Есть некоторые условия, при которых его можно использовать в полной и полной безопасности, да.
Но это всегда риск (например, риск написания неверного сценария).
Так получилось, что eval увеличивает риск до некоторой степени.
Это обязательно потребует подробного описания:
Команда eval (всегда встроенная) позволяет дважды проанализировать командную строку.
В принципе, она не более или менее опасна, чем любая другая команда (вспомните rm -rf /
). Он будет выполнен с текущими правами пользователя, поэтому дважды подумайте, прежде чем использовать его с правами root. Но это также верно для команды rm
, показанной выше. Для ограниченного пользователя rm завершится ошибкой в большинстве каталогов, принадлежащих пользователю root. Но rm
по-прежнему опасная команда.
Команда eval очень полезна (и совершенно безопасна) в известных идиомах.
Например, это выражение выводит значение последнего позиционного
параметра :
$ set -- "ls -l" "*" "c;date"
$ eval echo \"\$\{$#\}\" ### command under test
c;date
Команда не является условно безопасной (как есть), потому что единственный возможный результат из $ #
- число. И единственный возможный результат $ n
(где n - число) - это содержимое такой позиционной переменной.
Если вы хотите увидеть, что делает команда, замените eval на echo
$ set -- "ls -l" "*" "c; date"
$ echo echo \"\$\{$#\}\" ### command under test
echo "${3}"
А echo $ {3}
- очень распространенная (и безопасная) идиома.
Мы все еще можем изменить некоторые цитаты и получить тот же результат:
$ echo echo '"${'$#\}\"
echo "${3}"
$ eval echo '"${'$#\}\"
c;date
Я надеюсь, что вам легко увидеть, что этот новый способ цитирования несколько более неясен, чем приведенный выше.
$ b=book
$ book="A Tale of Two Cities"
$ eval 'a=$'"$b" ### safe for some values of $b.
$ echo "$a"
A Tale of Two Cities
И здесь мы находим первую (из двух) и основную проблему с eval:
недостаточное цитирование:
$ b='book;date'
$ eval 'a=$'"$b" ### safe for some values of $b.
Fri Apr 22 22:03:09 UTC 2016
Дата команды была выполнена (чего мы не собирались).
Но это не выполнит дату
(по крайней мере, я так подумал, спасибо @Wildcard).
Правильным решением этой команды является очистка ввода, но я отложу обсуждение этого вопроса до тех пор, пока не будет определена вторая проблема eval.
$ eval 'a="$'"$b"\"
$ echo "$a"
A Tale of Two Cities;date
Уловка несложная, просто замените eval на echo и оцените, безопасна ли напечатанная командная строка. Сравните небезопасную и безопасную команду:
$ echo 'a=$'"$b"
a=$book;date
$ echo 'a="$'"$b"\"
a="$book;date"
Эти простые двойные кавычки сохранят строку как строку и не будут преобразованы в команду для выполнения оболочкой.
Возможно, будет легче понять логику цитирования, если мы поместим «внешние кавычки» и «внутренние кавычки» (добавлены пробелы для разборчивости):
$ echo a\=\"\$ "$b" \"
Внутренние кавычки - это те, которые находятся вокруг $ b
которые всегда являются «хорошей идеей».
Все внешние обозначены обратной косой чертой (в этом примере).
Команда без пробелов (как и следовало бы использовать):
$ echo a\=\"\$"$b"\"
a="$book;date"
$ eval a\=\"\$"$b"\"
$ echo "$a"
A Tale of Two Cities;date
Но даже этот пример был очень быстро сломан пользователем (спасибо @Wildcard):
$ b='";date;:"'
$ eval a\=\"\$"$b"\"
Fri Apr 22 23:25:43 UTC 2016
Выполнение команды eval было прервано стать злым, давайте использовать echo:
$ echo a\=\"\$"$b"\"
a="$";date;:""
Думать о том, что является результатом двойного цитирования, никогда не бывает просто.
И это даже более запутанно, чем первое. В некоторых случаях мы не хотим, чтобы результат первого раскрытия параметров eval оставался в кавычках. Во многих случаях потому, что мы хотим выполнить команду внутри переменной.
Или злоумышленник (как уже было показано выше) создает строку, которая соответствует квотам, а затем помещает команду вне кавычек. Команда будет выполнена, и (если нам повезет) будет сообщено об ошибке.
Это нарушает первый уровень защиты: цитирование
И оставляет значения переменных полностью открытыми для выполнения.
В этом случае обязательно, чтобы мы были уверены в значениях, которые будет иметь переменная. Все возможные значения.
Если значение переменной контролируется каким-то пользователем, мы сообщаем этому пользователю:
Дайте мне любую команду, я выполню ее с моими разрешениями.
Это всегда опасная ставка, верный баг, а корень - безумный поступок.
Сказав все вышесказанное, это совершенно безопасно:
#!/bin/bash
a=${1//[^0-9]} ### Only leave a number (one or many digits).
eval echo $(( a + 1 ))
Независимо от того, что внешний пользователь поместил в позиционный параметр, он станет числом, это может быть большое число, для которого $ ((...)) завершится ошибкой, но этот ввод не вызовет выполнение какой-либо команды.
Теперь мы можем поговорить об очистке переменной b
из приведенной выше команды. Команда была:
$ b='";date;:"'
$ eval a\=\"\$"$b"\"
Sat Apr 23 01:56:30 UTC 2016
Потому что b содержало несколько символов, которые использовались для изменения строки.
$ echo a\=\"\$"$b"\"
a="$";date;:""
Переход на одинарные кавычки не сработает, им может соответствовать другое значение b. Нам нужно «очистить» b
, удалив (как минимум) кавычки.
Мы могли бы заменить $ b
на $ {b // \ "}
:
$ eval a\=\"\$"${b//\"}"\"
$ echo "$a"
$;date;:
Но мы можно сделать его еще лучше, признав, что имя переменной в оболочке может иметь только 0-9a-zA_Z
и символы подчеркивания. Мы можем удалить все остальные с помощью этого:
$ c="$( LC_COLLATE=C; echo "${b//[^0-9A-Za-z_]}" )"
Это очистит переменную b
на допустимое имя переменной.
А затем мы можем добавить еще более строгую проверку, фактически проверив, что имя переменной внутри $ b
(используя очищенный ] c
) существует:
$ if declare -p "$c" &>/dev/null; then eval a\=\"\$"$c"\" ; fi
Вопрос будет привлекать мнения ...
eval
является частью стандартной оболочки:
Утилита
eval
должна создать команду, объединяя аргументы вместе, разделяя каждый с помощью символ<пробел>
. Созданная команда должна быть прочитана и выполнена оболочкой.
Вероятные причины недовольства eval
:
eval
позволяет вам устанавливать переменные на основе имен других переменных). С другой стороны, если данные, входящие в eval
, проверяются, например, результаты проверки имен файлов и проверки отсутствия кавычек, которые усложняют ситуацию, то это полезный инструмент. Обычно предлагаемые альтернативы менее переносимы, например, специфичны для некоторой конкретной реализации оболочки.
Дальнейшее чтение (но имейте в виду, что bash предлагает специфичные для реализации / нестандартные альтернативы):
Eval - простое решение для плохого кода. Вот пример, который приходит на ум:
Допустим, вы хотите получить элемент массива по ссылке. Вы можете использовать eval:
$ nov=(osc pap que)
$ rom=nov
$ eval echo \${$rom[2]}
que
Лучшим способом будет:
$ nov=(osc pap que)
$ rom=nov[2]
$ echo ${!rom}
que
Еще лучше:
$ tail -1 <<+
> osc
> pap
> que
> +
que