Подключение функций, экспортируемых по порядковому значению
Должен честно сказать, что почти не поддерживал функций подключения, экспортируемых по порядковому значению, потому что подобная попытка весьма чревата ошибками (из-за того, что разные версии 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.