Stack-Shield
Несмотря на схожесть в названии со своим собратом, Shack-Shield действует совсем по другому принципу. Это еще одно расширение к gcc, последнюю версию которого можно скачать с http://www.angelfire.com/sk/stackshield, но иного типа. Если Stack-Guard реализован как патч к компилятору, "исправляющий" function_prologue и function_epilogue, то Stack-Shield "захватывает" ассемблерные файлы, сгенерированные компилятором (в UNIX-мире они имеют расширение .s), обрабатывает их, выплевывая защищенный ассемблерный файл, возвращаемый компилятору для окончательной трансляции в двоичный код. Такая схема дает Stack-Shiled'у намного большие возможности и мыщъху сразу же захотелось посмотреть как он ими воспользовался и можно ли его одолеть.
Соблазненный процессорными архитектурами с разнесенным стеком (один стек для хранения адресов возврата, другой— для локальный переменных), создатель Stack-Guard'а попытался "проэмулировать" на x86 нечто подобное. Для этой цели он использовал глобальный массив retarray на 256 адресов: эпилог копирует текущий адрес на вершину массива, определяемую указателем retprt, а пролог "стягивает" этот адрес с вершины и передает ему управление. Эта эмуляция далека от идеала, но сохраненный в стеке адрес возврата в ней вообще не используется и выполнение программы продолжится даже после того, как он будет затерт, что предотвращает DoS (впрочем, поскольку локальные переменные искажены, программа все равно рухнет).
function_prologue:
push eax ; // сохраняем регистры, которые изменяет Stack-Shied
push edx
mov eax, offset retpt ; // копируем в eax смещение указателя массива
retpt
cmp rettop, eax ; // смотрим - есть ли еще место?
jbe .LSHIELDPROLOG ; // если места нет,отказываемся от записи нового адреса
mov edx, [esp+8] ; // заносим в edx адрес возврата со стека
mov [eax], edx ; // сохраняем его в массиве адресов возврата
.LSHIELDPROLOG:
add [retptr],4 ; // увеличиваем указатель массива возвратов
; // на первый взгляд это явный баг,
; // но на самом деле - оптимизация!
pop edx ; // восстанавливаем регистры назад
pop eax ;
push ebp ; // сохраняем старый указатель карда стека
mov ebp, esp ; // открываем новый кадр
sub esp, 98h ; // резервируем место под локальные переменные
; // тело
функции
; // (такое же как в случае с Stack-Guard)
function_epilogue:
leave ; // закрываем кадр стека небезопасным путем
push eax ; // сохраняем регистры
push edx
add [retptr], -4 ; // уменьшаем указатель массива возвратов
mov eax, offset retptr ; // заносим в eax смещение массива возвратов
cmp eax, rettop ; // как на счет свободного места?
jbe .LSHIELDEPILOG ; // если места нет, значит и выталкивать нечего
mov edx, [eax] ; // снимаем сохраненный адрес со стека возвратов
mov [esp+8],edx ; // восстанавливаем стековый адрес не проверяя его
.LSHIELDEPILOG:
pop edx ; // восстанавливаем регистры
pop eax ;
ret ; // выходим в материнскую функцию