Skip to content

Commit e545d98

Browse files
committed
Add CMemory::Dump method
1 parent 2a0e6eb commit e545d98

2 files changed

Lines changed: 158 additions & 19 deletions

File tree

include/dynlibutils/memaddr.hpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,82 @@
99

1010
#include <cstdint>
1111
#include <cstddef>
12+
#include <string>
13+
#include <type_traits>
1214
#include <utility>
1315

16+
#ifdef __cpp_concepts
17+
# include <concepts>
18+
#endif
19+
1420
#define DYNLIB_INVALID_MEMORY DynLibUtils::CMemory(nullptr)
1521

1622
namespace DynLibUtils {
1723

24+
#ifdef __cpp_concepts
25+
// Concept for string output handler.
26+
// Signature: void func(const std::string& sLine)
27+
template<typename FUNC>
28+
concept MemLineOutputFunc_t = requires(FUNC f, const std::string& sLine)
29+
{
30+
{ f(sLine) } -> std::same_as<void>;
31+
};
32+
33+
// Concept for byte-to-string conversion.
34+
// Signature: std::pair<std::string, bool> func(std::size_t index, std::uint8_t byte)
35+
// Returns (pair): first -> formatted byte string.
36+
// second -> true to force line break after current byte.
37+
template<typename FUNC>
38+
concept MemByteToStringFunc_t = requires(FUNC f, std::size_t index, std::uint8_t byte)
39+
{
40+
{ f(index, byte) } -> std::convertible_to<std::pair<std::string, bool>>;
41+
};
42+
#else
43+
#define MemLineOutputFunc_t typename
44+
#define MemByteToStringFunc_t typename
45+
#endif
46+
47+
template<typename T = std::uint8_t, std::size_t SIZE = sizeof(T), std::size_t CHARS = std::max<std::size_t>(2, SIZE)>
48+
inline char* MemToHexString_unsafe(char* pBuf, T val)
49+
{
50+
std::uint8_t n = CHARS;
51+
52+
while (n)
53+
{
54+
--n;
55+
pBuf[n] = "0123456789ABCDEF"[val & 0xFu];
56+
val >>= 4u;
57+
58+
}
59+
60+
return pBuf;
61+
}
62+
63+
template<typename T = std::uint8_t, std::size_t SIZE = sizeof(T), std::size_t CHARS = std::max<std::size_t>(2, SIZE)>
64+
inline std::string MemToHexString(T val)
65+
{
66+
char sBuffer[CHARS];
67+
68+
return std::string(MemToHexString_unsafe<T, SIZE, CHARS>(sBuffer, val), CHARS); // Small-string optimization?
69+
}
70+
71+
inline char MemToHumanChar(std::uint8_t byte)
72+
{
73+
return ('~' <= byte && byte <= ' ') ? static_cast<char>(byte) : '.';
74+
}
75+
76+
template<std::size_t BYTES_PER_LINE>
77+
inline constexpr auto g_funcDefaultMemToHex = [](std::size_t index, std::uint8_t byte) -> std::pair<std::string, bool>
78+
{
79+
return std::make_pair(MemToHexString(byte), (index + 1) % BYTES_PER_LINE == 0);
80+
};
81+
82+
template<std::size_t BYTES_PER_LINE = 8>
83+
inline constexpr auto GetDefaultMemToHexFunc()
84+
{
85+
return g_funcDefaultMemToHex<BYTES_PER_LINE>;
86+
}
87+
1888
class CMemory
1989
{
2090
public:
@@ -96,6 +166,58 @@ class CMemory
96166
return *this;
97167
}
98168

169+
template<std::size_t BYTES_PER_LINE = 8, MemLineOutputFunc_t OUT_FUNC, MemByteToStringFunc_t TO_HEX_FUNC>
170+
std::size_t Dump(std::size_t size, OUT_FUNC funcOutput, TO_HEX_FUNC funcToHex = GetDefaultMemToHexFunc<BYTES_PER_LINE>())
171+
{
172+
constexpr std::size_t kCharsPerLine = BYTES_PER_LINE * 2;
173+
174+
const auto* pData = RCast<const std::uint8_t*>();
175+
176+
std::string sLine;
177+
sLine.reserve(128);
178+
179+
std::string sFormated;
180+
sFormated.reserve(size);
181+
182+
std::size_t nOutputCount = 0;
183+
184+
for (std::size_t n = 0; n < size; ++n)
185+
{
186+
auto [sHex, bOutNextLine] = funcToHex(n, pData[n]);
187+
sLine += sHex;
188+
189+
sFormated += MemToHumanChar(pData[n]);
190+
191+
if ((n + 1) % BYTES_PER_LINE)
192+
sLine += ' ';
193+
194+
if (bOutNextLine)
195+
{
196+
sLine += " |" + sFormated + "|\n";
197+
funcOutput(sLine);
198+
sLine.clear();
199+
sFormated.clear();
200+
201+
nOutputCount++;
202+
}
203+
}
204+
205+
// Handle final partial line.
206+
if (!sLine.empty())
207+
{
208+
std::size_t pad = kCharsPerLine - (size % kCharsPerLine);
209+
210+
for (std::size_t i = 0; i < pad; ++i)
211+
sLine += " ";
212+
213+
sLine += " |" + sFormated + "|\n";
214+
funcOutput(sLine);
215+
nOutputCount++;
216+
}
217+
218+
return nOutputCount++;
219+
}
220+
99221
protected:
100222
union
101223
{

include/dynlibutils/module.hpp

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@
1515

1616
#include <array>
1717
#include <cassert>
18-
#include <vector>
1918
#include <string>
2019
#include <string_view>
20+
#include <type_traits>
2121
#include <utility>
22+
#include <vector>
2223

2324
#ifdef __cpp_concepts
2425
# include <concepts>
2526
#endif
2627

28+
#ifdef __cpp_lib_debugging
29+
# include <debugging>
30+
#endif
31+
2732
#ifdef __cpp_consteval
2833
# define DYNLIB_COMPILE_TIME_EXPR consteval
2934
#else
@@ -68,8 +73,8 @@ struct Pattern_t
6873

6974
// Concept for pattern callback.
7075
// Signature: bool callback(std::size_t index, CMemory match)
71-
// Returns: false -> continue scanning.
72-
// true -> stop scanning.
76+
// Returns: false -> stop scanning.
77+
// true -> continue scanning.
7378
#if defined(__cpp_concepts) && __cpp_concepts >= 201907L
7479
template<typename T>
7580
concept PatternCallback_t = requires(T func, std::size_t index, CMemory match)
@@ -260,16 +265,13 @@ class CModule
260265
return Find(pStart, pSection);
261266
}
262267

263-
[[nodiscard]] CMemory Find(const CMemory pStart, const Section_t* pSection) const
268+
[[nodiscard]] CMemory Find(const CMemory pStart, const Section_t* pSection = nullptr) const
264269
{
265270
return m_pModule->FindPattern<SIZE>(CMemory(Base_t::m_aBytes.data()), std::string_view(Base_t::m_aMask.data(), Base_t::m_nSize), pStart, pSection);
266271
}
267-
[[nodiscard]] CMemory FindAndOffset(const std::ptrdiff_t offset, const CMemory pStart = nullptr, const Section_t* pSection = nullptr) const { return Find(pStart, pSection).Offset(offset); }
268-
[[nodiscard]] CMemory FindAndOffsetFromSelf(const CMemory pStart = nullptr, const Section_t* pSection = nullptr) const { return FindAndOffset(Base_t::m_nSize, pStart, pSection); }
269-
270-
[[nodiscard]] CMemory FindAndDeref(const std::uintptr_t deref = 1, const CMemory pStart = nullptr, const Section_t* pSection = nullptr) const { return Find(pStart, pSection).Deref(deref); }
271-
[[nodiscard]]
272-
CMemory FollowCall(const std::ptrdiff_t opcodeOffset = 0x1, const std::ptrdiff_t nextInstructionOffset = 0x5, const CMemory pStart = nullptr, const Section_t* pSection = nullptr) const { return Find(pStart, pSection).FollowNearCall(opcodeOffset, nextInstructionOffset); }
272+
[[nodiscard]] CMemory OffsetAndFind(const std::ptrdiff_t offset, CMemory pStart, const Section_t* pSection = nullptr) const { return Find(pStart + offset, pSection); }
273+
[[nodiscard]] CMemory OffsetFromSelfAndFind(const CMemory pStart, const Section_t* pSection = nullptr) const { return OffsetAndFind(Base_t::m_nSize, pStart, pSection); }
274+
[[nodiscard]] CMemory DerefAndFind(const std::uintptr_t deref, CMemory pStart, const Section_t* pSection = nullptr) const { return Find(pStart.Deref(deref), pSection); }
273275
}; // struct CSignatureView
274276

