Skip to content

Instantly share code, notes, and snippets.

@0x1F9F1
Last active September 18, 2018 06:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0x1F9F1/7b975ad0b22cfb9b3cac591b4aedf762 to your computer and use it in GitHub Desktop.
Save 0x1F9F1/7b975ad0b22cfb9b3cac591b4aedf762 to your computer and use it in GitHub Desktop.
/*
Copyright 2018 Brick
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
Usage:
There are currently 5 defines used to control the compilation of mem.
MEM_HEADER_ONLY:
Specifies the header is to be compiled as header only. Any functionality which requires the usage of a source file is disabled.
Not compatible with MEM_IMPLEMENTATION
MEM_IMPLEMENTATION:
To use the full functionality of mem, you must include mem a second time inside a source file with MEM_IMPLEMENTATION defined.
Not compatible with MEM_HEADER_ONLY
MEM_INLINE_LEVEL (OPTIONAL):
Influences whether functions are stored in the header file (inline) or source file (non-inline).
Possible values:
MEM_INLINE_LEVEL_NEVER:
All functions are defined in the source file.
MEM_INLINE_LEVEL_SOMETIMES:
Functions are split between the header and source file depending on their complexity.
MEM_INLINE_LEVEL_ALWAYS:
All functions are defined in the header file. This is required for header only builds.
MEM_WINDOWS (OPTIONAL):
Enables windows specific functionality.
Possible values:
32
64
MEM_DEMANGLE (OPTIONAL):
Requires MEM_WINDOWS, enables demangling of RTTI names.
MEM_MIN_SKIP (OPTIONAL):
Minimum skip size required to generate a skip table for pattern scanning.
*/
#if !defined(MEM_H)
#define MEM_H
#define MEM_IN_HEADER
#define MEM_INLINE_LEVEL_NEVER 0
#define MEM_INLINE_LEVEL_SOMETIMES 1
#define MEM_INLINE_LEVEL_ALWAYS 2
#if !defined(MEM_INLINE_LEVEL)
# if defined (MEM_ALWAYS_INLINE) || defined(MEM_HEADER_ONLY)
# define MEM_INLINE_LEVEL MEM_INLINE_LEVEL_ALWAYS
# elif defined (MEM_NEVER_INLINE)
# define MEM_INLINE_LEVEL MEM_INLINE_LEVEL_NEVER
# else
# define MEM_INLINE_LEVEL MEM_INLINE_LEVEL_SOMETIMES
# endif
#endif
#if !defined(MEM_WINDOWS)
# if defined(MEM_AUTO_WINDOWS)
# if defined(_WIN32)
# if defined(_M_IX86)
# define MEM_WINDOWS 32
# elif defined(_M_AMD64)
# define MEM_WINDOWS 64
# endif
# endif
# if !defined(MEM_WINDOWS)
# error "Failed to choose windows bits"
# endif
# endif
#endif
#if !defined(MEM_MIN_SKIP)
# define MEM_MIN_SKIP 5
#endif
#define MEM_FUNC inline
#include <type_traits>
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <vector>
#include <list>
#include <string>
#include <functional>
#include <algorithm>
#if !defined(MEM_SINGLE_THREADED)
# include <future>
# include <mutex>
#endif
namespace mem
{
class pointer;
class region;
class module;
class pattern;
class protect;
namespace conventions
{
template <typename Result, typename... Args>
using func_t = Result(*)(Args...);
template <typename Result, typename Class, typename... Args>
using thiscall_t = Result(Class::*)(Args...);
#if defined(MEM_WINDOWS)
template <typename Result, typename... Args>
using cdecl_t = Result(__cdecl*)(Args...);
template <typename Result, typename... Args>
using stdcall_t = Result(__stdcall*)(Args...);
template <typename Result, typename... Args>
using fastcall_t = Result(__fastcall*)(Args...);
#endif // MEM_WINDOWS
}
class pointer
{
protected:
uintptr_t value_ {0};
public:
template <typename T>
pointer(const T& value) noexcept;
bool null() const noexcept;
pointer add(const ptrdiff_t value) const noexcept;
pointer sub(const ptrdiff_t value) const noexcept;
ptrdiff_t dist(const pointer& value) const noexcept;
pointer shift(const pointer& from, const pointer& to) const noexcept;
pointer rip(const ptrdiff_t offset) const;
pointer& deref() const noexcept;
pointer operator+(const ptrdiff_t value) const noexcept;
pointer operator-(const ptrdiff_t value) const noexcept;
pointer& operator+=(const ptrdiff_t value) noexcept;
pointer& operator-=(const ptrdiff_t value) noexcept;
pointer& operator++() noexcept;
pointer& operator--() noexcept;
pointer operator++(int) noexcept;
pointer operator--(int) noexcept;
bool operator==(const pointer& value) const noexcept;
bool operator!=(const pointer& value) const noexcept;
bool operator<(const pointer& value) const noexcept;
bool operator>(const pointer& value) const noexcept;
bool operator<=(const pointer& value) const noexcept;
bool operator>=(const pointer& value) const noexcept;
template <typename T>
const T& get(const size_t offset) const noexcept;
template <typename T>
void put(const size_t offset, const T& value) const noexcept(std::is_nothrow_copy_assignable<T>::value);
#if defined(__cpp_lib_invoke)
template <typename Func, typename... Args>
decltype(auto) invoke(Args&&... args) const;
#endif // __cpp_lib_invoke
template <typename T>
typename std::enable_if<std::is_same<T, pointer>::value, T>::type as() const noexcept;
template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type as() const noexcept;
template <typename T>
typename std::enable_if<std::is_pointer<T>::value || std::is_member_pointer<T>::value, T>::type as() const noexcept;
template <typename T>
typename std::enable_if<std::is_lvalue_reference<T>::value, T>::type as() const noexcept;
template <typename T>
typename std::enable_if<std::is_array<T>::value, typename std::add_lvalue_reference<T>::type>::type as() const noexcept;
};
class region
{
protected:
pointer base_ {nullptr};
size_t size_ {0};
public:
region(const pointer& base, const size_t size) noexcept;
pointer base() const noexcept;
size_t size() const noexcept;
pointer at(const size_t offset) const noexcept;
bool contains(const region& value) const noexcept;
bool contains(const pointer& value) const noexcept;
bool contains(const pointer& value, const size_t size) const noexcept;
template <typename T>
bool contains(const pointer& value) const noexcept;
void copy(const pointer& data) const noexcept;
void fill(const uint8_t value) const noexcept;
region sub_region(const pointer& address) const noexcept;
#if defined(MEM_WINDOWS)
protect unprotect() const noexcept;
#endif // MEM_WINDOWS
pointer scan(const pattern& pattern) const;
std::vector<pointer> scan_all(const pattern& pattern) const;
bool is_ascii() const;
std::string str() const;
std::string hex(bool upper_case = true, bool padded = false) const;
};
#if defined(MEM_WINDOWS)
class protect
: public region
{
protected:
DWORD old_protect_;
bool success_;
public:
protect(region region, DWORD new_protect);
~protect();
protect(protect&&);
protect(const protect&) = delete;
};
#endif // MEM_WINDOWS
#if defined(MEM_WINDOWS)
class module
: public region
{
protected:
static module get_nt_module(const pointer& address);
public:
using region::region;
static module named(const char* name);
static module named(const wchar_t* name);
static module main();
static module self();
};
#endif // MEM_WINDOWS
static constexpr const int8_t hex_char_table[256][2]
{
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1, 0},{-1,-1},{-1,-1},{-1,-1},{-1, 0},{-1,-1},
{ 0,15},{ 1,15},{ 2,15},{ 3,15},{ 4,15},{ 5,15},{ 6,15},{ 7,15},{ 8,15},{ 9,15},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1, 0},
{-1,-1},{10,15},{11,15},{12,15},{13,15},{14,15},{15,15},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{10,15},{11,15},{12,15},{13,15},{14,15},{15,15},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1, 0},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},
};
class pattern
{
protected:
std::vector<uint8_t> bytes_;
std::vector<uint8_t> masks_;
std::vector<uint8_t> skips_;
size_t skip_pos_;
void finalize();
public:
pattern(const char* pattern);
pattern(const char* pattern, const char* mask);
pattern(const char* pattern, const char* mask, size_t length);
bool match(const pointer& address) const;
pointer scan(const region& region) const;
std::vector<pointer> scan_all(const region& region) const;
};
template <typename... Args>
class event
{
protected:
struct listener
{
std::function<void(Args...)> function;
size_t id;
int32_t order;
bool once;
listener(std::function<void(Args...)> func, size_t id, int32_t order, bool once)
: function(std::move(func))
, id(id)
, order(order)
, once(once)
{ }
};
std::list<listener> listeners_;
size_t id_;
public:
event()
: listeners_()
, id_(0)
{ }
template <typename Func>
size_t connect(int32_t order, bool once, Func&& func)
{
size_t id = id_++;
auto find = std::find_if(listeners_.begin(), listeners_.end(), [order] (const listener& entry)
{
return entry.order < order;
});
listeners_.emplace(find, std::forward<Func>(func), id, order, once);
return id;
}
template <typename Func>
size_t connect(Func&& func)
{
return connect(0, false, std::forward<Func>(func));
}
void remove(size_t id)
{
listeners_.remove_if([id] (const listener& entry)
{
return entry.id == id;
});
}
void operator()(const Args&... args)
{
for (auto iter = listeners_.begin(); iter != listeners_.end();)
{
iter->function(args...);
if (iter->once)
{
iter = listeners_.erase(iter);
}
else
{
++iter;
}
}
}
};
#if !defined(MEM_HEADER_ONLY)
class init_function
{
protected:
using callback_t = void(*)(void);
init_function * next_;
callback_t callback_;
static init_function* first_;
public:
init_function(callback_t callback);
init_function(init_function& parent, callback_t callback);
static void init();
};
class deferred_pointer
{
protected:
pointer value_;
using callback_t = pointer(*)(void);
struct pending_entry
{
pointer* value;
callback_t callback;
};
using callback_list_t = std::vector<pending_entry>;
static callback_list_t pending_;
static init_function init_;
static void process_entry(const pending_entry& entry);
static void run();
public:
deferred_pointer(callback_t callback);
};
#endif
#if defined(MEM_WINDOWS)
namespace rtti
{
struct PMD;
struct RTTITypeDescriptor;
struct RTTICompleteObjectLocator;
struct RTTIClassHierarchyDescriptor;
struct RTTIBaseClassArray;
struct RTTIBaseClassDescriptor;
struct PMD
{
int32_t mdisp; // member displacement
int32_t pdisp; // vbtable displacement
int32_t vdisp; // displacement inside vbtable
};
struct RTTITypeDescriptor // type_info
{
void* vTable;
const char* UndecoratedName;
const char DecoratedName[1];
#if defined(MEM_DEMANGLE)
std::string demangle() const;
#endif // MEM_DEMANGLE
};
struct RTTICompleteObjectLocator
{
uint32_t signature; // 0 = x86, 1 = x64
uint32_t offset; // offset of this vtable in the complete class
uint32_t cdOffset; // constructor displacement offset
uint32_t pTypeDescriptor; // TypeDescriptor of the complete class
uint32_t pClassDescriptor; // describes inheritance hierarchy
#if MEM_WINDOWS == 64
uint32_t pSelf;
#endif // MEM_WINDOWS == 64
bool check_signature() const;
RTTITypeDescriptor* get_type(const region& region) const;
RTTIClassHierarchyDescriptor* get_class(const region& region) const;
#if MEM_WINDOWS == 64
RTTICompleteObjectLocator* get_self(const region& region) const;
#endif // MEM_WINDOWS == 64
};
struct RTTIClassHierarchyDescriptor
{
uint32_t signature; // 0 = x86, 1 = x64
uint32_t attributes; // bit 0 set = multiple inheritance, bit 1 set = virtual inheritance
uint32_t numBaseClasses; // number of base classes
uint32_t pBaseClassArray;
bool check_signature() const;
uint32_t get_base_count() const;
RTTIBaseClassArray* get_base_classes(const region& region) const;
bool inherits_from(const region& region, const RTTITypeDescriptor* type) const;
};
struct RTTIBaseClassArray
{
uint32_t arrayOfBaseClassDescriptors[1];
RTTIBaseClassDescriptor* get_base_class(const region& region, const uint32_t index) const;
};
struct RTTIBaseClassDescriptor
{
uint32_t pTypeDescriptor; // type descriptor of the class
uint32_t numContainedBases; // number of nested classes following in the Base Class Array
PMD where; // pointer-to-member displacement info
uint32_t attributes; // flags, usually 0
RTTITypeDescriptor* get_type(const region& region) const;
};
void enumerate_rtti(const region& region, const std::function<bool(const void** vTable, const RTTICompleteObjectLocator* object, const RTTITypeDescriptor* type)> callback);
const RTTITypeDescriptor* find_rtti_type(const region& region, const char* name);
}
#endif // MEM_WINDOWS
namespace misc
{
template <typename ForwardIt, typename UnaryFunction>
void parallel_for_each(ForwardIt first, ForwardIt last, const UnaryFunction& func);
template <typename T, typename U = T>
T exchange(T& obj, U&& new_value);
}
template <typename T>
MEM_FUNC typename std::enable_if<std::is_pointer<T>::value || std::is_null_pointer<T>::value || std::is_member_pointer<T>::value, uintptr_t>::type to_address(const T value) noexcept
{
static_assert(sizeof(T) == sizeof(uintptr_t), "This Shouldn't Happen");
return reinterpret_cast<const uintptr_t&>(value);
}
template <typename T>
MEM_FUNC typename std::enable_if<std::is_convertible<T, uintptr_t>::value, uintptr_t>::type to_address(const T& value) noexcept
{
return static_cast<uintptr_t>(value);
}
template <typename T>
MEM_FUNC pointer::pointer(const T& value) noexcept
: value_(to_address(value))
{ }
#if defined(__cpp_lib_invoke)
template <typename Func, typename... Args>
MEM_FUNC decltype(auto) pointer::invoke(Args&&... args) const
{
return std::invoke(as<Func>(), std::forward<Args>(args)...);
}
#endif
template <typename T>
MEM_FUNC typename std::enable_if<std::is_same<T, pointer>::value, T>::type pointer::as() const noexcept
{
return *this;
}
template <typename T>
MEM_FUNC typename std::enable_if<std::is_integral<T>::value, T>::type pointer::as() const noexcept
{
static_assert(std::is_same<typename std::make_unsigned<T>::type, uintptr_t>::value, "Invalid Integer Type");
return static_cast<T>(value_);
}
template <typename T>
MEM_FUNC typename std::enable_if<std::is_pointer<T>::value || std::is_member_pointer<T>::value, T>::type pointer::as() const noexcept
{
return reinterpret_cast<const T&>(value_);
}
template <typename T>
MEM_FUNC typename std::enable_if<std::is_lvalue_reference<T>::value, T>::type pointer::as() const noexcept
{
return *as<typename std::add_pointer<T>::type>();
}
template <typename T>
MEM_FUNC typename std::enable_if<std::is_array<T>::value, typename std::add_lvalue_reference<T>::type>::type pointer::as() const noexcept
{
return as<typename std::add_lvalue_reference<T>::type>();
}
template <typename T>
MEM_FUNC bool region::contains(const pointer& value) const noexcept
{
return contains(value, sizeof(T));
}
MEM_FUNC bool pattern::match(const pointer& address) const
{
return scan(region(address, bytes_.size())) == address;
}
namespace misc
{
template <typename ForwardIt, typename UnaryFunction>
MEM_FUNC void parallel_for_each(ForwardIt first, ForwardIt last, const UnaryFunction& func)
{
#if !defined(MEM_SINGLE_THREADED)
uint32_t thread_count = std::thread::hardware_concurrency();
if (thread_count == 0)
{
thread_count = 4;
}
std::mutex mutex;
auto thread_loop = [&]
{
while (true)
{
std::unique_lock<std::mutex> guard(mutex);
if (first != last)
{
auto value = *first++;
guard.unlock();
func(std::move(value));
}
else
{
break;
}
}
};
std::vector<std::thread> threads;
for (uint32_t i = 0; i < thread_count; ++i)
{
threads.emplace_back(thread_loop);
}
for (auto& thread : threads)
{
thread.join();
}
#else
std::for_each(first, last, func);
#endif
}
template <typename T, typename U>
MEM_FUNC T exchange(T& obj, U&& new_value)
{
T old_value = std::move(obj);
obj = std::forward<U>(new_value);
return old_value;
}
}
#undef MEM_FUNC
}
#endif // !MEM_H
#if !defined(MEM_INLINE_LEVEL)
# error "MEM_INLINE_LEVEL not defined"
#endif
#if defined(MEM_HEADER_ONLY)
# if defined(MEM_IMPLEMENTATION)
# error "MEM_HEADER_ONLY is defined, but so is MEM_IMPLEMENTATION"
# endif
# if MEM_INLINE_LEVEL < MEM_INLINE_LEVEL_ALWAYS
# error "MEM_HEADER_ONLY is defined, but MEM_INLINE_LEVEL < MEM_INLINE_LEVEL_ALWAYS"
# endif
#endif
#if MEM_INLINE_LEVEL > MEM_INLINE_LEVEL_NEVER
# if defined(MEM_IN_HEADER)
# define MEM_FUNC inline
# endif
#elif defined (MEM_IMPLEMENTATION)
# define MEM_FUNC
#endif
#if defined(MEM_FUNC)
namespace mem
{
MEM_FUNC bool pointer::null() const noexcept
{
return !value_;
}
MEM_FUNC pointer pointer::add(const ptrdiff_t value) const noexcept
{
return value_ + value;
}
MEM_FUNC pointer pointer::sub(const ptrdiff_t value) const noexcept
{
return value_ - value;
}
MEM_FUNC ptrdiff_t pointer::dist(const pointer& value) const noexcept
{
return static_cast<ptrdiff_t>(value.value_ - value_);
}
MEM_FUNC pointer pointer::shift(const pointer& from, const pointer& to) const noexcept
{
return (value_ - from.value_) + to.value_;
}
MEM_FUNC pointer pointer::rip(const ptrdiff_t offset) const
{
return value_ + offset + as<int32_t&>();
}
MEM_FUNC pointer& pointer::deref() const noexcept
{
return as<pointer&>();
}
MEM_FUNC pointer pointer::operator+(const ptrdiff_t value) const noexcept
{
return value_ + value;
}
MEM_FUNC pointer pointer::operator-(const ptrdiff_t value) const noexcept
{
return value_ - value;
}
MEM_FUNC pointer& pointer::operator+=(const ptrdiff_t value) noexcept
{
value_ += value;
return *this;
}
MEM_FUNC pointer& pointer::operator-=(const ptrdiff_t value) noexcept
{
value_ -= value;
return *this;
}
MEM_FUNC pointer& pointer::operator++() noexcept
{
return *this = *this + 1;
}
MEM_FUNC pointer& pointer::operator--() noexcept
{
return *this = *this - 1;
}
MEM_FUNC pointer pointer::operator++(int) noexcept
{
pointer result = *this;
++value_;
return result;
}
MEM_FUNC pointer pointer::operator--(int) noexcept
{
pointer result = *this;
--value_;
return result;
}
MEM_FUNC bool pointer::operator==(const pointer& value) const noexcept
{
return value_ == value.value_;
}
MEM_FUNC bool pointer::operator!=(const pointer& value) const noexcept
{
return value_ != value.value_;
}
MEM_FUNC bool pointer::operator<(const pointer& value) const noexcept
{
return value_ < value.value_;
}
MEM_FUNC bool pointer::operator>(const pointer& value) const noexcept
{
return value_ > value.value_;
}
MEM_FUNC bool pointer::operator<=(const pointer& value) const noexcept
{
return value_ <= value.value_;
}
MEM_FUNC bool pointer::operator>=(const pointer& value) const noexcept
{
return value_ >= value.value_;
}
template <typename T>
MEM_FUNC const T& pointer::get(const size_t offset) const noexcept
{
return add(offset).as<const T&>();
}
template <typename T>
MEM_FUNC void pointer::put(const size_t offset, const T& value) const noexcept(std::is_nothrow_copy_assignable<T>::value)
{
add(offset).as<T&>() = value;
}
MEM_FUNC region::region(const pointer& base, const size_t size) noexcept
: base_(base)
, size_(size)
{ }
MEM_FUNC pointer region::base() const noexcept
{
return base_;
}
MEM_FUNC size_t region::size() const noexcept
{
return size_;
}
MEM_FUNC pointer region::at(const size_t offset) const noexcept
{
pointer result = base().add(offset);
return contains(result) ? result : nullptr;
}
MEM_FUNC bool region::contains(const region& value) const noexcept
{
return base().dist(value.base()) + value.size() <= size();
}
MEM_FUNC bool region::contains(const pointer& value) const noexcept
{
return contains(region(value, 1));
}
MEM_FUNC bool region::contains(const pointer& value, const size_t size) const noexcept
{
return contains(region(value, size));
}
MEM_FUNC void region::copy(const pointer& data) const noexcept
{
memcpy(base().as<void*>(), data.as<const void*>(), size());
}
MEM_FUNC void region::fill(const uint8_t value) const noexcept
{
memset(base().as<void*>(), value, size_);
}
MEM_FUNC region region::sub_region(const pointer& address) const noexcept
{
return region(address, size() - base().dist(address));
}
MEM_FUNC pointer region::scan(const pattern& pattern) const
{
return pattern.scan(*this);
}
MEM_FUNC std::vector<pointer> region::scan_all(const pattern& pattern) const
{
return pattern.scan_all(*this);
}
MEM_FUNC bool region::is_ascii() const
{
for (size_t i = 0; i < size(); ++i)
{
if ((at(i).as<uint8_t&>() - 0x20) <= 0x5F)
{
return false;
}
}
return true;
}
MEM_FUNC std::string region::str() const
{
return std::string(base().as<const char*>(), size());
}
MEM_FUNC std::string region::hex(bool upper_case, bool padded) const
{
const char* const char_hex_table = upper_case ? "0123456789ABCDEF" : "0123456789abcdef";
std::string result;
result.reserve(size_ * (padded ? 3 : 2));
for (size_t i = 0; i < size_; ++i)
{
const uint8_t v = base().add(i).as<const uint8_t&>();
if (i && padded)
{
result.push_back(' ');
}
result.push_back(char_hex_table[(v >> 4) & 0xF]);
result.push_back(char_hex_table[(v >> 0) & 0xF]);
}
return result;
}
#if !defined(MEM_HEADER_ONLY)
MEM_FUNC init_function::init_function(callback_t callback)
: next_(misc::exchange(first_, this))
, callback_(callback)
{ }
MEM_FUNC init_function::init_function(init_function& parent, callback_t callback)
: next_(misc::exchange(parent.next_, this))
, callback_(callback)
{ }
MEM_FUNC void init_function::init()
{
for (init_function* i = misc::exchange(first_, nullptr); i; i = misc::exchange(i->next_, nullptr))
{
misc::exchange(i->callback_, nullptr)();
}
}
MEM_FUNC deferred_pointer::deferred_pointer(callback_t callback)
: value_(nullptr)
{
pending_.push_back({ &value_, std::move(callback) });
}
MEM_FUNC void deferred_pointer::process_entry(const pending_entry& entry)
{
*entry.value = entry.callback();
}
MEM_FUNC void deferred_pointer::run()
{
misc::parallel_for_each(pending_.begin(), pending_.end(), &process_entry);
pending_.clear();
}
#endif
}
#undef MEM_FUNC
#endif
#if MEM_INLINE_LEVEL > MEM_INLINE_LEVEL_SOMETIMES
# if defined(MEM_IN_HEADER)
# define MEM_FUNC inline
# endif
#elif defined (MEM_IMPLEMENTATION)
# define MEM_FUNC
#endif
#if defined(MEM_FUNC)
namespace mem
{
#if defined(MEM_WINDOWS)
MEM_FUNC protect region::unprotect() const noexcept
{
return protect(*this, PAGE_EXECUTE_READWRITE);
}
#endif // MEM_WINDOWS
#if defined(MEM_WINDOWS)
MEM_FUNC protect::protect(region region, DWORD new_protect)
: region(region)
, old_protect_(0)
, success_(false)
{
success_ = VirtualProtect(base().as<void*>(), size(), new_protect, &old_protect_);
}
MEM_FUNC protect::~protect()
{
if (success_)
{
VirtualProtect(base().as<void*>(), size(), old_protect_, &old_protect_);
}
}
MEM_FUNC protect::protect(protect && rhs)
: region(rhs)
, old_protect_(misc::exchange(rhs.old_protect_, 0))
, success_(misc::exchange(rhs.success_, false))
{ }
#endif // MEM_WINDOWS
#if defined(MEM_WINDOWS)
extern "C" namespace detail
{
IMAGE_DOS_HEADER __ImageBase;
}
MEM_FUNC module module::get_nt_module(const pointer& address)
{
const IMAGE_DOS_HEADER* dos = address.as<const IMAGE_DOS_HEADER*>();
const IMAGE_NT_HEADERS* nt = address.add(dos->e_lfanew).as<const IMAGE_NT_HEADERS*>();
return module(address, nt->OptionalHeader.SizeOfImage);
}
MEM_FUNC module module::named(const char* name)
{
return get_nt_module(GetModuleHandleA(name));
}
MEM_FUNC module module::named(const wchar_t* name)
{
return get_nt_module(GetModuleHandleW(name));
}
MEM_FUNC module module::main()
{
return module::named(static_cast<const char*>(nullptr));
}
MEM_FUNC module module::self()
{
return get_nt_module(&detail::__ImageBase);
}
#endif // MEM_WINDOWS
MEM_FUNC pattern::pattern(const char* pattern)
{
while (true)
{
uint8_t b = 0x00;
uint8_t m = 0x00;
char c = 0;
size_t i = 0;
while ((c = *pattern++) != '\0')
{
const int8_t mask = hex_char_table[c][1];
if (mask != -1)
{
b <<= 4;
m <<= 4;
if (mask > 0)
{
const int8_t value = hex_char_table[c][0];
if (value != -1)
{
b |= value;
}
}
m |= mask;
if (++i >= 2)
{
break;
}
}
else if (i)
{
break;
}
}
if (c || i)
{
bytes_.push_back(b);
masks_.push_back(m);
}
if (!c)
{
break;
}
}
finalize();
}
MEM_FUNC pattern::pattern(const char* pattern, const char* mask)
{
if (mask)
{
const size_t size = strlen(mask);
bytes_.resize(size);
masks_.resize(size);
for (size_t i = 0; i < size; ++i)
{
if (mask[i] == '?')
{
bytes_[i] = 0x00;
masks_[i] = 0x00;
}
else
{
const char c = pattern[i];
bytes_[i] = static_cast<uint8_t>(c);
masks_[i] = 0xFF;
}
}
}
else
{
const size_t size = strlen(pattern);
bytes_.resize(size);
masks_.resize(size);
for (size_t i = 0; i < size; ++i)
{
const char c = pattern[i];
bytes_[i] = static_cast<uint8_t>(c);
masks_[i] = 0xFF;
}
}
finalize();
}
MEM_FUNC pattern::pattern(const char* pattern, const char* mask, size_t length)
{
const size_t size = length;
bytes_.resize(size);
masks_.resize(size);
for (size_t i = 0; i < size; ++i)
{
const char c = pattern[i];
const char m = mask[i];
bytes_[i] = static_cast<uint8_t>(c);
masks_[i] = static_cast<uint8_t>(m);
}
finalize();
}
MEM_FUNC void pattern::finalize()
{
if (!masks_.empty())
{
size_t last_mask = masks_.size();
while (last_mask && (masks_[last_mask - 1] == 0x00))
{
--last_mask;
}
bytes_.resize(last_mask);
masks_.resize(last_mask);
}
if (!bytes_.empty())
{
size_t max_skip = 0;
size_t skip_pos = 0;
{
size_t current_skip = 0;
for (size_t i = 0; i < bytes_.size(); ++i)
{
if (masks_[i] != 0xFF)
{
if (current_skip > max_skip)
{
max_skip = current_skip;
skip_pos = i - current_skip;
}
current_skip = 0;
}
else
{
++current_skip;
}
}
if (current_skip > max_skip)
{
max_skip = current_skip;
skip_pos = bytes_.size() - current_skip;
}
}
if (max_skip > MEM_MIN_SKIP)
{
skips_.resize(256, static_cast<uint8_t>(max_skip <= 0xFF ? max_skip : 0xFF));
skip_pos_ = skip_pos + max_skip - 1;
for (size_t i = skip_pos, last = skip_pos + max_skip - 1; i < last; ++i)
{
const size_t skip = last - i;
skips_[bytes_[i]] = static_cast<uint8_t>(skip <= 0xFF ? skip : 0xFF);
}
}
if (max_skip == masks_.size())
{
masks_.clear();
}
}
}
MEM_FUNC pointer pattern::scan(const region& region) const
{
const size_t size = bytes_.size();
const size_t region_size = region.size();
if ((size == 0) || (size > region_size))
{
return nullptr;
}
const size_t last = size - 1;
const uint8_t* const region_base = region.base().as<const uint8_t*>();
const uint8_t* current = region_base;
const uint8_t* const end = region_base + region_size - last;
const volatile uint8_t* const skips = skips_.data();
const uint8_t* const bytes = bytes_.data();
const uint8_t* const masks = masks_.data();
const size_t skip_pos = skip_pos_;
#if defined(__GNUC__) || defined(__clang__)
# define MEM_LIKELY(x) __builtin_expect(!!(x), 1)
#else
# define MEM_LIKELY(x) !!(x)
#endif
if (masks)
{
if (skips)
{
while (MEM_LIKELY(current < end))
{
const size_t skip = skips[current[skip_pos]];
size_t i = last;
do
{
if (MEM_LIKELY((current[i] ^ bytes[i]) & masks[i]))
{
goto $mask_skip_next;
}
} while (MEM_LIKELY(i--));
goto $found;
$mask_skip_next:
current += skip;
}
goto $not_found;
}
else
{
while (MEM_LIKELY(current < end))
{
size_t i = last;
do
{
if (MEM_LIKELY((current[i] ^ bytes[i]) & masks[i]))
{
goto $mask_noskip_next;
}
} while (MEM_LIKELY(i--));
goto $found;
$mask_noskip_next:
current += 1;
}
goto $not_found;
}
}
else
{
if (skips)
{
while (MEM_LIKELY(current < end))
{
const size_t skip = skips[current[skip_pos]];
size_t i = last;
do
{
if (MEM_LIKELY(current[i] ^ bytes[i]))
{
goto $nomask_skip_next;
}
} while (MEM_LIKELY(i--));
goto $found;
$nomask_skip_next:
current += skip;
}
goto $not_found;
}
else
{
while (MEM_LIKELY(current < end))
{
size_t i = last;
do
{
if (MEM_LIKELY(current[i] ^ bytes[i]))
{
goto $nomask_noskip_next;
}
} while (MEM_LIKELY(i--));
goto $found;
$nomask_noskip_next:
current += 1;
}
goto $not_found;
}
}
#undef MEM_LIKELY
$not_found:
return nullptr;
$found:
return current;
}
MEM_FUNC std::vector<pointer> pattern::scan_all(const region& region) const
{
std::vector<pointer> results;
if (bytes_.size() > region.size())
{
return results;
}
for (pointer p = region.base(); p = scan(region.sub_region(p)), !p.null(); ++p)
{
results.push_back(p);
}
return results;
}
#if defined(MEM_WINDOWS)
namespace rtti
{
MEM_FUNC bool check_rtti_signature(const uint32_t signature)
{
#if MEM_WINDOWS == 64
return signature == 1;
#elif MEM_WINDOWS == 32
return signature == 0;
#else
# error "Invalid Architecture"
#endif
}
template <typename T>
MEM_FUNC T* get_rtti_pointer(const region& region, const uint32_t address)
{
const pointer result =
#if MEM_WINDOWS == 64
region.base().add(address);
#elif MEM_WINDOWS == 32
address;
#else
# error "Invalid Architecture"
#endif
if (region.contains<T>(result))
{
return result.as<T*>();
}
return nullptr;
}
#if defined(MEM_DEMANGLE)
MEM_FUNC std::string RTTITypeDescriptor::demangle() const
{
#if defined(_DBGHELP_)
char buffer[1024];
if (DWORD symbol_size = UnDecorateSymbolName(DecoratedName + 1, buffer, 1024, UNDNAME_32_BIT_DECODE | UNDNAME_NAME_ONLY | UNDNAME_NO_ARGUMENTS | UNDNAME_NO_MS_KEYWORDS))
{
return std::string(buffer, symbol_size);
}
return "";
#else
# error "Missing UnDecorateSymbolName"
#endif // _DBGHELP_
}
#endif // MEM_DEMANGLE
MEM_FUNC bool RTTICompleteObjectLocator::check_signature() const
{
return check_rtti_signature(signature);
}
MEM_FUNC RTTITypeDescriptor* RTTICompleteObjectLocator::get_type(const region& region) const
{
return get_rtti_pointer<RTTITypeDescriptor>(region, pTypeDescriptor);
}
MEM_FUNC RTTIClassHierarchyDescriptor* RTTICompleteObjectLocator::get_class(const region& region) const
{
return get_rtti_pointer<RTTIClassHierarchyDescriptor>(region, pClassDescriptor);
}
#if MEM_WINDOWS == 64
MEM_FUNC RTTICompleteObjectLocator* RTTICompleteObjectLocator::get_self(const region& region) const
{
return get_rtti_pointer<RTTICompleteObjectLocator>(region, pSelf);
}
#endif // MEM_WINDOWS == 64
MEM_FUNC bool RTTIClassHierarchyDescriptor::check_signature() const
{
return check_rtti_signature(signature);
}
MEM_FUNC uint32_t RTTIClassHierarchyDescriptor::get_base_count() const
{
return numBaseClasses;
}
MEM_FUNC RTTIBaseClassArray* RTTIClassHierarchyDescriptor::get_base_classes(const region& region) const
{
return get_rtti_pointer<RTTIBaseClassArray>(region, pBaseClassArray);
}
MEM_FUNC bool RTTIClassHierarchyDescriptor::inherits_from(const region& region, const RTTITypeDescriptor* type) const
{
const RTTIBaseClassArray* base_classes = get_base_classes(region);
for (uint32_t i = 1; i < get_base_count(); ++i)
{
const RTTIBaseClassDescriptor* base_class = base_classes->get_base_class(region, i);
const RTTITypeDescriptor* base_type = base_class->get_type(region);
if (!strcmp(base_type->DecoratedName, type->DecoratedName))
{
return true;
}
}
return false;
}
MEM_FUNC RTTIBaseClassDescriptor* RTTIBaseClassArray::get_base_class(const region& region, const uint32_t index) const
{
return get_rtti_pointer<RTTIBaseClassDescriptor>(region, arrayOfBaseClassDescriptors[index]);
}
MEM_FUNC RTTITypeDescriptor* RTTIBaseClassDescriptor::get_type(const region& region) const
{
return get_rtti_pointer<RTTITypeDescriptor>(region, pTypeDescriptor);
}
MEM_FUNC void enumerate_rtti(const region& region, const std::function<bool(const void** vTable, const RTTICompleteObjectLocator* object, const RTTITypeDescriptor* type)> callback)
{
for (size_t i = 0; i < region.size(); i += sizeof(void*))
{
const RTTICompleteObjectLocator*& locator = region.base().add(i).as<const RTTICompleteObjectLocator*&>();
if (!region.contains<RTTICompleteObjectLocator>(locator))
{
continue;
}
if (!locator->check_signature())
{
continue;
}
#if MEM_WINDOWS == 64
if (locator->get_self(region) != locator)
{
continue;
}
#endif // MEM_WINDOWS == 64
const RTTITypeDescriptor* type = locator->get_type(region);
if (!type)
{
continue;
}
if (!region.contains<void*>(type->vTable))
{
continue;
}
if (strncmp(type->DecoratedName, ".?", 2))
{
continue;
}
if (callback(reinterpret_cast<const void**>(&locator + 1), locator, type))
{
break;
}
}
}
#if defined(MEM_DEMANGLE)
MEM_FUNC const RTTITypeDescriptor* find_rtti_type(const region& region, const char* name)
{
const RTTITypeDescriptor* result = nullptr;
enumerate_rtti(region, [&result, name](const void**, const RTTICompleteObjectLocator*, const RTTITypeDescriptor* type) -> bool
{
if (type->demangle() == name)
{
result = type;
return true;
}
return false;
});
return result;
}
#endif // MEM_DEMANGLE
}
#endif // MEM_WINDOWS
}
#undef MEM_FUNC
#endif // MEM_FUNC
#if defined(MEM_IMPLEMENTATION)
namespace mem
{
init_function* init_function::first_ = nullptr;
deferred_pointer::callback_list_t deferred_pointer::pending_;
init_function deferred_pointer::init_(&deferred_pointer::run);
}
#undef MEM_IMPLEMENTATION
#endif // MEM_IMPLEMENTATION
#if defined(MEM_MACROS)
#define MEM_PASTE_HELPER(lhs, rhs) lhs ## rhs
#define MEM_PASTE(lhs, rhs) MEM_PASTE_HELPER(lhs, rhs)
#define MEM_RUN_ONCE(body) static mem::init_function MEM_PASTE(RunOnce, __LINE__)(body);
#define MEM_RUN_ONCE_AFTER(parent, body) static mem::init_function MEM_PASTE(RunOnce, __LINE__)(parent, body);
#define MEM_RUN_ONCE_NAMED(name, body) static mem::init_function name(body);
#define MEM_RUN_ONCE_NAMED_AFTER(name, parent, body) static mem::init_function name(parent, body);
#endif // MEM_MACROS
#undef MEM_IN_HEADER
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment