Date: Thu, 31 Oct 2024 11:55:33 +0000
On Mon, Oct 28, 2024 at 9:49 AM Frederick Virchanza Gotham wrote:
>
> By the way my previous GodBolt code writes machine code into a buffer
> of executable memory . . . I was thinking I would be able to do away
> with the allocating (and permission setting) of memory if I were to
> write just one function something like:
>
> Func:
> call Increment
> call Increment
> call Increment
> call Increment
> call Increment
> [ rest of function goes here ]
>
> So the first interceptor's address will be "&Func", and the second
> one's address will be "&Func + 8" and the third will be "&Func + 16".
> And so the when the rest of the function code is reached, the value of
> the incremented thread-local variable is checked to determine which
> method is being intercepted. This means we don't have to change the
> permissions on a page of dynamically-allocated memory.
I got this working:
https://godbolt.org/z/K19j7EeEK
So now it's possible to:
(1) Copy the VTable belonging to MyClass
(2) Edit the copied VTable so that each function pointer is replaced
with a pointer to an interceptor
(3) The address of each interceptor is 5 bytes greater than the
address of the previous interceptor (because the 'call' instruction
is 5 bytes of machine code)
(4) Change the VTable pointer inside any given object
So it's possible to intercept all the method calls on a polymorphic
object without needing to allocate a page of writeable executable
memory -- in fact it's possible without any dynamic allocation or
permission setting at all.
I think I'll put this into a debug library, along with some other
interceptor-related and "man in the middle" stuff -- which I find
immensely useful when debugging gets extreme (in particular when
dealing with proprietary libraries for which I don't have the source
code). It would be nice if stuff like this could go into the Standard
under the namespace 'std::debug::', but I think it might be too much
grief for the whole "object model" thingie if we were able to alter an
individual object's vtable.
>
> By the way my previous GodBolt code writes machine code into a buffer
> of executable memory . . . I was thinking I would be able to do away
> with the allocating (and permission setting) of memory if I were to
> write just one function something like:
>
> Func:
> call Increment
> call Increment
> call Increment
> call Increment
> call Increment
> [ rest of function goes here ]
>
> So the first interceptor's address will be "&Func", and the second
> one's address will be "&Func + 8" and the third will be "&Func + 16".
> And so the when the rest of the function code is reached, the value of
> the incremented thread-local variable is checked to determine which
> method is being intercepted. This means we don't have to change the
> permissions on a page of dynamically-allocated memory.
I got this working:
https://godbolt.org/z/K19j7EeEK
So now it's possible to:
(1) Copy the VTable belonging to MyClass
(2) Edit the copied VTable so that each function pointer is replaced
with a pointer to an interceptor
(3) The address of each interceptor is 5 bytes greater than the
address of the previous interceptor (because the 'call' instruction
is 5 bytes of machine code)
(4) Change the VTable pointer inside any given object
So it's possible to intercept all the method calls on a polymorphic
object without needing to allocate a page of writeable executable
memory -- in fact it's possible without any dynamic allocation or
permission setting at all.
I think I'll put this into a debug library, along with some other
interceptor-related and "man in the middle" stuff -- which I find
immensely useful when debugging gets extreme (in particular when
dealing with proprietary libraries for which I don't have the source
code). It would be nice if stuff like this could go into the Standard
under the namespace 'std::debug::', but I think it might be too much
grief for the whole "object model" thingie if we were able to alter an
individual object's vtable.
Received on 2024-10-31 11:55:46