Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
362
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 15 Блокировка в многопоточных приложениях

553

 

 

DWORD dwOldProtect ;

//Возвращение параметра защиты в состояние,

//бывшее до перезаписи указателя на функцию. VERIFY ( VirtualProtect ( mbi_thunk.BaseAddress ,

mbi_thunk.RegionSize

,

mbi_thunk.Protect

,

&dwOldProtect

) ) ;

if ( NULL != pdwHooked )

{

// Увеличение общего числа перехваченных функций. *pdwHooked += 1 ;

}

}

}

// Увеличение указателей на обе таблицы. pOrigThunk++ ;

pRealThunk++ ;

}

// Все OK!

SetLastError ( ERROR_SUCCESS ) ;

return ( TRUE ) ;

}

HookImportedFunctionsByName не должна быть слишком сложной для понимания. После тщательной профилактической проверки каждого параметра я вызываю вспомогательную функцию GetNamedImportDescriptor, выполняющую поиск IMAGE_IM PORT_DESCRIPTOR для запрошенного модуля. Получив указатели на исходную и дей! ствительную IAT, я просматриваю исходную IAT и изучаю каждую функцию, им! портируемую по имени, чтобы узнать, есть ли она в списке paHookArray. Если фун! кция имеется в списке перехватываемых функций, я просто разрешаю запись в область памяти действительной IAT, записываю вместо адреса действительной функции адрес ловушки и возвращаю защиту памяти в исходное состояние. В исходный код BUGSLAYERUTIL.DLL я включил функцию блочного теста для Hook ImportedFunctionsByName, которая поможет вам со всем разобраться, если вы не очень внимательно следили за происходящим.

Теперь, когда вы представляете механизм перехвата импортируемых функций, займемся реализацией остальной части DeadlockDetection.

Детали реализации

Одна из моих основных целей при реализации DeadlockDetection состояла в том, чтобы сделать утилиту максимально ориентированной на использование данных и таблиц. Поразмыслив о том, как выполняется перехват функций DLL, вы пой! мете, что его механизм почти идентичен для всех функций, указанных в табл. 15!1. Функция!ловушка вызывается, определяет, отслеживается ли ее класс функций,

554 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

вызывает действительную функцию и (если для этого класса включено протоко! лирование) записывает информацию и выполняет возврат. Я должен был напи! сать ряд похожих функций!ловушек и хотел сделать их как можно проще. Слож! ные функции!ловушки — плодородная почва для ошибок, которые могут прокра! сться в ваш код, даже когда вы пытаетесь все упростить. Чуть ниже я расскажу про одну неприятную ошибку в коде DeadlockDetection в первом издании этой книги.

Лучше всего показать эту простоту, обсудив написание DLL DeadDetExt. Биб! лиотека DeadDetExt должна иметь три экспортируемых функции. Роль первых двух,

DeadDetExtOpen и DeadDetExtClose, очевидна. Интерес представляет DeadDetProcessEvent, вызываемая каждой функцией!ловушкой при наличии информации для записи. DeadDetProcessEvent принимает единственный параметр — указатель на структуру

DDEVENTINFO:

typedef struct tagDDEVENTINFO

{

// Идентификатор, определяющий содержание оставшейся части структуры.

eFuncEnum

eFunc

;

 

// Индикатор

предварительного

или заключительного вызова.

ePrePostEnum

ePrePost

;

 

// Адрес возврата. Он нужен

для нахождения вызвавшей функции.

DWORD

dwAddr

;

 

// Идентификатор вызвавшего

потока.

DWORD

dwThreadId

;

 

// Значение,

возвращаемое при

заключительных вызовах.

DWORD

dwRetValue

;

 

//Информация о параметрах. Приводите этот элемент к указателю

//на структуру, соответствующую функции, как описано ниже. При доступе

//к параметрам обращайтесь с ними, как со значениями только для чтения.

DWORD dwParams ;

} DDEVENTINFO , * LPDDEVENTINFO ;

