SilentPatch/SilentPatch/MemoryMgr.h
2018-04-22 00:13:15 +02:00

429 lines
No EOL
11 KiB
C++

#ifndef __MEMORYMGR
#define __MEMORYMGR
// Switches:
// _MEMORY_NO_CRT - don't include anything "complex" like ScopedUnprotect or memset
// _MEMORY_DECLS_ONLY - don't include anything but macroes
#define WRAPPER __declspec(naked)
#define DEPRECATED __declspec(deprecated)
#define EAXJMP(a) { _asm mov eax, a _asm jmp eax }
#define VARJMP(a) { _asm jmp a }
#define WRAPARG(a) ((int)a)
#define NOVMT __declspec(novtable)
#define SETVMT(a) *((uintptr_t*)this) = (uintptr_t)a
#ifndef _MEMORY_DECLS_ONLY
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <cstdint>
#include <cassert>
#ifndef _MEMORY_NO_CRT
#include <initializer_list>
#include <iterator>
#endif
enum
{
PATCH_CALL,
PATCH_JUMP
};
template<typename AT>
inline AT DynBaseAddress(AT address)
{
return (ptrdiff_t)GetModuleHandle(nullptr) - 0x400000 + address;
}
namespace Memory
{
template<typename T, typename AT>
inline void Patch(AT address, T value)
{*(T*)address = value; }
#ifndef _MEMORY_NO_CRT
template<typename AT>
inline void Patch(AT address, std::initializer_list<uint8_t> list )
{
uint8_t* addr = reinterpret_cast<uint8_t*>(address);
std::copy( list.begin(), list.end(), stdext::make_checked_array_iterator(addr, list.size()) );
}
#endif
template<typename AT>
inline void Nop(AT address, size_t count)
#ifndef _MEMORY_NO_CRT
{ memset((void*)address, 0x90, count); }
#else
{ do {
*(uint8_t*)address++ = 0x90;
} while ( --count != 0 ); }
#endif
template<typename AT, typename Func>
inline void InjectHook(AT address, Func hook)
{
union member_cast
{
intptr_t addr;
Func funcPtr;
} cast;
static_assert( sizeof(cast.addr) == sizeof(cast.funcPtr), "member_cast failure!" );
cast.funcPtr = hook;
*(int32_t*)((intptr_t)address + 1) = static_cast<int32_t>(cast.addr - (intptr_t)address - 5);
}
template<typename AT, typename Func>
inline void InjectHook(AT address, Func hook, unsigned int nType)
{
*(uint8_t*)address = nType == PATCH_JUMP ? 0xE9 : 0xE8;
InjectHook(address, hook);
}
template<typename Func, typename AT>
inline void ReadCall(AT address, Func& func)
{
union member_cast
{
intptr_t addr;
Func funcPtr;
} cast;
static_assert( sizeof(cast.addr) == sizeof(cast.funcPtr), "member_cast failure!" );
cast.addr = (intptr_t)address + 5 + *(int32_t*)((intptr_t)address+1);
func = cast.funcPtr;
}
template<typename AT>
inline void* ReadCallFrom(AT address, ptrdiff_t offset = 0)
{
uintptr_t addr;
ReadCall( address, addr );
return reinterpret_cast<void*>( addr + offset );
}
#ifndef _MEMORY_NO_CRT
inline bool MemEquals(uintptr_t address, std::initializer_list<uint8_t> val)
{
const uint8_t* mem = reinterpret_cast<const uint8_t*>(address);
return std::equal( val.begin(), val.end(), stdext::make_checked_array_iterator(mem, val.size()) );
}
#endif
template<typename AT>
inline AT Verify(AT address, uintptr_t expected)
{
assert( uintptr_t(address) == expected );
return address;
}
namespace DynBase
{
template<typename T, typename AT>
inline void Patch(AT address, T value)
{
Memory::Patch(DynBaseAddress(address), value);
}
#ifndef _MEMORY_NO_CRT
template<typename AT>
inline void Patch(AT address, std::initializer_list<uint8_t> list )
{
Memory::Patch(DynBaseAddress(address), std::move(list));
}
#endif
template<typename AT>
inline void Nop(AT address, size_t count)
{
Memory::Nop(DynBaseAddress(address), count);
}
template<typename AT, typename HT>
inline void InjectHook(AT address, HT hook)
{
Memory::InjectHook(DynBaseAddress(address), hook);
}
template<typename AT, typename HT>
inline void InjectHook(AT address, HT hook, unsigned int nType)
{
Memory::InjectHook(DynBaseAddress(address), hook, nType);
}
template<typename Func, typename AT>
inline void ReadCall(AT address, Func& func)
{
Memory::ReadCall(DynBaseAddress(address), func);
}
template<typename AT>
inline void* ReadCallFrom(AT address, ptrdiff_t offset = 0)
{
return Memory::ReadCallFrom(DynBaseAddress(address), offset);
}
#ifndef _MEMORY_NO_CRT
inline bool MemEquals(uintptr_t address, std::initializer_list<uint8_t> val)
{
return Memory::MemEquals(DynBaseAddress(address), std::move(val));
}
template<typename AT>
inline AT Verify(AT address, uintptr_t expected)
{
return Memory::Verify(address, DynBaseAddress(expected));
}
#endif
};
namespace VP
{
template<typename T, typename AT>
inline void Patch(AT address, T value)
{
DWORD dwProtect[2];
VirtualProtect((void*)address, sizeof(T), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
Memory::Patch( address, value );
VirtualProtect((void*)address, sizeof(T), dwProtect[0], &dwProtect[1]);
}
#ifndef _MEMORY_NO_CRT
template<typename AT>
inline void Patch(AT address, std::initializer_list<uint8_t> list )
{
DWORD dwProtect[2];
VirtualProtect((void*)address, list.size(), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
Memory::Patch(address, std::move(list));
VirtualProtect((void*)address, list.size(), dwProtect[0], &dwProtect[1]);
}
#endif
template<typename AT>
inline void Nop(AT address, size_t count)
{
DWORD dwProtect[2];
VirtualProtect((void*)address, count, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
Memory::Nop( address, count );
VirtualProtect((void*)address, count, dwProtect[0], &dwProtect[1]);
}
template<typename AT, typename HT>
inline void InjectHook(AT address, HT hook)
{
DWORD dwProtect[2];
VirtualProtect((void*)((DWORD_PTR)address + 1), 4, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
Memory::InjectHook( address, hook );
VirtualProtect((void*)((DWORD_PTR)address + 1), 4, dwProtect[0], &dwProtect[1]);
}
template<typename AT, typename HT>
inline void InjectHook(AT address, HT hook, unsigned int nType)
{
DWORD dwProtect[2];
VirtualProtect((void*)address, 5, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
Memory::InjectHook( address, hook, nType );
VirtualProtect((void*)address, 5, dwProtect[0], &dwProtect[1]);
}
template<typename Func, typename AT>
inline void ReadCall(AT address, Func& func)
{
Memory::ReadCall(address, func);
}
template<typename AT>
inline void* ReadCallFrom(AT address, ptrdiff_t offset = 0)
{
return Memory::ReadCallFrom(address, offset);
}
#ifndef _MEMORY_NO_CRT
inline bool MemEquals(uintptr_t address, std::initializer_list<uint8_t> val)
{
return Memory::MemEquals(address, std::move(val));
}
#endif
template<typename AT>
inline AT Verify(AT address, uintptr_t expected)
{
return Memory::Verify(address, expected);
}
namespace DynBase
{
template<typename T, typename AT>
inline void Patch(AT address, T value)
{
VP::Patch(DynBaseAddress(address), value);
}
#ifndef _MEMORY_NO_CRT
template<typename AT>
inline void Patch(AT address, std::initializer_list<uint8_t> list )
{
VP::Patch(DynBaseAddress(address), std::move(list));
}
#endif
template<typename AT>
inline void Nop(AT address, size_t count)
{
VP::Nop(DynBaseAddress(address), count);
}
template<typename AT, typename HT>
inline void InjectHook(AT address, HT hook)
{
VP::InjectHook(DynBaseAddress(address), hook);
}
template<typename AT, typename HT>
inline void InjectHook(AT address, HT hook, unsigned int nType)
{
VP::InjectHook(DynBaseAddress(address), hook, nType);
}
template<typename Func, typename AT>
inline void ReadCall(AT address, Func& func)
{
Memory::ReadCall(DynBaseAddress(address), func);
}
template<typename AT>
inline void* ReadCallFrom(AT address, ptrdiff_t offset = 0)
{
Memory::ReadCallFrom(DynBaseAddress(address), offset);
}
#ifndef _MEMORY_NO_CRT
inline bool MemEquals(uintptr_t address, std::initializer_list<uint8_t> val)
{
return Memory::MemEquals(DynBaseAddress(address), std::move(val));
}
#endif
template<typename AT>
inline AT Verify(AT address, uintptr_t expected)
{
return Memory::Verify(address, DynBaseAddress(expected));
}
};
};
};
#ifndef _MEMORY_NO_CRT
#include <forward_list>
#include <tuple>
#include <memory>
namespace ScopedUnprotect
{
class Unprotect
{
public:
~Unprotect()
{
for ( auto& it : m_queriedProtects )
{
DWORD dwOldProtect;
VirtualProtect( std::get<0>(it), std::get<1>(it), std::get<2>(it), &dwOldProtect );
}
}
protected:
Unprotect() = default;
void UnprotectRange( DWORD_PTR BaseAddress, SIZE_T Size )
{
SIZE_T QueriedSize = 0;
while ( QueriedSize < Size )
{
MEMORY_BASIC_INFORMATION MemoryInf;
DWORD dwOldProtect;
VirtualQuery( (LPCVOID)(BaseAddress + QueriedSize), &MemoryInf, sizeof(MemoryInf) );
if ( MemoryInf.State == MEM_COMMIT && (MemoryInf.Type & MEM_IMAGE) != 0 &&
(MemoryInf.Protect & (PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY|PAGE_READWRITE|PAGE_WRITECOPY)) == 0 )
{
const bool wasExecutable = (MemoryInf.Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ)) != 0;
VirtualProtect( MemoryInf.BaseAddress, MemoryInf.RegionSize, wasExecutable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &dwOldProtect );
m_queriedProtects.emplace_front( MemoryInf.BaseAddress, MemoryInf.RegionSize, MemoryInf.Protect );
}
QueriedSize += MemoryInf.RegionSize;
}
}
private:
std::forward_list< std::tuple< LPVOID, SIZE_T, DWORD > > m_queriedProtects;
};
class Section : public Unprotect
{
public:
Section( HINSTANCE hInstance, const char* name )
{
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)hInstance + ((PIMAGE_DOS_HEADER)hInstance)->e_lfanew);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(ntHeader);
DWORD_PTR VirtualAddress = DWORD_PTR(-1);
SIZE_T VirtualSize = SIZE_T(-1);
for ( SIZE_T i = 0, j = ntHeader->FileHeader.NumberOfSections; i < j; ++i, ++pSection )
{
if ( strncmp( (const char*)pSection->Name, name, IMAGE_SIZEOF_SHORT_NAME ) == 0 )
{
VirtualAddress = (DWORD_PTR)hInstance + pSection->VirtualAddress;
VirtualSize = pSection->Misc.VirtualSize;
m_locatedSection = true;
break;
}
}
if ( VirtualAddress == DWORD_PTR(-1) )
return;
UnprotectRange( VirtualAddress, VirtualSize );
};
bool SectionLocated() const { return m_locatedSection; }
private:
bool m_locatedSection = false;
};
class FullModule : public Unprotect
{
public:
FullModule( HINSTANCE hInstance )
{
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)hInstance + ((PIMAGE_DOS_HEADER)hInstance)->e_lfanew);
UnprotectRange( (DWORD_PTR)hInstance, ntHeader->OptionalHeader.SizeOfImage );
}
};
inline std::unique_ptr<Unprotect> UnprotectSectionOrFullModule( HINSTANCE hInstance, const char* name )
{
std::unique_ptr<Section> section = std::make_unique<Section>( hInstance, name );
if ( !section->SectionLocated() )
{
return std::make_unique<FullModule>( hInstance );
}
return section;
}
};
#endif
#endif
#endif