#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
using u8 = uint8_t;
// --- simple replacement for optional (tiny)
template<typename T>
struct Optional {
bool has;
T val;
Optional(): has(false) {}
Optional(const T& v): has(true), val(v) {}
};
// get PID by process name (wide)
DWORD getProcessIdByName(const std::wstring& procName) {
PROCESSENTRY32W pe{ sizeof(pe) };
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return 0;
if (Process32FirstW(snap, &pe)) {
do {
if (procName == pe.szExeFile) {
CloseHandle(snap);
return pe.th32ProcessID;
}
} while (Process32NextW(snap, &pe));
}
CloseHandle(snap);
return 0;
}
Optional<MODULEENTRY32W> getModuleByName(DWORD pid, const std::wstring& moduleName) {
MODULEENTRY32W me{ sizeof(me) };
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
if (snap == INVALID_HANDLE_VALUE) return Optional<MODULEENTRY32W>();
if (Module32FirstW(snap, &me)) {
do {
if (moduleName == me.szModule) {
CloseHandle(snap);
return Optional<MODULEENTRY32W>(me);
}
} while (Module32NextW(snap, &me));
}
CloseHandle(snap);
return Optional<MODULEENTRY32W>();
}
// parse pattern like "40 55 56 57 48 83 EC 70" supporting "??"
bool parsePattern(const std::string& s, std::vector<u8>& bytes, std::string& mask) {
bytes.clear();
mask.clear();
std::istringstream iss(s);
std::string token;
while (iss >> token) {
if (token == "??" || token == "?") {
bytes.push_back(0);
mask.push_back('?');
} else {
if (token.size() != 2) return false;
unsigned int val;
std::istringstream hex(token);
hex >> std::hex >> val;
if (hex.fail()) return false;
bytes.push_back((u8)val);
mask.push_back('x');
}
}
return true;
}
uintptr_t scanPattern(const u8* data, size_t dataSize, const std::vector<u8>& pat, const std::string& mask, uintptr_t baseAddr) {
size_t patLen = pat.size();
if (patLen == 0 || dataSize < patLen) return 0;
for (size_t i = 0; i <= dataSize - patLen; ++i) {
bool ok = true;
for (size_t j = 0; j < patLen; ++j) {
if (mask[j] == 'x' && data[i + j] != pat[j]) { ok = false; break; }
}
if (ok) return baseAddr + i;
}
return 0;
}
bool readRemoteMemory(HANDLE hProc, uintptr_t addr, std::vector<u8>& out, SIZE_T size) {
out.resize(size);
SIZE_T read = 0;
if (!ReadProcessMemory(hProc, (LPCVOID)addr, out.data(), size, &read) || read != size) {
return false;
}
return true;
}
bool writeRemoteMemory(HANDLE hProc, uintptr_t addr, const std::vector<u8>& data) {
SIZE_T written = 0;
DWORD old = 0;
if (!VirtualProtectEx(hProc, (LPVOID)addr, data.size(), PAGE_EXECUTE_READWRITE, &old)) {
std::cerr << "VirtualProtectEx failed: " << GetLastError() << "\n";
return false;
}
if (!WriteProcessMemory(hProc, (LPVOID)addr, data.data(), data.size(), &written) || written != data.size()) {
std::cerr << "WriteProcessMemory failed: " << GetLastError() << "\n";
VirtualProtectEx(hProc, (LPVOID)addr, data.size(), old, &old);
return false;
}
VirtualProtectEx(hProc, (LPVOID)addr, data.size(), old, &old);
return true;
}
// helper: convert wide string to utf8 (avoids wstring_convert)
std::string wideToUtf8(const std::wstring& w) {
if (w.empty()) return {};
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, w.data(), (int)w.size(), nullptr, 0, nullptr, nullptr);
std::string strTo(sizeNeeded, 0);
WideCharToMultiByte(CP_UTF8, 0, w.data(), (int)w.size(), &strTo[0], sizeNeeded, nullptr, nullptr);
return strTo;
}
int main() {
std::cout << "ETS2 Patcher - aplica/restaura patches (singleplayer only)\n";
std::cout << "Procurando eurotrucks2.exe e aplicando patches...\n\n";
const std::wstring targetProc = L"eurotrucks2.exe";
const std::wstring moduleName = L"eurotrucks2.exe";
// patterns (originais + trailer)
const std::string pat1_str = "40 55 56 57 48 83 EC 70 48 8B B1 48 01 00 00 0F 57 E4"; // NoWearTruck
const std::string pat2_str = "48 83 EC 18 4C 8B 81 48 01 00 00"; // NoDamageTruck
const std::string pat3_str = "40 55 41 56 41 57 48 81 EC 80 00 00 00 48 8B A9 48 01 00 00"; // NoTrailerWear
const std::string pat4_str = "48 8B 81 48 01 00 00 0F 57 ED"; // NoTrailerDamage
std::vector<u8> pat1, pat2, pat3, pat4;
std::string mask1, mask2, mask3, mask4;
if (!parsePattern(pat1_str, pat1, mask1) || !parsePattern(pat2_str, pat2, mask2)
|| !parsePattern(pat3_str, pat3, mask3) || !parsePattern(pat4_str, pat4, mask4)) {
std::cerr << "Erro ao parsear pattern\n";
return 1;
}
DWORD pid = 0;
std::cout << "Procurando processo '" << wideToUtf8(targetProc) << "'...\n";
for (int attempts = 0; attempts < 10 && pid == 0; ++attempts) {
pid = getProcessIdByName(targetProc);
if (pid == 0) {
std::cout << "Processo não encontrado. Tentando novamente em 1s...\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
if (pid == 0) {
std::cerr << "Processo não encontrado. Execute o Euro Truck Simulator 2 primeiro.\n";
return 1;
}
std::cout << "PID: " << pid << "\n";
auto modOpt = getModuleByName(pid, moduleName);
if (!modOpt.has) {
std::cerr << "Módulo " << wideToUtf8(moduleName) << " não encontrado no processo.\n";
return 1;
}
MODULEENTRY32W me = modOpt.val;
uintptr_t base = (uintptr_t)me.modBaseAddr;
SIZE_T modSize = me.modBaseSize;
std::cout << "Module base: 0x" << std::hex << base << " size: 0x" << modSize << std::dec << "\n";
HANDLE hProc = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProc) {
std::cerr << "OpenProcess falhou: " << GetLastError() << "\n";
return 1;
}
// read module
std::vector<u8> moduleData;
moduleData.resize(modSize);
SIZE_T actuallyRead = 0;
if (!ReadProcessMemory(hProc, (LPCVOID)base, moduleData.data(), modSize, &actuallyRead) || actuallyRead != modSize) {
std::cerr << "Leitura do modulo falhou: " << GetLastError() << "\n";
CloseHandle(hProc);
return 1;
}
uintptr_t addr1 = scanPattern(moduleData.data(), moduleData.size(), pat1, mask1, base);
uintptr_t addr2 = scanPattern(moduleData.data(), moduleData.size(), pat2, mask2, base);
uintptr_t addr3 = scanPattern(moduleData.data(), moduleData.size(), pat3, mask3, base);
uintptr_t addr4 = scanPattern(moduleData.data(), moduleData.size(), pat4, mask4, base);
if (!addr1) std::cout << "Pattern 1 (NoWearTruck) não encontrado.\n"; else std::cout << "Pattern 1 encontrado em 0x" << std::hex << addr1 << std::dec << "\n";
if (!addr2) std::cout << "Pattern 2 (NoDamageTruck) não encontrado.\n"; else std::cout << "Pattern 2 encontrado em 0x" << std::hex << addr2 << std::dec << "\n";
if (!addr3) std::cout << "Pattern 3 (NoTrailerWear) não encontrado.\n"; else std::cout << "Pattern 3 encontrado em 0x" << std::hex << addr3 << std::dec << "\n";
if (!addr4) std::cout << "Pattern 4 (NoTrailerDamage) não encontrado.\n"; else std::cout << "Pattern 4 encontrado em 0x" << std::hex << addr4 << std::dec << "\n";
struct Patch {
uintptr_t addr;
std::vector<u8> original;
std::vector<u8> patchBytes;
std::string desc;
};
std::vector<Patch> patches;
auto preparePatch = [&](uintptr_t addr, const std::vector<u8>& pat, const std::string& desc) -> bool {
if (!addr) return false;
Patch p;
p.addr = addr;
size_t len = pat.size();
if (!readRemoteMemory(hProc, addr, p.original, len)) {
std::cerr << "Erro ao ler bytes originais (" << desc << ")\n";
return false;
}
p.patchBytes.assign(len, 0xC3); // 0xC3 = ret
p.desc = desc;
patches.push_back(std::move(p));
return true;
};
bool anyPrepared = false;
anyPrepared |= preparePatch(addr1, pat1, "NoWearTruck");
anyPrepared |= preparePatch(addr2, pat2, "NoDamageTruck");
anyPrepared |= preparePatch(addr3, pat3, "NoTrailerWear");
anyPrepared |= preparePatch(addr4, pat4, "NoTrailerDamage");
if (!anyPrepared) {
std::cerr << "Nenhum pattern foi localizado/preparado. Saindo.\n";
CloseHandle(hProc);
return 1;
}
// apply patches
bool okAll = true;
for (auto &p : patches) {
std::cout << "Aplicando patch (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << " (len=" << p.patchBytes.size() << ")\n";
if (!writeRemoteMemory(hProc, p.addr, p.patchBytes)) {
std::cerr << "Falha ao aplicar patch (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << "\n";
okAll = false;
} else {
std::cout << "Patch aplicado: " << p.desc << "\n";
}
}
if (!okAll) {
std::cerr << "Alguns patches falharam. Tente executar como Administrador.\n";
} else {
std::cout << "Todos patches aplicados com sucesso (se localizados).\n";
}
std::cout << "\nPressione Enter para restaurar os bytes originais e sair...\n";
std::cin.get();
for (auto &p : patches) {
std::cout << "Restaurando (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << " ... ";
if (!writeRemoteMemory(hProc, p.addr, p.original)) {
std::cerr << "Falha ao restaurar (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << "\n";
} else {
std::cout << "OK\n";
}
}
CloseHandle(hProc);
std::cout << "Concluído. Saindo.\n";
return 0;
}
#include <tlhelp32.h>
#include <psapi.h>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
using u8 = uint8_t;
// --- simple replacement for optional (tiny)
template<typename T>
struct Optional {
bool has;
T val;
Optional(): has(false) {}
Optional(const T& v): has(true), val(v) {}
};
// get PID by process name (wide)
DWORD getProcessIdByName(const std::wstring& procName) {
PROCESSENTRY32W pe{ sizeof(pe) };
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) return 0;
if (Process32FirstW(snap, &pe)) {
do {
if (procName == pe.szExeFile) {
CloseHandle(snap);
return pe.th32ProcessID;
}
} while (Process32NextW(snap, &pe));
}
CloseHandle(snap);
return 0;
}
Optional<MODULEENTRY32W> getModuleByName(DWORD pid, const std::wstring& moduleName) {
MODULEENTRY32W me{ sizeof(me) };
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);
if (snap == INVALID_HANDLE_VALUE) return Optional<MODULEENTRY32W>();
if (Module32FirstW(snap, &me)) {
do {
if (moduleName == me.szModule) {
CloseHandle(snap);
return Optional<MODULEENTRY32W>(me);
}
} while (Module32NextW(snap, &me));
}
CloseHandle(snap);
return Optional<MODULEENTRY32W>();
}
// parse pattern like "40 55 56 57 48 83 EC 70" supporting "??"
bool parsePattern(const std::string& s, std::vector<u8>& bytes, std::string& mask) {
bytes.clear();
mask.clear();
std::istringstream iss(s);
std::string token;
while (iss >> token) {
if (token == "??" || token == "?") {
bytes.push_back(0);
mask.push_back('?');
} else {
if (token.size() != 2) return false;
unsigned int val;
std::istringstream hex(token);
hex >> std::hex >> val;
if (hex.fail()) return false;
bytes.push_back((u8)val);
mask.push_back('x');
}
}
return true;
}
uintptr_t scanPattern(const u8* data, size_t dataSize, const std::vector<u8>& pat, const std::string& mask, uintptr_t baseAddr) {
size_t patLen = pat.size();
if (patLen == 0 || dataSize < patLen) return 0;
for (size_t i = 0; i <= dataSize - patLen; ++i) {
bool ok = true;
for (size_t j = 0; j < patLen; ++j) {
if (mask[j] == 'x' && data[i + j] != pat[j]) { ok = false; break; }
}
if (ok) return baseAddr + i;
}
return 0;
}
bool readRemoteMemory(HANDLE hProc, uintptr_t addr, std::vector<u8>& out, SIZE_T size) {
out.resize(size);
SIZE_T read = 0;
if (!ReadProcessMemory(hProc, (LPCVOID)addr, out.data(), size, &read) || read != size) {
return false;
}
return true;
}
bool writeRemoteMemory(HANDLE hProc, uintptr_t addr, const std::vector<u8>& data) {
SIZE_T written = 0;
DWORD old = 0;
if (!VirtualProtectEx(hProc, (LPVOID)addr, data.size(), PAGE_EXECUTE_READWRITE, &old)) {
std::cerr << "VirtualProtectEx failed: " << GetLastError() << "\n";
return false;
}
if (!WriteProcessMemory(hProc, (LPVOID)addr, data.data(), data.size(), &written) || written != data.size()) {
std::cerr << "WriteProcessMemory failed: " << GetLastError() << "\n";
VirtualProtectEx(hProc, (LPVOID)addr, data.size(), old, &old);
return false;
}
VirtualProtectEx(hProc, (LPVOID)addr, data.size(), old, &old);
return true;
}
// helper: convert wide string to utf8 (avoids wstring_convert)
std::string wideToUtf8(const std::wstring& w) {
if (w.empty()) return {};
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, w.data(), (int)w.size(), nullptr, 0, nullptr, nullptr);
std::string strTo(sizeNeeded, 0);
WideCharToMultiByte(CP_UTF8, 0, w.data(), (int)w.size(), &strTo[0], sizeNeeded, nullptr, nullptr);
return strTo;
}
int main() {
std::cout << "ETS2 Patcher - aplica/restaura patches (singleplayer only)\n";
std::cout << "Procurando eurotrucks2.exe e aplicando patches...\n\n";
const std::wstring targetProc = L"eurotrucks2.exe";
const std::wstring moduleName = L"eurotrucks2.exe";
// patterns (originais + trailer)
const std::string pat1_str = "40 55 56 57 48 83 EC 70 48 8B B1 48 01 00 00 0F 57 E4"; // NoWearTruck
const std::string pat2_str = "48 83 EC 18 4C 8B 81 48 01 00 00"; // NoDamageTruck
const std::string pat3_str = "40 55 41 56 41 57 48 81 EC 80 00 00 00 48 8B A9 48 01 00 00"; // NoTrailerWear
const std::string pat4_str = "48 8B 81 48 01 00 00 0F 57 ED"; // NoTrailerDamage
std::vector<u8> pat1, pat2, pat3, pat4;
std::string mask1, mask2, mask3, mask4;
if (!parsePattern(pat1_str, pat1, mask1) || !parsePattern(pat2_str, pat2, mask2)
|| !parsePattern(pat3_str, pat3, mask3) || !parsePattern(pat4_str, pat4, mask4)) {
std::cerr << "Erro ao parsear pattern\n";
return 1;
}
DWORD pid = 0;
std::cout << "Procurando processo '" << wideToUtf8(targetProc) << "'...\n";
for (int attempts = 0; attempts < 10 && pid == 0; ++attempts) {
pid = getProcessIdByName(targetProc);
if (pid == 0) {
std::cout << "Processo não encontrado. Tentando novamente em 1s...\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
if (pid == 0) {
std::cerr << "Processo não encontrado. Execute o Euro Truck Simulator 2 primeiro.\n";
return 1;
}
std::cout << "PID: " << pid << "\n";
auto modOpt = getModuleByName(pid, moduleName);
if (!modOpt.has) {
std::cerr << "Módulo " << wideToUtf8(moduleName) << " não encontrado no processo.\n";
return 1;
}
MODULEENTRY32W me = modOpt.val;
uintptr_t base = (uintptr_t)me.modBaseAddr;
SIZE_T modSize = me.modBaseSize;
std::cout << "Module base: 0x" << std::hex << base << " size: 0x" << modSize << std::dec << "\n";
HANDLE hProc = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProc) {
std::cerr << "OpenProcess falhou: " << GetLastError() << "\n";
return 1;
}
// read module
std::vector<u8> moduleData;
moduleData.resize(modSize);
SIZE_T actuallyRead = 0;
if (!ReadProcessMemory(hProc, (LPCVOID)base, moduleData.data(), modSize, &actuallyRead) || actuallyRead != modSize) {
std::cerr << "Leitura do modulo falhou: " << GetLastError() << "\n";
CloseHandle(hProc);
return 1;
}
uintptr_t addr1 = scanPattern(moduleData.data(), moduleData.size(), pat1, mask1, base);
uintptr_t addr2 = scanPattern(moduleData.data(), moduleData.size(), pat2, mask2, base);
uintptr_t addr3 = scanPattern(moduleData.data(), moduleData.size(), pat3, mask3, base);
uintptr_t addr4 = scanPattern(moduleData.data(), moduleData.size(), pat4, mask4, base);
if (!addr1) std::cout << "Pattern 1 (NoWearTruck) não encontrado.\n"; else std::cout << "Pattern 1 encontrado em 0x" << std::hex << addr1 << std::dec << "\n";
if (!addr2) std::cout << "Pattern 2 (NoDamageTruck) não encontrado.\n"; else std::cout << "Pattern 2 encontrado em 0x" << std::hex << addr2 << std::dec << "\n";
if (!addr3) std::cout << "Pattern 3 (NoTrailerWear) não encontrado.\n"; else std::cout << "Pattern 3 encontrado em 0x" << std::hex << addr3 << std::dec << "\n";
if (!addr4) std::cout << "Pattern 4 (NoTrailerDamage) não encontrado.\n"; else std::cout << "Pattern 4 encontrado em 0x" << std::hex << addr4 << std::dec << "\n";
struct Patch {
uintptr_t addr;
std::vector<u8> original;
std::vector<u8> patchBytes;
std::string desc;
};
std::vector<Patch> patches;
auto preparePatch = [&](uintptr_t addr, const std::vector<u8>& pat, const std::string& desc) -> bool {
if (!addr) return false;
Patch p;
p.addr = addr;
size_t len = pat.size();
if (!readRemoteMemory(hProc, addr, p.original, len)) {
std::cerr << "Erro ao ler bytes originais (" << desc << ")\n";
return false;
}
p.patchBytes.assign(len, 0xC3); // 0xC3 = ret
p.desc = desc;
patches.push_back(std::move(p));
return true;
};
bool anyPrepared = false;
anyPrepared |= preparePatch(addr1, pat1, "NoWearTruck");
anyPrepared |= preparePatch(addr2, pat2, "NoDamageTruck");
anyPrepared |= preparePatch(addr3, pat3, "NoTrailerWear");
anyPrepared |= preparePatch(addr4, pat4, "NoTrailerDamage");
if (!anyPrepared) {
std::cerr << "Nenhum pattern foi localizado/preparado. Saindo.\n";
CloseHandle(hProc);
return 1;
}
// apply patches
bool okAll = true;
for (auto &p : patches) {
std::cout << "Aplicando patch (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << " (len=" << p.patchBytes.size() << ")\n";
if (!writeRemoteMemory(hProc, p.addr, p.patchBytes)) {
std::cerr << "Falha ao aplicar patch (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << "\n";
okAll = false;
} else {
std::cout << "Patch aplicado: " << p.desc << "\n";
}
}
if (!okAll) {
std::cerr << "Alguns patches falharam. Tente executar como Administrador.\n";
} else {
std::cout << "Todos patches aplicados com sucesso (se localizados).\n";
}
std::cout << "\nPressione Enter para restaurar os bytes originais e sair...\n";
std::cin.get();
for (auto &p : patches) {
std::cout << "Restaurando (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << " ... ";
if (!writeRemoteMemory(hProc, p.addr, p.original)) {
std::cerr << "Falha ao restaurar (" << p.desc << ") em 0x" << std::hex << p.addr << std::dec << "\n";
} else {
std::cout << "OK\n";
}
}
CloseHandle(hProc);
std::cout << "Concluído. Saindo.\n";
return 0;
}