Load binaries as diffs
This commit is contained in:
parent
1ed00dcaf9
commit
48ee9476e1
2
deps/premake/libHDiffPatch.lua
vendored
2
deps/premake/libHDiffPatch.lua
vendored
@ -13,7 +13,7 @@ end
|
|||||||
function libHDiffPatch.includes()
|
function libHDiffPatch.includes()
|
||||||
includedirs {
|
includedirs {
|
||||||
path.join(libHDiffPatch.source, "HDiff"),
|
path.join(libHDiffPatch.source, "HDiff"),
|
||||||
path.join(libHDiffPatch.source, "HDiff"),
|
path.join(libHDiffPatch.source, "HPatch"),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -9,21 +9,21 @@ namespace game
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
launcher::mode mode = launcher::mode::NONE;
|
launcher::mode mode = launcher::mode::none;
|
||||||
|
|
||||||
bool is_mp()
|
bool is_mp()
|
||||||
{
|
{
|
||||||
return mode == launcher::mode::MULTIPLAYER;
|
return mode == launcher::mode::multiplayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_sp()
|
bool is_sp()
|
||||||
{
|
{
|
||||||
return mode == launcher::mode::SINGLEPLAYER;
|
return mode == launcher::mode::singleplayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_dedi()
|
bool is_dedi()
|
||||||
{
|
{
|
||||||
return mode == launcher::mode::SERVER;
|
return mode == launcher::mode::server;
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize(const launcher::mode _mode)
|
void initialize(const launcher::mode _mode)
|
||||||
|
@ -9,8 +9,8 @@ launcher::launcher() : window_("Open-IW5", 615, 300), image_sp_(IMAGE_SP), image
|
|||||||
this->image_sp_.set_size({100, 100});
|
this->image_sp_.set_size({100, 100});
|
||||||
this->image_mp_.set_size({100, 100});
|
this->image_mp_.set_size({100, 100});
|
||||||
|
|
||||||
this->image_sp_.set_click_listener(std::bind(&launcher::select_mode, this, mode::SINGLEPLAYER));
|
this->image_sp_.set_click_listener(std::bind(&launcher::select_mode, this, mode::singleplayer));
|
||||||
this->image_mp_.set_click_listener(std::bind(&launcher::select_mode, this, mode::MULTIPLAYER));
|
this->image_mp_.set_click_listener(std::bind(&launcher::select_mode, this, mode::multiplayer));
|
||||||
|
|
||||||
this->window_.set_callback(std::bind(&launcher::handler, this, std::placeholders::_1, std::placeholders::_2,
|
this->window_.set_callback(std::bind(&launcher::handler, this, std::placeholders::_1, std::placeholders::_2,
|
||||||
std::placeholders::_3));
|
std::placeholders::_3));
|
||||||
|
@ -7,10 +7,10 @@ class launcher final
|
|||||||
public:
|
public:
|
||||||
enum mode
|
enum mode
|
||||||
{
|
{
|
||||||
NONE,
|
none,
|
||||||
SINGLEPLAYER,
|
singleplayer,
|
||||||
MULTIPLAYER,
|
multiplayer,
|
||||||
SERVER,
|
server,
|
||||||
};
|
};
|
||||||
|
|
||||||
launcher();
|
launcher();
|
||||||
@ -18,7 +18,7 @@ public:
|
|||||||
mode run() const;
|
mode run() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mode mode_ = NONE;
|
mode mode_ = none;
|
||||||
|
|
||||||
window window_;
|
window window_;
|
||||||
|
|
||||||
|
120
src/loader/binary_loader.cpp
Normal file
120
src/loader/binary_loader.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "binary_loader.hpp"
|
||||||
|
#include "utils/nt.hpp"
|
||||||
|
#include "utils/io.hpp"
|
||||||
|
#include "utils/cryptography.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
#include "utils/compression.hpp"
|
||||||
|
|
||||||
|
#define DEDI_HASH "F271C305117B79242E254E9F64BD5AA2993CAC8E57975243EBD44CD576418D20"
|
||||||
|
|
||||||
|
namespace binary_loader
|
||||||
|
{
|
||||||
|
std::string load_resource(const int id)
|
||||||
|
{
|
||||||
|
const auto res = FindResource(::utils::nt::module(), MAKEINTRESOURCE(id), RT_RCDATA);
|
||||||
|
if (!res) return {};
|
||||||
|
|
||||||
|
const auto handle = LoadResource(nullptr, res);
|
||||||
|
if (!handle) return {};
|
||||||
|
|
||||||
|
return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string load_delta(const launcher::mode mode)
|
||||||
|
{
|
||||||
|
if (mode == launcher::mode::singleplayer)
|
||||||
|
{
|
||||||
|
return load_resource(BINARY_SP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == launcher::mode::multiplayer)
|
||||||
|
{
|
||||||
|
return load_resource(BINARY_MP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string load_base(bool verify = true)
|
||||||
|
{
|
||||||
|
std::string data;
|
||||||
|
if (!utils::io::read_file("iw5mp_server.exe", &data))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unable to load iw5mp_server.exe");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verify && utils::cryptography::sha256::compute(data, true) != DEDI_HASH)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Your iw5mp_server.exe is incompatible with this client.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_for_file(const std::string& file, const std::string& base)
|
||||||
|
{
|
||||||
|
std::string data;
|
||||||
|
if (!utils::io::read_file(file, &data))
|
||||||
|
{
|
||||||
|
throw std::runtime_error(utils::string::va("Unable to load file %s!", file.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto new_data = reinterpret_cast<const unsigned char*>(data.data());
|
||||||
|
const auto old_data = reinterpret_cast<const unsigned char*>(base.data());
|
||||||
|
|
||||||
|
std::vector<unsigned char> diff;
|
||||||
|
create_diff(new_data, new_data + data.size(), old_data, old_data + base.size(), diff);
|
||||||
|
|
||||||
|
const unsigned long long size = data.size();
|
||||||
|
|
||||||
|
std::string result(reinterpret_cast<char*>(diff.data()), diff.size());
|
||||||
|
result.append(reinterpret_cast<const char*>(&size), sizeof(size));
|
||||||
|
result = utils::compression::zlib::compress(result);
|
||||||
|
|
||||||
|
utils::io::write_file(file + ".diff", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create()
|
||||||
|
{
|
||||||
|
const auto base = load_base(false);
|
||||||
|
|
||||||
|
utils::io::write_file("hash.txt", utils::cryptography::sha256::compute(base, true));
|
||||||
|
|
||||||
|
create_for_file("iw5sp.exe", base);
|
||||||
|
create_for_file("iw5mp.exe", base);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string build_binary(const std::string& base, const std::string& diff)
|
||||||
|
{
|
||||||
|
const auto* size = reinterpret_cast<const unsigned long long*>(diff.data() + diff.size() - sizeof(unsigned long long));
|
||||||
|
|
||||||
|
std::string binary;
|
||||||
|
binary.resize(size_t(*size));
|
||||||
|
|
||||||
|
const auto new_data = reinterpret_cast<unsigned char*>(binary.data());
|
||||||
|
const auto old_data = reinterpret_cast<const unsigned char*>(base.data());
|
||||||
|
const auto diff_data = reinterpret_cast<const unsigned char*>(diff.data());
|
||||||
|
|
||||||
|
if (patch(new_data, new_data + binary.size(), old_data, old_data + base.size(), diff_data,
|
||||||
|
diff_data + diff.size() - sizeof(*size)) == hpatch_FALSE || binary.empty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unable to create binary from patch!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string load(const launcher::mode mode)
|
||||||
|
{
|
||||||
|
auto base = load_base();
|
||||||
|
if (mode == launcher::mode::server)
|
||||||
|
{
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = load_delta(mode);
|
||||||
|
delta = utils::compression::zlib::decompress(delta);
|
||||||
|
return build_binary(base, delta);
|
||||||
|
}
|
||||||
|
}
|
8
src/loader/binary_loader.hpp
Normal file
8
src/loader/binary_loader.hpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "launcher/launcher.hpp"
|
||||||
|
|
||||||
|
namespace binary_loader
|
||||||
|
{
|
||||||
|
void create();
|
||||||
|
std::string load(launcher::mode mode);
|
||||||
|
}
|
@ -1,13 +1,16 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader.hpp"
|
#include "loader.hpp"
|
||||||
|
#include "binary_loader.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
loader::loader(const launcher::mode mode) : mode_(mode)
|
loader::loader(const launcher::mode mode) : mode_(mode)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FARPROC loader::load(const utils::nt::module& module) const
|
FARPROC loader::load(const utils::nt::module& module) const
|
||||||
{
|
{
|
||||||
const auto buffer = this->load_binary();
|
const auto buffer = binary_loader::load(this->mode_);
|
||||||
if (buffer.empty()) return nullptr;
|
if (buffer.empty()) return nullptr;
|
||||||
|
|
||||||
utils::nt::module source(HMODULE(buffer.data()));
|
utils::nt::module source(HMODULE(buffer.data()));
|
||||||
@ -33,7 +36,7 @@ FARPROC loader::load(const utils::nt::module& module) const
|
|||||||
VirtualProtect(PVOID(target_tls->StartAddressOfRawData),
|
VirtualProtect(PVOID(target_tls->StartAddressOfRawData),
|
||||||
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData, PAGE_READWRITE, &old_protect);
|
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData, PAGE_READWRITE, &old_protect);
|
||||||
|
|
||||||
const LPVOID tls_base = *reinterpret_cast<LPVOID*>(__readfsdword(0x2C));
|
const auto tls_base = *reinterpret_cast<LPVOID*>(__readfsdword(0x2C));
|
||||||
std::memmove(tls_base, PVOID(source_tls->StartAddressOfRawData),
|
std::memmove(tls_base, PVOID(source_tls->StartAddressOfRawData),
|
||||||
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData);
|
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData);
|
||||||
std::memmove(PVOID(target_tls->StartAddressOfRawData), PVOID(source_tls->StartAddressOfRawData),
|
std::memmove(PVOID(target_tls->StartAddressOfRawData), PVOID(source_tls->StartAddressOfRawData),
|
||||||
@ -58,32 +61,6 @@ void loader::set_import_resolver(const std::function<FARPROC(const std::string&,
|
|||||||
this->import_resolver_ = resolver;
|
this->import_resolver_ = resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string loader::load_binary() const
|
|
||||||
{
|
|
||||||
if (this->mode_ == launcher::mode::SINGLEPLAYER)
|
|
||||||
{
|
|
||||||
return loader::load_resource(BINARY_SP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->mode_ == launcher::mode::MULTIPLAYER)
|
|
||||||
{
|
|
||||||
return loader::load_resource(BINARY_MP);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string loader::load_resource(const int id)
|
|
||||||
{
|
|
||||||
const auto res = FindResource(::utils::nt::module(), MAKEINTRESOURCE(id), RT_RCDATA);
|
|
||||||
if (!res) return {};
|
|
||||||
|
|
||||||
const auto handle = LoadResource(nullptr, res);
|
|
||||||
if (!handle) return {};
|
|
||||||
|
|
||||||
return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res));
|
|
||||||
}
|
|
||||||
|
|
||||||
void loader::load_section(const utils::nt::module& target, const utils::nt::module& source,
|
void loader::load_section(const utils::nt::module& target, const utils::nt::module& source,
|
||||||
IMAGE_SECTION_HEADER* section)
|
IMAGE_SECTION_HEADER* section)
|
||||||
{
|
{
|
||||||
@ -92,13 +69,12 @@ void loader::load_section(const utils::nt::module& target, const utils::nt::modu
|
|||||||
|
|
||||||
if (PBYTE(target_ptr) >= (target.get_ptr() + BINARY_PAYLOAD_SIZE))
|
if (PBYTE(target_ptr) >= (target.get_ptr() + BINARY_PAYLOAD_SIZE))
|
||||||
{
|
{
|
||||||
MessageBoxA(nullptr, "Section exceeds the binary payload size, please increase it!", nullptr, MB_ICONERROR);
|
throw std::runtime_error("Section exceeds the binary payload size, please increase it!");
|
||||||
TerminateProcess(GetCurrentProcess(), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section->SizeOfRawData > 0)
|
if (section->SizeOfRawData > 0)
|
||||||
{
|
{
|
||||||
const auto size_of_data = min(section->SizeOfRawData, section->Misc.VirtualSize);
|
const auto size_of_data = std::min(section->SizeOfRawData, section->Misc.VirtualSize);
|
||||||
std::memmove(target_ptr, source_ptr, size_of_data);
|
std::memmove(target_ptr, source_ptr, size_of_data);
|
||||||
|
|
||||||
DWORD old_protect;
|
DWORD old_protect;
|
||||||
@ -166,10 +142,7 @@ void loader::load_imports(const utils::nt::module& target, const utils::nt::modu
|
|||||||
|
|
||||||
if (!function)
|
if (!function)
|
||||||
{
|
{
|
||||||
auto error = "Unable to load import '"s + function_name + "' from module '"s + name + "'"s;
|
throw std::runtime_error(utils::string::va("Unable to load import '%s' from module '%s'", function_name.data(), name.data()));
|
||||||
|
|
||||||
MessageBoxA(nullptr, error.data(), nullptr, MB_ICONERROR);
|
|
||||||
TerminateProcess(GetCurrentProcess(), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*address_table_entry = reinterpret_cast<uintptr_t>(function);
|
*address_table_entry = reinterpret_cast<uintptr_t>(function);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
class loader final
|
class loader final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit loader(launcher::mode mode);
|
loader(launcher::mode mode);
|
||||||
|
|
||||||
FARPROC load(const utils::nt::module& module) const;
|
FARPROC load(const utils::nt::module& module) const;
|
||||||
|
|
||||||
@ -15,9 +15,6 @@ private:
|
|||||||
launcher::mode mode_;
|
launcher::mode mode_;
|
||||||
std::function<FARPROC(const std::string&, const std::string&)> import_resolver_;
|
std::function<FARPROC(const std::string&, const std::string&)> import_resolver_;
|
||||||
|
|
||||||
std::string load_binary() const;
|
|
||||||
static std::string load_resource(const int id);
|
|
||||||
|
|
||||||
static void load_section(const utils::nt::module& target, const utils::nt::module& source, IMAGE_SECTION_HEADER* section);
|
static void load_section(const utils::nt::module& target, const utils::nt::module& source, IMAGE_SECTION_HEADER* section);
|
||||||
void load_sections(const utils::nt::module& target, const utils::nt::module& source) const;
|
void load_sections(const utils::nt::module& target, const utils::nt::module& source) const;
|
||||||
void load_imports(const utils::nt::module& target, const utils::nt::module& source) const;
|
void load_imports(const utils::nt::module& target, const utils::nt::module& source) const;
|
||||||
|
18
src/main.cpp
18
src/main.cpp
@ -3,6 +3,9 @@
|
|||||||
#include "loader/loader.hpp"
|
#include "loader/loader.hpp"
|
||||||
#include "loader/module_loader.hpp"
|
#include "loader/module_loader.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
#include "loader/binary_loader.hpp"
|
||||||
|
|
||||||
|
//#define GENERATE_DIFFS
|
||||||
|
|
||||||
void exit_hook(const int code)
|
void exit_hook(const int code)
|
||||||
{
|
{
|
||||||
@ -14,11 +17,17 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR
|
|||||||
{
|
{
|
||||||
FARPROC entry_point = nullptr;
|
FARPROC entry_point = nullptr;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
|
#ifdef GENERATE_DIFFS
|
||||||
|
binary_loader::create();
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
launcher launcher;
|
launcher launcher;
|
||||||
const auto mode = launcher.run();
|
const auto mode = launcher.run();
|
||||||
|
|
||||||
if (mode == launcher::mode::NONE) return 0;
|
if (mode == launcher::mode::none) return 0;
|
||||||
|
|
||||||
loader loader(mode);
|
loader loader(mode);
|
||||||
loader.set_import_resolver([](const std::string& module, const std::string& function) -> FARPROC
|
loader.set_import_resolver([](const std::string& module, const std::string& function) -> FARPROC
|
||||||
@ -36,11 +45,16 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR
|
|||||||
});
|
});
|
||||||
|
|
||||||
entry_point = loader.load({});
|
entry_point = loader.load({});
|
||||||
if (!entry_point) return 1;
|
if (!entry_point) throw std::runtime_error("Unable to load inject binary into memory");
|
||||||
|
|
||||||
game::initialize(mode);
|
game::initialize(mode);
|
||||||
module_loader::post_load();
|
module_loader::post_load();
|
||||||
}
|
}
|
||||||
|
catch (std::exception e)
|
||||||
|
{
|
||||||
|
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return entry_point();
|
return entry_point();
|
||||||
}
|
}
|
||||||
|
@ -89,8 +89,8 @@ END
|
|||||||
102 ICON "resources/icon.ico"
|
102 ICON "resources/icon.ico"
|
||||||
IMAGE_SP BITMAP "resources/singleplayer.bmp"
|
IMAGE_SP BITMAP "resources/singleplayer.bmp"
|
||||||
IMAGE_MP BITMAP "resources/multiplayer.bmp"
|
IMAGE_MP BITMAP "resources/multiplayer.bmp"
|
||||||
BINARY_SP RCDATA "resources/iw5sp.exe"
|
BINARY_SP RCDATA "resources/iw5sp.exe.diff"
|
||||||
BINARY_MP RCDATA "resources/iw5mp.exe"
|
BINARY_MP RCDATA "resources/iw5mp.exe.diff"
|
||||||
|
|
||||||
|
|
||||||
#endif // English (United States) resources
|
#endif // English (United States) resources
|
||||||
|
BIN
src/resources/iw5mp.exe.diff
Normal file
BIN
src/resources/iw5mp.exe.diff
Normal file
Binary file not shown.
BIN
src/resources/iw5sp.exe.diff
Normal file
BIN
src/resources/iw5sp.exe.diff
Normal file
Binary file not shown.
@ -12,6 +12,15 @@
|
|||||||
#include <objidl.h>
|
#include <objidl.h>
|
||||||
#include <gdiplus.h>
|
#include <gdiplus.h>
|
||||||
|
|
||||||
|
// min and max is required by gdi, therefore NOMINMAX won't work
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -19,6 +28,11 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <diff.h>
|
||||||
|
#include <patch.h>
|
||||||
|
#include <tomcrypt.h>
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
73
src/utils/compression.cpp
Normal file
73
src/utils/compression.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "memory.hpp"
|
||||||
|
#include "compression.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace compression
|
||||||
|
{
|
||||||
|
std::string zlib::compress(const std::string& data)
|
||||||
|
{
|
||||||
|
memory::allocator allocator;
|
||||||
|
unsigned long length = (data.size() * 2);
|
||||||
|
if (!length) length = 2;
|
||||||
|
|
||||||
|
if (length < 100) length *= 10;
|
||||||
|
|
||||||
|
const auto buffer = allocator.allocate_array<char>(length);
|
||||||
|
if (compress2(reinterpret_cast<Bytef*>(buffer), &length,
|
||||||
|
reinterpret_cast<Bytef*>(const_cast<char*>(data.data())), data.size(),
|
||||||
|
Z_BEST_COMPRESSION) != Z_OK)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string(buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string zlib::decompress(const std::string& data)
|
||||||
|
{
|
||||||
|
z_stream stream;
|
||||||
|
ZeroMemory(&stream, sizeof(stream));
|
||||||
|
std::string buffer;
|
||||||
|
|
||||||
|
if (inflateInit(&stream) != Z_OK)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
memory::allocator allocator;
|
||||||
|
|
||||||
|
const auto dest = allocator.allocate_array<uint8_t>(CHUNK);
|
||||||
|
auto data_ptr = data.data();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
stream.avail_in = std::min(static_cast<size_t>(CHUNK), data.size() - (data_ptr - data.data()));
|
||||||
|
stream.next_in = reinterpret_cast<const uint8_t*>(data_ptr);
|
||||||
|
data_ptr += stream.avail_in;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
stream.avail_out = CHUNK;
|
||||||
|
stream.next_out = dest;
|
||||||
|
|
||||||
|
ret = inflate(&stream, Z_NO_FLUSH);
|
||||||
|
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
inflateEnd(&stream);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append(reinterpret_cast<const char*>(dest), CHUNK - stream.avail_out);
|
||||||
|
}
|
||||||
|
while (stream.avail_out == 0);
|
||||||
|
}
|
||||||
|
while (ret != Z_STREAM_END);
|
||||||
|
|
||||||
|
inflateEnd(&stream);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/utils/compression.hpp
Normal file
16
src/utils/compression.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CHUNK 16384
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace compression
|
||||||
|
{
|
||||||
|
class zlib final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string compress(const std::string& data);
|
||||||
|
static std::string decompress(const std::string& data);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
277
src/utils/cryptography.cpp
Normal file
277
src/utils/cryptography.cpp
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "cryptography.hpp"
|
||||||
|
|
||||||
|
/// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace cryptography
|
||||||
|
{
|
||||||
|
ecc::key::key()
|
||||||
|
{
|
||||||
|
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
|
||||||
|
}
|
||||||
|
|
||||||
|
ecc::key::~key()
|
||||||
|
{
|
||||||
|
this->free();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ecc::key::is_valid() const
|
||||||
|
{
|
||||||
|
return (!memory::is_set(&this->key_storage_, 0, sizeof(this->key_storage_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ecc_key* ecc::key::get()
|
||||||
|
{
|
||||||
|
return &this->key_storage_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ecc::key::get_public_key() const
|
||||||
|
{
|
||||||
|
uint8_t buffer[512] = {0};
|
||||||
|
DWORD length = sizeof(buffer);
|
||||||
|
|
||||||
|
if (ecc_ansi_x963_export(&this->key_storage_, buffer, &length) == CRYPT_OK)
|
||||||
|
{
|
||||||
|
return std::string(reinterpret_cast<char*>(buffer), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecc::key::set(const std::string& pub_key_buffer)
|
||||||
|
{
|
||||||
|
this->free();
|
||||||
|
|
||||||
|
if (ecc_ansi_x963_import(reinterpret_cast<const uint8_t*>(pub_key_buffer.data()), pub_key_buffer.size(),
|
||||||
|
&this->key_storage_) != CRYPT_OK)
|
||||||
|
{
|
||||||
|
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecc::key::deserialize(const std::string& key)
|
||||||
|
{
|
||||||
|
this->free();
|
||||||
|
|
||||||
|
if (ecc_import(reinterpret_cast<const uint8_t*>(key.data()), key.size(), &this->key_storage_) != CRYPT_OK)
|
||||||
|
{
|
||||||
|
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ecc::key::serialize(const int type) const
|
||||||
|
{
|
||||||
|
uint8_t buffer[4096] = {0};
|
||||||
|
DWORD length = sizeof(buffer);
|
||||||
|
|
||||||
|
if (ecc_export(buffer, &length, type, &this->key_storage_) == CRYPT_OK)
|
||||||
|
{
|
||||||
|
return std::string(reinterpret_cast<char*>(buffer), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ecc::key::free()
|
||||||
|
{
|
||||||
|
if (this->is_valid())
|
||||||
|
{
|
||||||
|
ecc_free(&this->key_storage_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ecc::key::operator==(key& key) const
|
||||||
|
{
|
||||||
|
return (this->is_valid() && key.is_valid() && this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC));
|
||||||
|
}
|
||||||
|
|
||||||
|
ecc::key ecc::generate_key(const int bits)
|
||||||
|
{
|
||||||
|
key key;
|
||||||
|
|
||||||
|
ltc_mp = ltm_desc;
|
||||||
|
register_prng(&sprng_desc);
|
||||||
|
ecc_make_key(nullptr, find_prng("sprng"), bits / 8, key.get());
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ecc::sign_message(key key, const std::string& message)
|
||||||
|
{
|
||||||
|
if (!key.is_valid()) return "";
|
||||||
|
|
||||||
|
uint8_t buffer[512];
|
||||||
|
DWORD length = sizeof(buffer);
|
||||||
|
|
||||||
|
ltc_mp = ltm_desc;
|
||||||
|
register_prng(&sprng_desc);
|
||||||
|
ecc_sign_hash(reinterpret_cast<const uint8_t*>(message.data()), message.size(), buffer, &length, nullptr,
|
||||||
|
find_prng("sprng"), key.get());
|
||||||
|
|
||||||
|
return std::string(reinterpret_cast<char*>(buffer), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ecc::verify_message(key key, const std::string& message, const std::string& signature)
|
||||||
|
{
|
||||||
|
if (!key.is_valid()) return false;
|
||||||
|
|
||||||
|
ltc_mp = ltm_desc;
|
||||||
|
|
||||||
|
auto result = 0;
|
||||||
|
return ( ecc_verify_hash(reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
|
||||||
|
reinterpret_cast<const uint8_t*>(message.data()), message.size(), &result, key.get()) == CRYPT_OK && result != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string des3::encrypt(const std::string& data, const std::string& iv, const std::string& key)
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
std::string enc_data;
|
||||||
|
enc_data.resize(data.size());
|
||||||
|
|
||||||
|
symmetric_CBC cbc;
|
||||||
|
const auto des3 = find_cipher("3des");
|
||||||
|
|
||||||
|
cbc_start(des3, reinterpret_cast<const uint8_t*>(iv.data()), reinterpret_cast<const uint8_t*>(key.data()),
|
||||||
|
key.size(), 0, &cbc);
|
||||||
|
cbc_encrypt(reinterpret_cast<const uint8_t*>(data.data()),
|
||||||
|
reinterpret_cast<uint8_t*>(const_cast<char*>(enc_data.data())), data.size(), &cbc);
|
||||||
|
cbc_done(&cbc);
|
||||||
|
|
||||||
|
return enc_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string des3::decrypt(const std::string& data, const std::string& iv, const std::string& key)
|
||||||
|
{
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
std::string dec_data;
|
||||||
|
dec_data.resize(data.size());
|
||||||
|
|
||||||
|
symmetric_CBC cbc;
|
||||||
|
const auto des3 = find_cipher("3des");
|
||||||
|
|
||||||
|
cbc_start(des3, reinterpret_cast<const uint8_t*>(iv.data()), reinterpret_cast<const uint8_t*>(key.data()),
|
||||||
|
key.size(), 0, &cbc);
|
||||||
|
cbc_decrypt(reinterpret_cast<const uint8_t*>(data.data()),
|
||||||
|
reinterpret_cast<uint8_t*>(const_cast<char*>(dec_data.data())), data.size(), &cbc);
|
||||||
|
cbc_done(&cbc);
|
||||||
|
|
||||||
|
return dec_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void des3::initialize()
|
||||||
|
{
|
||||||
|
static auto initialized = false;
|
||||||
|
if (initialized) return;
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
register_cipher(&des3_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tiger::compute(const std::string& data, const bool hex)
|
||||||
|
{
|
||||||
|
return compute(reinterpret_cast<const uint8_t*>(data.data()), data.size(), hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tiger::compute(const uint8_t* data, const size_t length, const bool hex)
|
||||||
|
{
|
||||||
|
uint8_t buffer[24] = {0};
|
||||||
|
|
||||||
|
hash_state state;
|
||||||
|
tiger_init(&state);
|
||||||
|
tiger_process(&state, data, length);
|
||||||
|
tiger_done(&state, buffer);
|
||||||
|
|
||||||
|
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
|
||||||
|
if (!hex) return hash;
|
||||||
|
|
||||||
|
return string::dump_hex(hash, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha1::compute(const std::string& data, const bool hex)
|
||||||
|
{
|
||||||
|
return compute(reinterpret_cast<const uint8_t*>(data.data()), data.size(), hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha1::compute(const uint8_t* data, size_t length, const bool hex)
|
||||||
|
{
|
||||||
|
uint8_t buffer[20] = {0};
|
||||||
|
|
||||||
|
hash_state state;
|
||||||
|
sha1_init(&state);
|
||||||
|
sha1_process(&state, data, length);
|
||||||
|
sha1_done(&state, buffer);
|
||||||
|
|
||||||
|
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
|
||||||
|
if (!hex) return hash;
|
||||||
|
|
||||||
|
return string::dump_hex(hash, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha256::compute(const std::string& data, bool hex)
|
||||||
|
{
|
||||||
|
return compute(reinterpret_cast<const uint8_t*>(data.data()), data.size(), hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha256::compute(const uint8_t* data, size_t length, bool hex)
|
||||||
|
{
|
||||||
|
uint8_t buffer[32] = {0};
|
||||||
|
|
||||||
|
hash_state state;
|
||||||
|
sha256_init(&state);
|
||||||
|
sha256_process(&state, data, length);
|
||||||
|
sha256_done(&state, buffer);
|
||||||
|
|
||||||
|
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
|
||||||
|
if (!hex) return hash;
|
||||||
|
|
||||||
|
return string::dump_hex(hash, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha512::compute(const std::string& data, bool hex)
|
||||||
|
{
|
||||||
|
return compute(reinterpret_cast<const uint8_t*>(data.data()), data.size(), hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha512::compute(const uint8_t* data, size_t length, bool hex)
|
||||||
|
{
|
||||||
|
uint8_t buffer[64] = {0};
|
||||||
|
|
||||||
|
hash_state state;
|
||||||
|
sha512_init(&state);
|
||||||
|
sha512_process(&state, data, length);
|
||||||
|
sha512_done(&state, buffer);
|
||||||
|
|
||||||
|
std::string hash(reinterpret_cast<char*>(buffer), sizeof(buffer));
|
||||||
|
if (!hex) return hash;
|
||||||
|
|
||||||
|
return string::dump_hex(hash, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int jenkins_one_at_a_time::compute(const std::string& data)
|
||||||
|
{
|
||||||
|
return compute(data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int jenkins_one_at_a_time::compute(const char* key, size_t len)
|
||||||
|
{
|
||||||
|
unsigned int hash, i;
|
||||||
|
for (hash = i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
hash += key[i];
|
||||||
|
hash += (hash << 10);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
}
|
||||||
|
hash += (hash << 3);
|
||||||
|
hash ^= (hash >> 11);
|
||||||
|
hash += (hash << 15);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
src/utils/cryptography.hpp
Normal file
87
src/utils/cryptography.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "memory.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace cryptography
|
||||||
|
{
|
||||||
|
class ecc final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class key final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
key();
|
||||||
|
~key();
|
||||||
|
|
||||||
|
bool is_valid() const;
|
||||||
|
|
||||||
|
ecc_key* get();
|
||||||
|
|
||||||
|
std::string get_public_key() const;
|
||||||
|
|
||||||
|
void set(const std::string& pub_key_buffer);
|
||||||
|
|
||||||
|
void deserialize(const std::string& key);
|
||||||
|
|
||||||
|
std::string serialize(const int type = PK_PRIVATE) const;
|
||||||
|
|
||||||
|
void free();
|
||||||
|
|
||||||
|
bool operator==(key& key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ecc_key key_storage_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
static key generate_key(int bits);
|
||||||
|
static std::string sign_message(key key, const std::string& message);
|
||||||
|
static bool verify_message(key key, const std::string& message, const std::string& signature);
|
||||||
|
};
|
||||||
|
|
||||||
|
class des3 final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string encrypt(const std::string& data, const std::string& iv, const std::string& key);
|
||||||
|
static std::string decrypt(const std::string& data, const std::string& iv, const std::string& key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void initialize();
|
||||||
|
};
|
||||||
|
|
||||||
|
class tiger final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string compute(const std::string& data, bool hex = false);
|
||||||
|
static std::string compute(const uint8_t* data, size_t length, bool hex = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
class sha1 final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string compute(const std::string& data, bool hex = false);
|
||||||
|
static std::string compute(const uint8_t* data, size_t length, bool hex = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
class sha256 final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string compute(const std::string& data, bool hex = false);
|
||||||
|
static std::string compute(const uint8_t* data, size_t length, bool hex = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
class sha512 final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string compute(const std::string& data, bool hex = false);
|
||||||
|
static std::string compute(const uint8_t* data, size_t length, bool hex = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
class jenkins_one_at_a_time final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static unsigned int compute(const std::string& data);
|
||||||
|
static unsigned int compute(const char* key, size_t len);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ namespace utils
|
|||||||
unsigned int j;
|
unsigned int j;
|
||||||
for (j = 0; j < strlen(container->mask); ++j)
|
for (j = 0; j < strlen(container->mask); ++j)
|
||||||
{
|
{
|
||||||
if (container->mask[j] != '?' &&container->signature[j] != address[j])
|
if (container->mask[j] != '?' && container->signature[j] != address[j])
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ namespace utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hook* hook::initialize(const DWORD place, void(*stub)(), const bool use_jump)
|
hook* hook::initialize(const DWORD place, void (*stub)(), const bool use_jump)
|
||||||
{
|
{
|
||||||
return this->initialize(place, reinterpret_cast<void*>(stub), use_jump);
|
return this->initialize(place, reinterpret_cast<void*>(stub), use_jump);
|
||||||
}
|
}
|
||||||
@ -69,7 +69,8 @@ namespace utils
|
|||||||
this->place_ = place;
|
this->place_ = place;
|
||||||
this->stub_ = stub;
|
this->stub_ = stub;
|
||||||
|
|
||||||
this->original_ = static_cast<char*>(this->place_) + 5 + *reinterpret_cast<DWORD*>((static_cast<char*>(this->place_) + 1));
|
this->original_ = static_cast<char*>(this->place_) + 5 + *reinterpret_cast<DWORD*>((static_cast<char*>(this->
|
||||||
|
place_) + 1));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -92,9 +93,11 @@ namespace utils
|
|||||||
|
|
||||||
*code = static_cast<char>(this->use_jump_ ? 0xE9 : 0xE8);
|
*code = static_cast<char>(this->use_jump_ ? 0xE9 : 0xE8);
|
||||||
|
|
||||||
*reinterpret_cast<size_t*>(code + 1) = reinterpret_cast<size_t>(this->stub_) - (reinterpret_cast<size_t>(this->place_) + 5);
|
*reinterpret_cast<size_t*>(code + 1) = reinterpret_cast<size_t>(this->stub_) - (reinterpret_cast<size_t>(this->
|
||||||
|
place_) + 5);
|
||||||
|
|
||||||
if (unprotect && !keep_unprotected) VirtualProtect(this->place_, sizeof(this->buffer_), this->protection_, &this->protection_);
|
if (unprotect && !keep_unprotected) VirtualProtect(this->place_, sizeof(this->buffer_), this->protection_,
|
||||||
|
&this->protection_);
|
||||||
|
|
||||||
FlushInstructionCache(GetCurrentProcess(), this->place_, sizeof(this->buffer_));
|
FlushInstructionCache(GetCurrentProcess(), this->place_, sizeof(this->buffer_));
|
||||||
|
|
||||||
|
@ -18,9 +18,17 @@ namespace utils
|
|||||||
std::function<void(char*)> callback;
|
std::function<void(char*)> callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
signature(void* start, const size_t length) : start_(start), length_(length) {}
|
signature(void* start, const size_t length) : start_(start), length_(length)
|
||||||
signature(const DWORD start, const size_t length) : signature(reinterpret_cast<void*>(start), length) {}
|
{
|
||||||
signature() : signature(0x400000, 0x800000) {}
|
}
|
||||||
|
|
||||||
|
signature(const DWORD start, const size_t length) : signature(reinterpret_cast<void*>(start), length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
signature() : signature(0x400000, 0x800000)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void process();
|
void process();
|
||||||
void add(const container& container);
|
void add(const container& container);
|
||||||
@ -31,14 +39,33 @@ namespace utils
|
|||||||
std::vector<container> signatures_;
|
std::vector<container> signatures_;
|
||||||
};
|
};
|
||||||
|
|
||||||
hook() : initialized_(false), installed_(false), place_(nullptr), stub_(nullptr), original_(nullptr), use_jump_(false), protection_(0) { ZeroMemory(this->buffer_, sizeof(this->buffer_)); }
|
hook() : initialized_(false), installed_(false), place_(nullptr), stub_(nullptr), original_(nullptr),
|
||||||
|
use_jump_(false), protection_(0)
|
||||||
|
{
|
||||||
|
ZeroMemory(this->buffer_, sizeof(this->buffer_));
|
||||||
|
}
|
||||||
|
|
||||||
hook(void* place, void* stub, const bool use_jump = true) : hook() { this->initialize(place, stub, use_jump); }
|
hook(void* place, void* stub, const bool use_jump = true) : hook() { this->initialize(place, stub, use_jump); }
|
||||||
hook(void* place, void(*stub)(), const bool use_jump = true) : hook(place, reinterpret_cast<void*>(stub), use_jump) {}
|
|
||||||
|
|
||||||
hook(const DWORD place, void* stub, const bool use_jump = true) : hook(reinterpret_cast<void*>(place), stub, use_jump) {}
|
hook(void* place, void (*stub)(), const bool use_jump = true) : hook(
|
||||||
hook(const DWORD place, const DWORD stub, const bool use_jump = true) : hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub), use_jump) {}
|
place, reinterpret_cast<void*>(stub), use_jump)
|
||||||
hook(const DWORD place, void(*stub)(), const bool use_jump = true) : hook(reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub), use_jump) {}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
hook(const DWORD place, void* stub, const bool use_jump = true) : hook(
|
||||||
|
reinterpret_cast<void*>(place), stub, use_jump)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
hook(const DWORD place, const DWORD stub, const bool use_jump = true) : hook(
|
||||||
|
reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub), use_jump)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
hook(const DWORD place, void (*stub)(), const bool use_jump = true) : hook(
|
||||||
|
reinterpret_cast<void*>(place), reinterpret_cast<void*>(stub), use_jump)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
hook(const hook&) = delete;
|
hook(const hook&) = delete;
|
||||||
hook(const hook&&) = delete;
|
hook(const hook&&) = delete;
|
||||||
@ -47,7 +74,7 @@ namespace utils
|
|||||||
|
|
||||||
hook* initialize(void* place, void* stub, bool use_jump = true);
|
hook* initialize(void* place, void* stub, bool use_jump = true);
|
||||||
hook* initialize(DWORD place, void* stub, bool use_jump = true);
|
hook* initialize(DWORD place, void* stub, bool use_jump = true);
|
||||||
hook* initialize(DWORD place, void(*stub)(), bool use_jump = true); // For lambdas
|
hook* initialize(DWORD place, void (*stub)(), bool use_jump = true); // For lambdas
|
||||||
hook* install(bool unprotect = true, bool keep_unprotected = false);
|
hook* install(bool unprotect = true, bool keep_unprotected = false);
|
||||||
hook* uninstall(bool unprotect = true);
|
hook* uninstall(bool unprotect = true);
|
||||||
|
|
||||||
@ -57,7 +84,8 @@ namespace utils
|
|||||||
static void nop(void* place, size_t length);
|
static void nop(void* place, size_t length);
|
||||||
static void nop(DWORD place, size_t length);
|
static void nop(DWORD place, size_t length);
|
||||||
|
|
||||||
template <typename T> static void set(void* place, T value)
|
template <typename T>
|
||||||
|
static void set(void* place, T value)
|
||||||
{
|
{
|
||||||
DWORD old_protect;
|
DWORD old_protect;
|
||||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
|
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
|
||||||
@ -68,7 +96,8 @@ namespace utils
|
|||||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> static void set(const DWORD place, T value)
|
template <typename T>
|
||||||
|
static void set(const DWORD place, T value)
|
||||||
{
|
{
|
||||||
return set<T>(reinterpret_cast<void*>(place), value);
|
return set<T>(reinterpret_cast<void*>(place), value);
|
||||||
}
|
}
|
||||||
|
110
src/utils/io.cpp
Normal file
110
src/utils/io.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "io.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace io
|
||||||
|
{
|
||||||
|
bool file_exists(const std::string& file)
|
||||||
|
{
|
||||||
|
return std::ifstream(file).good();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write_file(const std::string& file, const std::string& data, const bool append)
|
||||||
|
{
|
||||||
|
const auto pos = file.find_last_of("/\\");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
create_directory(file.substr(0, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream stream(
|
||||||
|
file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : std::ofstream::out));
|
||||||
|
|
||||||
|
if (stream.is_open())
|
||||||
|
{
|
||||||
|
stream.write(data.data(), data.size());
|
||||||
|
stream.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string read_file(const std::string& file)
|
||||||
|
{
|
||||||
|
std::string data;
|
||||||
|
read_file(file, &data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool read_file(const std::string& file, std::string* data)
|
||||||
|
{
|
||||||
|
if (!data) return false;
|
||||||
|
data->clear();
|
||||||
|
|
||||||
|
if (file_exists(file))
|
||||||
|
{
|
||||||
|
std::ifstream stream(file, std::ios::binary);
|
||||||
|
if (!stream.is_open()) return false;
|
||||||
|
|
||||||
|
stream.seekg(0, std::ios::end);
|
||||||
|
const std::streamsize size = stream.tellg();
|
||||||
|
stream.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
if (size > -1)
|
||||||
|
{
|
||||||
|
data->resize(static_cast<uint32_t>(size));
|
||||||
|
stream.read(const_cast<char*>(data->data()), size);
|
||||||
|
stream.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t file_size(const std::string& file)
|
||||||
|
{
|
||||||
|
if (file_exists(file))
|
||||||
|
{
|
||||||
|
std::ifstream stream(file, std::ios::binary);
|
||||||
|
|
||||||
|
if (stream.good())
|
||||||
|
{
|
||||||
|
stream.seekg(0, std::ios::end);
|
||||||
|
return static_cast<size_t>(stream.tellg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool create_directory(const std::string& directory)
|
||||||
|
{
|
||||||
|
return std::experimental::filesystem::create_directories(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool directory_exists(const std::string& directory)
|
||||||
|
{
|
||||||
|
return std::experimental::filesystem::is_directory(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool directory_is_empty(const std::string& directory)
|
||||||
|
{
|
||||||
|
return std::experimental::filesystem::is_empty(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> list_files(const std::string& directory)
|
||||||
|
{
|
||||||
|
std::vector<std::string> files;
|
||||||
|
|
||||||
|
for (auto& file : std::experimental::filesystem::directory_iterator(directory))
|
||||||
|
{
|
||||||
|
files.push_back(file.path().generic_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/utils/io.hpp
Normal file
17
src/utils/io.hpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace io
|
||||||
|
{
|
||||||
|
bool file_exists(const std::string& file);
|
||||||
|
bool write_file(const std::string& file, const std::string& data, bool append = false);
|
||||||
|
bool read_file(const std::string& file, std::string* data);
|
||||||
|
std::string read_file(const std::string& file);
|
||||||
|
size_t file_size(const std::string& file);
|
||||||
|
bool create_directory(const std::string& directory);
|
||||||
|
bool directory_exists(const std::string& directory);
|
||||||
|
bool directory_is_empty(const std::string& directory);
|
||||||
|
std::vector<std::string> list_files(const std::string& directory);
|
||||||
|
}
|
||||||
|
}
|
142
src/utils/memory.cpp
Normal file
142
src/utils/memory.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "memory.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
memory::allocator memory::mem_allocator_;
|
||||||
|
|
||||||
|
memory::allocator::~allocator()
|
||||||
|
{
|
||||||
|
this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::clear()
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
for (auto& data : this->pool_)
|
||||||
|
{
|
||||||
|
memory::free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pool_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::free(void* data)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
|
||||||
|
if (j != this->pool_.end())
|
||||||
|
{
|
||||||
|
memory::free(data);
|
||||||
|
this->pool_.erase(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::free(const void* data)
|
||||||
|
{
|
||||||
|
this->free(const_cast<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memory::allocator::allocate(const size_t length)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto data = memory::allocate(length);
|
||||||
|
this->pool_.push_back(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::allocator::empty() const
|
||||||
|
{
|
||||||
|
return this->pool_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
char* memory::allocator::duplicate_string(const std::string& string)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto data = memory::duplicate_string(string);
|
||||||
|
this->pool_.push_back(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memory::allocate(const size_t length)
|
||||||
|
{
|
||||||
|
const auto data = calloc(length, 1);
|
||||||
|
assert(data != nullptr);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* memory::duplicate_string(const std::string& string)
|
||||||
|
{
|
||||||
|
const auto new_string = allocate_array<char>(string.size() + 1);
|
||||||
|
std::memcpy(new_string, string.data(), string.size());
|
||||||
|
return new_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::free(void* data)
|
||||||
|
{
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
::free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::free(const void* data)
|
||||||
|
{
|
||||||
|
free(const_cast<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::is_set(const void* mem, const char chr, const size_t length)
|
||||||
|
{
|
||||||
|
const auto mem_arr = reinterpret_cast<const char*>(mem);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (mem_arr[i] != chr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::is_bad_read_ptr(const void* ptr)
|
||||||
|
{
|
||||||
|
MEMORY_BASIC_INFORMATION mbi = {};
|
||||||
|
if (VirtualQuery(ptr, &mbi, sizeof(mbi)))
|
||||||
|
{
|
||||||
|
const DWORD mask = (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ |
|
||||||
|
PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||||
|
auto b = !(mbi.Protect & mask);
|
||||||
|
// check the page is not a guard page
|
||||||
|
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true;
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::is_bad_code_ptr(const void* ptr)
|
||||||
|
{
|
||||||
|
MEMORY_BASIC_INFORMATION mbi = {};
|
||||||
|
if (VirtualQuery(ptr, &mbi, sizeof(mbi)))
|
||||||
|
{
|
||||||
|
const DWORD mask = (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY);
|
||||||
|
auto b = !(mbi.Protect & mask);
|
||||||
|
// check the page is not a guard page
|
||||||
|
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) b = true;
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory::allocator* memory::get_allocator()
|
||||||
|
{
|
||||||
|
return &memory::mem_allocator_;
|
||||||
|
}
|
||||||
|
}
|
71
src/utils/memory.hpp
Normal file
71
src/utils/memory.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
class memory final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class allocator final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~allocator();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void free(void* data);
|
||||||
|
|
||||||
|
void free(const void* data);
|
||||||
|
|
||||||
|
void* allocate(const size_t length);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T* allocate()
|
||||||
|
{
|
||||||
|
return this->allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T* allocate_array(const size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
char* duplicate_string(const std::string& string);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::vector<void*> pool_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* allocate(size_t length);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T* allocate()
|
||||||
|
{
|
||||||
|
return allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline T* allocate_array(const size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* duplicate_string(const std::string& string);
|
||||||
|
|
||||||
|
static void free(void* data);
|
||||||
|
static void free(const void* data);
|
||||||
|
|
||||||
|
static bool is_set(const void* mem, char chr, size_t length);
|
||||||
|
|
||||||
|
static bool is_bad_read_ptr(const void* ptr);
|
||||||
|
static bool is_bad_code_ptr(const void* ptr);
|
||||||
|
|
||||||
|
static allocator* get_allocator();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static allocator mem_allocator_;
|
||||||
|
};
|
||||||
|
}
|
@ -32,7 +32,7 @@ namespace utils
|
|||||||
this->module_ = handle;
|
this->module_ = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool module::operator==(const module &obj) const
|
bool module::operator==(const module& obj) const
|
||||||
{
|
{
|
||||||
return this->module_ == obj.module_;
|
return this->module_ == obj.module_;
|
||||||
}
|
}
|
||||||
@ -90,7 +90,8 @@ namespace utils
|
|||||||
if (!this->is_valid()) return;
|
if (!this->is_valid()) return;
|
||||||
|
|
||||||
DWORD protection;
|
DWORD protection;
|
||||||
VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE, &protection);
|
VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE,
|
||||||
|
&protection);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t module::get_relative_entry_point() const
|
size_t module::get_relative_entry_point() const
|
||||||
@ -125,7 +126,7 @@ namespace utils
|
|||||||
{
|
{
|
||||||
if (!this->is_valid()) return "";
|
if (!this->is_valid()) return "";
|
||||||
|
|
||||||
char name[MAX_PATH] = { 0 };
|
char name[MAX_PATH] = {0};
|
||||||
GetModuleFileNameA(this->module_, name, sizeof name);
|
GetModuleFileNameA(this->module_, name, sizeof name);
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
@ -158,22 +159,26 @@ namespace utils
|
|||||||
auto* header = this->get_optional_header();
|
auto* header = this->get_optional_header();
|
||||||
if (!header) return nullptr;
|
if (!header) return nullptr;
|
||||||
|
|
||||||
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(this->get_ptr() + header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(this->get_ptr() + header->DataDirectory
|
||||||
|
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||||
|
|
||||||
while (import_descriptor->Name)
|
while (import_descriptor->Name)
|
||||||
{
|
{
|
||||||
if (!_stricmp(reinterpret_cast<char*>(this->get_ptr() + import_descriptor->Name), module_name.data()))
|
if (!_stricmp(reinterpret_cast<char*>(this->get_ptr() + import_descriptor->Name), module_name.data()))
|
||||||
{
|
{
|
||||||
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->OriginalFirstThunk + this->get_ptr());
|
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->
|
||||||
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->FirstThunk + this->get_ptr());
|
OriginalFirstThunk + this->get_ptr());
|
||||||
|
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->FirstThunk + this->
|
||||||
|
get_ptr());
|
||||||
|
|
||||||
while(original_thunk_data->u1.AddressOfData)
|
while (original_thunk_data->u1.AddressOfData)
|
||||||
{
|
{
|
||||||
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
|
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
|
||||||
|
|
||||||
if (ordinal_number > 0xFFFF) continue;
|
if (ordinal_number > 0xFFFF) continue;
|
||||||
|
|
||||||
if (GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number)) == target_function)
|
if (GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number)) ==
|
||||||
|
target_function)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<void**>(&thunk_data->u1.Function);
|
return reinterpret_cast<void**>(&thunk_data->u1.Function);
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,12 @@ namespace utils
|
|||||||
explicit module(const std::string& name);
|
explicit module(const std::string& name);
|
||||||
explicit module(HMODULE handle);
|
explicit module(HMODULE handle);
|
||||||
|
|
||||||
module(const module& a) : module_(a.module_) {}
|
module(const module& a) : module_(a.module_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool operator!=(const module &obj) const { return !(*this == obj); };
|
bool operator!=(const module& obj) const { return !(*this == obj); };
|
||||||
bool operator==(const module &obj) const;
|
bool operator==(const module& obj) const;
|
||||||
|
|
||||||
operator bool() const;
|
operator bool() const;
|
||||||
operator HMODULE() const;
|
operator HMODULE() const;
|
||||||
@ -48,26 +50,26 @@ namespace utils
|
|||||||
return reinterpret_cast<T*>(this->get_proc<void*>(process));
|
return reinterpret_cast<T*>(this->get_proc<void*>(process));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
T invoke(const std::string& process, Args... args)
|
T invoke(const std::string& process, Args ... args)
|
||||||
{
|
{
|
||||||
auto method = this->get<T(__cdecl)(Args...)>(process);
|
auto method = this->get<T(__cdecl)(Args ...)>(process);
|
||||||
if (method) return method(args...);
|
if (method) return method(args...);
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
T invoke_pascal(const std::string& process, Args... args)
|
T invoke_pascal(const std::string& process, Args ... args)
|
||||||
{
|
{
|
||||||
auto method = this->get<T(__stdcall)(Args...)>(process);
|
auto method = this->get<T(__stdcall)(Args ...)>(process);
|
||||||
if (method) return method(args...);
|
if (method) return method(args...);
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
T invoke_this(const std::string& process, void* thisPtr, Args... args)
|
T invoke_this(const std::string& process, void* thisPtr, Args ... args)
|
||||||
{
|
{
|
||||||
auto method = this->get<T(__thiscall)(void*,Args...)>(thisPtr, process);
|
auto method = this->get<T(__thiscall)(void*, Args ...)>(thisPtr, process);
|
||||||
if (method) return method(args...);
|
if (method) return method(args...);
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
38
src/utils/string.cpp
Normal file
38
src/utils/string.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "string.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace string
|
||||||
|
{
|
||||||
|
const char* va(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
static thread_local va_provider<8, 256> provider;
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
const char* result = provider.get(fmt, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string dump_hex(const std::string& data, const std::string& separator)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < data.size(); ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
result.append(separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(va("%02X", data[i] & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
src/utils/string.hpp
Normal file
81
src/utils/string.hpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "memory.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace string
|
||||||
|
{
|
||||||
|
template <size_t Buffers, size_t MinBufferSize>
|
||||||
|
class va_provider final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static_assert(Buffers != 0 && MinBufferSize != 0, "Buffers and MinBufferSize mustn't be 0");
|
||||||
|
|
||||||
|
va_provider() : current_buffer_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get(const char* format, const va_list ap)
|
||||||
|
{
|
||||||
|
++this->current_buffer_ %= ARRAYSIZE(this->string_pool_);
|
||||||
|
auto entry = &this->string_pool_[this->current_buffer_];
|
||||||
|
|
||||||
|
if (!entry->size || !entry->buffer)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("String pool not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const int res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap);
|
||||||
|
if (res > 0) break; // Success
|
||||||
|
if (res == 0) return nullptr; // Error
|
||||||
|
|
||||||
|
entry->double_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class entry final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit entry(size_t _size = MinBufferSize) : size(_size), buffer(nullptr)
|
||||||
|
{
|
||||||
|
if (this->size < MinBufferSize) this->size = MinBufferSize;
|
||||||
|
this->allocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
~entry()
|
||||||
|
{
|
||||||
|
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||||
|
this->size = 0;
|
||||||
|
this->buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void allocate()
|
||||||
|
{
|
||||||
|
if (this->buffer) memory::get_allocator()->free(this->buffer);
|
||||||
|
this->buffer = memory::get_allocator()->allocate_array<char>(this->size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void double_size()
|
||||||
|
{
|
||||||
|
this->size *= 2;
|
||||||
|
this->allocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
char* buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t current_buffer_;
|
||||||
|
entry string_pool_[Buffers];
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* va(const char* fmt, ...);
|
||||||
|
|
||||||
|
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user