Понимание работы программы на низком уровне важно для программиста. Я объясню что такое фрейм в стеке.
Что такое стек фрейм?
В программах написанных таких, как C/C++, возможно работать со стеком. В linux, стек всегда находится выше всех в адресном пространстве, например ему дается адрес 0xff_ff_ff_ff, если это 32 битная архитектура процессора, то каждый фрейм хранит в себе только 32 битное число, если 64, то 64 битное число.
Когда мы вызываем функцию, то в стековый фрейм помещается адрес следующий за этим вызовом. В данном случае, после выхода из test, будет выполняться call func_0.
Функция test выглядит вот так.
С 16 по 18 строку мы сохраняем адрес стека в регистре ebp и выделяем для локальных переменных 0x10 (16 байт памяти). Если наш стек перед вызовом функции test был 0xff_ff_ff_ff, то так как оператор call помещает в стек следующий за ним адрес, то регистр стека начинает указывать на 0xff_ff_ff_fb. Получается, что при добавлении новых данных в стек, он уменьшается. Теперь мы выделили ещё 16 байт для локальных переменных с помощью вычитания 0x10 (в шестнадцатеричном исчислении). Теперь адрес стека в регистре esp равен 0xff_ff_ff_eb.
В 19 строке мы загружаем текущий адрес стека в регистр eax.
В 20 строке мы помещаем по этом адресу, который указан в eax, то-есть в локальную переменную 32 битную или можно сказать, что мы помещаем значение 0x10101010 в стек фрейм.
Далее в 21 строке есть оператор leave, который возвращает стек в состояние, до 16 строки, и у нас остается на верхушке стека адрес, где есть оператор call func_0. В 22 строке с помощью оператора ret мы переходим на адрес, который находится на верхушке стека и убираем этот фрейм. Теперь стек снова равен 0xff_ff_ff_ff.
Далее выполняется функция func_0
В строках с 6 по 8 происходит такая же работа для выделения стека для локальных переменных. В 9 строке мы помещаем в ebx значение верхушки стека, как вы думаете, какое там будет значение? Значение будет 0x10101010, то, которое мы указали в прошлой функции в локальной переменной. Всё потому, что когда верхушка стека перемещается, данные никак не затираются и мы можем получить данные из прошлых функций, просто выделив память для стека или указать адрес смещения от текущего стека, если не выделять память для локальных переменных. Давайте теперь посмотрим наш код на C.
Как видно из кода, мы в func0 создали две переменных с первоначальными значениями. В func1 мы решили понадеяться на наш стек и создали переменные без начальных значений. Если вы выполните этот код, то printf выведет значения 18 и 24. Так можно скрывать какую-то работу или определять что кто-то скрывает такую работу и искать где в стеке дается нужное нам значение.
Всем спасибо за внимание!