Технологии, необходимые для выполнения этого, я описал в статьях «Динамическая модификация кода» и «Перехват вызова функций заменой байтов заголовка». Проблема с внесением исправлений в функцию CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces заключается в том, что она не экспортируема. Для поиска ее адреса внутри образа ole32.dll применили метод «поиск по шаблону«.
Поиск кода по шаблону
Идея метода проста. Необходимо сформировать массив байт кода (сигнатуру), длина которого достаточна для однозначного нахождения требуемого участка. Те байты сигнатуры, которые соответствуют смещениям и могут меняться при изменении базового адреса образа dll в памяти, помечаются как не используемые при сравнении.
Для поиска CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces была сформирована следующая сигнатура:
WORD tmpltVistaIBeginMQI[] = { 0x008D, 0x004D, 0x00F8, // lea ecx,[ebp-8] 0x0051, // push ecx 0x0057, // push edi 0x006A, 0x000C, // push 0Ch 0x0033, 0x00C0, // xor eax,eax 0x0050, // push eax 0x0053, // push ebx 0x00E8, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // call __allmul (766143D3h) 0x0052, // push edx 0x0050, // push eax 0x00E8, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // call ULongLongToUInt (7661537Ah) 0x003B, 0x00C7, // cmp eax,edi 0x0050, // push eax 0x007D, 0x0006, // jge CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces+0A5h (7668340Ch) 0x008B, 0x004E, 0x0028, // mov ecx,dword ptr [esi+28h] 0x0051, // push ecx 0x00EB, 0x00D6, // jmp CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces+7Bh (766833E2h) 0x0057, // push edi 0x00FF, 0x0035, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // push dword ptr [g_hHeap (766EE304h)] 0x00FF, 0x0015, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, // call dword ptr [pfnHeapAlloc (766EE8B0h)] }; #define SIZE_OF_BEGINMQI_TEMPLATE \ (sizeof(tmpltVistaIBeginMQI) / sizeof(tmpltVistaIBeginMQI[0]))
Для каждого байта кода, который мы хотим найти в образе, используется два байта. Первый содержит код инструкции. Если второй содержит 0xFF, сравнение в этой позиции не используется при поиске.
Поиск кода для внесения исправлений осуществляется строкой:
LPBYTE pFragment = FindCodeByTemplate( PBYTE(mi.lpBaseOfDll) // базовый адрес ole32.dll , mi.SizeOfImage // размер образа ole32.dll в памяти , tmpltVistaIBeginMQI , SIZE_OF_BEGINMQI_TEMPLATE);
Реализацию FindCodeByTemplate можно посмотреть, скачав этот файл источников.
Внесение исправления
Для внесения исправления код функции оказался удачным. Так как фрагмент заканчивается инструкцией длинного вызова (far call), есть возможность перенаправить вызов на наш код с исправлениями (stub), перезаписав байты смещения. После выполнения выделения памяти код исправления может выполнить инструкцию ret и CAsyncUnknownMgr::IBegin_QueryMultipleInterfaces, ничего не заметив, будет продолжать работу. Но при этом размер выделенного блока будет корректный.
Код заглушки не сложен (полный код можно скачать по этой ссылке):
IBeginMQIStub PROC C push dword ptr [ebp - 8] // размер блока для выделения push dword ptr [esp + 12] // flags push dword ptr [esp + 12] // hHeap call [pfnHeapAlloc] // вызов функции выделения памяти ret 12 // возврат в IBegin_QueryMultipleInterfaces IBeginMQIStub ENDP
Ну и, собственно, код модификации байтов кода функции IBegin_QueryMultipleInterfaces, для передачи управления IBeginMQIStub:
PVOID *ppfnHeapAlloc = (PVOID *)(pFragment + SIZE_OF_BEGINMQI_TEMPLATE - 4); ppfnHeapAlloc = (PVOID *)*ppfnHeapAlloc; pfnHeapAlloc = *ppfnHeapAlloc; BYTE Code[6]; DWORD offs = DWORD(LPBYTE(&IBeginMQIStub) - (pFragment + SIZE_OF_BEGINMQI_TEMPLATE - 1)); Code[0] = 0xE8; // Call near *(DWORD *)&Code[1] = offs; // offs Code[5] = 0x90; // Nop WriteProcessMemory( hProcess , pFragment + SIZE_OF_BEGINMQI_TEMPLATE - 6 , Code, sizeof(Code) , NULL );
Заключение
Проблему удалось решить достаточно просто, применив несколько из описанных мной в этой и предыдущих статьях методов. В некоторых случаях применение их оправдано. Более того, иногда и нет другого выхода. Но вот злоупотреблять модификацией кода не стоит. Ошибки в реализации stub могут оказаться сложными для отладки и опасными для устойчивости работы ваших приложений.