Весь вывод для какой!либо функции из листинга 15!1 основан на информа! ции, содержащейся в структуре DDEVENTINFO. Большинство полей DDEVENTINFO гово! рит само за себя, а вот dwParams требует пояснения. Это поле является на самом деле указателем на параметры в том порядке, в котором они расположены в па! мяти.

Вглаве 7 я рассказал о том, как параметры передаются в стек. Напомню, что параметры функций с соглашениями вызова __stdcall и __cdecl передаются спра! ва налево, а стек растет по направлению от старших адресов памяти к младшим. Поле dwParams структуры DDEVENTINFO указывает на последний параметр в стеке, т. е. слева направо. Чтобы обеспечить легкое преобразование dwParams, я прибегнул к приведению типов.

Вфайле DEADLOCKDETECTION.H содержатся объявления typedef, описываю! щие списки параметров каждой перехватываемой функции. Например, если бы поле eFunc соответствовало значению eWaitForSingleObjectEx, то для получения параметров нужно было бы привести тип dwParams к LPWAITFORSINGLEOBJECTEX_PARAMS. Чтобы увидеть все это творческое приведение типов в действии, изучите код биб! лиотеки TEXTFILEDDEXT.DLL (см. CD, прилагаемый к книге).

ГЛАВА 15 Блокировка в многопоточных приложениях

555

 

 

Хотя обработка вывода относительно проста, сбор информации может оказаться сложным. Мне требовалось, чтобы DeadlockDetection перехватывала функции синхронизации из табл. 15!1, но я не хотел, чтобы функции!ловушки изменяли поведение действительных функций. Я также хотел получать параметры и возвра! щаемые значения и с легкостью писать функции!ловушки на C/C++. Я провел за отладчиком и дизассемблером немало времени, пока мне удалось сделать это правильно.

Первоначально я сделал все функции!ловушки сквозными (pass!through func! tion), чтобы они вызывали действительные функции непосредственно. Этот под! ход работал отлично. Затем я поместил параметры функций и возвращаемые ими значения в локальные переменные. Получение возвращаемого значения из дей! ствительной функции оказалось простым, но из!за того, что я начал реализацию DeadlockDetection на Visual C++ 6, у меня не было чистого способа получения адресов возврата в моих функциях!ловушках C/C++. Visual C++ .NET поддержива! ет внутреннюю (intrinsic) функцию _ReturnAddress, но в Visual C++ 6 такой возмож! ности не было. Мне нужно было значение DWORD прямо перед текущим указателем стека. Увы, в обычном C/C++ пролог функции уже выполнил бы все свои действия к тому времени, когда я смог бы получить управление, и указатель стека имел бы не то значение, которое мне было нужно.

Вы можете подумать, что указатель стека — это просто смещение, определяе! мое числом локальных переменных, но это не всегда так. Компилятор Visual C++ выполняет великолепную оптимизацию, так что при различных конфигурациях флагов оптимизации указатель стека может иметь разные значения. Так, когда вы объявляете переменную как локальную, компилятор может оптимизировать ра! боту с ней, сохранив в регистре, из!за чего она даже не появится в стеке.

Мне нужен был гарантированный способ получения указателя стека незави! симо от параметров оптимизации. В этот момент я начал думать, почему бы не объявить функции!ловушки как __declspec(naked) и не создать собственные про! лог и эпилог? Это дало бы мне полный контроль над регистром ESP независимо от параметров оптимизации. Кроме того, это облегчило бы и получение адреса возврата и параметров, так как они находятся по смещениям ESP+04h и ESP+08h соответственно. Помните, что мои пролог и эпилог не представляют собой ниче! го сверхъестественного, поэтому я все же выполняю обычные команды PUSH EBP и MOV EBP, ESP в прологе и MOV ESP, EBP и POP EBP в эпилоге.

Решив объявлять все функции!ловушки как __declspec(naked), я написал для обработки пролога и эпилога два макроса: HOOKFN_PROLOG и HOOKFN_EPILOG. Кроме того, я заблаговременно объявил в HOOKFN_PROLOG некоторые общие локальные переменные, нужные всем функциям!ловушкам. В число этих переменных вошли значение последней ошибки, dwLastError, и структура информации о событии, stEvtInfo, передаваемая в DLL DeadDetExt. Переменная dwLastError — просто еще один при! знак состояния, который мне нужно сохранять при перехвате функций.

