Skip to content

Commit 5f4ae83

Browse files
committed
fix: add safe memory accessor, protector and some other improvements
1 parent 5550fc1 commit 5f4ae83

24 files changed

Lines changed: 764 additions & 143 deletions

CMakeLists.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ elseif(MACOS)
4343
endif()
4444

4545
set(COMPILE_DEFINITIONS
46-
-DDYNLIBUTILS_SEPARATE_SOURCE_FILES
46+
#DYNLIBUTILS_SEPARATE_SOURCE_FILES
47+
DYNLIBUTILS_PLATFORM_LINUX=${LINUX}
48+
DYNLIBUTILS_PLATFORM_WINDOWS=${WIN32}
49+
DYNLIBUTILS_PLATFORM_APPLE=${APPLE}
4750
)
4851

4952
set(INCLUDE_DIRS
@@ -52,21 +55,29 @@ set(INCLUDE_DIRS
5255

5356
if(WINDOWS)
5457
set(SOURCE_FILES
58+
${SOURCE_DIR}/windows/memaccessor.cpp
59+
${SOURCE_DIR}/windows/memprotector.cpp
5560
${SOURCE_DIR}/windows/module.cpp
5661
)
5762
elseif(LINUX)
5863
set(SOURCE_FILES
64+
${SOURCE_DIR}/linux/memaccessor.cpp
65+
${SOURCE_DIR}/linux/memprotector.cpp
5966
${SOURCE_DIR}/linux/module.cpp
6067
)
6168
elseif(MACOS)
6269
set(SOURCE_FILES
70+
${SOURCE_DIR}/apple/memaccessor.cpp
71+
${SOURCE_DIR}/apple/memprotector.cpp
6372
${SOURCE_DIR}/apple/module.cpp
6473
)
6574
else()
6675
message(FATAL_ERROR "Unsupported platform")
6776
endif()
6877

6978
list(APPEND SOURCE_FILES
79+
${SOURCE_DIR}/memaccessor.cpp
80+
${SOURCE_DIR}/memprotector.cpp
7081
${SOURCE_DIR}/module.cpp # always include last
7182
)
7283

include/dynlibutils/macros.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
//
12
// DynLibUtils
2-
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r) & Borys Komashchenko (Phoenix)
3+
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r), Borys Komashchenko (Phoenix), Nikita Ushakov (qubka)
34
// Licensed under the MIT license. See LICENSE file in the project root for details.
5+
//
46

57
#ifndef DYNLIBUTILS_MACROS_H
68
#define DYNLIBUTILS_MACROS_H
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// DynLibUtils
3+
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r), Borys Komashchenko (Phoenix), Nikita Ushakov (qubka)
4+
// Licensed under the MIT license. See LICENSE file in the project root for details.
5+
//
6+
7+
#ifndef DYNLIBUTILS_MEMACCESSOR_H
8+
#define DYNLIBUTILS_MEMACCESSOR_H
9+
#pragma once
10+
11+
#include "memaddr.hpp"
12+
#include "protflag.hpp"
13+
14+
namespace DynLibUtils
15+
{
16+
/**
17+
* @class MemAccessor
18+
* @brief A class providing various memory access routines.
19+
*/
20+
class CMemAccessor
21+
{
22+
public:
23+
/**
24+
* @brief Defines a memory read/write routine that may fail ungracefully.
25+
* It's expected this library will only ever use this routine in cases that are expected to succeed.
26+
* @param dest The destination memory address.
27+
* @param src The source memory address.
28+
* @param size The number of bytes to copy.
29+
* @return True if the memory copy succeeds, false otherwise.
30+
*/
31+
static bool MemCopy(CMemory dest, CMemory src, size_t size);
32+
33+
/**
34+
* @brief Defines a memory write routine that will not throw exceptions, and can handle potential
35+
* writes to NO_ACCESS or otherwise inaccessible memory pages. Defaults to WriteProcessMemory.
36+
* Must fail gracefully.
37+
* @param dest The destination memory address.
38+
* @param src The source memory address.
39+
* @param size The number of bytes to copy.
40+
* @param written A reference to the variable that will hold the number of bytes successfully written.
41+
* @return True if the memory copy succeeds, false otherwise.
42+
*/
43+
static bool SafeMemCopy(CMemory dest, CMemory src, size_t size, size_t& written) noexcept;
44+
45+
/**
46+
* @brief Defines a memory read routine that will not throw exceptions, and can handle potential
47+
* reads from NO_ACCESS or otherwise inaccessible memory pages. Defaults to ReadProcessMemory.
48+
* Must fail gracefully.
49+
* @param src The source memory address.
50+
* @param dest The destination memory address.
51+
* @param size The number of bytes to read.
52+
* @param read A reference to the variable that will hold the number of bytes successfully read.
53+
* @return True if the memory read succeeds, false otherwise.
54+
*/
55+
static bool SafeMemRead(CMemory src, CMemory dest, size_t size, size_t& read) noexcept;
56+
57+
/**
58+
* @brief Defines a memory protection set/unset routine that may fail ungracefully.
59+
* @param dest The memory address to change protection for.
60+
* @param size The number of bytes to change protection for.
61+
* @param newProtection The new protection flags to set.
62+
* @param status A reference to a variable that will hold the success status of the operation.
63+
* @return The previous protection flags if the operation succeeds, otherwise an appropriate error code.
64+
*/
65+
static ProtFlag MemProtect(CMemory dest, size_t size, ProtFlag newProtection, bool& status);
66+
};
67+
68+
static constexpr size_t MemoryRound(size_t numToRound, size_t multiple)
69+
{
70+
return numToRound & (static_cast<size_t>(-1) ^ (multiple - 1));
71+
}
72+
73+
static constexpr size_t MemoryRoundUp(size_t numToRound, size_t multiple)
74+
{
75+
return (numToRound + (multiple - 1)) & (static_cast<size_t>(-1) ^ (multiple - 1));
76+
}
77+
} // namespace DynLibUtils
78+
79+
#endif //DYNLIBUTILS_MEMACCESSOR_H

