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