При помощи функции SetLastError Windows API возвращает специальный код ошибки, предоставляя более подробную информацию в случае неудачи функции. Этот код ошибки может быть настоящим благословением, потому что он сооб! щает о причине неудачи API!функции. Так, если GetLastError возвратит 122, вы будете знать, что причиной ошибки стал недостаточный размер переданного в функцию

556 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

буфера. Все возвращаемые ОС коды ошибок указаны в файле WINERROR.H. Про! блема с функциями!ловушками в том, что во время своего выполнения они могут перезаписать значение последенй ошибки. Это может привести к катастрофе, если значение последней ошибки используется вашей программой.

Если при вызове CreateEvent вы хотите узнать, был ли возвращаемый описатель создан или просто открыт, вы также можете использовать код последней ошиб! ки: если CreateEvent просто открыла описатель, код будет иметь значение ERROR_AL READY_EXISTS. Одно из важнейших правил перехвата функций гласит, что вы не можете изменять ожидаемое поведение функции, поэтому сразу же после вызова действительной функции я должен был вызыать GetLastError, чтобы моя функция! ловушка могла правильно установить код последней ошибки, возвращаемый дей! ствительной функцией. Общее правило написания функций!ловушек таково: сразу после вызова действительной функции нужно вызывать GetLastError, а непосред! ственно перед выходом из ловушки устанавливать код ошибки при помощи Set LastError.

Стандартный вопрос отладки

Если теперь доступна ReturnAddress, почему вы не использовали ее, а пошли на все эти проблемы?

Когда пришло время обновить DeadlockDetection для второго издания этой книги, я думал немного упростить свою жизнь и изменить макрос HOOK FN_PROLOG, чтобы он использовал новую внутреннюю функцию _ReturnAddress. Это значио бы, что я могу избавиться от объявлений naked, привести функ! ции к более нормальному виду и не создавать собственные пролог и эпи! лог. Однако существующие макросы дают мне одно большое преимущество: я могу обращаться с параметрами, как с блоками памяти, и передавать их прямо в функцию вывода. Если б я применял стандартные функции, мне нужно было бы выполнять странное приведение типов для достижения того же результата. Кроме того, у меня был самый весомый аргумент: имевший! ся код работал очень хорошо, и мне не хотелось его переписывать. Поэто! му я оставил функции с соглашением naked прежними.

В этот момент я подумал, что все, кроме тестирования, сделано. Увы, во время первого теста я нашел ошибку: между вызовами ловушек я не сохранял регистры ESI и EDI, потому что в документации к встроенному ассемблеру сказано, что их сохранять не требуется. После решения этой проблемы казалось, что Deadlock! Detection работает прекрасно. Однако когда я начал сравнивать регистры до, во время и после вызовов функций, я заметил, что я не возвращаю значения, сохра! няемые действительными функциями в EBX, ECX и EDX и, что еще хуже, в регистре флагов. Хотя я не видел в этом никаких проблем и в документации говорилось, что эти регистры сохранять не требуется, я все же был озабочен тем, что мои функции!ловушки изменяли состояние приложения. Для сохранения значений регистров после вызовов действительных функций я объявил структуру REGSTATE, чтобы можно было восстанавливать регистры по возвращении из функции!ловуш! ки. Для сохранения и восстановления регистров я создал два дополнительных

ГЛАВА 15 Блокировка в многопоточных приложениях

557

 

 

макроса, REAL_FUNC_PRE_CALL и REAL_FUNC_POST_CALL, которые размещаю до и после вызова действительной функции, выполняемого функцией!ловушкой.

После дополнительного тестирования я обнаружил еще одну проблему: в заклю! чительных компоновках с полной оптимизацией программа по необъяснимой причине часто терпела крах. В конце концов я выяснил, что ошибки вызваны влиянием оптимизации на некоторые из моих функций!ловушек. Конечно, опти! мизатор пытался помочь, но в итоге приносил больше вреда, чем пользы. Я очень внимательно подошел к работе с регистрами в своих ловушках и использовал только EAX или непосредственно память стека. Однако, несмотря на все меры предосторожности по сохранению регистров, я обнаружил, что в отладочных компоновках команды:

