diff --git a/.gitmodules b/.gitmodules index dce1debb8..1a0be190b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,3 +12,6 @@ [submodule "public/safetyhook"] path = public/safetyhook url = https://github.com/alliedmodders/safetyhook +[submodule "core/logic/libaddrz"] + path = core/logic/libaddrz + url = https://github.com/dvander/libaddrz.git diff --git a/core/logic/AMBuilder b/core/logic/AMBuilder index 3af40f19d..581f0c91a 100644 --- a/core/logic/AMBuilder +++ b/core/logic/AMBuilder @@ -86,9 +86,15 @@ for cxx in builder.targets: 'DatabaseConfBuilder.cpp', 'LumpManager.cpp', 'smn_entitylump.cpp', + 'libaddrz/addrz.cpp', + 'libaddrz/mapping.cpp', + 'libaddrz/platform.cpp', + 'libaddrz/proc_maps.cpp', + 'PseudoAddrManager.cpp', ] - - if binary.compiler.target.arch == 'x86_64': - binary.sources += ['PseudoAddrManager.cpp'] + if binary.compiler.target.platform == 'linux': + binary.sources += ['libaddrz/platform_linux.cpp'] + elif binary.compiler.target.platform == 'windows': + binary.sources += ['libaddrz/platform_windows.cpp'] SM.binaries += [builder.Add(binary)] diff --git a/core/logic/PluginSys.cpp b/core/logic/PluginSys.cpp index 33f1c3af3..74d4161dc 100644 --- a/core/logic/PluginSys.cpp +++ b/core/logic/PluginSys.cpp @@ -330,6 +330,13 @@ bool CPlugin::ReadInfo() else m_MaxClientsVar = nullptr; + if (base->FindPubvarByName("PointerSize", &idx) == SP_ERROR_NONE) { + sp_pubvar_t* var = nullptr; + if (base->GetPubvarByIndex(idx, &var) == SP_ERROR_NONE && var) { + *var->offs = sizeof(void*); + } + } + return true; } diff --git a/core/logic/PseudoAddrManager.cpp b/core/logic/PseudoAddrManager.cpp index 75e860467..c784545cb 100644 --- a/core/logic/PseudoAddrManager.cpp +++ b/core/logic/PseudoAddrManager.cpp @@ -28,6 +28,7 @@ */ #include "PseudoAddrManager.h" +#include #ifdef PLATFORM_APPLE #include #include @@ -35,135 +36,88 @@ #ifdef PLATFORM_LINUX #include #endif +#ifdef PLATFORM_WINDOWS +#include +#endif -PseudoAddressManager::PseudoAddressManager() : m_NumEntries(0) +PseudoAddressManager::PseudoAddressManager() : m_dictionary(am::IPlatform::GetDefault()) { } -// A pseudo address consists of a table index in the upper 6 bits and an offset in the -// lower 26 bits. The table consists of memory allocation base addresses. +void PseudoAddressManager::Initialize() { +#ifdef PLATFORM_WINDOWS + auto process = GetCurrentProcess(); + auto get_module_details = [process](const char* name, void*& baseAddress, size_t& moduleSize) { + if (process == NULL) { + return false; + } + auto hndl = GetModuleHandle(name); + if (hndl == NULL) { + return false; + } + MODULEINFO info; + if (!GetModuleInformation(process, hndl, &info, sizeof(info))) { + return false; + } + moduleSize = info.SizeOfImage; + baseAddress = info.lpBaseOfDll; + return true; + }; +#endif +#ifdef PLATFORM_LINUX + auto get_module_details = [](const char* name, void* baseAddress, size_t& moduleSize) { + auto hndl = dlopen(name, RTLD_NOLOAD); + if (hndl == NULL) { + return false; + } + void* addr = dlsym(hndl, "CreateInterface"); + dlclose(hndl); + + if (!addr) { + return false; + } + + Dl_info info; + if (dladdr(addr, &info) == 0) { + return false; + } + + baseAddress = info.dli_fbase; + // It doesn't matter much if we figure out the module size + // libaddrz coalesce maps on linux + moduleSize = 0; + return true; + }; +#endif + + // Early map commonly used modules, it's okay if not all of them are here + // Everything else will be caught by "ToPseudoAddress" but you risk running out of ranges by then + const char* libs[] = { "engine", "server", "tier0", "vstdlib" }; + + char formattedName[64]; + for (int i = 0; i < sizeof(libs) / sizeof(const char*); i++) { + bridge->FormatSourceBinaryName(libs[i], formattedName, sizeof(formattedName)); + void* base_addr = nullptr; + size_t module_size = 0; + if (get_module_details(formattedName, base_addr, module_size)) { + // Create the mapping (hopefully) + m_dictionary.Make32bitAddress(base_addr, module_size); + } + } +} + void *PseudoAddressManager::FromPseudoAddress(uint32_t paddr) { -#ifdef KE_ARCH_X64 - uint8_t index = paddr >> PSEUDO_OFFSET_BITS; - uint32_t offset = paddr & ((1 << PSEUDO_OFFSET_BITS) - 1); - - if (index >= m_NumEntries) + if (paddr == 0) { return nullptr; - - return reinterpret_cast(uintptr_t(m_AllocBases[index]) + offset); -#else - return nullptr; -#endif + } + return m_dictionary.RecoverAddress(paddr).value_or(nullptr); } uint32_t PseudoAddressManager::ToPseudoAddress(void *addr) { -#ifdef KE_ARCH_X64 - uint8_t index = 0; - uint32_t offset = 0; - bool hasEntry = false; - void *base = GetAllocationBase(addr); - - if (base) { - for (int i = 0; i < m_NumEntries; i++) { - if (m_AllocBases[i] == base) { - index = i; - hasEntry = true; - break; - } - } - } else { + if (addr == nullptr) { return 0; } - - if (!hasEntry) { - index = m_NumEntries; - if (m_NumEntries < SM_ARRAYSIZE(m_AllocBases)) - m_AllocBases[m_NumEntries++] = base; - else - return 0; // Table is full - } - - ptrdiff_t diff = uintptr_t(addr) - uintptr_t(base); - - // Ensure difference fits in 26 bits - if (diff > (UINT32_MAX >> PSEUDO_INDEX_BITS)) - return 0; - - return (index << PSEUDO_OFFSET_BITS) | diff; -#else - return 0; -#endif -} - -void *PseudoAddressManager::GetAllocationBase(void *ptr) -{ -#if defined PLATFORM_WINDOWS - - MEMORY_BASIC_INFORMATION info; - if (!VirtualQuery(ptr, &info, sizeof(MEMORY_BASIC_INFORMATION))) - return nullptr; - return info.AllocationBase; - -#elif defined PLATFORM_APPLE - -#ifdef KE_ARCH_X86 - typedef vm_region_info_t mach_vm_region_info_t; - typedef vm_region_basic_info_data_t mach_vm_region_basic_info_data_t; - const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO; - const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT; - #define mach_vm_region vm_region -#elif defined KE_ARCH_X64 - typedef vm_region_info_64_t mach_vm_region_info_t ; - typedef vm_region_basic_info_data_64_t mach_vm_region_basic_info_data_t; - const vm_region_flavor_t MACH_VM_REGION_BASIC_INFO = VM_REGION_BASIC_INFO_64; - const mach_msg_type_number_t MACH_VM_REGION_BASIC_INFO_COUNT = VM_REGION_BASIC_INFO_COUNT_64; - #define mach_vm_region vm_region_64 -#endif - vm_size_t size; - vm_address_t vmaddr = reinterpret_cast(ptr); - mach_vm_region_basic_info_data_t info; - memory_object_name_t obj; - vm_region_flavor_t flavor = MACH_VM_REGION_BASIC_INFO; - mach_msg_type_number_t count = MACH_VM_REGION_BASIC_INFO_COUNT; - - kern_return_t kr = mach_vm_region(mach_task_self(), &vmaddr, &size, flavor, - reinterpret_cast(&info), - &count, &obj); - - if (kr != KERN_SUCCESS) - return nullptr; - - return reinterpret_cast(vmaddr); - -#elif defined PLATFORM_LINUX - - uintptr_t addr = reinterpret_cast(ptr); - - // Format: - // lower upper prot stuff path - // 08048000-0804c000 r-xp 00000000 03:03 1010107 /bin/cat - FILE *fp = fopen("/proc/self/maps", "r"); - if (fp) { - uintptr_t lower, upper; - while (fscanf(fp, "%" PRIxPTR "-%" PRIxPTR, &lower, &upper) != EOF) { - if (addr >= lower && addr <= upper) { - fclose(fp); - return reinterpret_cast(lower); - } - - // Read to end of line - int c; - while ((c = fgetc(fp)) != '\n') { - if (c == EOF) - break; - } - if (c == EOF) - break; - } - fclose(fp); - } - return nullptr; -#endif + return m_dictionary.Make32bitAddress(addr).value_or(0); } diff --git a/core/logic/PseudoAddrManager.h b/core/logic/PseudoAddrManager.h index 5de802aa2..4c6ca59d7 100644 --- a/core/logic/PseudoAddrManager.h +++ b/core/logic/PseudoAddrManager.h @@ -31,6 +31,7 @@ #define _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_ #include "common_logic.h" +#include "libaddrz/addrz.h" class PseudoAddressManager { @@ -39,13 +40,9 @@ public: public: void *FromPseudoAddress(uint32_t paddr); uint32_t ToPseudoAddress(void *addr); + void Initialize(); private: - void *GetAllocationBase(void *ptr); -private: - static constexpr uint8_t PSEUDO_OFFSET_BITS = 26; - static constexpr uint8_t PSEUDO_INDEX_BITS = sizeof(uint32_t) * 8 - PSEUDO_OFFSET_BITS; - void *m_AllocBases[1 << PSEUDO_INDEX_BITS]; - uint8_t m_NumEntries; + am::AddressDict m_dictionary; }; #endif // _INCLUDE_SOURCEMOD_PSEUDOADDRESSMANAGER_H_ diff --git a/core/logic/common_logic.cpp b/core/logic/common_logic.cpp index f88137321..cc9e1de6c 100644 --- a/core/logic/common_logic.cpp +++ b/core/logic/common_logic.cpp @@ -57,6 +57,7 @@ #include "RootConsoleMenu.h" #include "CellArray.h" #include "smn_entitylump.h" +#include "PseudoAddrManager.h" #include #include @@ -86,9 +87,7 @@ IScriptManager *scripts = &g_PluginSys; IExtensionSys *extsys = &g_Extensions; ILogger *logger = &g_Logger; CNativeOwner g_CoreNatives; -#ifdef KE_ARCH_X64 PseudoAddressManager pseudoAddr; -#endif EntityLumpParseResult lastParseResult; @@ -122,20 +121,12 @@ static void RegisterProfiler(IProfilingTool *tool) static void *FromPseudoAddress(uint32_t paddr) { -#ifdef KE_ARCH_X64 return pseudoAddr.FromPseudoAddress(paddr); -#else - return nullptr; -#endif } static uint32_t ToPseudoAddress(void *addr) { -#ifdef KE_ARCH_X64 return pseudoAddr.ToPseudoAddress(addr); -#else - return 0; -#endif } static void SetEntityLumpWritable(bool writable) @@ -236,6 +227,7 @@ static void logic_init(CoreProvider* core, sm_logic_t* _logic) g_pSourcePawn2 = *core->spe2; SMGlobalClass::head = core->listeners; + pseudoAddr.Initialize(); g_ShareSys.Initialize(); g_pCoreIdent = g_ShareSys.CreateCoreIdentity(); diff --git a/core/logic/libaddrz b/core/logic/libaddrz new file mode 160000 index 000000000..661cd316e --- /dev/null +++ b/core/logic/libaddrz @@ -0,0 +1 @@ +Subproject commit 661cd316e6ff8e8560efa20db1794f3fa479647c diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp index 67bf66c89..0c98e481b 100644 --- a/core/logic/smn_core.cpp +++ b/core/logic/smn_core.cpp @@ -59,6 +59,7 @@ #include #include #include +#include "PseudoAddrManager.h" #include using namespace SourceMod; @@ -863,11 +864,10 @@ enum NumberType static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params) { -#ifdef KE_ARCH_X86 void *addr = reinterpret_cast(params[1]); -#else - void *addr = pseudoAddr.FromPseudoAddress(params[1]); -#endif + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + addr = pseudoAddr.FromPseudoAddress(params[1]); + } if (addr == NULL) { @@ -892,14 +892,12 @@ static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params) } } - static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params) { -#ifdef KE_ARCH_X86 void *addr = reinterpret_cast(params[1]); -#else - void *addr = pseudoAddr.FromPseudoAddress(params[1]); -#endif + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + addr = pseudoAddr.FromPseudoAddress(params[1]); + } if (addr == NULL) { @@ -950,6 +948,60 @@ static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params) return 0; } +static cell_t LoadAddressFromAddress(IPluginContext *pContext, const cell_t *params) +{ + void *addr = reinterpret_cast(params[1]); + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + addr = pseudoAddr.FromPseudoAddress(params[1]); + } + + if (addr == NULL) + { + return pContext->ThrowNativeError("Address cannot be null"); + } + else if (reinterpret_cast(addr) < VALID_MINIMUM_MEMORY_ADDRESS) + { + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr); + } + + void* data = *reinterpret_cast(addr); + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + return pseudoAddr.ToPseudoAddress(data); + } + return reinterpret_cast(data); +} + +static cell_t StoreAddressToAddress(IPluginContext *pContext, const cell_t *params) +{ + void *addr = reinterpret_cast(params[1]); + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + addr = pseudoAddr.FromPseudoAddress(params[1]); + } + + if (addr == NULL) + { + return pContext->ThrowNativeError("Address cannot be null"); + } + else if (reinterpret_cast(addr) < VALID_MINIMUM_MEMORY_ADDRESS) + { + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr); + } + + void *data = reinterpret_cast(params[2]); + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + data = pseudoAddr.FromPseudoAddress(params[2]); + } + + bool updateMemAccess = params[3]; + + if (updateMemAccess) { + SourceHook::SetMemAccess(addr, sizeof(void*), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + } + *reinterpret_cast(addr) = data; + + return 0; +} + static cell_t IsNullVector(IPluginContext *pContext, const cell_t *params) { cell_t *pNullVec = pContext->GetNullRef(SP_NULL_VECTOR); @@ -1157,6 +1209,8 @@ REGISTER_NATIVES(coreNatives) {"RequireFeature", RequireFeature}, {"LoadFromAddress", LoadFromAddress}, {"StoreToAddress", StoreToAddress}, + {"LoadAddressFromAddress", LoadAddressFromAddress}, + {"StoreAddressToAddress", StoreAddressToAddress}, {"IsNullVector", IsNullVector}, {"IsNullString", IsNullString}, {"LogStackTrace", LogStackTrace}, diff --git a/core/logic/smn_gameconfigs.cpp b/core/logic/smn_gameconfigs.cpp index f475954aa..f8c6cbc83 100644 --- a/core/logic/smn_gameconfigs.cpp +++ b/core/logic/smn_gameconfigs.cpp @@ -32,6 +32,7 @@ #include "common_logic.h" #include #include "GameConfigs.h" +#include "PseudoAddrManager.h" HandleType_t g_GameConfigsType; @@ -155,11 +156,10 @@ static cell_t smn_GameConfGetAddress(IPluginContext *pCtx, const cell_t *params) if (!gc->GetAddress(key, &val)) return 0; -#ifdef KE_ARCH_X86 - return (cell_t)val; -#else - return pseudoAddr.ToPseudoAddress(val); -#endif + if (pCtx->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + return pseudoAddr.ToPseudoAddress(val); + } + return reinterpret_cast(val); } static cell_t smn_GameConfGetMemSig(IPluginContext *pCtx, const cell_t *params) @@ -187,11 +187,10 @@ static cell_t smn_GameConfGetMemSig(IPluginContext *pCtx, const cell_t *params) return 0; } -#ifdef KE_ARCH_X86 - return (cell_t)val; -#else - return pseudoAddr.ToPseudoAddress(val); -#endif + if (pCtx->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + return pseudoAddr.ToPseudoAddress(val); + } + return reinterpret_cast(val); } static GameConfigsNatives s_GameConfigsNatives; diff --git a/core/smn_entities.cpp b/core/smn_entities.cpp index c23f9b911..62a3cd74c 100644 --- a/core/smn_entities.cpp +++ b/core/smn_entities.cpp @@ -724,11 +724,10 @@ static cell_t GetEntDataEnt2(IPluginContext *pContext, const cell_t *params) static cell_t LoadEntityFromHandleAddress(IPluginContext *pContext, const cell_t *params) { -#ifdef KE_ARCH_X86 void *addr = reinterpret_cast(params[1]); -#else - void *addr = g_SourceMod.FromPseudoAddress(params[1]); -#endif + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + addr = g_SourceMod.FromPseudoAddress(params[1]); + } if (addr == NULL) { @@ -835,11 +834,10 @@ static cell_t SetEntDataEnt2(IPluginContext *pContext, const cell_t *params) static cell_t StoreEntityToHandleAddress(IPluginContext *pContext, const cell_t *params) { -#ifdef KE_ARCH_X86 void *addr = reinterpret_cast(params[1]); -#else - void *addr = g_SourceMod.FromPseudoAddress(params[1]); -#endif + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + addr = g_SourceMod.FromPseudoAddress(params[1]); + } if (addr == NULL) { @@ -2772,11 +2770,10 @@ static cell_t GetEntityAddress(IPluginContext *pContext, const cell_t *params) return pContext->ThrowNativeError("Entity %d (%d) is invalid", g_HL2.ReferenceToIndex(params[1]), params[1]); } -#ifdef KE_ARCH_X86 - return reinterpret_cast(pEntity); -#else - return g_SourceMod.ToPseudoAddress(pEntity); -#endif + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + return g_SourceMod.ToPseudoAddress(pEntity); + } + return reinterpret_cast(pEntity); } REGISTER_NATIVES(entityNatives) diff --git a/extensions/dhooks/dynhooks_sourcepawn.cpp b/extensions/dhooks/dynhooks_sourcepawn.cpp index 6ad744d16..2204c8fa2 100644 --- a/extensions/dhooks/dynhooks_sourcepawn.cpp +++ b/extensions/dhooks/dynhooks_sourcepawn.cpp @@ -376,7 +376,7 @@ ReturnAction_t HandleDetour(HookType_t hookType, CHook* pDetour) { // The this pointer is implicitly always the first argument. void *thisPtr = pDetour->GetArgument(0); - cell_t thisAddr = GetThisPtr(thisPtr, pWrapper->thisType); + cell_t thisAddr = GetThisPtr(pCallback->GetParentContext(), thisPtr, pWrapper->thisType); pCallback->PushCell(thisAddr); } diff --git a/extensions/dhooks/natives.cpp b/extensions/dhooks/natives.cpp index 82b154ada..1310b5309 100644 --- a/extensions/dhooks/natives.cpp +++ b/extensions/dhooks/natives.cpp @@ -174,7 +174,12 @@ cell_t Native_CreateHook(IPluginContext *pContext, const cell_t *params) //native Handle:DHookCreateDetour(Address:funcaddr, CallingConvention:callConv, ReturnType:returntype, ThisPointerType:thistype); cell_t Native_CreateDetour(IPluginContext *pContext, const cell_t *params) { - HookSetup *setup = new HookSetup((ReturnType)params[3], PASSFLAG_BYVAL, (CallingConvention)params[2], (ThisPointerType)params[4], (void *)params[1]); + void* addr = reinterpret_cast(params[1]); + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + addr = g_pSM->FromPseudoAddress(params[1]); + } + + HookSetup *setup = new HookSetup((ReturnType)params[3], PASSFLAG_BYVAL, (CallingConvention)params[2], (ThisPointerType)params[4], addr); Handle_t hndl = handlesys->CreateHandle(g_HookSetupHandle, setup, pContext->GetIdentity(), myself->GetIdentity(), NULL); @@ -641,7 +646,10 @@ cell_t HookRawImpl(IPluginContext *pContext, const cell_t *params, int callbackI if (removalcbIndex > 0) removalcb = pContext->GetFunctionById(params[removalcbIndex]); - void *iface = (void *)(params[3]); + void* iface = reinterpret_cast(params[3]); + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + iface = g_pSM->FromPseudoAddress(params[3]); + } for(int i = g_pHooks.size() -1; i >= 0; i--) { @@ -1482,6 +1490,10 @@ cell_t Native_GetParamAddress(IPluginContext *pContext, const cell_t *params) } size_t offset = GetParamOffset(paramStruct, index); + + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + return g_pSM->ToPseudoAddress(*(void**)((intptr_t)paramStruct->orgParams + offset)); + } return *(cell_t *)((intptr_t)paramStruct->orgParams + offset); } diff --git a/extensions/dhooks/vhook.cpp b/extensions/dhooks/vhook.cpp index 778c43013..673c0b80f 100644 --- a/extensions/dhooks/vhook.cpp +++ b/extensions/dhooks/vhook.cpp @@ -465,19 +465,18 @@ HookReturnStruct *GetReturnStruct(DHooksCallback *dg) return res; } -cell_t GetThisPtr(void *iface, ThisPointerType type) +cell_t GetThisPtr(IPluginContext* pContext, void *iface, ThisPointerType type) { - if(type == ThisPointer_CBaseEntity) + if (type == ThisPointer_CBaseEntity) { if (!iface) return -1; return gamehelpers->EntityToBCompatRef((CBaseEntity *)iface); } -#ifdef KE_ARCH_X64 - return g_pSM->ToPseudoAddress(iface); -#else + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + return g_pSM->ToPseudoAddress(iface); + } return (cell_t)iface; -#endif } #if defined( WIN32 ) && !defined( KE_ARCH_X64 ) @@ -500,7 +499,7 @@ void *Callback(DHooksCallback *dg, void **argStack) if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address) { - dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType)); + dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType)); } if(dg->returnType != ReturnType_Void) { @@ -684,7 +683,7 @@ float Callback_float(DHooksCallback *dg, void **argStack) if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address) { - dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType)); + dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType)); } returnStruct = GetReturnStruct(dg); @@ -841,7 +840,7 @@ SDKVector *Callback_vector(DHooksCallback *dg, void **argStack) if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address) { - dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType)); + dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType)); } returnStruct = GetReturnStruct(dg); @@ -995,7 +994,7 @@ string_t *Callback_stringt(DHooksCallback *dg, void **argStack) if(dg->thisType == ThisPointer_CBaseEntity || dg->thisType == ThisPointer_Address) { - dg->plugin_callback->PushCell(GetThisPtr(g_SHPtr->GetIfacePtr(), dg->thisType)); + dg->plugin_callback->PushCell(GetThisPtr(dg->plugin_callback->GetParentContext(), g_SHPtr->GetIfacePtr(), dg->thisType)); } returnStruct = GetReturnStruct(dg); diff --git a/extensions/dhooks/vhook.h b/extensions/dhooks/vhook.h index 941a975f5..b8ff69cf8 100644 --- a/extensions/dhooks/vhook.h +++ b/extensions/dhooks/vhook.h @@ -339,7 +339,7 @@ public: }; size_t GetStackArgsSize(DHooksCallback *dg); -cell_t GetThisPtr(void *iface, ThisPointerType type); +cell_t GetThisPtr(IPluginContext* pContext, void *iface, ThisPointerType type); extern IBinTools *g_pBinTools; extern HandleType_t g_HookParamsHandle; diff --git a/extensions/sdktools/vcaller.cpp b/extensions/sdktools/vcaller.cpp index 7236edf18..3ce3e4388 100644 --- a/extensions/sdktools/vcaller.cpp +++ b/extensions/sdktools/vcaller.cpp @@ -63,7 +63,8 @@ inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &ty type = PassType_Basic; if (vtype == Valve_POD || vtype == Valve_Float - || vtype == Valve_Bool) + || vtype == Valve_Bool + || vtype == Valve_VirtualAddress) { flags = PASSFLAG_BYVAL | PASSFLAG_ASPOINTER; } else { @@ -86,6 +87,13 @@ inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &ty static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params) { + auto call_type = (ValveCallType)params[1]; + if (call_type == ValveCall_Raw && pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + return pContext->ThrowNativeError("SDKCall_Raw is unavailable for plugins that have enabled virtual address."); + } + if (call_type == ValveCall_VirtualAddress && pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) != SP_ERROR_NONE) { + return pContext->ThrowNativeError("SDKCall_VirtualAddress is unavailable for plugins that haven't enabled virtual address."); + } s_numparams = 0; s_vtbl_index = -1; s_call_addr = NULL; @@ -172,11 +180,10 @@ static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *p static cell_t PrepSDKCall_SetAddress(IPluginContext *pContext, const cell_t *params) { -#ifdef KE_ARCH_X86 - s_call_addr = reinterpret_cast(params[1]); -#else - s_call_addr = g_pSM->FromPseudoAddress(params[1]); -#endif + s_call_addr = reinterpret_cast(params[1]); + if (pContext->GetRuntime()->FindPubvarByName("__Virtual_Address__", nullptr) == SP_ERROR_NONE) { + s_call_addr = g_pSM->FromPseudoAddress(params[1]); + } return (s_call_addr != NULL) ? 1 : 0; } @@ -413,6 +420,36 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params) startparam++; } break; + case ValveCall_VirtualAddress: + { + //params[startparam] is an address to a pointer to THIS + //params following this are params to the method we will invoke later + if (startparam > numparams) + { + vc->stk_put(ptr); + return pContext->ThrowNativeError("Expected a ThisPtr address, it wasn't found"); + } + + //note: varargs pawn args are passed by-ref + cell_t *cell; + pContext->LocalToPhysAddr(params[startparam], &cell); + void* thisptr = reinterpret_cast(g_pSM->FromPseudoAddress(*cell)); + + if (thisptr == nullptr) + { + vc->stk_put(ptr); + return pContext->ThrowNativeError("ThisPtr address cannot be null"); + } + else if (reinterpret_cast(thisptr) < VALID_MINIMUM_MEMORY_ADDRESS) + { + vc->stk_put(ptr); + return pContext->ThrowNativeError("Invalid ThisPtr address %p is pointing to reserved memory.", thisptr); + } + + *(void **)ptr = thisptr; + startparam++; + } + break; default: { vc->stk_put(ptr); @@ -536,6 +573,13 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params) addr = *(bool **)addr; } return *addr ? 1 : 0; + } else if (vc->retinfo->vtype == Valve_VirtualAddress) { + void *addr = *(void **)vc->retbuf; + if (vc->retinfo->flags & PASSFLAG_ASPOINTER) + { + addr = *(void **)addr; + } + return g_pSM->ToPseudoAddress(addr); } else { cell_t *addr = (cell_t *)vc->retbuf; if (vc->retinfo->flags & PASSFLAG_ASPOINTER) diff --git a/extensions/sdktools/vdecoder.cpp b/extensions/sdktools/vdecoder.cpp index 3fb2618d5..b2d52a8dd 100644 --- a/extensions/sdktools/vdecoder.cpp +++ b/extensions/sdktools/vdecoder.cpp @@ -164,6 +164,21 @@ size_t ValveParamToBinParam(ValveType type, return sizeof(float); } } + case Valve_VirtualAddress: + { + info->flags = flags; + if (flags & PASSFLAG_ASPOINTER) + { + needs_extra = true; + info->type = PassType_Basic; + info->size = sizeof(void**); + return sizeof(void**) + sizeof(void*); + } else { + info->type = PassType_Basic; + info->size = sizeof(void*); + return sizeof(void*); + } + } } return 0; @@ -276,6 +291,19 @@ DataStatus EncodeValveParam(IPluginContext *pContext, *addr = *(bool *)buffer ? 1 : 0; + return Data_Okay; + } + case Valve_VirtualAddress: + { + cell_t *addr; + pContext->LocalToPhysAddr(param, &addr); + + if (data->flags & PASSFLAG_ASPOINTER) + { + buffer = *(void **)buffer; + } + *addr = g_pSM->ToPseudoAddress(*(void**)buffer); + return Data_Okay; } } @@ -585,6 +613,29 @@ DataStatus DecodeValveParam(IPluginContext *pContext, *(char **)buffer = addr; return Data_Okay; } + case Valve_VirtualAddress: + { + if (data->decflags & VDECODE_FLAG_BYREF) + { + cell_t *addr; + pContext->LocalToPhysAddr(param, &addr); + param = *addr; + } + if (data->flags & PASSFLAG_ASPOINTER) + { + *(void **)buffer = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset; + buffer = *(void **)buffer; + } + void* addr = g_pSM->FromPseudoAddress(param); + if (addr == nullptr && (data->decflags & VDECODE_FLAG_ALLOWNULL) == 0) + { + pContext->ThrowNativeError("NULL Address not allowed"); + return Data_Fail; + } + + *(void **)buffer = addr; + return Data_Okay; + } } return Data_Fail; diff --git a/extensions/sdktools/vdecoder.h b/extensions/sdktools/vdecoder.h index 16478cf10..8f2822401 100644 --- a/extensions/sdktools/vdecoder.h +++ b/extensions/sdktools/vdecoder.h @@ -53,6 +53,7 @@ enum ValveType Valve_Edict, /**< Edict */ Valve_String, /**< String */ Valve_Bool, /**< Boolean */ + Valve_VirtualAddress, /**< SM Virtual Address */ Valve_Object, /**< Object, not matching one of the above types */ }; @@ -84,6 +85,7 @@ enum ValveCallType ValveCall_Raw, /**< Thiscall (address explicit first parameter) */ ValveCall_Server, /**< Thiscall (CBaseServer implicit first parameter) */ ValveCall_Engine, /**< Thiscall (CVEngineServer implicit first parameter) */ + ValveCall_VirtualAddress /**< Thiscall (address explicit first parameter) */ }; /** diff --git a/plugins/include/sdktools.inc b/plugins/include/sdktools.inc index 07fc55f82..15d1dfda1 100644 --- a/plugins/include/sdktools.inc +++ b/plugins/include/sdktools.inc @@ -60,9 +60,10 @@ enum SDKCallType SDKCall_Player, /**< CBasePlayer call */ SDKCall_GameRules, /**< CGameRules call */ SDKCall_EntityList, /**< CGlobalEntityList call */ - SDKCall_Raw, /**< |this| pointer with an arbitrary address */ + SDKCall_Raw, /**< |this| pointer with an arbitrary address. This is not available if SM's virtual addresses are enabled */ SDKCall_Server, /**< CBaseServer call */ - SDKCall_Engine /**< CVEngineServer call */ + SDKCall_Engine, /**< CVEngineServer call */ + SDKCall_VirtualAddress /**< |this| pointer with an arbitrary SM virtual address */ }; enum SDKLibrary @@ -88,7 +89,8 @@ enum SDKType SDKType_Float, /**< Float (any) */ SDKType_Edict, /**< edict_t (always as pointer) */ SDKType_String, /**< NULL-terminated string (always as pointer) */ - SDKType_Bool /**< Boolean (any) */ + SDKType_Bool, /**< Boolean (any) */ + SDKType_VirtualAddress, /**< SM Virtual Address */ }; enum SDKPassMethod diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index 192701568..1e6743b36 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -754,6 +754,26 @@ native any LoadFromAddress(Address addr, NumberType size); */ native void StoreToAddress(Address addr, any data, NumberType size, bool updateMemAccess = true); +/** + * Load sizeof(void*) from a memory address. + * + * @param addr Address to a memory location. + * @return The address that is stored at that address. + * @error Address is null or pointing to reserved memory. + */ +native Address LoadAddressFromAddress(Address addr); + +/** + * Store sizeof(void*) bytes to a memory address. + * + * @param addr Address to a memory location. + * @param data Address to store at that location. + * @param updateMemAccess If true, SourceMod will set read / write / exec permissions + * on the memory page being written to. + * @error Address is null or pointing to reserved memory. + */ +native void StoreAddressToAddress(Address addr, Address data, bool updateMemAccess = true); + methodmap FrameIterator < Handle { // Creates a stack frame iterator to build your own stack traces. // @return New handle to a FrameIterator. diff --git a/plugins/include/virtual_address.inc b/plugins/include/virtual_address.inc new file mode 100644 index 000000000..542b4244a --- /dev/null +++ b/plugins/include/virtual_address.inc @@ -0,0 +1,39 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod (C)2004-2025 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This file is part of the SourceMod/SourcePawn SDK. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#if defined _virtual_address_included + #endinput +#endif +#define _virtual_address_included + +public const Address __Virtual_Address__ = view_as
(0); +public const Address PointerSize = view_as
(0); /**< Size of a pointer in bytes (same as `sizeof(void*)`) */