После того, как main
вернется в ваш шелл-код, ESP, вероятно, будет указывать чуть выше этого буфера. И EIP указывает на начало этого; вот что означает возврат в него.
Пара push
инструкций может изменить машинный код в конце буфера, приводя к SIGILL с EIP, указывающим на байт, который вы только что выдвинули.
Вероятно, самое простое решение - от add esp, -128
до go до вашего буфера. Или sub esp, -128
до go выше по стеку. (-128
- это наибольшее значение 8-битного немедленного значения, которое вы можете использовать, избегая введения нулей в машинном коде с помощью sub esp, 128
или 1024
. Если вы хотите переместить стек дальше, вы, конечно, можете построить большее значение в регистр.)
Я не проверял это предположение, но вы можете подтвердить его в GDB, войдя в свой шелл-код одним нажатием si
с конца main
до пошаговые инструкции.
Используйте disas
после каждой инструкции, чтобы увидеть разборку. Или используйте layout reg
. См. Нижнюю часть { ссылка } для получения дополнительных советов по отладке GDB.
Данное решение является более сложным, поскольку оно, по-видимому, устанавливает фактический массив argv
вместо простой передачи значения NULL. указатели для char **argv
и char **envp
. (Который в Linux обрабатывается так же, как действительные указатели для пустых массивов с нулевым символом в конце: http://man7.org/linux/man-pages/man2/execve.2.html#NOTES).
Но ключевое отличие состоит в том, что он использует jmp / call / pop чтобы получить указатель на строку уже в памяти. Это только один слот стека, а не три . (Конец его полезной нагрузки перед адресом возврата - данные, а не инструкции, но он потерпит неудачу по-другому, если он сделает слишком много нажатий и перезапишет строку вместо простого хранения терминатора 0
. call
переходит назад до выдвинутый адрес возврата фактически изменяет буфер, но если он перезапишет что-нибудь ближе к концу, он все равно сломается.)
@ Маргарет рассмотрела это более подробно, и заметил, что это всего лишь 3-й пу sh, который ломает что-либо . Это имеет смысл: первые два предположительно перезаписывают часть полезной нагрузки, которая содержала новый адрес возврата и сохраненное значение EBP. И так уж получилось, что компилятор поместил буфер main
в смежный с этим.
Если вы действительно использовали tcc
, а не gcc
, это, вероятно, не удивительно. G CC выровнял бы его на 16 и, вероятно, по той или иной причине оставил бы пробел между буфером и вершиной стекового кадра.