MOV DWORD PTR [EBP 018h] , 00000002h

MOV DWORD PTR [EBP 014h] , 00000002h

преобразовывались оптимизатором в:

PUSH

002h

POP

EBX

MOV

DWORD PTR [EBP 01Ch] , EBX

MOV

DWORD PTR [EBP 018h] , EBX

Легко увидеть, что во втором фрагменте команда POP EBX искажает значение реги! стра. Чтобы помешать оптимизатору искажать регистры без моего ведома, я от! ключил оптимизацию для всех функций!ловушек, поместив директиву:

#pragma optimize("", off )

в начало каждого файла. Кроме того, отключение оптимизации упростило отлад! ку, потому что генерируемый компилятором неоптимизированный код очень похож

ив заключительных, и в отладочных компоновках.

Влистинге 15!3 приведена заключительная версия внутреннего заголовочно! го файла DD_FUNCS.H, в котором объявлены все специальные макросы для функ! ций!ловушек. В комментарии в начале файла вы можете найти два примера функ! ций!ловушек, поясняющих применение каждого специального макроса. Внима! тельно изучите пример DDSimpTest, который можно найти на CD. Исследуйте вызовы функций на языке ассемблера полностью, потому что это единственный способ увидеть все выполняемые действия.

Листинг 15-3. Файл DD_FUNCS.H

/*——————————————————————————————————————————————————————————————————————

Отладка приложений для Microsoft .NET и Microsoft Windows Copyright © 1997 2003 John Robbins — All rights reserved.

——————————————————————————————————————————————————————————————————————— Прототипы для всех функций ловушек и кода пролога/эпилога

——————————————————————————————————————————————————————————————————————*/

#ifndef _DD_FUNCS_H #define _DD_FUNCS_H

/*//////////////////////////////////////////////////////////////////////

см. след. стр.

558 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

Все функции ловушки объявляются с соглашением __declspec(naked), поэтому я должен сам написать пролог и эпилог. Я должен предоставить собственные пролог и эпилог по нескольким причинам.

1.Функции, написанные на C, не позволяют контролировать использование регистров и сохранение исходных регистров компилятором.

Отсутствие контроля над регистрами означает, что получить адрес возврата почти невозможно. Для проекта DeadlockDetection адрес возврата очень важен.

2.Я хотел передавать параметры в функцию обработки из DLL расширения, не копируя при каждом вызове функции большие объемы данных.

3.Так как почти все функции ловушки ведут себя похожим образом, я могу присвоить значения общим переменным, нужным во всех функциях.

4.Функции ловушки не должны изменять возвращаемые значения, в том числе значение, возвращаемое GetLastError. Собственные пролог

и эпилог позволяют мне значительно упростить возвращение правильного значения. Кроме того, я должен восстанавливать значения регистров в то состояние, в каком они были после вызова действительной функции.

Базовая функция ловушка требует только двух макросов: HOOKFN_STARTUP и HOOKFN_SHUTDOWN.

Как вы можете видеть, это здорово облегчает работу!

BOOL NAKEDDEF DD_InitializeCriticalSectionAndSpinCount (

LPCRITICAL_SECTION lpCriticalSection,

DWORD

dwSpinCount

)

{

 

 

 

HOOKFN_STARTUP ( eInitializeCriticalSectionAndSpinCount

,

 

DDOPT_CRITSEC

 

,

 

0

 

) ;

 

InitializeCriticalSectionAndSpinCount ( lpCriticalSection ,

 

 

dwSpinCount

) ;

 

HOOKFN_SHUTDOWN ( 2 , DDOPT_CRITSEC ) ;

}

Если надо выполнить специальную обработку и вы не хотите делать чего то, что нельзя выполнить, используя обычные макросы, вам помогут макросы:

HOOKFN_PROLOG

REAL_FUNC_PRE_CALL

REAL_FUNC_POST_CALL

HOOKFN_EPILOG

Пример функции, использующей указанные макросы:

HMODULE NAKEDDEF DD_LoadLibraryA ( LPCSTR lpLibFileName )

{

// Все локальные переменные должны быть объявлены

ГЛАВА 15 Блокировка в многопоточных приложениях

559

 

 

//до макроса HOOKFN_PROLOG. Он создает фактический

//пролог функции и автоматически определяет некоторые

//важные переменные, такие как stEvtInfo (DDEVENTINFO). HOOKFN_PROLOG ( ) ;

//Перед вызовом действительной функции нужно указать

//макрос REAL_FUNC_PRE_CALL, чтобы регистры имели те

//же значения, что и при вызове функции ловушки.

REAL_FUNC_PRE_CALL ( ) ;

//Вызов действительной функции. LoadLibraryA ( lpLibFileName ) ;

//Макрос для сохранения регистров после вызова действительной

//функции. Благодаря этому я могу присвоить регистрам нужные

//значения перед выходом из ловушки, сделав ее "невидимой". REAL_FUNC_POST_CALL ( ) ;

//Операции, специфичные для ловушки LoadLibraryA.

if ( NULL != stEvtInfo.dwRetValue )

{

HookAllLoadedModules ( ) ;

}

//Этот макрос создает эпилог функции, восстанавливая

//регистры и стек. Значение, передаваемое макросу, — это

//число параметров, переданных в функцию. Вырезая и вставляя

//данный макрос в другие функции, будьте очень внимательны,

//чтобы не допустить ошибку "наследования при редактировании"! HOOKFN_EPILOG ( 1 ) ;

}

//////////////////////////////////////////////////////////////////////*/

