Created
September 2, 2020 23:23
-
-
Save branw/a9f88e588b3e2946ae48ed0cb3eb35fe to your computer and use it in GitHub Desktop.
Adjust GTA 5/Online car values while on the Los Santos Custom's Sell Vehicle screen (GTA 1.50)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <iomanip> | |
#include <iostream> | |
#include <optional> | |
#include <psapi.h> | |
#include <tlhelp32.h> | |
DWORD find_process_by_name(const std::wstring &processName) { | |
PROCESSENTRY32 process_info; | |
process_info.dwSize = sizeof(process_info); | |
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); | |
if (snapshot == INVALID_HANDLE_VALUE) { | |
return 0; | |
} | |
if (Process32First(snapshot, &process_info)) { | |
while (Process32Next(snapshot, &process_info)) { | |
if (!processName.compare(process_info.szExeFile)) { | |
CloseHandle(snapshot); | |
return process_info.th32ProcessID; | |
} | |
} | |
} | |
CloseHandle(snapshot); | |
return 0; | |
} | |
HMODULE get_base_module(HANDLE process_handle) { | |
HMODULE modules[1024]; | |
DWORD bytes_used; | |
unsigned int i; | |
if (EnumProcessModules(process_handle, modules, sizeof(modules), | |
&bytes_used)) { | |
for (i = 0; i < (bytes_used / sizeof(HMODULE)); i++) { | |
TCHAR module_name[MAX_PATH]; | |
if (GetModuleFileNameEx(process_handle, modules[i], module_name, | |
sizeof(module_name) / sizeof(TCHAR))) { | |
if (std::wstring(module_name).find(L"GTA5.exe") != std::wstring::npos) { | |
return modules[i]; | |
} | |
} | |
} | |
} | |
return nullptr; | |
} | |
uintptr_t find_pattern(HANDLE process_handle, uintptr_t start, uintptr_t size, | |
char const *sig, char const *mask) { | |
auto data = new uint8_t[size]; | |
SIZE_T bytes_read; | |
if (!ReadProcessMemory(process_handle, (LPVOID)start, data, size, | |
&bytes_read)) { | |
return NULL; | |
} | |
auto block_matches = [](auto data, auto bMask, auto szMask) -> bool { | |
for (; *szMask; ++szMask, ++data, ++bMask) | |
if (*szMask == 'x' && *data != *bMask) | |
return FALSE; | |
return (*szMask == NULL); | |
}; | |
for (size_t i = 0; i < size; i++) { | |
if (block_matches(reinterpret_cast<uint8_t const *>(data + i), | |
reinterpret_cast<uint8_t const *>(sig), mask)) { | |
return start + i; | |
} | |
} | |
delete[] data; | |
return NULL; | |
} | |
DWORD get_module_size(HANDLE process_handle, HMODULE module_handle) { | |
MODULEINFO module_info; | |
if (!GetModuleInformation(process_handle, module_handle, &module_info, | |
sizeof(module_info))) { | |
return 0; | |
} | |
return module_info.SizeOfImage; | |
} | |
template <typename T> T read(HANDLE process_handle, uintptr_t addr) { | |
T buffer; | |
if (!ReadProcessMemory(process_handle, (LPVOID)addr, &buffer, sizeof(T), | |
nullptr)) { | |
return T{}; | |
} | |
return buffer; | |
} | |
template <typename T> | |
bool write(HANDLE process_handle, uintptr_t addr, T value) { | |
return WriteProcessMemory(process_handle, reinterpret_cast<LPVOID>(addr), | |
&value, sizeof(value), nullptr); | |
} | |
int main() { | |
std::cout << "Waiting for GTA5..."; | |
DWORD process_id; | |
while ((process_id = find_process_by_name(L"GTA5.exe")) == 0) { | |
Sleep(500); | |
} | |
std::cout << " done (pid=" << process_id << ")" << std::endl; | |
auto const process_handle = | |
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | | |
PROCESS_VM_READ | PROCESS_VM_WRITE, | |
0, process_id); | |
if (process_handle == nullptr) { | |
std::cerr << "Failed to open process handle" << std::endl; | |
return EXIT_FAILURE; | |
} | |
auto const base_module = get_base_module(process_handle); | |
if (base_module == nullptr) { | |
std::cerr << "Failed to find base module" << std::endl; | |
return EXIT_FAILURE; | |
} | |
auto const module_size = get_module_size(process_handle, base_module); | |
if (module_size == 0) { | |
std::cerr << "Failed to find base module size" << std::endl; | |
return EXIT_FAILURE; | |
} | |
auto const base_addr = reinterpret_cast<uintptr_t>(base_module); | |
auto const global_ptr_deref = find_pattern( | |
process_handle, base_addr, module_size, | |
"\x4C\x8D\x05\x00\x00\x00\x00\x4D\x8B\x08\x4D\x85\xC9\x74\x11", | |
"xxx????xxxxxxxx"); | |
if (global_ptr_deref == 0) { | |
std::cerr << "Failed to find global pointer dereference" << std::endl; | |
return EXIT_FAILURE; | |
} | |
auto const global_ptrs = | |
global_ptr_deref + read<uint32_t>(process_handle, global_ptr_deref + 3) + | |
7; | |
auto write_global = [=](uint64_t index, uint64_t value) { | |
auto const page = read<uint64_t>( | |
process_handle, global_ptrs + (8 * (index >> 0x12 & 0x3F))); | |
write<uint64_t>(process_handle, page + (8 * (index & 0x3FFFF)), value); | |
}; | |
std::cout | |
<< "Put a Tracker on the car, then go to Sell and enter a value below\n" | |
<< "You should see the new value reflected on the Sell screen" | |
<< std::endl; | |
while (!std::cin.fail()) { | |
std::cout << "Enter a new vehicle value: $"; | |
uint64_t value; | |
std::cin >> value; | |
if (value > 67'000'000) { | |
std::cout | |
<< "Warning: selling a car for this price will cause an error\n" | |
<< "Press ALT+F4 and close the game instead of accepting the error!" | |
<< std::endl; | |
} | |
// Visual sell value | |
write_global(26269, value); | |
// Actual sell value (Global_98796.f_963) | |
write_global(98796 + 963, value); | |
} | |
CloseHandle(process_handle); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment