Napisałem też podobną klasę. Używa inteligentnych wskaźników z C++11 oraz boost do przeładowania funkcji na podstawie typu szablonowego. Jeżeli nie chcesz bawić się w C++11 to inteligentne wskaźniki też można zamienić na te z boost.
Klasy używa się bardzo prosto i cała magia to metody Read
oraz Write
.
Write
ma trzy wersje. Pierwsza jest podstawowa, przyjmuje adres docelowy, adres źródłowy oraz ilość bajtów do zapisania.
Druga wersja służy do zapisywania pojedynczych obiektów. Przyjmuje adres docelowy i referencję do obiektu.
Trzecia wersja służy do zapisywania tablic. Przyjmuje adres docelowy i tablicę.
Jeżeli mamy do zapisania wiele obiektów, do których dostęp mamy tylko za pomocą wskaźnika to używamy pierwszej wersji.
Read
ma cztery wersje. Pierwsza - podstawowa, przyjmuje adres źródłowy, adres do bufora i jego wielkość.
Druga wersja służy do odczytania jednego obiektu i zwraca go (może zostać skopiowany, jeżeli nie zadziała return value optimization).
Trzecia wersja również służy do odczytania jednego obiektu, ale zwraca inteligentny wskaźnik.
Czwarta wersja odczytuje wiele obiektów (pobiera ich ilość) i również zwraca do nich inteligentny wskaźnik.
Zastosowanie w praktyce:
Kopiuj
#include "ProcessMemory.hpp"
#include <memory>
#include <string>
#include <Windows.h>
#include <assert.h>
struct Test
{
char Values[16];
Test()
{
for(int i = 0; i < 16; i++)
Values[i] = rand() % 256;
}
bool IsEqualTo(const Test& another)
{
for(int i = 0; i < 16; i++)
{
if(Values[i] != another.Values[i])
return false;
}
return true;
}
};
int main()
{
ProcessMemory processMemory(GetCurrentProcessId());
{
double original = 3.14;
double retrieved = processMemory.Read<double>(&original);
assert(original == retrieved);
}
{
Test original;
std::unique_ptr<Test> retrieved = processMemory.Read<Test*>(&original);
assert(retrieved->IsEqualTo(original));
}
{
Test originals[5];
std::unique_ptr<Test[]> retrieved = processMemory.Read<Test[]>(originals, 5);
for(int i = 0; i < 5; i++)
assert(retrieved[i].IsEqualTo(originals[i]));
}
{
double original = 3.14;
double placeholder;
processMemory.Write(&placeholder, original);
assert(original == placeholder);
}
{
Test originals[5];
auto placeholder = std::unique_ptr<Test[]>(new Test[5]);
processMemory.Write(placeholder.get(), originals);
for(int i = 0; i < 5; i++)
assert(placeholder[i].IsEqualTo(originals[i]));
}
{
const char* original = "testowy napis";
std::unique_ptr<char[]> retrieved = processMemory.Read<char[]>(original, strlen(original) + 1);
assert(strcmp(original, retrieved.get()) == 0);
}
{
std::string original = "testowy napis";
std::string retreived = processMemory.Read<char[]>(&original[0], original.size() + 1).get();
assert(original == retreived);
}
return 0;
}
I sama klasa (powinna być leak-free, ale nie testowałem):
Kopiuj
#pragma once
#include <Windows.h>
#include <memory>
#include <exception>
#include <boost\type_traits\is_pointer.hpp>
#include <boost\type_traits\is_array.hpp>
#include <boost\type_traits\remove_pointer.hpp>
#include <boost\type_traits\remove_bounds.hpp>
#include <boost\utility\enable_if.hpp>
class ProcessMemory
{
private:
HANDLE _processHandle;
bool IsValidHandle(HANDLE handle)
{
return handle != NULL && handle != INVALID_HANDLE_VALUE;
}
public:
ProcessMemory(int processId)
{
_processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, false, processId);
if(!IsValidHandle(_processHandle)) throw std::exception();
}
~ProcessMemory()
{
if(IsValidHandle(_processHandle))
{
CloseHandle(_processHandle);
_processHandle = NULL;
}
}
void Read(LPCVOID address, LPVOID buffer, SIZE_T size)
{
SIZE_T bytesRead;
if(ReadProcessMemory(_processHandle, address, buffer, size, &bytesRead) == 0)
throw std::exception();
if(bytesRead != size)
throw std::exception();
}
template<typename T>
T Read(LPCVOID address, ...)
{
T object;
Read(address, &object, sizeof(T));
return object;
}
template<typename T>
std::unique_ptr<typename boost::remove_pointer<T>::type>
Read(LPCVOID address, typename boost::enable_if<boost::is_pointer<T>>::type* dummy = 0)
{
typedef boost::remove_pointer<T>::type TType;
TType* object = new TType;
try
{
Read(address, object, sizeof(TType));
}
catch(...)
{
delete object;
throw;
}
return std::unique_ptr<TType>(object);
}
template<typename T>
std::unique_ptr<T> Read(LPCVOID address, size_t elementCount, typename boost::enable_if<boost::is_array<T>>::type* dummy = 0)
{
typedef boost::remove_bounds<T>::type TType;
TType* objects = new TType[elementCount];
try
{
Read(address, objects, sizeof(TType) * elementCount);
}
catch(...)
{
delete[] objects;
throw;
}
return std::unique_ptr<TType[]>(objects);
}
void Write(LPVOID address, LPCVOID buffer, SIZE_T size)
{
SIZE_T bytesWritten;
if(WriteProcessMemory(_processHandle, address, buffer, size, &bytesWritten) == 0)
throw new std::exception;
if(bytesWritten != size)
throw std::exception();
}
template<typename T>
void Write(LPVOID address, const T &object)
{
Write(address, &object, sizeof(T));
}
template<typename T, size_t elementCount>
void Write(LPVOID address, T (&objects)[elementCount])
{
Write(address, &objects, sizeof(objects));
}
};