Переполняющиеся буфера - активные средства защиты

         

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                  ; // выходим в материнскую функцию


Содержание раздела