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

         

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


Должен честно сказать, что почти не поддерживал функций подключения, экспортируемых по порядковому значению, потому что подобная попытка весьма чревата ошибками (из-за того, что разные версии MFC DLL используют различные порядковые значения). Однако если абстрагироваться от проблем, связанных с версиями, то процесс подключения по порядковому значению почти идентичен подключению по имени. Сравните функцию HookordinaiExport, показанную в листинге 14-4, с функцией HookimportedFunctionsByName, рассмотренной в главе 12, и вы увидите, что обе функции выполняют много одинаковых действий.

Листинг 14-4. Функция HookordinaiExport 

BOOL BUGSUTILJ3LLINTERFACE _stdcall

HookordinaiExport ( HMODULE hModule , 

LPCTSTR szImportMod, 

DWORD dwOrdinal ,

 PROC pHookFunc , 

PROC * ppOrigAddr )

{

// Проверить параметры с помощью утверждений.

 ASSERT ( NULL != hModule);

ASSERT ( FALSE == IsBadStringPtr ( szImportMod, MAX_PATH)); 

ASSERT ( 0 != dwOrdinal);

ASSERT ( FALSE = IsBadCodePtr ( pHookFunc));

 // Выполнить проверку ошибок для параметров.

if ( ( NULL == hModule ' ) | |

 ( TRUE == IsBadStringPtr ( szImportMod, MAX_PATH)) || 

( 0 == dwOrdinal ) I I ( TRUE == IsBadCodePtr ( pHookFunc) ) )

 {

SetLastErrorEx ( ERROR_INVALID_PARAMETER, SLE_ERROR);

 return ( FALSE); 

}

if ( NULL != ppOrigAddr)

{

ASSERT ( FALSE ==

IsBadWritePtr ( ppOrigAddr, sizeof ( PROG))); 

if ( TRUE == IsBadWritePtr ( ppOrigAddr, sizeof ( PROC)))

 {

SetLastErrorEx ( ERROR_INVALID_PARAMETER, SLE_ERROR);

 return ( FALSE);

 }

 }

// Получить конкретный дескриптор импорта.

 PIMAGE_IMPORT_DESCRIPTOR plmportDesc =

GetNamedlmportDescriptor ( hModule, szImportMod);

 if ( NULL == plmportDesc) 

{

// Запрошенный модуль не был импортирован. Не возвращать ошибку,

 return ( TRUE); 

}

// Получить информацию об исходных переходниках для этого DLL

. // Невозможно использовать информацию переходников, хранящуюся в


 // pImportDesc->FirstThunk, т. к. загрузчик уже изменил этот массив 

// при установке всех импортов. Исходный переходник обеспечивает

 // доступ к именам функций.

 PIMAGE_THUNK_DATA pOrigThunk =

MakePtr ( PIMAGE_THUNK_DATA

hModule , 

pImportDesc->OriginalFirstThunk );

// Получить массив p!mportDesc->FirstThunk, в котором будут

 // выполняться подключения и вся черная работа.

PIMAGE_THUNK_DATA pRealThunk = MakePtr ( PIMAGE_THUNK_DATA ,

hModule , 

pImportDesc->FirstThunk );

// Флажок будет устанавливаться из переходника,

// что облегчает его поиск.

DWORD dwCompareOrdinal = IMAGE JDRDINAL_FLAG | dwOrdinal;

// Цикл поиска подключаемых функций.

while ( NULL != pOrigThunk->ul.Function)

{

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

 // порядковому значению, а не по имени, 

if ( IMAGE__ORDINAL_FLAG ==

( pOrigThunk->ul.Ordinal & IMAGE_ORDINAL_FLAG))

 {

// Найдена ли функция подключения? 

if ( dwCompareOrdinal == pOrigThunk->ul.Ordinal) . 

{

// Функция для подключения найдена. Теперь нужно

 // изменить защиту памяти на "read-write" (для записи),

 // прежде чем перезаписывать указатели функций. Заметьте,

 // что ничего не записывается в реальную область 

// переходников!

MEMORY_BASIC__IN FORMATION mbi_thunk ; 

VirtualQuery ( pRealThunk , 

 &mbi_thunk , 

sizeof ( MEMORY_BASIC_INFORMATION) ); 

if ( FALSE == VirtualProtect ( mbi_thunk.BaseAddress,

rabi_thunk.RegionSize ,

 PAGE_READWRITE ,

&mbi_thunk.Protect ))

 {

ASSERT ( !"VirtualProtect failed!");

 // Здесь приходится фиксировать неуспешное 

// выполнение функции (возвращая FALSE),

 // предварительно указав причину ошибки.

 SetLastErrorEx ( ERROR__INVALID_PARAMETER,

SLE^ERROR );

return ( FALSE); 

}

// Сохранить исходные адреса, если требуется

 if ( NULL != ppOrigAddr)



 {

*ppOrigAddr = (PROC)pRealThunk->ul.Function; 

}

// Microsoft имеет два различных определения 

// РIМАСЕ_ТШ№С_ОАТА-полей для будущей поддержки Win64

 // Будет использован самый последний набор заголовков

//из W2K RC2 Platform SDK, с которыми будут иметь дело

 // заголовки из Visual C++ 6 Service Pack 3.

// Подключить функцию (DWORD*)SpRealThunk->ul.Function; 

*pTemp = (DWORD)(pHookFunc); 

DWORD dwOldProtect;

// Изменить защиту обратно к тому состоянию, которое

 // предшествовало переписыванию указателя функции.

 VERIFY ( VirtualProtect ( mbi_thunk.BaseAddress,

mbi_thunk.RegionSize ,

 mbi_thunk.Protect , 

sdwOldProtect )); 

// Жизнь прекрасна! 

SetLastError ( ERROR_SUCCESS);

 return ( TRUE);

 } 

}

// Инкремент обеих таблиц. pOrigThunk++; pRealThunk++;

 }

// Ничего не было подключено. Технически это не ошибка. Это просто

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

 SetLastError ( ERROR_SDCCESS);

 return ( FALSE); 

}

Для реализации обработки AfxTrace без ее подключения, пришлось бы просматривать стек при каждом вызове, чтобы вернуться к реальному вызову функции OutputDebugstring. Дополнительная работа на каждом вызове была бы медленнее, по сравнению с прямым подключением AfxTrace. Кроме того, если бы я игнорировал AfxTrace, то утилита LIMODS была бы, в основном, бесполезна для MFC-программистов. И, наконец, я предпочел создавать утилиту LIMODS настолько полной, насколько это возможно, причем я был вынужден дважды проверять версии MFC DLL.



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