итак решил накатать тутор, банальщина, но вдруг кто не врубается до сих пор из сишников)) собсна комменты в коде. и присобачил лейаут, как это всё выглядит в памяти. Code: #include <windows.h> // // для вывода отладочных сообщений // #define dmsg(X) \ {\ char rpt[256];\ wsprintf(rpt, "%s\n%s\t(%s)",X, __FUNCTION__, __FUNCDNAME__);\ MessageBox(0, rpt, "debug", 0);\ } // // наш тестовый класс // class CBase { private: int m_base0; int m_base1; public: CBase(); virtual ~CBase(); virtual void SetVar(int index, int value); }; CBase::CBase() { dmsg("вызван конструктор"); m_base0 = m_base1 = 0; } CBase::~CBase() { dmsg("вызван деструктор"); } void CBase::SetVar(int index, int value) { dmsg("вызван родной метод"); switch (index) { case 0: m_base0 = value; break; case 1: m_base1 = value; break; default: break; } } // // вспомогательный класс. нужен для того чтобы функция-перехватчик имела подходящее // соглашение о вызовах (calling convention: __thiscall) // кроме того эта функция(метод называется, по правильному) должен иметь одинаковый список аргументов // (имена значения не имеют) и тип возвращаемого значения, т.е. идентичный с CBase::SetVar // class Intercptr { public: void INTERCEPTOR(int a, int b); }; // // а это перехватчик, число и тип аргументов как и тип возвращаемого значения идентичны с CBase::SetVar // ЭТО ОБЯЗАТЕЛЬНО, иначе всё упадёт. // void Intercptr::INTERCEPTOR(int a, int b) { char rpt[512]; wsprintf(rpt, "аргументы: %d, %d", a, b); MessageBox(0, rpt, "вызван перехватчик", 0); } // // начало эксперимента, тащемта // int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { // создадим наш тестовый класс, в переменной pcb будет находится адрес, по которому // выделится память для экземляра класса CBase *pcb = new CBase(); // тестовые операции // // основная фишка в том, что если метод виртуальный, то компилятор будет генерить совершенно // другой код для вызова этой функции - он будет извлекать адрес из специальной таблицы, // в которой по порядку указаны адреса всех виртуальных методов класса, в простом случае. // если же метод не виртуальный, то код идентичен тому, что генерится для вызова обычных функций, // за исключением того что через регистр ecx передаётся адрес текущего экземпляра класса (тот // что вызвал метод) pcb->SetVar(0, 1); pcb->SetVar(1, 2); // извлечем указатель на vftable: // если класс имеет виртуальные методы (даже один) то первым полем по адресу, который содержится в // pcb будет не первый член данных класса, а адрес, по которому находится массив адресов виртуальных // методов - таблица виртуальных функций - vftable. // а вот за указателем на нее будут уже последовательно располагаться члены m_base0 и m_base1 // немного заковыристо выглядит, но это просто указатель на указатель на массив DWORD. // каждый DWORD в этом массиве - непосредственно адрес виртуального метода, так удобнее. // а сам массив, если вы догадались - и есть таблица vftable DWORD(**vftable)[1]; vftable = (DWORD(**)[1]) pcb; // адреса функций в таблице идут по порядку, так как объявлены в классе: //vftable[0]: CBase::~CBase //vftable[1]: CBase::SetVar // в нашем классе их всего 2 - деструктор и SetVar // // небольшое колдовство с приведением типов к нужному виду, иначе компилер заругает: void(__thiscall Intercptr::*pInt)(int, int) = &Intercptr::INTERCEPTOR; void *pCastHelper = (void*&) pInt; // к слову сказать, данные-члены класса тоже расположены в памяти в строгом порядке, // как указано в объявлении класса // таблица находится в секции .rdata, права доступа к которой READ_ONLY, установим // возможность записи: DWORD op; VirtualProtect(*vftable, 4096, PAGE_READWRITE, &op); // теперь перепишем в таблице адрес CBase::SetVar адресом Intercptr::INTERCEPTOR (**vftable)[1] = (DWORD) pCastHelper; // протестируем: // тут должно произойти то чего мы добивались - должен быть вызван перехватчик pcb->SetVar(7, 7); delete pcb; // усё :) return 0; } после перехвата vftable станет такой: