int
- это код операции для запуска программного прерывания.Программные прерывания нумеруются (от 0 до 255) и обрабатываются ядром.В системах Linux прерывание 128 (0x80) является обычной точкой входа для системных вызовов.Ядро ожидает аргументы системного вызова в регистрах;в частности, регистр% eax определяет, о каком системном вызове идет речь.
- Установите% ebx равным 0
- Вычислите% ebx + 23 и сохраните результат в% eax (код операции -
lea
как «эффективный адрес загрузки», но не требуется доступ к памяти; это просто хитрый способ сделать дополнение). - Системный вызов.% eax содержит 23, что означает, что системный вызов
setuid
.Этот системный вызов использует один аргумент (целевой UID), который можно найти в% ebx, который обычно содержит 0 в этой точке (он был задан в первой инструкции).Примечание: по возвращении регистры не изменяются, за исключением% eax, который содержит возвращаемое значение системного вызова, обычно 0 (если вызов был успешным). - Push% ebx в стек (который все еще0).
- Вставьте $ 0x68732f6e в стек.
- Вставьте $ 0x69622f2f в стек.Поскольку стек растет «вниз» и поскольку процессоры x86 используют кодирование с прямым порядком байтов, эффект от инструкций 4–6 состоит в том, что% esp (указатель стека) теперь указывает на последовательность из двенадцати байтов значений 2f 2f 62 69 6e 2f73 68 00 00 00 00 (в шестнадцатеричном формате).Это кодировка строки "// bin / sh" (с завершающим нулем и тремя дополнительными нулями после него).
- Переместить% esp в% ebx.Теперь% ebx содержит указатель на строку "// bin / sh", которая была построена выше.
- Push% eax в стеке (% eax равно 0 в этот момент, это возвращаемый статус из
setuid
). - Вставить% ebx в стек (указатель на "// bin / sh").Инструкции 8 и 9 строят в стеке массив из двух указателей, первый из которых является указателем на «// bin / sh», а второй - указателем NULL.Этот массив - то, что системный вызов
execve
будет использовать в качестве второго аргумента. - Переместить% esp в% ecx.Теперь% ecx указывает на массив, построенный с инструкциями 8 и 9.
- Расширение знака% eax в% edx:% eax.
cltd
- это синтаксис AT & T для того, что в документации Intel называется cdq
.Поскольку% eax в этой точке равен нулю, это также устанавливает% edx в ноль. - Установите% al (наименее значимый байт% eax) равным 11. Поскольку% eax было равно нулю, все значение% eaxсейчас 11.
- Системный вызов.Значение% eax (11) идентифицирует системный вызов как
execve
.execve
ожидает три аргумента, в% ebx (указатель на строку с именем исполняемого файла),% ecx (указатель на массив указателей на строки, которые являются аргументами программы, первый из которых является копией имени программы, для использования самой вызываемой программой) и% edx (указатель на массив указателей на строки, которые являются переменными среды; Linux допускает, что это значение равно NULL, для пустой среды), соответственно.
Таким образом, код сначала вызывает setuid(0)
, а затем вызывает execve("//bin/sh", x, 0)
, где x
указывает на массив из двух указателей, первый из которых является указателем на "// bin / sh", а другой равен NULL.
Этот код довольно запутанный, потому что он хочет избежать нулей: при сборке в двоичные коды операций последовательность команд использует только ненулевые байты.Например, если бы 12-я инструкция была movl $0xb,%eax
(установив весь% eax на 11), то двоичное представление этого кода операции содержало бы три байта значения 0. Отсутствие нуля делает эту последовательность пригодной для использования в качестве содержимогостроки с нулем в конце.Разумеется, это предназначено для атаки на ошибочные программы через переполнение буфера.