是否可以更改 vtable 中的任何 func 指针?
Is it possible to change any func pointer in vtable?
这是一个学术问题。我制作了一个宏来通过 vtable 调用并获取指向虚函数的指针。但是更改 vfunc 地址变量会导致内存损坏。
所以原来这个内存区域是受保护的?
它的 classic 实现。我将 GCC (MinGW) 用于 64 位程序,编译器在对象的第一个隐藏字段处添加了指向其 class 的 vtable(数组)的指针。
#define VTABLE_CALL(FUNC_TYPE, OBJECT, OFFSET, ...) \
((FUNC_TYPE)(*((size_t**)&(OBJECT))[(OFFSET)]))(__VA_ARGS__)
#define VTABLE_GET(OBJECT, OFFSET) \
((void*)((*(size_t**)&(OBJECT))[(OFFSET)]))
#define VTABLE_SET(OBJECT, OFFSET, NEW_FUNC_P) \
((*((size_t***)&(OBJECT))[(OFFSET)]) = (size_t*)(NEW_FUNC_P))
所以,那段代码给出了 SegFault
class A {
virtual void foo(void) { printf("Test\n"); }
};
int main()
{
typedef void (*VMethod) (void*); // void* for "this"
A a; // our object with first hidden vTablePtr;
VMethod vm;
VTABLE_CALL(VMethod, a, 0, &a); // call A::foo
vm = (VMethod) VTABLE_GET(a, 0);
vm(&a); // call again A::foo
/* replace the pointer to ourselves */
VTABLE_SET(a, 0, vm); // segmentation fault
/* foot shot completed */
return 0;
}
像常量一样,Vtables 以只读方式加载和映射。 vtable 永远不会被修改。
为此,您需要更改完成加载和映射的方式,可能是通过更改程序集或目标代码以将部分标记为可读写,或者在 运行 时进行另一次映射启动后(我不知道这是否可行)。
这是一个学术问题。我制作了一个宏来通过 vtable 调用并获取指向虚函数的指针。但是更改 vfunc 地址变量会导致内存损坏。 所以原来这个内存区域是受保护的?
它的 classic 实现。我将 GCC (MinGW) 用于 64 位程序,编译器在对象的第一个隐藏字段处添加了指向其 class 的 vtable(数组)的指针。
#define VTABLE_CALL(FUNC_TYPE, OBJECT, OFFSET, ...) \
((FUNC_TYPE)(*((size_t**)&(OBJECT))[(OFFSET)]))(__VA_ARGS__)
#define VTABLE_GET(OBJECT, OFFSET) \
((void*)((*(size_t**)&(OBJECT))[(OFFSET)]))
#define VTABLE_SET(OBJECT, OFFSET, NEW_FUNC_P) \
((*((size_t***)&(OBJECT))[(OFFSET)]) = (size_t*)(NEW_FUNC_P))
所以,那段代码给出了 SegFault
class A {
virtual void foo(void) { printf("Test\n"); }
};
int main()
{
typedef void (*VMethod) (void*); // void* for "this"
A a; // our object with first hidden vTablePtr;
VMethod vm;
VTABLE_CALL(VMethod, a, 0, &a); // call A::foo
vm = (VMethod) VTABLE_GET(a, 0);
vm(&a); // call again A::foo
/* replace the pointer to ourselves */
VTABLE_SET(a, 0, vm); // segmentation fault
/* foot shot completed */
return 0;
}
像常量一样,Vtables 以只读方式加载和映射。 vtable 永远不会被修改。
为此,您需要更改完成加载和映射的方式,可能是通过更改程序集或目标代码以将部分标记为可读写,或者在 运行 时进行另一次映射启动后(我不知道这是否可行)。