Полностью переопределяя VMT (таблица виртуальных методов)

Я вызываю виртуальный метод в vmt путем разыменования, пока не получу указатель на метод.

Это все хорошо, однако, как бы я полностью изменил указатель на таблицу VM на объекте?

Пример:

PP A; // указывает на свою таблицу виртуальных машин по умолчанию

ПП Б; // указывает на совершенно другую таблицу виртуальных машин

A-> MethodOne () // вызывает как указано выше

B-> MethodOne () // вызывает совершенно другой метод, поскольку мы переопределяем его указатель на таблицу VM на альтернативную таблицу с различными указателями на методы

Как бы я это сделал?

Мой код:

#include <Windows.h>
#include <iostream>

class PP
{
public:
PP() { }
~PP() { }

virtual void MethodOne() { std::cout << "1" << std::endl; }
virtual void MethodTwo() { std::cout << "2" << std::endl; }
};

typedef void (*MyFunc)(void);

int main()
{
PP* A = new PP();

//(*(void(**)(void))(*(DWORD*)A + (4*1)))();

( *(MyFunc*) ( *(DWORD*)A + (4*0) ) )(); // call index 0 (4bytes*0)
A->MethodOne();
A->MethodTwo();
system("PAUSE");
delete A;
return 0;
}

2

Решение

Поскольку обычный метод получения другого класса не будет работать для вас, я могу придумать три решения.

  1. Измените указатель vtable. Это непереносимо и имеет много способов просто пойти ужасно неправильно. Предполагая, что vtable находится в начале класса (что и для простых классов в WinAPI), вы можете заменить этот указатель указателем на собственную таблицу.

    *(void **)A = newVtable;
    

с newVtable, определенным с соответствующими указателями на функции-члены. Вы должны будете быть предельно осторожны, чтобы настроить это. Это также может испортить удаление и обработку исключений.

  1. Создайте свой собственный vtable. Определите класс с необходимыми указателями на метод-функции, затем определите указатель в вашем классе на один из них. Затем вы можете изменить указатель на таблицу по мере необходимости. Это было бы более многословно при вызове, хотя вы могли бы определить другие функции-члены, чтобы скрыть уродливый код.

    class vtable;
    
    class PP {
    public:
    PP();
    ~PP() { }
    
    void MethodOne() { std::cout << "1" << std::endl; }
    void MethodTwo() { std::cout << "2" << std::endl; }
    
    const vtable *pVtable;
    };
    
    class vtable {
    public:
    void (PP::*MethodOne)();
    };
    
    vtable One = {&PP::MethodOne};
    vtable Two = {&PP::MethodTwo};
    
    PP::PP(): pVtable(&One) { }
    
    void main() {
    PP* A = new PP();A->pVtable = &One;
    
    // call with
    (A->*(A->pVtable->MethodOne))();    // calls MethodOne
    
    A->pVtable = &Two;
    (A->*(A->pVtable->MethodOne))();    // calls MethodTwo
    }
    

(Составлено и протестировано с сообществом VS2015). Это было бы портативно и безопасно.

  1. Определите указатели методов в классе и обновите их индивидуально.
4

Другие решения

Если я правильно понимаю ваш вопрос — вы хотите заменить таблицу VM объекта во время выполнения. Не уверены, почему вы используете язык C ++ для таких низкоуровневых модификаций?

В любом случае, Microsoft C / C ++ поддерживает нечто, называемое «голым» соглашением о вызовах (в отличие от «stdcall», «fastcall» и т. Д., Которые отличаются тем, как / как порядок параметров передается в функцию и передаются ли они в стеке). или в регистрах). В соглашении об открытом вызове ВЫ полностью контролируете передачу параметров — вы пишете свои собственные фрагменты встроенной сборки, отвечающие за размещение материала в стеке и раскручивание стека.

https://msdn.microsoft.com/en-us/library/5ekezyy2.aspx

Например, вы можете использовать соглашение об открытых вызовах для конструктора (если компилятор не будет жаловаться) и передать новую таблицу VM как «скрытый» параметр, точно так же, как «этот» параметр является «скрытым» параметром, передаваемым в функции-члены (в соглашении о вызовах thiscall). И вы могли бы сделать свое волшебство во встроенной сборке, чтобы заменить виртуальную машину в конструкторе.

Однако все это кажется ужасной, хрупкой идеей, потому что новая версия компилятора, которая меняет свои внутренние компоненты (как правило, не подвергается вам), может потенциально сломать ваш код. Если вам действительно нужен какой-то динамический механизм выбора того, какой метод вызывать, звучит так, как будто вы должны реализовать свой собственный механизм вместо того, чтобы использовать его как хак поверх механизма VMT в C ++.

0

По вопросам рекламы [email protected]