Skip to content

Instantly share code, notes, and snippets.

@sedmelluq
Created April 28, 2019 12:15
Show Gist options
  • Save sedmelluq/7dc8cc5d7b78c3d42deca216f35e7c5c to your computer and use it in GitHub Desktop.
Save sedmelluq/7dc8cc5d7b78c3d42deca216f35e7c5c to your computer and use it in GitHub Desktop.
Mod limit patcher
#include <Windows.h>
#include <string>
#include <filesystem>
#include <fstream>
#include <cstdint>
namespace fs = std::experimental::filesystem;
static const uint64_t address_not_found = 0;
static const uint64_t address_already_patched = 1;
static const uint64_t check_range_start = 0xB94000;
static const uint64_t check_range_end = 0xB98000;
static const uint64_t patch_offset_in_sequence = 1;
static const uint32_t patched_limit = 500;
static const std::vector<uint64_t> known_candidates = { 0xB95CF6, 0xB96366, 0xB967A6, 0xB95E56 };
static const std::vector<uint8_t> unpatched_sequence = { 0xBA, 0xC0, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x4B };
static const std::vector<uint8_t> patched_sequence = { 0xBA, 0xF4, 0x01, 0x00, 0x00, 0x48, 0x8D, 0x4B };
uint64_t find_patch_address(const std::vector<uint8_t>& search_buffer) {
for (auto candidate : known_candidates) {
size_t candidate_range_start = candidate - check_range_start - patch_offset_in_sequence;
std::vector<uint8_t> checked_bytes(&search_buffer[candidate_range_start], &search_buffer[candidate_range_start + unpatched_sequence.size()]);
if (checked_bytes == unpatched_sequence) {
return candidate;
} else if (checked_bytes == patched_sequence) {
return address_already_patched;
}
}
for (size_t i = 0; i < search_buffer.size() - unpatched_sequence.size(); i++) {
std::vector<uint8_t> checked_bytes(&search_buffer[i], &search_buffer[i + unpatched_sequence.size()]);
if (checked_bytes == unpatched_sequence) {
return i + check_range_start + patch_offset_in_sequence;
}
}
for (size_t i = 0; i < search_buffer.size() - patched_sequence.size(); i++) {
std::vector<uint8_t> checked_bytes(&search_buffer[i], &search_buffer[i + patched_sequence.size()]);
if (checked_bytes == patched_sequence) {
return address_already_patched;
}
}
return address_not_found;
}
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous_instance, PWSTR command_line, int show_command) {
auto path = fs::absolute(fs::current_path() / "witcher3.exe");
if (!fs::exists(path)) {
MessageBoxW(nullptr, L"Could not find witcher3.exe file, make sure you are running this in the bin/x64 directory.", L"Error", MB_OK);
return 0;
}
size_t read_size = check_range_end - check_range_start;
std::vector<uint8_t> search_buffer(read_size);
{
std::ifstream file(path.wstring(), std::ios::binary);
file.seekg(check_range_start);
if (file.eof() || !file.read(reinterpret_cast<char*>(search_buffer.data()), read_size)) {
MessageBoxW(nullptr, L"witcher3.exe is unexpectedly small, aborting patching.", L"Error", MB_OK);
return 0;
}
}
uint64_t patch_address = find_patch_address(search_buffer);
if (patch_address == address_not_found) {
MessageBoxW(nullptr, L"Did not find the address to patch, possibly an unexpected version of executable. Contact sedmelluq and give him your exe.", L"Error", MB_OK);
return 0;
} else if (patch_address == address_already_patched) {
MessageBoxW(nullptr, L"It seems the executable is already patched", L"Error", MB_OK);
return 0;
}
auto backup_path = fs::absolute(fs::current_path() / "witcher3.exe.orig");
if (fs::exists(backup_path)) {
MessageBoxW(nullptr, L"Backup file witcher3.exe.orig already exists. Rename it or delete it (might be left over from previous patch attempt).", L"Error", MB_OK);
return 0;
} else if (!fs::copy_file(path, backup_path)) {
MessageBoxW(nullptr, L"Failed to create backup file, try running as administrator.", L"Error", MB_OK);
return 0;
}
{
std::fstream file(path.wstring(), std::fstream::binary | std::fstream::in | std::fstream::out);
file.seekp(patch_address);
if (file.eof()) {
MessageBoxW(nullptr, L"Unexpectedly encountered file end when writing patch.", L"Error", MB_OK);
} else if (!file.write((char*)& patched_limit, sizeof(patched_limit))) {
MessageBoxW(nullptr, L"Failed to write patch", L"Error", MB_OK);
} else {
MessageBoxW(nullptr, L"Successfully patched.", L"Success", MB_OK);
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment