Предполагается, что пролог вашей функции выглядит следующим образом:
pushl %ebp
movl %esp, %ebp
...
% ebp будет регистром, хранящим базовый указатель (также называемый указателем кадра). Базовый указатель - это то место, где в стеке начинается текущий кадр стека. Так как в x86 стек растет вниз, местные жители упоминаются как отрицательное смещение от% ebp. На параметры и обратный адрес ссылаются положительные смещения из% ebp. Значение в% ebp указывает на значение% ebp вызывающего абонента в стеке (которое было выдвинуто прологом). Это эффективно формирует связанный список базовых указателей, которые можно использовать для «обхода» стека. Примечание: это предполагает, что каждый кадр стека имеет базовый указатель; Существует оптимизация, называемая пропуском указателей кадров (FPO), которая освобождает% ebp для других целей.
Таким образом, при наличии функции с этим прологом, и если она была вызвана с помощью инструкции вызова (т. Е. Адрес возврата вызывающей стороны был помещен в стек), то 0x4 (% ebp) сохранит адрес возврата, поскольку это был последний отправленный элемент в стек до выполнения пролога вызываемого. Следовательно, ваш фрагмент кода приведет к выполнению следующей инструкции после того, как вызываемый абонент получит 15 байтов от конца инструкции вызова вызывающего, вместо следующей инструкции после вызова.
Edit: мои многочисленные правки до сих пор были для того, чтобы лучше объяснить мой ответ.