/*//////////////////////////////////////////////////////////////////////

//Структура состояния регистров. Я использую эту структуру, чтобы

//гарантировать, что ВСЕ регистры по возвращении будут иметь такие

//же значения, какие были после выполнения действительной функции.

//Обратите внимание, что EBP и ESP обрабатываются во время пролога.

//////////////////////////////////////////////////////////////////////*/

typedef struct tag_REGSTATE

{

DWORD dwEAX ; DWORD dwEBX ; DWORD dwECX ; DWORD dwEDX ;

DWORD dwEDI ;

DWORD dwESI ;

DWORD dwEFL ;

} REGSTATE , * PREGSTATE ;

/*////////////////////////////////////////////////////////////////////// // Макросы для сохранения и восстановления ESI между

см. след. стр.

560ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

//вызовами функций в отладочных компоновках.

//////////////////////////////////////////////////////////////////////*/

#ifdef _DEBUG

#define SAVE_ESI()

__asm PUSH

ESI

#define RESTORE_ESI()

__asm POP

ESI

#else

 

 

#define SAVE_ESI()

 

 

#define RESTORE_ESI()

 

 

#endif

 

 

/*////////////////////////////////////////////////////////////////////// // Общий пролог для всех функций DD_*.

//////////////////////////////////////////////////////////////////////*/

#define HOOKFN_PROLOG()

 

 

 

\

/* Все функции ловушки автоматически получают

*/\

/* три одинаковых локальных

переменных.

*/\

DDEVENTINFO

stEvtInfo

;

/*

Информация о событии для функции.

*/\

DWORD

dwLastError

;

/*

Значение последней ошибки.

*/\

REGSTATE

stRegState

;

/*

Состояние регистров, нужное для их

*/\

 

 

 

/*

правильного восстановления.

*/\