include/dynlibutils/memaddr.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
//
12
// DynLibUtils
2-
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r) & Borys Komashchenko (Phoenix)
3+
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r), Borys Komashchenko (Phoenix), Nikita Ushakov (qubka)
34
// Licensed under the MIT license. See LICENSE file in the project root for details.
5+
//
46

57
#ifndef DYNLIBUTILS_MEMADDR_HPP
68
#define DYNLIBUTILS_MEMADDR_HPP
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// DynLibUtils
3+
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r), Borys Komashchenko (Phoenix), Nikita Ushakov (qubka)
4+
// Licensed under the MIT license. See LICENSE file in the project root for details.
5+
//
6+
7+
#ifndef DYNLIBUTILS_MEMPROTECTOR_H
8+
#define DYNLIBUTILS_MEMPROTECTOR_H
9+
#pragma once
10+
11+
#include "memaddr.hpp"
12+
#include "protflag.hpp"
13+
14+
namespace DynLibUtils
15+
{
16+
class CMemAccessor;
17+
/**
18+
* @class CMemProtector
19+
* @brief A class to manage memory protection settings.
20+
*
21+
* This class allows setting and unsetting memory protection for a given
22+
* memory region. It ensures that the original protection settings are restored
23+
* when the object is destroyed.
24+
*/
25+
class CMemProtector
26+
{
27+
public:
28+
CMemProtector() = delete; /**< Deleted default constructor. */
29+
30+
/**
31+
* @brief Constructs a CMemProtector object to set memory protection.
32+
*
33+
* @param address The memory address to protect.
34+
* @param length The length of the memory region to protect.
35+
* @param prot The protection flags to set.
36+
* @param unsetOnDestroy If true, the original protection settings will be restored when the object is destroyed.
37+
*/
38+
CMemProtector(CMemory address, size_t length, ProtFlag prot, bool unsetOnDestroy = true);
39+
40+
/**
41+
* @brief Destructor to restore the original protection settings if required.
42+
*/
43+
~CMemProtector();
44+
45+
/**
46+
* @brief Gets the original protection settings.
47+
*
48+
* @return The original protection flags.
49+
*/
50+
ProtFlag OriginalProt() const noexcept { return m_origProtection; }
51+
52+
/**
53+
* @brief Checks if the memory protection was successfully set.
54+
*
55+
* @return True if the memory protection was successfully set, false otherwise.
56+
*/
57+
bool IsValid() const noexcept { return m_status; }
58+
59+
private:
60+
void* m_address; /**< The memory address to protect. */
61+
size_t m_length; /**< The length of the memory region to protect. */
62+
bool m_status; /**< The status of the memory protection operation. */
63+
bool m_unsetLater; /**< Whether to unset the protection on destruction. */
64+
65+
ProtFlag m_origProtection = ProtFlag::UNSET; /**< The original protection flags. */
66+
};
67+
68+
/**
69+
* @brief Translates protection flags to an integer representation.
70+
*
71+
* @param flags The protection flags to translate.
72+
* @return An integer representation of the protection flags.
73+
*/
74+
int TranslateProtection(ProtFlag flags) noexcept;
75+
76+
/**
77+
* @brief Translates an integer representation of protection flags to ProtFlag.
78+
*
79+
* @param prot The integer representation of the protection flags.
80+
* @return The corresponding ProtFlag.
81+
*/
82+
ProtFlag TranslateProtection(int prot) noexcept;
83+
84+
} // namespace DynLibUtils
85+
86+
#endif //DYNLIBUTILS_MEMPROTECTOR_H
87+

