Я использую расширенный код внедрения кода для запуска .dll на удаленном процессе.
Вы можете найти, как это работает / фрагмент кода, например, здесь:
https://sourceforge.net/p/diagnostic/svn/HEAD/tree/src/RemoteInit.cpp
Я заметил, что в некоторых приложениях этот подход не работает — он приводит к сбою хост-приложения. Похоже, что основной проблемой является специальное программное обеспечение сторонних производителей, такое как ConEmuHk64.dll
который перехватывает kernel32.dll GetProcAddress
предоставляя свою собственную функцию ловушки — после этого я получаю указатель на функцию следующим образом:
*((FARPROC*) &info.pfuncGetProcAddress) = GetProcAddress(hKernel32, "GetProcAddress");
Но вместо этого я получаю указатель на функцию, расположенную в ConEmuHk64.dll.
В моем собственном процессе вызов этой функции приемлем, но при попытке сделать то же самое в удаленном процессе — происходит сбой, так как ConEmuHk64.dll
не обязательно доступен там.
Я выяснил механизм автоматического определения правильного адреса этой функции путем ручного перехода в другой заголовок DOS / NE — вот фрагмент кода:
//
// We use GetProcAddress as a base function, with exception to when GetProcAddress itself is hooked by 3-rd party
// software and pointer to function returned to us is incorrect - then we try to locate function manually by
// ourselfes.
//
FARPROC GetProcAddress2( HMODULE hDll, char* funcName )
{
FARPROC p = GetProcAddress( hDll, funcName );
if( !p )
return NULL;
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER *) hDll;
if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
return p;
IMAGE_NT_HEADERS* pNtHeaders = (IMAGE_NT_HEADERS *) (((char*) pDosHeader) + pDosHeader->e_lfanew);
if ( pNtHeaders->Signature != IMAGE_NT_SIGNATURE )
return p;
IMAGE_OPTIONAL_HEADER* pOptionalHeader = &pNtHeaders->OptionalHeader;
if( (char*) p >= (char*)hDll && (char*) p <= ((char*)hDll) + pOptionalHeader->SizeOfCode )
// Sounds like valid address.
return p;
// Does not sounds right, may be someone hooked given function ? (ConEmuHk64.dll or ConEmuHk.dll)
IMAGE_DATA_DIRECTORY* pDataDirectory = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
IMAGE_EXPORT_DIRECTORY* pExp = (IMAGE_EXPORT_DIRECTORY *) ((size_t) pDosHeader + pDataDirectory->VirtualAddress);
ULONG* addrofnames = (ULONG *) ((BYTE*) hDll + pExp->AddressOfNames);
ULONG* funcaddr = (ULONG*) ((BYTE*) hDll + pExp->AddressOfFunctions);
for ( DWORD i = 0; i < pExp->NumberOfNames; i++ )
{
char* funcname = (char*) ((BYTE*) hDll + addrofnames[i]);
if ( strcmp( funcname, funcName ) == 0 )
{
void* p2 = (void*) ((BYTE*) hDll + funcaddr[i]);
return (FARPROC) p2;
}
} //for
return p;
} //GetProcAddress2
Кажется, это работает для GetProcAddress
— Я могу обнаружить подключенную функцию и переопределить ее поведение. Однако этот подход не является общим. Я пробовал аналогичные вызовы функций для других методов, например для FreeLibrary/AddDllDirectory/RemoveDllDirectory
— и эти указатели на функции указывают вне границы DLL — GetProcAddress
возвращает адрес перед заголовком DOS.
Я подозреваю, что сравнение по диапазону размера DLL / кода не является правильным:
if( (char*) p >= (char*)hDll && (char*) p <= ((char*)hDll) + pOptionalHeader->SizeOfCode )
Но не имею понятия, как можно улучшить формулу.
Можете ли вы порекомендовать мне, как сделать это исправление полностью — чтобы любое стороннее программное обеспечение могло перехватить любую функцию, и я мог бы выжить без сбоев?
Неправильное разрешение указателя функции в случае, если используется «Экспортированная функция вперед» (может быть введено с помощью этого термина).
Правильное разрешение функции может быть написано так: (То, что вы видите выше, это некоторая скопированная функция с какого-то форума).
//
// We use GetProcAddress as a base function, with exception to when GetProcAddress itself is hooked by 3-rd party
// software and pointer to function returned to us is incorrect - then we try to locate function manually by
// ourselfes.
//
FARPROC GetProcAddress2( HMODULE hDll, char* funcName )
{
FARPROC p = GetProcAddress( hDll, funcName );
if( !p )
return NULL;
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER *) hDll;
if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE )
return p;
IMAGE_NT_HEADERS* pNtHeaders = (IMAGE_NT_HEADERS *) (((char*) pDosHeader) + pDosHeader->e_lfanew);
if ( pNtHeaders->Signature != IMAGE_NT_SIGNATURE )
return p;
IMAGE_OPTIONAL_HEADER* pOptionalHeader = &pNtHeaders->OptionalHeader;
if( (char*) p >= (char*)hDll && (char*) p <= ((char*)hDll) + pOptionalHeader->SizeOfCode )
// Sounds like valid address.
return p;
// Does not sounds right, may be someone hooked given function ? (ConEmuHk64.dll or ConEmuHk.dll)
IMAGE_DATA_DIRECTORY* pDataDirectory = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
IMAGE_EXPORT_DIRECTORY* pExp = (IMAGE_EXPORT_DIRECTORY *) ((size_t) pDosHeader + pDataDirectory->VirtualAddress);
ULONG* addrofnames = (ULONG *) ((BYTE*) hDll + pExp->AddressOfNames);
ULONG* funcaddr = (ULONG*) ((BYTE*) hDll + pExp->AddressOfFunctions);
for ( DWORD i = 0; i < pExp->NumberOfNames; i++ )
{
char* funcname = (char*) ((BYTE*) hDll + addrofnames[i]);
if ( strcmp( funcname, funcName ) == 0 )
{
ULONG addressOfFunction = funcaddr[i];
void* p2 = (void*) ((BYTE*) hDll + addressOfFunction);
if( addressOfFunction >= pDataDirectory->VirtualAddress && addressOfFunction < pDataDirectory->VirtualAddress + pDataDirectory->Size )
{
// "Exported function forward" - address of function can be found in another module.
// Actually for example AddDllDirectory is truly located in KernelBase.dll (alias api-ms-win-core-libraryloader-l1-1-0.dll ?)
char* dll_func = (char*) p2;
char* pdot = strchr(dll_func, '.');
if( !pdot ) pdot = dll_func + strlen( dll_func );
CStringA dllName(dll_func, (int)(pdot - dll_func));
dllName += ".dll";
HMODULE hDll2 = GetModuleHandleA(dllName);
if( hDll2 == NULL )
return p;
return GetProcAddress2( hDll2, pdot + 1 );
}
return (FARPROC) p2;
}
} //for
return p;
} //GetProcAddress2
Кроме того, все еще можно загрузить .dll для загрузки по другому адресу, но этого не происходит с kernel32.dll или kernelbase.dll.
Но если проблема с перебазированием .dll — один из подходов, который нужно решить, это использование подхода EasyHook — можно найти здесь:
Смотрите функцию GetRemoteFuncAddress.
Других решений пока нет …