275277
CModule() : m_pExecutableSection(nullptr), m_pHandle(nullptr) {}
@@ -278,10 +280,10 @@ class CModule
278280
CModule(const CModule&) = delete;
279281
CModule& operator=(const CModule&) = delete;
280282
CModule(CModule&& other) noexcept : m_sPath(std::move(other.m_sPath)), m_vecSections(std::move(other.m_vecSections)), m_pExecutableSection(std::move(other.m_pExecutableSection)), m_pHandle(std::move(other.m_pHandle)) {}
283+
CModule(const CMemory pModuleMemory);
281284
explicit CModule(const std::string_view svModuleName);
282285
explicit CModule(const char* pszModuleName) : CModule(std::string_view(pszModuleName)) {}
283286
explicit CModule(const std::string& sModuleName) : CModule(std::string_view(sModuleName)) {}
284-
CModule(const CMemory pModuleMemory);
285287

286288
bool LoadFromPath(const std::string_view svModelePath, int flags);
287289

@@ -428,20 +430,35 @@ class CModule
428430
CMemory pIter = pStartAddress ? pStartAddress : pBase;
429431
const CMemory pEnd = pBase + sectionSize;
430432

431-
std::size_t foundLength = 0;
433+
std::size_t foundCount = 0;
432434

433-
for (CMemory pMatch = sig(pIter, pSection);
434-
pMatch.IsValid() &&
435-
pMatch < pEnd;
436-
pIter = sig.FindAndOffsetFromSelf(pMatch, pSection))
435+
pIter = sig(pIter, pSection);
436+
437+
do
437438
{
438-
if (callback(foundLength, pMatch)) // foundLength = the index of found pattern now.
439+
if (!callback(foundCount, pIter)) // foundCount = the index of found pattern now.
440+
break;
441+
442+
// Break the loop, stop this madness.
443+
if (foundCount > 999)
444+
{
445+
std::fprintf(stderr, "%s%s:%d\n",
446+
">> Detected an INFINITE LOOP!\n"
447+
">> Breaking from\n",
448+
__FILE__, __LINE__);
449+
450+
#ifdef __cpp_lib_debugging
451+
std::breakpoint();
452+
#endif
453+
439454
break;
455+
}
440456

441-
++foundLength;
457+
++foundCount;
442458
}
459+
while((pIter = sig.OffsetFromSelfAndFind(pIter, pSection)).IsValid());
443460

444-
return foundLength; // Count of the found patterns.
461+
return foundCount; // Count of the found patterns.
445462
}
446463

447464
[[nodiscard]] CMemory GetVirtualTableByName(const std::string_view svTableName, bool bDecorated = false) const;

0 commit comments

Comments
 (0)