@krocki, если вдруг интересно, то регистр ESP - это вершинка стека. А штуковины вида "MOV EAX, [ESP+4]" - это чтение аргументов функции. Попробую примерно объяснить суть. Вызываешь ты функцию:
invoke Foo,1,2
Эквивалент:
PUSH 2
PUSH 1
CALL [Foo]
Как при этом работает PUSH? Допустим, что оба параметра у тебя - 32-битные. Стек изначально выглядит как-то так:
0000
0000
0000
Когда ты делаешь PUSH, стек сдвигается:
PUSH 2
0002
0000
0000
0000
А после вызова функции через стек передается адрес возврата, вместе с параметрами:
0x1234
0001
0002
0000
0000
0000
И вот ESP при этом будет указывать на 0x1234, [ESP+4] - 1, [ESP+8] - 2, типа того. Сама по себе команда push делает вот так:
PUSH arg - это:
SUB ESP,4
MOV [ESP], arg
Т.е. "отрезается" 4 байта на стеке и в них записывается аргумент, который на стек нужно положить.
PS: Я чуток дополню. Копирование ESP в EBP нужно для того, чтобы всегда знать, где изначально лежали параметры. Процедура, которую вызывают, обращается к аргументам на стеке через EBP, в который предварительно сохраняет ESP в прологе функции. Обычно даже в асме все стараются придерживаться сишного соглашения о вызовах.