#pragma once /** * Adapted from https://gist.github.com/altalk23/29b97969e9f0624f783b673f6c1cd279 */ #include #include #include #include #include "../loader/Mod.hpp" #include "../loader/Log.hpp" #include "general.hpp" #include "casts.hpp" namespace geode::addresser { template inline intptr_t getVirtual(T func); template inline intptr_t getNonVirtual(T func); template inline F thunkAdjust(T func, F self); template inline F rthunkAdjust(T func, F self); class GEODE_DLL Addresser final { template struct SingleInheritance { virtual ~SingleInheritance() {} }; struct MultipleInheritance : SingleInheritance<'L'>, SingleInheritance<'F'> { virtual ~MultipleInheritance() {} }; static MultipleInheritance* instance(); template static ptrdiff_t indexOf(R(T::*func)(Ps...)) { using method_t = ptrdiff_t(T::*)(); return (reinterpret_cast(instance())->*reinterpret_cast(func))(); } template static ptrdiff_t indexOf(R(T::*func)(Ps...) const) { return indexOf(reinterpret_cast(func)); } template static ptrdiff_t thunkOf(T ptr) { // msvc if (sizeof(T) == sizeof(ptrdiff_t)) return 0; // all auto thunk = *(reinterpret_cast(&ptr)+1); // arm if (thunk & 1) thunk >>= 1; return thunk; } /** * Specialized functionss */ template static intptr_t addressOfVirtual(R(T::*func)(Ps...), typename std::enable_if_t>* = 0) { using geode::cast::reference_cast; // Create a random memory block with the size of T // Assign a pointer to that block and cast it to type T* uint8_t dum[sizeof(T)] {}; auto ptr = reinterpret_cast(dum); // Now you have a object of T that actually isn't an object of T and is just a random memory // But C++ doesn't know that of course // So now you can copy an object that wasn't there in the first place // ((oh also get the offsets of the virtual tables)) auto ins = new T(*ptr); // this is how the first human was made auto index = indexOf(func); auto thunk = thunkOf(func); // log::debug("[[" + utils::intToHex((void*)ins) + " + " + utils::intToHex(thunk) + "] + " + utils::intToHex(index) + "]"); log::debug("[[{} + {}] + {}]", utils::intToHex((void*)ins), utils::intToHex(thunk), utils::intToHex(index)); // [[this + thunk] + offset] is the f we want auto address = *(intptr_t*)(*(intptr_t*)(reference_cast(ins) + thunk) + index); #ifdef GEODE_IS_WINDOWS // check if first instruction is a jmp, i.e. if the func is a thunk if (*reinterpret_cast(address) == 0x25ff) { // read where the jmp points to and jump there address = *reinterpret_cast(address + 2); // that then contains the actual address of the func address = *reinterpret_cast(address); } #endif // And we delete the new instance because we are good girls // and we don't leak memories operator delete(ins); return address; } template static intptr_t addressOfVirtual(R(T::*func)(Ps...) const, typename std::enable_if_t && !std::is_abstract_v>* = 0) { return addressOfVirtual(reinterpret_cast(func)); } template static intptr_t addressOfVirtual(R(T::*func)(Ps...), typename std::enable_if_t>* = 0) { return 0; } template static intptr_t addressOfVirtual(R(T::*func)(Ps...) const, typename std::enable_if_t>* = 0) { return 0; } template static intptr_t addressOfNonVirtual(R(T::*func)(Ps...) const) { return addressOfNonVirtual(reinterpret_cast(func)); } template static intptr_t addressOfNonVirtual(R(T::*func)(Ps...)) { return geode::cast::reference_cast(func); } template static intptr_t addressOfNonVirtual(R(*func)(Ps...)) { return geode::cast::reference_cast(func); } template friend intptr_t getVirtual(T func); template friend intptr_t getNonVirtual(T func); template friend F thunkAdjust(T func, F self); template friend F rthunkAdjust(T func, F self); }; #ifdef GEODE_ADDRESSER_TEST template inline intptr_t getVirtual(T func) { return Addresser::addressOfVirtual(func); } template inline intptr_t getNonVirtual(T func) { return Addresser::addressOfNonVirtual(func); } #else template inline intptr_t getVirtual(T func) { log::debug("Get virtual function address from {}", utils::intToHex(geode::cast::reference_cast(func))); auto addr = Addresser::addressOfVirtual(func); log::debug("The address is: {}", utils::intToHex(addr)); return addr; } template inline intptr_t getNonVirtual(T func) { log::debug("Get non-virtual function address from {}", utils::intToHex(geode::cast::reference_cast(func))); auto addr = Addresser::addressOfNonVirtual(func); log::debug("The address is: {}", utils::intToHex(addr)); return addr; } template inline F thunkAdjust(T func, F self) { return (F)((intptr_t)self + Addresser::thunkOf(func)); } template inline F rthunkAdjust(T func, F self) { return (F)((intptr_t)self - Addresser::thunkOf(func)); } #endif }