Вызов и возврат из процедур
CALL вызов процедуры
RET возврат из процедуры
Теперь, поговорив о том, как выглядят процедуры, посмотрим, как нужно их вызывать и возвращаться из них. Инструкция CALL проста. Она неявно помещает адрес возврата в стек, так что, остановившись на первой инструкции вызванной процедуры и посмотрев на ESP, на вершине стека можно увидеть адрес возврата.
Операндом инструкции CALL может быть почти все, и в окне Disassembly отображаются вызовы, в параметрах которых указаны регистры, ссылки на память, параметры и глобальные смещения. Если в качестве параметра CALL выступает указатель с адресной ссылкой на память, то, для того чтобы точно увидеть процедуру, которую вы собираетесь вызвать, можно использовать поле эффективного адреса в окне Registers.
Вызов локальной функции будет прямым обращением по определенному адресу. Однако чаще можно видеть вызовы, выполняющиеся через указатели, которые, в общем случае, являются обращениями к импортированным функциям через таблицу адресов импорта (Import Address Table — IAT). Если загружены символы двоичного файла, через который выполняется пошаговый проход, то вы увидите нечто вроде первой инструкции CALL, показанной ниже в примере функции CallSomeFunctions. Этот код указывает, что вызов выполняется через IAT (префикс _imp_ является страшной тайной!). Пример функции CallSomeFunctions также показывает, как нужно вызвать локальную функцию.
void CaiiSomeFunctions ( void }
{
_asm
{
// Вызвать импортированную функцию GetLastError, у которой нет
// параметров. Регистр ЕАХ будет содержать возвращаемое значение.
// Это вызов через IAT, т. е. вызов через указатель.
CALL DWORD PTR [GetLastError]
// Если символы загружены, окно Disassembly покажет
// CALL DWORD PTR [_imp__GetLastError@0 (00402000)].
// Если символы не загружены, окно Disassembly покажет
// CALL DWORD PTR [00402000].
////////////////////////////////////////////////////////////////
// Вызвать функцию внутри этого файла.
CALL NOPFuncOne
// Если символы загружены, окно Disassembly покажет
// CALL NOPFuncOne (00401000).
// Если символы не загружены, окно Disassembly покажет
// CALL 00401000.
}
}
Инструкция RET выполняет возврат в вызывающую функцию, используя адрес на вершине стека (без какой бы то ни было его проверки). Нетрудно представить, что испорченный стек может выполнить возврат в любую точку приложения. За инструкцией RET иногда следует фиксированное число, которое определяет, сколько байт нужно извлечь из стека, чтобы учесть все параметры, помещенные в стек и переданные функции
.