diff --git a/core/logic/PseudoAddrManager.cpp b/core/logic/PseudoAddrManager.cpp index 2979b1ab09..6c0d40fa58 100644 --- a/core/logic/PseudoAddrManager.cpp +++ b/core/logic/PseudoAddrManager.cpp @@ -43,10 +43,15 @@ PseudoAddressManager::PseudoAddressManager() : m_NumEntries(0) // 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::FromPseudoAddress(uint32_t paddr) +{ + return FromPseudoAddress(paddr, 0); +} + +void *PseudoAddressManager::FromPseudoAddress(uint32_t paddr, uint32_t offset) { #ifdef PLATFORM_X64 uint8_t index = paddr >> PSEUDO_OFFSET_BITS; - uint32_t offset = paddr & ((1 << PSEUDO_OFFSET_BITS) - 1); + offset += paddr & ((1 << PSEUDO_OFFSET_BITS) - 1); if (index >= m_NumEntries) return nullptr; diff --git a/core/logic/PseudoAddrManager.h b/core/logic/PseudoAddrManager.h index 5de802aa26..175fc8e8c0 100644 --- a/core/logic/PseudoAddrManager.h +++ b/core/logic/PseudoAddrManager.h @@ -38,6 +38,7 @@ class PseudoAddressManager PseudoAddressManager(); public: void *FromPseudoAddress(uint32_t paddr); + void *FromPseudoAddress(uint32_t paddr, uint32_t offset); uint32_t ToPseudoAddress(void *addr); private: void *GetAllocationBase(void *ptr); diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp index 490a6a00da..4b90cc5462 100644 --- a/core/logic/smn_core.cpp +++ b/core/logic/smn_core.cpp @@ -816,6 +816,143 @@ enum NumberType //memory addresses below 0x10000 are automatically considered invalid for dereferencing #define VALID_MINIMUM_MEMORY_ADDRESS 0x10000 +static inline void *GetAddress(cell_t caddr, cell_t coffset) +{ +#ifdef PLATFORM_X86 + return reinterpret_cast(caddr + coffset); +#else + return pseudoAddr.FromPseudoAddress((uint32_t)caddr, (uint32_t)coffset); +#endif +} + +static inline bool IsAddressValidRange(void *addr) +{ + return addr != NULL && reinterpret_cast(addr) >= VALID_MINIMUM_MEMORY_ADDRESS; +} + +static inline int GetAddressAccess(void *addr) +{ + int bits; + + return SourceHook::GetPageBits(addr, &bits) ? bits : 0; +} + +static inline bool SetAddressAccess(void *addr, size_t len, int access) +{ + return SourceHook::SetMemAccess(addr, len, access); +} + +// Very slowly if iterate by each address cell. +template +static inline bool HasAddressAccess(void *addr) +{ + return (GetAddressAccess(addr) & A) == A; +} + +template +static inline cell_t ReadSecureAddressCell(IPluginContext *pContext, cell_t caddr, cell_t coffset) +{ + void *addr = GetAddress(caddr, coffset); + + if (!IsAddressValidRange(addr)) + { + #ifdef _DEBUG + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory (base is 0x%x, offset is 0x%x, read block size is %d)", addr, caddr, coffset, sizeof(T)); + #else + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory", addr); + #endif + } + +#ifdef _DEBUG + if (!HasAddressAccess(addr)) + { + return pContext->ThrowNativeError("Invalid address access by 0x%x to read memory (base is 0x%x, offset is 0x%x, read block size is %d)", addr, caddr, coffset, sizeof(T)); + } +#endif + + // If you have crash, enable _DEBUG for profiling which plugin the address is not valid. + return (cell_t)*reinterpret_cast(addr); +} + +template +static inline cell_t WriteSecureAddressCell(IPluginContext *pContext, cell_t caddr, cell_t coffset, cell_t cvalue) +{ + void *addr = GetAddress(caddr, coffset); + + if (!IsAddressValidRange(addr)) + { + #ifdef _DEBUG + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory (base is 0x%x, offset is 0x%x, read block size is %d)", addr, caddr, coffset, sizeof(T)); + #else + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory", addr); + #endif + } + +#ifdef _DEBUG + if (!HasAddressAccess(addr)) + { + return pContext->ThrowNativeError("Invalid address access by 0x%x to write memory (base is 0x%x, offset is 0x%x, read block size is %d)", addr, caddr, coffset, sizeof(T)); + } +#endif + + // If you have crash, enable _DEBUG for profiling which plugin the address is not valid. + cell_t old_cvalue = (cell_t)*reinterpret_cast(addr); + + *reinterpret_cast(addr) = (T)cvalue; + + return old_cvalue; +} + +static inline int GetSecureAddressAccessCell(IPluginContext *pContext, cell_t caddr, cell_t coffset) +{ + return GetAddressAccess(GetAddress(caddr, coffset)); +} + +static inline cell_t SetSecureAddressAccessCell(IPluginContext *pContext, cell_t caddr, cell_t coffset, cell_t size, cell_t access) +{ + return (cell_t)SetAddressAccess(GetAddress(caddr, coffset), (size_t)size, (int)access); +} + +static cell_t Address_ReadInt8(IPluginContext *pContext, const cell_t *params) +{ + return ReadSecureAddressCell(pContext, params[1], params[2]); +} + +static cell_t Address_ReadInt16(IPluginContext *pContext, const cell_t *params) +{ + return ReadSecureAddressCell(pContext, params[1], params[2]); +} + +static cell_t Address_ReadInt32(IPluginContext *pContext, const cell_t *params) +{ + return ReadSecureAddressCell(pContext, params[1], params[2]); +} + +static cell_t Address_WriteInt8(IPluginContext *pContext, const cell_t *params) +{ + return WriteSecureAddressCell(pContext, params[1], params[3], params[2]); +} + +static cell_t Address_WriteInt16(IPluginContext *pContext, const cell_t *params) +{ + return WriteSecureAddressCell(pContext, params[1], params[3], params[2]); +} + +static cell_t Address_WriteInt32(IPluginContext *pContext, const cell_t *params) +{ + return WriteSecureAddressCell(pContext, params[1], params[3], params[2]); +} + +static cell_t Address_GetAccess(IPluginContext *pContext, const cell_t *params) +{ + return (cell_t)GetSecureAddressAccessCell(pContext, params[1], params[2]); +} + +static cell_t Address_SetAccess(IPluginContext *pContext, const cell_t *params) +{ + return SetSecureAddressAccessCell(pContext, params[1], params[4], params[3], params[2]); +} + static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params) { #ifdef PLATFORM_X86 @@ -1114,6 +1251,15 @@ REGISTER_NATIVES(coreNatives) {"IsNullVector", IsNullVector}, {"IsNullString", IsNullString}, {"LogStackTrace", LogStackTrace}, + + {"Address.ReadInt8", Address_ReadInt8}, + {"Address.ReadInt16", Address_ReadInt16}, + {"Address.ReadInt32", Address_ReadInt32}, + {"Address.WriteInt8", Address_WriteInt8}, + {"Address.WriteInt16", Address_WriteInt16}, + {"Address.WriteInt32", Address_WriteInt32}, + {"Address.GetAccess", Address_GetAccess}, + {"Address.SetAccess", Address_SetAccess}, {"FrameIterator.FrameIterator", FrameIterator_Create}, {"FrameIterator.Next", FrameIterator_Next}, diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index 876a6d6834..2016a95af0 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -697,6 +697,10 @@ native FeatureStatus GetFeatureStatus(FeatureType type, const char[] name); native void RequireFeature(FeatureType type, const char[] name, const char[] fmt="", any ...); +#define SH_MEM_READ (1 << 0) // Memory block can be read. +#define SH_MEM_WRITE (1 << 1) // Memory block can be write. +#define SH_MEM_EXEC (1 << 2) // Memory block can be execute. Usually the bytecode to be executed by CPU. + /** * Represents how many bytes we can read from an address with one load */ @@ -712,6 +716,69 @@ enum Address Address_Null = 0 // a typical invalid result when an address lookup fails }; +methodmap Address +{ + // Reads 1 byte from a memory address. + // + // @param offset Offset from the base address (in bytes). + // @return The value that is stored. + // @error Address is null or pointing to reserved memory. + public native any ReadInt8(int offset = 0); + + // Reads 2 bytes from a memory address. + // + // @param offset Offset from the base address (in bytes). + // @return The value that is stored. + // @error Address is null or pointing to reserved memory. + public native any ReadInt16(int offset = 0); + + // Reads 4 bytes from a memory address. + // + // @param offset Offset from the base address (in bytes). + // @return The value that is stored. + // @error Address is null or pointing to reserved memory. + public native any ReadInt32(int offset = 0); + + // Writes 1 byte to a memory address. + // + // @param data Value to store at the address. + // @param offset Offset from the base address (in bytes). + // @return The old value that was stored. + // @error Address is null or pointing to reserved memory. + public native any WriteInt8(any data, int offset = 0); + + // Writes 2 bytes to a memory address. + // + // @param data Value to store at the address. + // @param offset Offset from the base address (in bytes). + // @return The old value that was stored. + // @error Address is null or pointing to reserved memory. + public native any WriteInt16(any data, int offset = 0); + + // Writes 4 bytes to a memory address. + // + // @param data Value to store at the address. + // @param offset Offset from the base address (in bytes). + // @return The old value that was stored. + // @error Address is null or pointing to reserved memory. + public native any WriteInt32(any data, int offset = 0); + + // Gets accesses from a memory address block. + // + // @param offset Offset from the base address (in bytes). + // @return Access flags. See SH_MEM_* for details. + public native int GetAccess(int offset = 0); + + // Sets access flags to a memory address. + // Very effectively with setting accesses for large memory block. + // + // @param access Access flags. See SH_MEM_* for details. + // @param size Memory block size to set accesses. + // @param offset Offset from the base address (in bytes). + // @return If true, accesses have been protected, otherwise failure. + public native bool SetAccess(int access, int size = 4, int offset = 0); +}; + /** * Load up to 4 bytes from a memory address. *