демонстрационная программа с переполняющийся буфером, которую мы будем защищать
Откомпилируем файл компилятором gcc с настойками по умолчанию (то есть без оптимизации) и загрузим полученный elf в дизассемблер, чтобы посмотреть как выглядит стандартный пролог/эпилог функции f().
function_prologue:
push ebp ; // сохраняем старый указатель карда
mov ebp, esp ; // открываем новый кадр стека
sub esp, 98h ; // резервируем место под локальные переменные
; // тело программы
mov eax, [ebp+arg_0] ; // копируем аргумент в регистр eax
mov [esp+98h+var_94], eax ; // кладем eax
в стек
; // (выглядит как засылка eax
в лок. переменную
; // но в действительности это такая передача
; // аргументов, необычно но компилятору удобно)
lea eax, [ebp+var_88] ; // получаем указатель на лок. переменную var_88
mov [esp+98h+var_98], eax ; // кладем его в стек
call _strcpy ; // вызываем
_strcpy(&arg_0[0], &var_88[0])
movsx eax, byte ptr [eax] ; // eax = *((signed char*) eax);
mov [ebp+var_C], eax ; // копируем eax в локальную переменную var_C
mov eax, [ebp+var_C] ; // копируем содержимое var_C в eax
function_epilogue:
leave ; // mov esp, ebp/pop ebp
retn ; // выходим в материнскую функцию