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()
|
||||
includedirs {
|
||||
path.join(libHDiffPatch.source, "HDiff"),
|
||||
path.join(libHDiffPatch.source, "HDiff"),
|
||||
path.join(libHDiffPatch.source, "HPatch"),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -9,21 +9,21 @@ namespace game
|
||||
}
|
||||
|
||||
|
||||
launcher::mode mode = launcher::mode::NONE;
|
||||
launcher::mode mode = launcher::mode::none;
|
||||
|
||||
bool is_mp()
|
||||
{
|
||||
return mode == launcher::mode::MULTIPLAYER;
|
||||
return mode == launcher::mode::multiplayer;
|
||||
}
|
||||
|
||||
bool is_sp()
|
||||
{
|
||||
return mode == launcher::mode::SINGLEPLAYER;
|
||||
return mode == launcher::mode::singleplayer;
|
||||
}
|
||||
|
||||
bool is_dedi()
|
||||
{
|
||||
return mode == launcher::mode::SERVER;
|
||||
return mode == launcher::mode::server;
|
||||
}
|
||||
|
||||
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_mp_.set_size({100, 100});
|
||||
|
||||
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_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->window_.set_callback(std::bind(&launcher::handler, this, std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3));
|
||||
|
@ -7,10 +7,10 @@ class launcher final
|
||||
public:
|
||||
enum mode
|
||||
{
|
||||
NONE,
|
||||
SINGLEPLAYER,
|
||||
MULTIPLAYER,
|
||||
SERVER,
|
||||
none,
|
||||
singleplayer,
|
||||
multiplayer,
|
||||
server,
|
||||
};
|
||||
|
||||
launcher();
|
||||
@ -18,7 +18,7 @@ public:
|
||||
mode run() const;
|
||||
|
||||
private:
|
||||
mode mode_ = NONE;
|
||||
mode mode_ = none;
|
||||
|
||||
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 "loader.hpp"
|
||||
#include "binary_loader.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
loader::loader(const launcher::mode mode) : mode_(mode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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),
|
||||
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),
|
||||
source_tls->EndAddressOfRawData - 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;
|
||||
}
|
||||
|
||||
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,
|
||||
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))
|
||||
{
|
||||
MessageBoxA(nullptr, "Section exceeds the binary payload size, please increase it!", nullptr, MB_ICONERROR);
|
||||
TerminateProcess(GetCurrentProcess(), 1);
|
||||
throw std::runtime_error("Section exceeds the binary payload size, please increase it!");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
DWORD old_protect;
|
||||
@ -166,10 +142,7 @@ void loader::load_imports(const utils::nt::module& target, const utils::nt::modu
|
||||
|
||||
if (!function)
|
||||
{
|
||||
auto error = "Unable to load import '"s + function_name + "' from module '"s + name + "'"s;
|
||||
|
||||
MessageBoxA(nullptr, error.data(), nullptr, MB_ICONERROR);
|
||||
TerminateProcess(GetCurrentProcess(), 1);
|
||||
throw std::runtime_error(utils::string::va("Unable to load import '%s' from module '%s'", function_name.data(), name.data()));
|
||||
}
|
||||
|
||||
*address_table_entry = reinterpret_cast<uintptr_t>(function);
|
||||
|
@ -5,7 +5,7 @@
|
||||
class loader final
|
||||
{
|
||||
public:
|
||||
explicit loader(launcher::mode mode);
|
||||
loader(launcher::mode mode);
|
||||
|
||||
FARPROC load(const utils::nt::module& module) const;
|
||||
|
||||
@ -15,9 +15,6 @@ private:
|
||||
launcher::mode mode_;
|
||||
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);
|
||||
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;
|
||||
|
18
src/main.cpp
18
src/main.cpp
@ -3,6 +3,9 @@
|
||||
#include "loader/loader.hpp"
|
||||
#include "loader/module_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "loader/binary_loader.hpp"
|
||||
|
||||
//#define GENERATE_DIFFS
|
||||
|
||||
void exit_hook(const int code)
|
||||
{
|
||||
@ -14,11 +17,17 @@ int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR
|
||||
{
|
||||
FARPROC entry_point = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
#ifdef GENERATE_DIFFS
|
||||
binary_loader::create();
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
launcher launcher;
|
||||
const auto mode = launcher.run();
|
||||
|
||||
if (mode == launcher::mode::NONE) return 0;
|
||||
if (mode == launcher::mode::none) return 0;
|
||||
|
||||
loader loader(mode);
|
||||
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({});
|
||||
if (!entry_point) return 1;
|
||||
if (!entry_point) throw std::runtime_error("Unable to load inject binary into memory");
|
||||
|
||||
game::initialize(mode);
|
||||
module_loader::post_load();
|
||||
}
|
||||
catch (std::exception e)
|
||||
{
|
||||
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return entry_point();
|
||||
}
|
||||
|
@ -89,8 +89,8 @@ END
|
||||
102 ICON "resources/icon.ico"
|
||||
IMAGE_SP BITMAP "resources/singleplayer.bmp"
|
||||
IMAGE_MP BITMAP "resources/multiplayer.bmp"
|
||||
BINARY_SP RCDATA "resources/iw5sp.exe"
|
||||
BINARY_MP RCDATA "resources/iw5mp.exe"
|
||||
BINARY_SP RCDATA "resources/iw5sp.exe.diff"
|
||||
BINARY_MP RCDATA "resources/iw5mp.exe.diff"
|
||||
|
||||
|
||||
#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 <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 <vector>
|
||||
#include <mutex>
|
||||
@ -19,6 +28,11 @@
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <diff.h>
|
||||
#include <patch.h>
|
||||
#include <tomcrypt.h>
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
@ -69,7 +69,8 @@ namespace utils
|
||||
this->place_ = place;
|
||||
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;
|
||||
}
|
||||
@ -92,9 +93,11 @@ namespace utils
|
||||
|
||||
*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_));
|
||||
|
||||
|
@ -18,9 +18,17 @@ namespace utils
|
||||
std::function<void(char*)> callback;
|
||||
};
|
||||
|
||||
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(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)
|
||||
{
|
||||
}
|
||||
|
||||
void process();
|
||||
void add(const container& container);
|
||||
@ -31,14 +39,33 @@ namespace utils
|
||||
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(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(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(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;
|
||||
@ -57,7 +84,8 @@ namespace utils
|
||||
static void nop(void* 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;
|
||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
|
||||
@ -68,7 +96,8 @@ namespace utils
|
||||
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);
|
||||
}
|
||||
|
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_;
|
||||
};
|
||||
}
|
@ -90,7 +90,8 @@ namespace utils
|
||||
if (!this->is_valid()) return;
|
||||
|
||||
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
|
||||
@ -158,14 +159,17 @@ namespace utils
|
||||
auto* header = this->get_optional_header();
|
||||
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)
|
||||
{
|
||||
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* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->FirstThunk + this->get_ptr());
|
||||
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->
|
||||
OriginalFirstThunk + this->get_ptr());
|
||||
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->FirstThunk + this->
|
||||
get_ptr());
|
||||
|
||||
while (original_thunk_data->u1.AddressOfData)
|
||||
{
|
||||
@ -173,7 +177,8 @@ namespace utils
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -14,7 +14,9 @@ namespace utils
|
||||
explicit module(const std::string& name);
|
||||
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;
|
||||
|
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