wishful thinking: https://gcc.godbolt.org/z/qWEe9r
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.
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.
I would not say that it is correct - the "object"'s actual existence is only through the interpretation that is made of it by the functions that work on your bytes. There's no struct definition in compiled code, only functions that do something with memory at a given offset. I won't go as far as to say that "objects" don't exist once your code leaves C++'s abstract machine to enter the compiled code world... but quite close.
With that said, 4 out of these 5 functions completely (and rightfully) disregard the vtable so if you're relying on that behaviour to be in place consistently to, say, fix a security issue in a given binary... you're in for some surprises.
It's hardly news but I guess it makes this common cracking technique more accessible.
And of course, Common Lisp has “change-class” (https://www.snellman.net/blog/archive/2015-07-27-use-cases-f..., discussed at https://news.ycombinator.com/item?id=734025) and Smalltalk has “become:” (https://gbracha.blogspot.com/2009/07/miracle-of-become.html. Short discussion at https://news.ycombinator.com/item?id=734025)
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).
ASM is just a bytecode reader/writer/visitor library that can then modify it. You could also just do it statically by having the bytecode you want injected ready to go in an array or resource.
if (className.equals("a/b/c/d$e"))
return fixedClassBytecode;
else
return bytecode;
It's possible to create custom loaders (e.g., loading a class from an encrypted zip-file, or one that creates custom bytecode on the fly) and things which are trying to obfuscate what they're doing will have custom loaders, but this is a choke point that every class that's read in and instantiated must pass through.I believe classes, once loaded by the JVM, cannot be changed. https://stackoverflow.com/a/43653466/