
Overriding C++ virtual functions at run time - topiolli
https://blog.visionappster.com/2020/08/06/overriding-virtual-functions-at-run-time/
======
jcelerier
> The C++ standard does not specify how virtual functions should be
> implemented. In practice, however, compilers generate a virtual function
> table and place a pointer to it as the first member of a class.

wishful thinking:
[https://gcc.godbolt.org/z/qWEe9r](https://gcc.godbolt.org/z/qWEe9r)

~~~
badsectoracula
Not sure what you are trying to show, the object still has a vtable and is
placed as the first member (and in your example, only) of the class, so that
quote is correct.

Obviously if you enable optimizations and one of those optimizations is
avoiding the virtual call when the compiler thinks it isn't necessary, then
sure you wont get a virtual call everywhere.

But if your code is relying on implementation assumptions like having a vtable
at the start of a class, then it should also make sure that this assumption
holds by not trying to work around it (e.g. via final) and using compiler
options that control that optimization (e.g. GCC has -fno-devirtualize).

It doesn't make much sense to both try and take advantage of implementation
details and work against taking advantage of implementation details at the
same time.

~~~
MaulingMonkey
The article talks about vtable patching in scenarios where you might not be
able to recompile the original target, which limits your ability to add
unusual flags like -fno-devirtualize, or remove common optimization flags like
-O3.

Which doesn't make the technique completely useless, but raising this
"obvious" important caveat - that it's likely to be an imperfect patch on it's
own - when the article completely fails to do so, is worthwhile. I promise you
there's C++ programmers out there who weren't aware of how aggressive
optimizers can be.

~~~
badsectoracula
The scenario is just an example of where it could be useful, but what you
describe isn't something that is unique to patching the vtable - it is
something that can happen with any form of function hotpatching, e.g. a
library might use its own functions and some of those might be inlined by the
compiler, so if you try to hotpatch one of those functions not all uses of
that function will be replaced with your own.

This is something that you should have in mind when hotpatching in general,
but that doesn't make hotpatching any less useful.

------
bregma
We were overriding non-virtual functions at run time in the 8-bit days. Even
in the feature article's case it would be easier and more reliable to patch
the GOT (since he's using ELF on Linux).

It's hardly news but I guess it makes this common cracking technique more
accessible.

~~~
saagarjha
Patching the GOT doesn't work for internal function calls, though.

------
mehrdada
As you might imagine, overwriting vtables in memory is a common technique to
hijack control flow and making your program execute attacker's code in an
exploit.

~~~
saagarjha
Patching function pointers in general is very desirable thing to be able to do
when writing these kinds of things. vtables are interesting because unlike
normal C function pointers they can only be used in a standards-compliant
manner in a few very limited ways, so if you’re implementing control flow
verification you can really ratchet up the security for these. For normal C
function pointers, sadly, the best you can do is usually very little, if
anything at all. Especially because the use of non-compliant constructs like
forging ordinary function pointers is extremely common in things like language
runtimes.

------
Someone
In Objective-C, that’s called “method swizzling”, and better supported by the
runtime. See [https://nshipster.com/method-
swizzling/](https://nshipster.com/method-swizzling/)

And of course, Common Lisp has “change-class”
([https://www.snellman.net/blog/archive/2015-07-27-use-
cases-f...](https://www.snellman.net/blog/archive/2015-07-27-use-cases-for-
change-class-in-common-lisp/), discussed at
[https://news.ycombinator.com/item?id=734025](https://news.ycombinator.com/item?id=734025))
and Smalltalk has “become:” ([https://gbracha.blogspot.com/2009/07/miracle-of-
become.html](https://gbracha.blogspot.com/2009/07/miracle-of-become.html).
Short discussion at
[https://news.ycombinator.com/item?id=734025](https://news.ycombinator.com/item?id=734025))

------
jamesu
Had to go a step further in a project and patch static functions in a codebase
with no source. It’s certainly enlightening how much you can do with just a
symbol map and type info.

I don’t think the articles vtable layout is entirely accurate for gcc though -
usually you’ll get 2 destructors at the start of the vtable (assuming the
first virtual func declared is the destructor).

~~~
greesil
Are you willing to share any info on how you got stuck with this project, and
what this "codebase" was? Can you call it a codebase is it doesn't have source
code?

~~~
Google234
I would guess a normal project that involves this would be making a hack for a
video game

~~~
greesil
Games ship with symbols unstripped?

~~~
diath
Wouldn't that be useful for crash reporting?

~~~
antiuniverse
The standard approach to this, at least on Windows, is to build the debug
symbols into a separate database (PDB file), and reconnect the addresses to
the symbol names on the back end. Microsoft makes tons of symbols available
for their own code via a symbol server which debuggers can query by the
combination of a module hash and a relative virtual address.

------
The_rationalist
I was wondering whether such a thing is possible for JVM based languages and
it turns out it is: [https://stackoverflow.com/questions/8273685/is-it-
possible-t...](https://stackoverflow.com/questions/8273685/is-it-possible-to-
override-a-method-at-runtime)

~~~
MaxBarraclough
So, is it possible? I get the impression you can create a new class using a
bytecode-manipulation library, but that's not the same as modifying an
existing class at runtime.

I believe classes, once loaded by the JVM, cannot be changed.
[https://stackoverflow.com/a/43653466/](https://stackoverflow.com/a/43653466/)

~~~
CHY872
It's generally possible to rewrite loaded classes. Some changes are possible
(e.g. replacing a method's body), others are not (adding new methods). The
state of the art library for doing this at the application layer is ByteBuddy
[https://github.com/raphw/byte-buddy#changing-existing-
classe...](https://github.com/raphw/byte-buddy#changing-existing-classes) but
this functionality is used by plenty of tooling - profilers such as YourKit
rewrite methods to add telemetry - I've seen some security libraries attempt
to add additional security related hooks.

~~~
The_rationalist
When would you use ByteBuddy versus [https://asm.ow2.io](https://asm.ow2.io) ?

~~~
CHY872
ASM is far lower level than ByteBuddy. To write some ASM code you probably
want to have some proficiency with the bytecode format itself (e.g. you're
typically outputting individual JVM bytecode instructions
;(invokespecial,athrow,etc). Personally I'd probably always use ByteBuddy
unless I had some very specific reason why not. For comparison, these two
examples [https://github.com/raphw/byte-buddy#changing-existing-
classe...](https://github.com/raphw/byte-buddy#changing-existing-classes)
[http://web.cs.ucla.edu/~msb/cs239-tutorial/](http://web.cs.ucla.edu/~msb/cs239-tutorial/)
explain how to do a System.out.println wrapper around a method.

In one case, the developer writes `System.out.println`. In the other, the
developer must individually get the static field System.err and push it to the
stack, reference PrintStream's method println, including the arguments
(Ljava.lang.String;)V. That means it takes an array of strings and returns
void.

------
ppg677
FDO compilation will often de-virtualize and remove vtable indirection.

------
rurban
AutoCad does this in their ObjectARX technology, with a fixed compiler
version, to support user or vendor provided plugins to extend classes. At
runtime. For decades.

~~~
bitwize
And this is why despite trying real hard several times, they couldn't remove
Autolisp completely...

------
rootlocus
This all fine and well for single public inheritance. Multiple inheritance and
virtual inheritance don't generate layouts this simple.

