Отладка приложений

         

Вызов и возврат из процедур


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 иногда следует фиксированное число, которое определяет, сколько байт нужно извлечь из стека, чтобы учесть все параметры, помещенные в стек и переданные функции

.



Содержание раздела