Часть перезаписи ret-адреса распределяется между обеими атаками. Как видно из приведенного выше ответа, вы просто возвращались в код сборки, который вы написали. Этот ассемблерный код, скажем, порождает оболочку пользователя root.
Ни в одной из этих атак вы не перезапишете исходный код. Рассматривая его с точки зрения сборки, исходный код находится в сегменте .text и всегда был (или, по крайней мере, все, что я знаю) защищен от записи. Чтобы воспользоваться этим преимуществом, нужно было написать код, который вы предварительно собрали в сегменты памяти, а затем перейти к этому коду. Код, как правило, находился в «сегменте стека» или рядом с ним, и, переполнив все, что вы выбрали для перекрытия, вы перенаправили бы трафик с (скажем) ретрансляционного адреса туда. Другие места атаки включали переменную среды, которую вы предварительно создали и заполнили своим шеллкодом; или куча, или указатели функций, или PLT. Вставленный таким образом код обычно использует вызов system () для выполнения желаемого, но для этого вам необходимо иметь возможность «выполнять» в областях памяти (или иметь записи перемещения, которые вы намереваетесь использовать, объявленные как доступные для записи).
Разница между этими двумя атаками заключается в том, что после того, как память стала практически неисполнимой, тип атаки a (переполнение стека) был в значительной степени облажан. Попытка обойти этот тип атаки была тогда, когда вы пишете, вместо этого получить прямой доступ к функциям совместно используемой библиотеки - иначе вам больше не нужно было сначала записывать свой код в сегмент стека или в другое место и выполнять его там. Тем не менее, я считаю, что атаки типа libc также в значительной степени исправлены; У меня нет подробностей под рукой; и возможно я ошибаюсь.
Если вы хотите узнать, как в наши дни предотвращается любая из этих атак, или хотя бы прочитать о некоторых ключевых идеях, начните с Google 'Smashing the Stack в 2011' (или 2010).