{

 

 

 

 

\

__asm PUSH

EBP

 

/*

Всегда явно сохраняйте EBP.

*/\

__asm MOV

EBP , ESP

 

/*

Настройка кадра стека.

*/\

__asm MOV

EAX , ESP

 

/*

Получение указателя стека для подсчета*/\

 

 

 

/*

адреса возврата и адреса параметров.

*/\

SAVE_ESI ( )

 

 

/*

Сохранение ESI в отлад. компоновках.

*/\

__asm SUB

ESP , __LOCAL_SIZE

/* Место для локальных переменных.

*/\

__asm ADD

EAX , 04h + 04h

/*

Нужно учесть команду PUSH EBP

*/\

 

 

 

/*

и адрес возврата.

*/\

 

 

 

/*

Сохранение начала параметров в стеке. */\

__asm MOV

[stEvtInfo.dwParams] , EAX

\

__asm SUB

EAX , 04h

 

/*

Вернуться к адресу возврата.

*/\

__asm MOV

EAX , [EAX]

 

/*

Теперь EAX содержит адрес возврата.

*/\

 

 

 

/*

Сохранение адреса возврата.

*/\

__asm MOV

[stEvtInfo.dwAddr]

, EAX

\

__asm MOV

dwLastError , 0

/*

Инициализация dwLastError.

*/\

 

 

 

/*

Инициализация информации о событии.

*/\

__asm MOV

[stEvtInfo.eFunc] , eUNINITIALIZEDFE

\

__asm MOV

[stRegState.dwEDI]

, EDI /* Сохранение двух регистров,

*/\

__asm MOV

[stRegState.dwESI]

, ESI /* которые нужно сохранять

*/\

 

 

 

 

/* между вызовами функций.

*/\

}

 

 

 

 

 

/*//////////////////////////////////////////////////////////////////////

//Общий эпилог для всех функций DD_*. INumParams — это число

//параметров функции, используемое для восстановления

//правильного состояния стека после вызова ловушки.

//////////////////////////////////////////////////////////////////////*/

#define HOOKFN_EPILOG(iNumParams)

\

{

\

 

ГЛАВА 15 Блокировка в многопоточных приложениях

561

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

SetLastError ( dwLastError ) ;

/*

Установка кода

последней

*/\

 

 

 

/*

ошибки действительной ф ции.

*/\

 

__asm ADD

ESP , __LOCAL_SIZE

/*

Добавление к ESP размера

*/\

 

 

 

/*

локальных переменных.

*/\

 

__asm MOV

EBX , [stRegState.dwEBX]/*

Восстановление

всех регистров,*/\

 

__asm MOV

ECX , [stRegState.dwECX]/*

чтобы этот вызов был

*/\

 

__asm MOV

EDX , [stRegState.dwEDX]/*

прозрачным для

перехваченной

*/\

 

__asm MOV

EDI , [stRegState.dwEDI]/*

функции.

 

*/\

 

__asm MOV

ESI , [stRegState.dwESI]

 

 

 

\

 

__asm MOV

EAX , [stRegState.dwEFL]

 

 

 

\

 

__asm SAHF

 

 

 

 

\

 

__asm MOV

EAX , [stRegState.dwEAX]

 

 

 

\

 

RESTORE_ESI

( )

/*

Восстановление

ESI в

*/\

 

 

 

/*

отладочных компоновках

*/\

 

__asm MOV

ESP , EBP

/*

Восстановление

ESP.

*/\

 

__asm POP

EBP

/*

Восстановление

EBP.

*/\

 

__asm RET

iNumParams * 4

/*

Восстановление

стека для

*/\

 

 

 

/*

функций с соглашением stdcall.*/\

 

}

 

 

 

 

 

 

/*//////////////////////////////////////////////////////////////////////

//Макрос REAL_FUNC_PRE_CALL нужно размещать НЕПОСРЕДСТВЕННО

//*ПЕРЕД* ЛЮБЫМ вызовом действительной функции, обрабатываемым

//ловушкой. Этот макрос гарантирует, что регистры EDI и ESI будут

//иметь те же значения, что и при вызове функции ловушки.

//////////////////////////////////////////////////////////////////////*/

#define REAL_FUNC_PRE_CALL()

 

 

 

\

{

 

 

 

 

 

 

\

__asm MOV

EDI

,

[stRegState.dwEDI]

/*

Восстановление

EDI.

*/\

__asm MOV

ESI

,

[stRegState.dwESI]

/*

Восстановление

ESI.

*/\

}

 

 

 

 

 

 

 