include/dynlibutils/module.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// DynLibUtils
3-
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r) & Borys Komashchenko (Phoenix)
3+
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r), Borys Komashchenko (Phoenix), Nikita Ushakov (qubka)
44
// Licensed under the MIT license. See LICENSE file in the project root for details.
55
//
66

@@ -333,9 +333,9 @@ struct CHash
333333

334334
struct CNullMutex
335335
{
336-
void lock() const {}
337-
void unlock() const {}
338-
bool try_lock() const { return true; }
336+
void lock() const noexcept {}
337+
void unlock() const noexcept {}
338+
bool try_lock() const noexcept { return true; }
339339

340340
void lock_shared() const noexcept {}
341341
void unlock_shared() const noexcept {}
@@ -390,7 +390,7 @@ class CAssemblyModule : public CMemory
390390

391391
const Section_t *m_pExecutableSection;
392392

393-
alignas(64) mutable std::unordered_map<CCache, CMemory, CHash> m_mapCached;
393+
mutable std::unordered_map<CCache, CMemory, CHash> m_mapCached;
394394
DYNLIB_NUA mutable Mutex m_mutex;
395395

396396
public:

include/dynlibutils/protflag.hpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// DynLibUtils
3+
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r), Borys Komashchenko (Phoenix), Nikita Ushakov (qubka)
4+
// Licensed under the MIT license. See LICENSE file in the project root for details.
5+
//
6+
7+
#ifndef DYNLIBUTILS_PROTFLAG_HPP
8+
#define DYNLIBUTILS_PROTFLAG_HPP
9+
#pragma once
10+
11+
#include <cstdint>
12+
#include <type_traits>
13+
14+
namespace DynLibUtils
15+
{
16+
/**
17+
* @enum ProtFlag
18+
* @brief Enum representing memory protection flags.
19+
*/
20+
enum ProtFlag
21+
{
22+
UNSET = 0, /**< Value means this gives no information about protection state (un-read) */
23+
X = 1 << 1, /**< Execute permission */
24+
R = 1 << 2, /**< Read permission */
25+
W = 1 << 3, /**< Write permission */
26+
S = 1 << 4, /**< Shared memory */
27+
P = 1 << 5, /**< Private memory */
28+
N = 1 << 6, /**< Value equaling the Linux flag PROT_UNSET (read the prot, and the prot is unset) */
29+
RWX = R | W | X /**< Read, Write, and Execute permissions */
30+
};
31+
32+
/**
33+
* @brief Overloads the binary OR operator for ProtFlag.
34+
*
35+
* @param lhs The left-hand side ProtFlag value.
36+
* @param rhs The right-hand side ProtFlag value.
37+
* @return The combined ProtFlag value.
38+
*/
39+
inline ProtFlag operator|(ProtFlag lhs, ProtFlag rhs) noexcept
40+
{
41+
using underlying = typename std::underlying_type<ProtFlag>::type;
42+
return static_cast<ProtFlag> (
43+
static_cast<underlying>(lhs) | static_cast<underlying>(rhs)
44+
);
45+
}
46+
47+
/**
48+
* @brief Overloads the binary AND operator for ProtFlag.
49+
*
50+
* @param lhs The left-hand side ProtFlag value.
51+
* @param rhs The right-hand side ProtFlag value.
52+
* @return True if lhs contains rhs, false otherwise.
53+
*/
54+
inline bool operator&(ProtFlag lhs, ProtFlag rhs) noexcept
55+
{
56+
using underlying = typename std::underlying_type<ProtFlag>::type;
57+
return static_cast<underlying>(lhs) & static_cast<underlying>(rhs);
58+
}
59+
60+
/**
61+
* @brief Bitwise OR assignment operator for ProtFlag enum class.
62+
*
63+
* @param lhs Left-hand side ProtFlag.
64+
* @param rhs Right-hand side ProtFlag.
65+
* @return Reference to the left-hand side ProtFlag.
66+
*/
67+
inline ProtFlag& operator|=(ProtFlag& lhs, ProtFlag rhs) noexcept
68+
{
69+
lhs = lhs | rhs;
70+
return lhs;
71+
}
72+
} // namespace DynLibUtils
73+
74+
#endif // DYNLIBUTILS_PROTFLAG_HPP

include/dynlibutils/virtual.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
//
12
// DynLibUtils
2-
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r) & Borys Komashchenko (Phoenix)
3+
// Copyright (C) 2023-2025 Vladimir Ezhikov (Wend4r), Borys Komashchenko (Phoenix), Nikita Ushakov (qubka)
34
// Licensed under the MIT license. See LICENSE file in the project root for details.
5+
//
46

57
#ifndef DYNLIBUTILS_VIRTUAL_HPP
68
#define DYNLIBUTILS_VIRTUAL_HPP

0 commit comments

Comments
 (0)