I'm wondering if there is a way how to reimplement a non-virtual function foo
in a 3rd party class Base
.
The motivation is that I just need to append a callback into foo
. However, the function is called from many places of the class Base
, and since it is not virtual, it would require to either change the Base
in place, or to dramatically rewrite implementation of the class that will derive from Base
. I would like to avoid both scenarios.
I do not need polymorphism at all, the derived class will have just one instance and the type will be known at compile-time (so e.g. CRTP instead of virtualization would suffice too).
I tried to use a class, which inherits from an auxiliary class that declares foo
as virtual, but with no luck. Here is an example, where bar
simulates any place of Base
s implementation from which foo
could be called:
/// Ideally, do not modify the `Base` at all
struct Base {
void foo()
{
cout << "Base::foo" << endl;
}
void bar()
{
cout << "bar" << endl;
foo(); //< foo is not virtual !
}
};
struct Virtual {
virtual void foo() = 0;
};
struct Virtual_base : Virtual, Base {
void foo() override = 0; //< it still does not affect Base !
};
struct Virtual_derived : Virtual_base {
void foo() override
{
cout << "Virtual_derived::foo" << endl;
Base::foo();
}
};
Well, Virtual_derived::foo
does override, but the Base::bar
, unsurprisingly, is still unchanged. I also tried the CRTP approach, but obviously with no luck, as it still clashes with the very same issue that Base
stays encapsulated.
I'm little afraid that the answer is that it is just impossible.. is it?
If you don't use virtual functions, you don't understand OOP yet. Because the virtual function is intimately bound with the concept of type, and type is at the core of object-oriented programming, there is no analog to the virtual function in a traditional procedural language.
Virtual functions are dangerous, somebody that derives your class can break your code by overriding the function and, say, not call the base function when they should. In Java you have the final keyword, that's not available in C++. Don't declare functions virtual unless you expect the function to be overridden.
You can call a virtual function in a constructor, but be careful. It may not do what you expect. In a constructor, the virtual call mechanism is disabled because overriding from derived classes hasn't yet happened. Objects are constructed from the base up, “base before derived”.
A virtual function can be private as C++ has access control, but not visibility control. As mentioned virtual functions can be overridden by the derived class but under all circumstances will only be called within the base class. Example: C++
On Windows you can use hotpatching: https://jpassing.com/2011/05/03/windows-hotpatching-a-walkthrough/ .
Compile with /hotpatch. This will add a two-byte NOP to the beginning of every function, and a 6-byte nop (5 on 32-bit) before, allowing you to patch in a redirection. What you want to do is modify the two-byte nop at the beginning to jump back into the 6-byte nop block, which then can jump to your callback wrapper, which then calls your callback and then jumps back into the function proper. To implement it, add this to a C++ source file:
void pages_allow_all_access(void* range_begin, size_t range_size) {
DWORD new_settings = PAGE_EXECUTE_READWRITE;
DWORD old_settings;
VirtualProtect(
range_begin,
range_size,
new_settings,
&old_settings
);
}
void patch(void* patch_func, void* callback_wrapper) {
char* patch_func_bytes = (char*)patch_func;
char* callback_wrapper_bytes = (char*)callback_wrapper;
pages_allow_all_access(patch_func_bytes - 6, 8);
// jmp short -5 (to the jmp rel32 instruction)
patch_func_bytes[0] = 0xEB;
patch_func_bytes[1] = 0x100 - 0x7;
// nop (probably won't be executed)
patch_func_bytes[-6] = 0x90;
// jmp rel32 to callback_wrapper
patch_func_bytes[-5] = 0xE9;
*(int32_t*)&patch_func_bytes[-4]
= (int32_t)(callback_wrapper_bytes - patch_func_bytes);
}
The callback wrapper might need to be defined in an assembly file:
callback_wrapper:
; save registers
pushad
pushfd
call QWORD PTR [callback]
popfd
popad
jmp QWORD PTR [after_trampoline]
The symbols callback and after_trampoline should be exposed in a C++ file (so at global scope).
void* callback = &callback_func;
void* after_trampoline = (char*)&patch_func + 2;
Then call patch
at the top of main or some other suitable initialization time and you're set.
Also, you may have to allow write permissions on the memory pages you're modifying (the ones that patch_func is in) using a VirtualProtect call: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect . EDIT: I've added this code to the example above.
I may add ways of doing this on Linux or other Unixy systems later.
When you don't have a convenient set of NOPs to use in the function, hooking becomes more difficult, particularly on the x86 architecture, since instructions have widely varying lengths and so it's difficult to find out programmatically where an instruction ends so you can jump back to the next instruction. @ajm suggests this library: https://github.com/kubo/funchook for Linux & OSX. However, personally, when I don't have hotpatching, I usually use a debugger to find out a sequence of instructions in the patch target of length at least 9 bytes that I can replace. Then in the program I replace those instructions with a jump to absolute immediate 64-bit, using a technique similar to the above, but I also add those replaced instructions to be executed near the end of the callback wrapper. Avoid replacing call or jmp instructions as these are often relative to the instruction pointer, which will have a different value in the callback wrapper than in the original function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With