int
- это код операции для запуска программного прерывания. Программные прерывания нумеруются (от 0 до 255) и обрабатываются ядром. В системах Linux прерывание 128 (0x80) является обычной точкой входа для системных вызовов. Ядро ожидает аргументы системного вызова в регистрах; в частности, регистр% eax определяет, какой системный вызов мы говорим.
- Set% EBX до 0
- Compute% EBX + 23 и сохранить результат в% EAX (опкод является
lea
в качестве «нагрузки эффективного адреса», но не доступ к памяти участвуют, это просто хитрый способ сделать дополнение).
- Системный вызов. % eax содержит 23, что означает, что системный вызов
setuid
. Этот системный вызов использует один аргумент (целевой UID), который можно найти в% ebx, который удобно содержит 0 в этой точке (он был установлен в первой инструкции). Примечание: после возврата регистры не изменяются, за исключением% eax, который содержит возвращаемое значение системного вызова, обычно 0 (если вызов был успешным).
- Нажмите% ebx на стек (который по-прежнему равен 0).
- Push $ 0x68732f6e в стеке.
- Push $ 0x69622f2f в стеке. Поскольку стек растет «вниз», и поскольку процессоры x86 используют малое кодирование endian, эффект от инструкций с 4 по 6 заключается в том, что% esp (указатель стека) теперь указывает на последовательность из двенадцати байтов, значений 2f 2f 62 69 6e 2f 73 68 00 00 00 00 (в шестнадцатеричной форме). Это кодировка строки «// bin/sh» (с завершающим нулем и тремя дополнительными нулями впоследствии).
- Переместить% esp на% ebx. Теперь% ebx содержит указатель на строку «// bin/sh», которая была построена выше.
- Push% eax в стеке (% eax - 0 в этой точке, это возвращаемый статус от
setuid
).
- Push% ebx в стеке (указатель на «// bin/sh»). Инструкции 8 и 9 строят на стеке массив из двух указателей, первый из которых является указателем на «// bin/sh», а второй - на указатель NULL. Этот массив является тем, что системный вызов
execve
будет использовать в качестве второго аргумента.
- Переместить% esp в% ecx. Теперь% ecx указывает на массив, построенный с инструкциями 8 и 9.
- Sign-extend% 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
указывает на массив из двух указателей, первый из которых указатель «// бен/ш», в то время как другой является NULL.
Этот код довольно запутан, потому что он хочет избежать нулей: при сборке в двоичные коды операций последовательность команд использует только ненулевые байты. Например, если 12-я инструкция была movl $0xb,%eax
(установка всего% eax на 11), тогда двоичное представление этого кода операции содержало бы три байта значения 0. Отсутствие нуля делает эту последовательность пригодной для использования в качестве содержимого нулевая строка C. Разумеется, это предназначено для атаки на багги-программы через переполнение буфера.
U r Great !!!! ... Thnx много !!!!!!!! –
Pomin: на самом деле код запутан, потому что он пытается быть как можно меньше. Это модифицированная версия известного 24-байтового шеллкода, используемого Gera из Core Security (надеюсь, что я получил право на атрибуцию, я не знаю, как искать самый ранний вид этого кода в сети). – ninjalj