Hello, This is my first post of this kind so I apologize in advance if anything is not as detailed as it should be, if you have any questions or comments please reply and I will do my best to get to you. I see a lot of people asking for help getting the new XOR values and the new pointer offset each update, this application will automatically read trove's memory to get those values for any minor updates and most major updates as well. The code itself will not update automatically in that case so I am hoping that it does not violate any rules.
Requirements: VS installed on computer with C++ desktop environment
Disclaimer: In my actual code some of my functions are in other files, I will not be posting those full files but will post necessary code snippets. I also have special .h files so that all my code doesn't get compiled each time, I will only post relevant .h includes.
Code:
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <vector>
#include <Windows.h>
#include <TlHelp32.h>
#include <iomanip>
using namespace std;
DWORD getFloatConvXOR(HANDLE hProcess)
{
void* xorAddr = sigscan::PatternScanExModule(hProcess, (wchar_t*)L"trove.exe", (wchar_t*)L"trove.exe", (char*)"\x55\x8B\xEC\x8B\x45\x00\x35\x00\x00\x00\x00\x89\x00\x5D\xC2", (char*)"xxxxx?x????x?xx");
if (xorAddr != nullptr) {
DWORD read;
xorAddr = (void*)((uintptr_t)xorAddr + (sizeof(char) * 7));
mem::ReadEx((BYTE*)xorAddr, (BYTE*)&read, sizeof(read), hProcess);
return read;
}
}
uintptr_t getBaseOffset(HANDLE hProcess)
{
void * baseAddr = sigscan::PatternScanExModule(hProcess, (wchar_t*)L"trove.exe", (wchar_t*)L"trove.exe", (char*)"\x8B\xEC\x83\x00\x00\x83\xEC\x00\xA1\x00\x00\x00\x00\x56\x8B\xF1\x57", (char*)"xxx??xx?x????xxxx");
if (baseAddr != nullptr) {
DWORD read;
xorAddr = (void*)((uintptr_t)xorAddr + (sizeof(char) * 9));
mem::ReadEx((BYTE*)baseAddr, (BYTE*)&read, sizeof(read), hProcess);
return read;
}
else {
std::cout << "failure";
}
}
int main()
{
//Get Handle to Process
HANDLE hProcess = 0;
//Get ProcId of the target process
DWORD procId = proc::GetProcId(L"trove.exe");
if (procId) {
hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, procId);
}
else {
std::cout << "Process not found, press enter to exit\n";
getchar();
return 0;
}
DWORD dwExit = 0;
while (GetExitCodeProcess(hProcess, &dwExit) && dwExit == STILL_ACTIVE) {
if (GetAsyncKeyState(VK_NUMPAD0) & 1) {
system("cls");
std::cout << "Base: 0x" << std::hex << getBaseOffset(hProcess) - proc::GetModuleBaseAddress(procId, L"trove.exe");
}
if (GetAsyncKeyState(VK_NUMPAD1) & 1) {
system("cls");
DWORD y = getFloatConvXOR(hProcess);
for (float x = 0.0; x <= 300.0; x++) {
std::cout << x << " " << std::dec << (UINT32)(*(PDWORD)&x ^ y) << endl;
}
}
Sleep(10);
}
std::cout << "Process not found, press enter to exit\n";
getchar();
return 0;
}
When compiled this opens a window that has a loop going, when you press 0 it prints the base address and when you press 1 it prints the xor'ed values from 0 to 300.
ReadEX:
Code:
void mem::ReadEx(BYTE* dst, BYTE* src, unsigned int size, HANDLE hProcess)
{
DWORD oldProtect;
VirtualProtectEx(hProcess, dst, size, PAGE_EXECUTE_READWRITE, &oldProtect);
ReadProcessMemory(hProcess, dst, src, size, nullptr);
VirtualProtectEx(hProcess, dst, size, oldProtect, &oldProtect);
}
This code block reads memory into the given parameter.
GetProcID:
Code:
DWORD proc::GetProcId(const wchar_t* procName)
{
// Assign to 0 for error handling
DWORD procId = 0;
// Takes snapshot of the processes
HANDLE hSnap = (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
// Check if snapshot exists and didn't error out
if (hSnap != INVALID_HANDLE_VALUE) {
PROCESSENTRY32 procEntry;
// Set entry size
procEntry.dwSize = sizeof(procEntry);
// Grabs first process in the snapshot and stores in procEntry
if (Process32First(hSnap, &procEntry)) {
// Loops through all processes
do
{
// Checks if the process name is our process name
if (!_wcsicmp(procEntry.szExeFile, procName)) {
// When found it saves the id and breaks out of the loop
procId = procEntry.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &procEntry));
}
}
// Closes Handle
CloseHandle(hSnap);
// Returns process id
return procId;
}
Basic method for getting the ProcId, credit to GuidedHacking
GetModuleBaseAddress:
Code:
uintptr_t proc::GetModuleBaseAddress(DWORD procId, const wchar_t* modName)
{
// Assign to 0 for error handling
uintptr_t modBaseAddr = 0;
// Takes snapshot of the processes
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
// Check if snapshot exists and didn't error out
if (hSnap != INVALID_HANDLE_VALUE) {
MODULEENTRY32 modEntry;
// Set entry size
modEntry.dwSize = sizeof(modEntry);
// Grabs first module in the snapshot and stores in procEntry
if (Module32First(hSnap, &modEntry)) {
do
{
// Checks if the module name is our module name
if (!_wcsicmp(modEntry.szModule, modName)) {
// When found it saves the address and breaks out of the loop
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
// Closes Handle
CloseHandle(hSnap);
// Returns moduke address
return modBaseAddr;
}
Pattern Scanning:
Code:
//Internal Pattern Scan
void * sigscan::PatternScan(char* base, size_t size, char* pattern, char* mask)
{
size_t patternLength = strlen(mask);
for (unsigned int i = 0; i < size - patternLength; i++)
{
bool found = true;
for (unsigned int j = 0; j < patternLength; j++)
{
if (mask[j] != '?' && pattern[j] != *(base + i + j))
{
found = false;
break;
}
}
if (found)
{
return (void*)(base + i);
}
}
return nullptr;
}
//External Wrapper
void * sigscan::PatternScanEx(HANDLE hProcess, uintptr_t begin, uintptr_t end, char* pattern, char* mask)
{
uintptr_t currentChunk = begin;
SIZE_T bytesRead;
while (currentChunk < end)
{
char buffer[4096];
DWORD oldprotect;
VirtualProtectEx(hProcess, (void*)currentChunk, sizeof(buffer), PAGE_EXECUTE_READWRITE, &oldprotect);
ReadProcessMemory(hProcess, (void*)currentChunk, &buffer, sizeof(buffer), &bytesRead);
VirtualProtectEx(hProcess, (void*)currentChunk, sizeof(buffer), oldprotect, &oldprotect);
if (bytesRead == 0)
{
return nullptr;
}
void* internalAddress = PatternScan((char*)&buffer, bytesRead, pattern, mask);
if (internalAddress != nullptr)
{
//calculate from internal to external
uintptr_t offsetFromBuffer = (uintptr_t)internalAddress - (uintptr_t)&buffer;
return (void*)(currentChunk + offsetFromBuffer);
}
else
{
//advance to next chunk
currentChunk = currentChunk + bytesRead;
}
}
return nullptr;
}
//Module wrapper for external pattern scan
void * sigscan::PatternScanExModule(HANDLE hProcess, wchar_t * exeName, wchar_t* module, char* pattern, char* mask)
{
DWORD processID = proc::GetProcId((const wchar_t*)exeName);
MODULEENTRY32 modEntry = proc::GetModule(processID, module);
if (!modEntry.th32ModuleID)
{
return nullptr;
}
uintptr_t begin = (uintptr_t)modEntry.modBaseAddr;
uintptr_t end = begin + modEntry.modBaseSize;
return PatternScanEx(hProcess, begin, end, pattern, mask);
}
Credit to guided hacking, highly recommended the tutorials there, I have since modified a lot of their tools for my own requirements but decided to write this application using the base ones so if anyone wanted to check how they are built they can.
For those who are learning, the way this works is that it uses opcode signatures, that's the "\x8B\xEC" etc. When using cheat engine you can often find the necessary opcode signatures for a function by checking what reads or writes to an address. For this bit of code, I used IDA to track back to the section of data where the base offset was stored and then found the signature for the function that accesses it and read at the necessary memory. The x's and ?'s are masks for the signature, sometimes the signature will change a bit and these are handles by the \x00 and ?.