/*//////////////////////////////////////////////////////////////////////

//Макрос REAL_FUNC_POST_CALL нужно размещать СРАЗУ ЖЕ *ПОСЛЕ*

//ЛЮБОГО вызова действительной функции, обрабатываемого ловушкой.

//Он сохраняет значения всех регистров после вызова действительной

//функции, чтобы эпилог функции ловушки мог вернуть те же значения,

//что и действительная функция.

//////////////////////////////////////////////////////////////////////*/

#define REAL_FUNC_POST_CALL()

 

\

{

 

 

\

__asm MOV

[stRegState.dwEAX] , EAX

/* Сохранение значения EAX.

*/\

__asm MOV

[stRegState.dwEBX] , EBX

/* Сохранение значения EBX.

*/\

__asm MOV

[stRegState.dwECX] , ECX

/* Сохранение значения ECX.

*/\

__asm MOV

[stRegState.dwEDX] , EDX

/* Сохранение значения EDX.

*/\

__asm MOV

[stRegState.dwEDI] , EDI

/* Сохранение значения EDI.

*/\

__asm MOV

[stRegState.dwESI] , ESI

/* Сохранение значения ESI.

*/\

__asm XOR

EAX , EAX

/* Обнуление EAX.

*/\

__asm LAHF

 

/* Загрузка флагов в AH.

*/\

__asm MOV

[stRegState.dwEFL] , EAX

/* Сохранение флагов.

*/\

см. след. стр.

562

ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

\

dwLastError = GetLastError ( ) ;

 

/* Сохр. кода последней ошибки.*/\

{

 

 

 

 

 

\

__asm MOV

EAX , [stRegState.dwEAX]

 

/* Восстановление EAX

*/\

 

 

 

 

 

/* Установка возвращаемого

*/\

 

 

 

 

 

/* значения.

*/\

__asm MOV

[stEvtInfo.dwRetValue] , EAX

\

}

 

 

 

 

 

 

/*//////////////////////////////////////////////////////////////////////

// Удобный макрос для заполнения структуры информации о событии

 

//////////////////////////////////////////////////////////////////////*/

#define FILL_EVENTINFO(eFn)

 

\

 

 

stEvtInfo.eFunc

= eFn

;

\

 

 

stEvtInfo.ePrePost

= ePostCall ;

\

 

 

stEvtInfo.dwThreadId = GetCurrentThreadId ( )

 

/*//////////////////////////////////////////////////////////////////////

//Макросы для второй версии программы, ЗНАЧИТЕЛЬНО

//облегчающие определение функций ловушек

//////////////////////////////////////////////////////////////////////*/

//Объявляйте его в начале каждой функции ловушки.

//eFunc значение перечисления, соответствующее функции.

//SynchClassType – значение флага DDOPT_*, указывающее на класс

//

обрабатываемой вами функции.

 

// bRecordPreCall – выполняет запись информации об этой функции.

 

#define HOOKFN_STARTUP(eFunc,SynchClassType,bRecordPreCall)

\

 

HOOKFN_PROLOG ( ) ;

\

 

if ( TRUE == DoLogging ( SynchClassType ) )

\

 

{

\

 

FILL_EVENTINFO ( eFunc ) ;

\

 

if ( TRUE == (int)bRecordPreCall )

\

 

{

\

 

stEvtInfo.ePrePost = ePreCall ;

\

 

ProcessEvent ( &stEvtInfo ) ;

\

 

}

\

 

}

\

 

REAL_FUNC_PRE_CALL ( ) ;

 

/*//////////////////////////////////////////////////////////////////////

//Макрос завершения функции ловушки.

//iNuMParams число параметров, переданных функции.

//SynchClassType – класс функции синхронизации.

//////////////////////////////////////////////////////////////////////*/

#define HOOKFN_SHUTDOWN(iNumParams,SynchClass)

\

REAL_FUNC_POST_CALL ( ) ;

\

if ( TRUE == DoLogging ( SynchClass ) )

\

{

\

stEvtInfo.ePrePost = ePostCall ;

\

ProcessEvent ( &stEvtInfo ) ;

\

Соседние файлы в предмете Программирование на C++