Add script patcher and update protections (#588)
This commit is contained in:
30
src/services/script_patcher/script_data.hpp
Normal file
30
src/services/script_patcher/script_data.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
class script_data
|
||||
{
|
||||
std::uint32_t m_num_pages;
|
||||
|
||||
public:
|
||||
std::uint32_t m_code_size;
|
||||
std::uint8_t** m_bytecode;
|
||||
|
||||
script_data(std::uint32_t code_size, std::uint8_t** bytecode, std::uint32_t num_pages) :
|
||||
m_code_size(code_size),
|
||||
m_bytecode(bytecode),
|
||||
m_num_pages(num_pages)
|
||||
{
|
||||
}
|
||||
|
||||
~script_data()
|
||||
{
|
||||
for (auto i = 0u; i < m_num_pages; i++)
|
||||
{
|
||||
delete[] m_bytecode[i];
|
||||
}
|
||||
|
||||
delete[] m_bytecode;
|
||||
}
|
||||
};
|
||||
}
|
68
src/services/script_patcher/script_patch.cpp
Normal file
68
src/services/script_patcher/script_patch.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "script_patch.hpp"
|
||||
#include "script_data.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
script_patch::script_patch(rage::joaat_t script, const memory::pattern pattern, int32_t offset, std::vector<std::uint8_t> patch, bool* enable_bool) :
|
||||
m_script(script),
|
||||
m_pattern(pattern),
|
||||
m_offset(offset),
|
||||
m_patch(std::move(patch)),
|
||||
m_bool(enable_bool),
|
||||
m_ip(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::uint8_t* script_patch::get_code_address(script_data* data, std::uint32_t index)
|
||||
{
|
||||
return &data->m_bytecode[index >> 14][index & 0x3FFF];
|
||||
}
|
||||
|
||||
const std::optional<uint32_t> script_patch::get_code_location_by_pattern(script_data* data, const memory::pattern& pattern)
|
||||
{
|
||||
std::uint32_t code_size = data->m_code_size;
|
||||
for (std::uint32_t i = 0; i < (code_size - pattern.m_bytes.size()); i++)
|
||||
{
|
||||
for (std::uint32_t j = 0; j < pattern.m_bytes.size(); j++)
|
||||
if (pattern.m_bytes[j].has_value())
|
||||
if (pattern.m_bytes[j].value() != *get_code_address(data, i + j))
|
||||
goto incorrect;
|
||||
|
||||
return i;
|
||||
incorrect:
|
||||
continue;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void script_patch::enable(script_data* data)
|
||||
{
|
||||
std::memcpy(get_code_address(data, m_ip), m_patch.data(), m_patch.size());
|
||||
}
|
||||
|
||||
void script_patch::disable(script_data* data)
|
||||
{
|
||||
std::memcpy(get_code_address(data, m_ip), m_original.data(), m_original.size());
|
||||
}
|
||||
|
||||
void script_patch::update(script_data* data)
|
||||
{
|
||||
if (m_ip == 0)
|
||||
{
|
||||
auto result = get_code_location_by_pattern(data, m_pattern);
|
||||
if (!result.has_value())
|
||||
LOG(FATAL) << "Failed to find pattern";
|
||||
|
||||
m_ip = result.value() + m_offset;
|
||||
|
||||
for (int i = 0; i < m_patch.size(); i++)
|
||||
m_original.push_back(*get_code_address(data, m_ip + i));
|
||||
}
|
||||
|
||||
if (!m_bool || *m_bool)
|
||||
enable(data);
|
||||
else
|
||||
disable(data);
|
||||
}
|
||||
}
|
29
src/services/script_patcher/script_patch.hpp
Normal file
29
src/services/script_patcher/script_patch.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "memory/pattern.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
struct script_data;
|
||||
|
||||
class script_patch
|
||||
{
|
||||
rage::joaat_t m_script;
|
||||
const memory::pattern m_pattern;
|
||||
int32_t m_offset;
|
||||
std::vector<uint8_t> m_patch;
|
||||
std::vector<uint8_t> m_original;
|
||||
bool* m_bool;
|
||||
int32_t m_ip;
|
||||
|
||||
static std::uint8_t* get_code_address(script_data* data, std::uint32_t index);
|
||||
static const std::optional<uint32_t> get_code_location_by_pattern(script_data* data, const memory::pattern& pattern);
|
||||
void enable(script_data* data);
|
||||
void disable(script_data* data);
|
||||
|
||||
public:
|
||||
inline rage::joaat_t get_script() { return m_script; }
|
||||
|
||||
script_patch(rage::joaat_t script, const memory::pattern pattern, int32_t offset, std::vector<std::uint8_t> patch, bool* enable_bool);
|
||||
void update(script_data* data);
|
||||
};
|
||||
}
|
87
src/services/script_patcher/script_patcher_service.cpp
Normal file
87
src/services/script_patcher/script_patcher_service.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include <script/scrProgram.hpp>
|
||||
#include "script_patcher_service.hpp"
|
||||
#include "script_patch.hpp"
|
||||
#include "script_data.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
script_data* script_patcher_service::get_data_for_script(rage::joaat_t script)
|
||||
{
|
||||
for (auto& p : m_script_data)
|
||||
{
|
||||
if (p.first == script)
|
||||
{
|
||||
return p.second.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool script_patcher_service::does_script_have_patches(rage::joaat_t script)
|
||||
{
|
||||
for (auto& p : m_script_patches)
|
||||
{
|
||||
if (p.get_script() == script)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void script_patcher_service::create_data_for_script(rage::scrProgram* program)
|
||||
{
|
||||
auto pages = new std::uint8_t * [program->get_num_code_pages()];
|
||||
|
||||
for (auto i = 0u; i < program->get_num_code_pages(); i++)
|
||||
{
|
||||
pages[i] = new std::uint8_t[program->get_code_page_size(i)];
|
||||
std::memcpy(pages[i], program->get_code_page(i), program->get_code_page_size(i));
|
||||
}
|
||||
|
||||
m_script_data.emplace(program->m_name_hash, std::make_unique<script_data>(program->m_code_size, pages, program->get_num_code_pages()));
|
||||
}
|
||||
|
||||
void script_patcher_service::update_all_patches_for_script(rage::joaat_t script)
|
||||
{
|
||||
auto data = get_data_for_script(script);
|
||||
|
||||
for (auto& p : m_script_patches)
|
||||
if (p.get_script() == script)
|
||||
p.update(data);
|
||||
}
|
||||
|
||||
void script_patcher_service::add_patch(script_patch&& patch)
|
||||
{
|
||||
m_script_patches.push_back(std::move(patch));
|
||||
}
|
||||
|
||||
void script_patcher_service::on_script_load(rage::scrProgram* program)
|
||||
{
|
||||
if (get_data_for_script(program->m_name_hash) == nullptr && does_script_have_patches(program->m_name_hash))
|
||||
{
|
||||
create_data_for_script(program);
|
||||
update_all_patches_for_script(program->m_name_hash);
|
||||
}
|
||||
}
|
||||
|
||||
std::uint8_t** script_patcher_service::get_script_bytecode(rage::joaat_t script)
|
||||
{
|
||||
if (auto data = get_data_for_script(script))
|
||||
return data->m_bytecode;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void script_patcher_service::update()
|
||||
{
|
||||
for (auto& p : m_script_patches)
|
||||
{
|
||||
auto data = get_data_for_script(p.get_script());
|
||||
if (data)
|
||||
{
|
||||
p.update(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
src/services/script_patcher/script_patcher_service.hpp
Normal file
26
src/services/script_patcher/script_patcher_service.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <script/scrProgram.hpp>
|
||||
#include "memory/pattern.hpp"
|
||||
#include "script_patch.hpp"
|
||||
#include "script_data.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class script_patcher_service
|
||||
{
|
||||
std::vector<script_patch> m_script_patches;
|
||||
std::unordered_map<rage::joaat_t, std::unique_ptr<script_data>> m_script_data;
|
||||
script_data* get_data_for_script(rage::joaat_t script);
|
||||
bool does_script_have_patches(rage::joaat_t script);
|
||||
void create_data_for_script(rage::scrProgram* program);
|
||||
void update_all_patches_for_script(rage::joaat_t script);
|
||||
|
||||
public:
|
||||
void add_patch(script_patch&& patch);
|
||||
void on_script_load(rage::scrProgram* program);
|
||||
std::uint8_t** get_script_bytecode(rage::joaat_t script);
|
||||
void update();
|
||||
};
|
||||
|
||||
inline script_patcher_service g_script_patcher_service;
|
||||
}
|
Reference in New Issue
Block a user