#pragma once /** * Adapted from https://gist.github.com/altalk23/29b97969e9f0624f783b673f6c1cd279 */ #include "../utils/casts.hpp" #include "casts.hpp" #include #include #include #include #include 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); template Class* friendCreate(typename std::void_t(&Class::create))>*) { auto ret = Class::create(); ret->retain(); return ret; } template concept HasCreate = requires() { { friendCreate(nullptr) } -> std::same_as; }; 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; } // I gave up template static Class* generateInstance(Class*) { return friendCreate(nullptr); } // I extra gave up static cocos2d::extension::CCScrollView* generateInstance(cocos2d::extension::CCScrollView*) { return cocos2d::extension::CCScrollView::create({0.0f, 0.0f}, cocos2d::CCLayer::create()); } template static Class* generateInstance(Class*) { // Create a random memory block with the size of Class // Assign a pointer to that block and cast it to type Class* uint8_t dum[sizeof(Class)]{}; auto ptr = reinterpret_cast(dum); // Now you have a object of Class that actually isn't an object of Class 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 Class(*ptr); // this is how the first human was made return ins; } template static Class* cachedInstance() { static auto ret = generateInstance(nullptr); return ret; } /** * Specialized functionss */ template static intptr_t addressOfVirtual( R (T::*func)(Ps...), typename std::enable_if_t>* = 0 ) { using geode::cast::reference_cast; auto index = indexOf(func); auto thunk = thunkOf(func); auto ins = cachedInstance(); // 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 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) { // do NOT delete the line below. // doing so breaks thunk adjusting on windows. // why? bruh idk auto _ = *geode::cast::template union_cast(&func); return (F)((intptr_t)self + Addresser::thunkOf(func)); } template inline F rthunkAdjust(T func, F self) { // do NOT delete the line below. // doing so breaks thunk adjusting on windows. // why? bruh idk auto _ = *geode::cast::template union_cast(&func); return (F)((intptr_t)self - Addresser::thunkOf(func)); } #endif }