You could take this one which provides you with the basic functionality of ProcMem:
Memory.h
Code:
#include <Windows.h>
#include <TlHelp32.h>
class Memory
{
private:
Memory( );
~Memory( );
HANDLE Handle_;
DWORD PID;
MODULEENTRY32 ClientDLL;
MODULEENTRY32 EngineDLL;
bool CompareData( const unsigned char* pbData, const unsigned char* pbMask, const char* pszString ) const;
public:
HANDLE GetHandle( ) const;
DWORD GetPID( ) const;
MODULEENTRY32 GetClientDLL( ) const;
MODULEENTRY32 GetEngineDLL( ) const;
static Memory* GetSingleton( );
bool AttachToProcess( );
template < class T >
T ReadMemory ( DWORD AddressToRead )
{
SIZE_T BytesRead;
T DataBuffer;
ReadProcessMemory( Handle_, reinterpret_cast< LPCVOID >( AddressToRead ), &DataBuffer, sizeof( DataBuffer ), &BytesRead );
if ( BytesRead >= 1 ) return DataBuffer;
OutputDebugString( "Couldn't read data at address: " + AddressToRead );
OutputDebugString( "\nGetLastError return code: " + GetLastError( ) );
OutputDebugString( "\n" );
return DataBuffer;
}
template < class T >
int WriteMemory ( DWORD AddressToWriteTo, T &DataToWrite )
{
SIZE_T BytesWritten;
WriteProcessMemory( Handle_, reinterpret_cast< LPVOID >(AddressToWriteTo), &DataToWrite, sizeof( DataToWrite ), &BytesWritten );
if ( BytesWritten >= 1 ) return 0;
int ErrorCode = GetLastError( );
OutputDebugString( "Couldn't write data at address: " + AddressToWriteTo );
OutputDebugString( "\nGetLastEror return code: " + ErrorCode );
OutputDebugString( "\n" );
return ErrorCode;
}
DWORD FindPattern( DWORD StartAddress, DWORD Size, const char* Signature, char* Mask ) const;
};
Memory.cpp
Code:
#include "Memory.h"
Memory::Memory( )
{
}
Memory::~Memory( )
{
CloseHandle( Handle_ );
}
bool Memory::CompareData( const unsigned char* pbData, const unsigned char* pbMask, const char* pszString ) const
{
for ( ; *pszString; ++pszString, ++pbData, ++pbMask )
if ( *pszString == 'x' && *pbData != *pbMask )
return FALSE;
return ( *pszString ) == NULL;
}
HANDLE Memory::GetHandle( ) const
{
return Handle_;
}
DWORD Memory::GetPID( ) const
{
return PID;
}
MODULEENTRY32 Memory::GetClientDLL( ) const
{
return ClientDLL;
}
MODULEENTRY32 Memory::GetEngineDLL( ) const
{
return EngineDLL;
}
Memory* Memory::GetSingleton( )
{
static Memory Singleton;
return &Singleton;
}
bool Memory::AttachToProcess( )
{
auto TempHandle = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS, NULL );
PROCESSENTRY32 PE;
PE.dwSize = sizeof( PROCESSENTRY32 );
MODULEENTRY32 ME;
ME.dwSize = sizeof( MODULEENTRY32 );
auto Found = false;
do
{
if ( PE.szExeFile == "csgo.exe" )
{
Found = true;
PID = PE.th32ProcessID;
Handle_ = OpenProcess( PROCESS_VM_READ | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, 0, PID );
do
{
if ( ME.szModule == "client.dll" )
{
ClientDLL = ME;
}
if ( ME.szModule == "engine.dll" )
{
EngineDLL = ME;
}
} while ( Module32First( TempHandle, &ME ) );
}
} while ( Process32First( TempHandle, &PE ) );
CloseHandle( TempHandle );
return Found ? 1 : 0;
}
DWORD Memory::FindPattern( DWORD StartAddress, DWORD Size, const char* Signature, char* Mask ) const
{
auto* Data = new BYTE [ Size ];
ULONG BytesRead;
if ( !ReadProcessMemory(Handle_, reinterpret_cast<LPCVOID>( StartAddress ), Data, Size, &BytesRead ) ) return NULL;
for ( DWORD i = 0; i < Size; i++ )
{
if ( CompareData ( static_cast< const BYTE* >(Data + i), ( unsigned char* ) Signature, Mask ) ) return StartAddress + i;
}
return NULL;
}
Keep in mind that this is using the Singleton design pattern, which means that there is only one instantiated object of Memory which will be used throughout your project.
Create pointers in your class that point to the object using the GetSingleton( ) function.
Usage:
Code:
#include "Memory.h"
int main( )
{
Memory* Mem = Memory::GetSingleton( );
Mem->AttachToProcess( );
int a = Mem->ReadMemory< int >( Address );
}
Also, this is made for CSGO, which means the AttachToProcess function is specifically made for CSGO.
Credits to whoever made the FindPattern and CompareData functions.
Also, this is another alternative made by reactiioN':
CProcess.h
Code:
/**
* File: CProcess.h
* Author: ReactiioN
*/
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <vector>
#include <memory>
#include <map>
class CModuleImage
{
public:
CModuleImage( void ) = default;
/**
* @brief Class constructor which fills out the base module data
*
* @param[in] name < string > name of the module
* @param[in] base < uintptr_t > base of the module
* @param[in] size < uintptr_t > size of the module
*/
CModuleImage( const std::string& name, uintptr_t base, uintptr_t size );
/**
* @brief Destructor, clears the byte vector
*/
~CModuleImage( void );
/**
* @brief Fill out the base module data
*
* @param[in] name < string > name of the module
* @param[in] base < uintptr_t > base of the module
* @param[in] size < uintptr_t > size of the module
*/
void Setup( const std::string& name, uintptr_t base, uintptr_t size );
public:
std::string m_Name = ""; // name of the module
uintptr_t m_Base = 0; // base address of the module
uintptr_t m_Size = 0; // size of the module
std::vector< byte > m_cBytes; // whole module buffered as bytes
};
class CProcess
{
public:
CProcess( void ) = default;
/**
* @brief Constructor, attach to a process
*
* @param[in] processName < string > name of the process
*/
explicit CProcess( const std::string& processName );
/**
* @brief Destructor, calls Release
*/
~CProcess( void );
/**
* @brief Attach to a process, get the process id + handle
*
* @param[in] processName < string > name of the process
*
* @return < bool > true if everything is ok, false if any call fails
*/
bool attach( const std::string& processName );
/**
* @brief Returns the status of attach()
*
* @return < bool > member variable: m_Attached
*/
bool IsAttached( void ) const;
/**
* @brief Build a copy of a module(save base, size, name + bytes)
*
* @param[in] name < string > name of the module
*
* @return < bool > true if we found the module, else if not
*/
bool BuildImageOfModule( const std::string& name );
/**
* @brief Wrapper to ReadProcessMemory
*
* @param[in] base < uintptr_t > address(location)
* @param buffer < void* > buffer
* @param[in] nSize < size_t > size of readed data
*
* @return < BOOL > value of ReadProcessMemory
*/
BOOL ReadMemory( uintptr_t base, void* buffer, size_t nSize );
/**
* @brief Wrapper for WirteProcessMemory
*
* @param[in] base < uintptr_t > address(location)
* @param buffer < void* > buffer
* @param[in] nSize < size-t > size of data to write
*
* @return < BOOL > value of WriteProcessMemory
*/
BOOL WriteMemory( uintptr_t base, void* buffer, size_t nSize );
/**
* @brief Return the process id of the current attached process
*
* @return < uint32_t > member variable: m_ProcessId
*/
uint32_t GetPid( void ) const;
/**
* @brief Return the process handle of the current attached process
*
* @return < HANDLE > member variable: m_hProcess
*/
HANDLE GetProcHandle( void ) const;
/**
* @brief Return a pointer to a build copy of a module
*
* @param[in] name < string > name of the module
*
* @return < shared_ptr< CModuleImage > > ModuleImage in map
*/
std::shared_ptr< CModuleImage > GetModuleImage( const std::string& name ) const;
/**
* @brief Wrapper for ReadMemory
*
* @param[in] base < uintptr_t > address(location)
* @param buffer < your declaration > buffer
*
* @tparam T your declaration
*
* @return value of ReadMemory
*/
template< class T > BOOL Read( uintptr_t base, T& buffer );
/**
* @brief Wrapper for WriteMemory
*
* @param[in] base < uintptr_t > address(location)
* @param[in] buffer < your declaration > value which gets written
*
* @tparam T your declaration
*
* @return value of WriteMemory
*/
template< class T > BOOL Write( uintptr_t base, T buffer );
/**
* @brief Wrapper for ReadMemory
*
* @param[in] base < uintptr_t > address(location)
*
* @tparam T < your declaration >
*
* @return declaration type value from memory
*/
template< class T > T Read( uintptr_t base );
/**
* @brief Release every initialized pointer/buffer
*/
void Release( void );
private:
bool m_Attached = false;
uint32_t m_ProcessId = 0;
HANDLE m_hProcess = nullptr;
std::string m_ProcessName = "";
std::map< std::string,
std::shared_ptr< CModuleImage > > m_cModuleImages;
};
template< class T > BOOL CProcess::Read( uintptr_t base, T& buffer )
{
return ReadMemory( base, &buffer, sizeof( T ) );
}
template< class T > BOOL CProcess::Write( uintptr_t base, T buffer )
{
return WriteMemory( base, &buffer, sizeof( T ) );
}
template< class T > T CProcess::Read( uintptr_t base )
{
T buffer;
ReadMemory( base, &buffer, sizeof( T ) );
return buffer;
}
CProcess.cpp
Code:
#include "CProcess.h"
CModuleImage::CModuleImage( const std::string& name, uintptr_t base, uintptr_t size ) :
m_Name( name ),
m_Base( base ),
m_Size( size )
{
}
CModuleImage::~CModuleImage( void )
{
if( !m_cBytes.empty() ) {
m_cBytes.clear();
}
}
void CModuleImage::Setup( const std::string& name, uintptr_t base, uintptr_t size )
{
m_Name = name;
m_Base = base;
m_Size = size;
}
CProcess::CProcess( const std::string& processName )
{
attach( processName );
}
CProcess::~CProcess( void )
{
Release();
}
bool CProcess::attach( const std::string& processName )
{
if( m_Attached ) {
return true;
}
if( processName.empty() ) {
if( m_ProcessName.empty() ) {
return false;
}
}
else {
m_ProcessName = processName;
}
auto hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
if( hSnapshot == INVALID_HANDLE_VALUE ) {
return false;
}
PROCESSENTRY32 ProcEntry = { sizeof( PROCESSENTRY32 ) };
if( Process32First( hSnapshot, &ProcEntry ) ) {
#ifdef _UNICODE
std::wstring processNameW( m_ProcessName.begin(), m_ProcessName.end() );
#endif
bool succeeded;
do {
#ifdef _UNICODE
succeeded = processNameW.compare( ProcEntry.szExeFile ) == 0;
#else
succeeded = m_ProcessName.compare( ProcEntry.szExeFile ) == 0;
#endif
if( succeeded ) {
m_ProcessId = ProcEntry.th32ProcessID;
break;
}
} while( Process32Next( hSnapshot, &ProcEntry ) );
}
CloseHandle( hSnapshot );
if( !m_ProcessId ) {
return false;
}
m_hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, m_ProcessId );
m_Attached = m_hProcess != INVALID_HANDLE_VALUE;
return m_Attached;
}
bool CProcess::IsAttached( void ) const
{
return m_Attached;
}
bool CProcess::BuildImageOfModule( const std::string& name )
{
if( name.empty() || !m_Attached ) {
return false;
}
if( !!m_cModuleImages.count( name ) ) {
return true;
}
auto hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, m_ProcessId );
if( hSnapshot == INVALID_HANDLE_VALUE ) {
return false;
}
auto succeeded = false;
MODULEENTRY32 ModEntry = { sizeof( MODULEENTRY32 ) };
if( Module32FirstW( hSnapshot, &ModEntry ) ) {
#ifdef _UNICODE
std::wstring nameW( name.begin(), name.end() );
#endif
do {
#ifdef _UNICODE
succeeded = nameW.compare( ModEntry.szModule ) == 0;
#else
succeeded = namew.compare( ModEntry.szModule ) == 0;
#endif
if( succeeded ) {
// Create a new pointer to CModuleImage if the current module is our target module
auto pModuleImage = std::make_shared< CModuleImage >( name, // name
reinterpret_cast< uintptr_t >( ModEntry.modBaseAddr ), // base address of the module
static_cast< uintptr_t >( ModEntry.modBaseSize ) ); // size of the module
// Read the whole module into a buffer which is declared as a vector of bytes
pModuleImage->m_cBytes = std::vector< byte >( pModuleImage->m_Size );
ReadMemory( pModuleImage->m_Base, &pModuleImage->m_cBytes.at( 0 ), pModuleImage->m_Size );
// Insert the module in our module buffer
m_cModuleImages.insert( make_pair( name, move( pModuleImage ) ) );
break;
}
} while( Module32Next( hSnapshot, &ModEntry ) );
}
CloseHandle( hSnapshot );
return succeeded;
}
BOOL CProcess::ReadMemory( uintptr_t base, void* buffer, size_t nSize )
{
if( !m_Attached ) {
return FALSE;
}
SIZE_T bytes_readed = 0;
return ReadProcessMemory( m_hProcess, reinterpret_cast< LPCVOID >( base ), buffer, nSize, &bytes_readed );
}
BOOL CProcess::WriteMemory( uintptr_t base, void* buffer, size_t nSize )
{
if( !m_Attached ) {
return FALSE;
}
SIZE_T bytes_written = 0;
return WriteProcessMemory( m_hProcess, reinterpret_cast< LPVOID >( base ), buffer, nSize, &bytes_written );
}
uint32_t CProcess::GetPid( void ) const
{
return m_ProcessId;
}
HANDLE CProcess::GetProcHandle( void ) const
{
return m_hProcess;
}
std::shared_ptr< CModuleImage > CProcess::GetModuleImage( const std::string& name ) const
{
if( !!m_cModuleImages.count( name ) ) {
return m_cModuleImages.at( name );
}
return nullptr;
}
void CProcess::Release( void )
{
m_Attached = false;
m_ProcessId = 0;
if( m_hProcess ) {
CloseHandle( m_hProcess );
m_hProcess = nullptr;
}
m_ProcessName.clear();
if( !m_cModuleImages.empty() ) {
m_cModuleImages.clear();
}
}
Usage:
Code:
#include "CProcess.h"
int32_t main( int32_t argc, char** argv )
{
std::cout << "# Waiting for Counter-Strike: Global Offensive";
while ( !g_pProcess->attach( "csgo.exe" ) )
{
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
std::cout << ".";
}
std::cout << "ok!" << std::endl;
if ( !g_pProcess->BuildImageOfModule( "client.dll" ) )
{
std::cout << "# failed to get module data of client.dll..." << std::endl;
std::cin.get( );
return 0;
}
if ( !g_pProcess->BuildImageOfModule( "engine.dll" ) )
{
std::cout << "# failed to get module data of engine.dll..." << std::endl;
std::cin.get( );
return 0;
}
auto pClientModule = g_pProcess->GetModuleImage( "client.dll" );
auto pEngineModule = g_pProcess->GetModuleImage( "engine.dll" );
const uintptr_t dwEntityList = 0x4A5C9C4;
const uintptr_t dwClientState = 0x6072C4;
while ( !( GetAsyncKeyState( VK_INSERT ) & 1 ) )
{
std::this_thread::sleep_for( std::chrono::milliseconds( 1 ) );
auto pClientState = g_pProcess->Read< uintptr_t >( pEngineModule->m_Base + dwClientState );
if ( !pClientState )
{
continue;
}
auto iLocalPlayerId = g_pProcess->Read< uintptr_t >( pClientState + 0x174 );
if ( iLocalPlayerId <= 0 || iLocalPlayerId > 64 )
{
continue;
}
auto pLocal = g_pProcess->Read< uintptr_t >( ( pClientModule->m_Base + dwEntityList + iLocalPlayerId * 0x10 ) - 0x10 );
auto health = g_pProcess->Read< int32_t >( pLocal + 0xFC );
printf( "Health: %d\n", health );
}
g_pProcess->Release( );
return 0;
}