Initial upload commit

This commit is contained in:
project-bo4
2023-03-06 12:40:07 -08:00
parent 4cdb62e14c
commit 8b606eae94
93 changed files with 8131 additions and 2 deletions

View File

@ -0,0 +1,85 @@
#include "minidump.hpp"
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
namespace exception
{
namespace
{
constexpr MINIDUMP_TYPE get_minidump_type()
{
constexpr auto type = MiniDumpIgnoreInaccessibleMemory //
| MiniDumpWithHandleData //
| MiniDumpScanMemory //
| MiniDumpWithProcessThreadData //
| MiniDumpWithFullMemoryInfo //
| MiniDumpWithThreadInfo //
| MiniDumpWithUnloadedModules;
return static_cast<MINIDUMP_TYPE>(type);
}
std::string get_temp_filename()
{
char filename[MAX_PATH] = {0};
char pathname[MAX_PATH] = {0};
GetTempPathA(sizeof(pathname), pathname);
GetTempFileNameA(pathname, "boiii-", 0, filename);
return filename;
}
HANDLE write_dump_to_temp_file(const LPEXCEPTION_POINTERS exceptioninfo)
{
MINIDUMP_EXCEPTION_INFORMATION minidump_exception_info = {GetCurrentThreadId(), exceptioninfo, FALSE};
auto* const file_handle = CreateFileA(get_temp_filename().data(), GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
nullptr);
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file_handle, get_minidump_type(),
&minidump_exception_info,
nullptr,
nullptr))
{
MessageBoxA(nullptr, "There was an error creating the minidump! Hit OK to close the program.",
"Minidump Error", MB_OK | MB_ICONERROR);
TerminateProcess(GetCurrentProcess(), 123);
}
return file_handle;
}
std::string read_file(const HANDLE file_handle)
{
FlushFileBuffers(file_handle);
SetFilePointer(file_handle, 0, nullptr, FILE_BEGIN);
std::string buffer{};
DWORD bytes_read = 0;
char temp_bytes[0x2000];
do
{
if (!ReadFile(file_handle, temp_bytes, sizeof(temp_bytes), &bytes_read, nullptr))
{
return {};
}
buffer.append(temp_bytes, bytes_read);
}
while (bytes_read == sizeof(temp_bytes));
return buffer;
}
}
std::string create_minidump(const LPEXCEPTION_POINTERS exceptioninfo)
{
const utils::nt::handle file_handle = write_dump_to_temp_file(exceptioninfo);
return read_file(file_handle);
}
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "../utils/nt.hpp"
namespace exception
{
std::string create_minidump(LPEXCEPTION_POINTERS exceptioninfo);
}

View File

@ -0,0 +1,75 @@
#include "binary_resource.hpp"
#include <utility>
#include "nt.hpp"
#include "io.hpp"
namespace utils
{
namespace
{
std::string get_temp_folder()
{
char path[MAX_PATH] = {0};
if (!GetTempPathA(sizeof(path), path))
{
throw std::runtime_error("Unable to get temp path");
}
return path;
}
std::string write_existing_temp_file(const std::string& file, const std::string& data,
const bool fatal_if_overwrite_fails)
{
const auto temp = get_temp_folder();
auto file_path = temp + file;
std::string current_data;
if (!io::read_file(file_path, &current_data))
{
if (!io::write_file(file_path, data))
{
throw std::runtime_error("Failed to write file: " + file_path);
}
return file_path;
}
if (current_data == data || io::write_file(file_path, data) || !fatal_if_overwrite_fails)
{
return file_path;
}
throw std::runtime_error(
"Temporary file was already written, but differs. It can't be overwritten as it's still in use: " +
file_path);
}
}
binary_resource::binary_resource(const int id, std::string file)
: filename_(std::move(file))
{
this->resource_ = nt::load_resource(id);
if (this->resource_.empty())
{
throw std::runtime_error("Unable to load resource: " + std::to_string(id));
}
}
std::string binary_resource::get_extracted_file(const bool fatal_if_overwrite_fails)
{
if (this->path_.empty())
{
this->path_ = write_existing_temp_file(this->filename_, this->resource_, fatal_if_overwrite_fails);
}
return this->path_;
}
const std::string& binary_resource::get_data() const
{
return this->resource_;
}
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <string>
namespace utils
{
class binary_resource
{
public:
binary_resource(int id, std::string file);
std::string get_extracted_file(bool fatal_if_overwrite_fails = false);
const std::string& get_data() const;
private:
std::string resource_;
std::string filename_;
std::string path_;
};
}

View File

@ -0,0 +1,134 @@
#include "com.hpp"
#include "nt.hpp"
#include "string.hpp"
#include "finally.hpp"
#include <stdexcept>
#include <ShlObj.h>
namespace utils::com
{
namespace
{
void initialize_com()
{
static struct x
{
x()
{
if (FAILED(CoInitialize(nullptr)))
{
throw std::runtime_error("Failed to initialize the component object model");
}
}
~x()
{
CoUninitialize();
}
} xx;
}
}
bool select_folder(std::string& out_folder, const std::string& title, const std::string& selected_folder)
{
initialize_com();
CComPtr<IFileOpenDialog> file_dialog{};
if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&file_dialog))))
{
throw std::runtime_error("Failed to create co instance");
}
DWORD dw_options;
if (FAILED(file_dialog->GetOptions(&dw_options)))
{
throw std::runtime_error("Failed to get options");
}
if (FAILED(file_dialog->SetOptions(dw_options | FOS_PICKFOLDERS)))
{
throw std::runtime_error("Failed to set options");
}
const std::wstring wide_title(title.begin(), title.end());
if (FAILED(file_dialog->SetTitle(wide_title.data())))
{
throw std::runtime_error("Failed to set title");
}
if (!selected_folder.empty())
{
file_dialog->ClearClientData();
std::wstring wide_selected_folder(selected_folder.begin(), selected_folder.end());
for (auto& chr : wide_selected_folder)
{
if (chr == L'/')
{
chr = L'\\';
}
}
IShellItem* shell_item = nullptr;
if (FAILED(SHCreateItemFromParsingName(wide_selected_folder.data(), NULL, IID_PPV_ARGS(&shell_item))))
{
throw std::runtime_error("Failed to create item from parsing name");
}
if (FAILED(file_dialog->SetDefaultFolder(shell_item)))
{
throw std::runtime_error("Failed to set default folder");
}
}
const auto result = file_dialog->Show(nullptr);
if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED))
{
return false;
}
if (FAILED(result))
{
throw std::runtime_error("Failed to show dialog");
}
CComPtr<IShellItem> result_item{};
if (FAILED(file_dialog->GetResult(&result_item)))
{
throw std::runtime_error("Failed to get result");
}
PWSTR raw_path = nullptr;
if (FAILED(result_item->GetDisplayName(SIGDN_FILESYSPATH, &raw_path)))
{
throw std::runtime_error("Failed to get path display name");
}
const auto _ = finally([raw_path]()
{
CoTaskMemFree(raw_path);
});
const std::wstring result_path = raw_path;
out_folder = string::convert(result_path);
return true;
}
CComPtr<IProgressDialog> create_progress_dialog()
{
initialize_com();
CComPtr<IProgressDialog> progress_dialog{};
if (FAILED(
CoCreateInstance(CLSID_ProgressDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&progress_dialog))))
{
throw std::runtime_error("Failed to create co instance");
}
return progress_dialog;
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "nt.hpp"
#include <ShlObj.h>
#include <atlbase.h>
namespace utils::com
{
bool select_folder(std::string& out_folder, const std::string& title = "Select a Folder", const std::string& selected_folder = {});
CComPtr<IProgressDialog> create_progress_dialog();
}

View File

@ -0,0 +1,399 @@
#include "memory.hpp"
#include "compression.hpp"
#include <zlib.h>
#include <zip.h>
#include <unzip.h>
#include "io.hpp"
#include "finally.hpp"
namespace utils::compression
{
namespace zlib
{
namespace
{
class zlib_stream
{
public:
zlib_stream()
{
memset(&stream_, 0, sizeof(stream_));
valid_ = inflateInit(&stream_) == Z_OK;
}
zlib_stream(zlib_stream&&) = delete;
zlib_stream(const zlib_stream&) = delete;
zlib_stream& operator=(zlib_stream&&) = delete;
zlib_stream& operator=(const zlib_stream&) = delete;
~zlib_stream()
{
if (valid_)
{
inflateEnd(&stream_);
}
}
z_stream& get()
{
return stream_; //
}
bool is_valid() const
{
return valid_;
}
private:
bool valid_{false};
z_stream stream_{};
};
}
std::string decompress(const std::string& data)
{
std::string buffer{};
zlib_stream stream_container{};
if (!stream_container.is_valid())
{
return {};
}
int ret{};
size_t offset = 0;
static thread_local uint8_t dest[CHUNK] = {0};
auto& stream = stream_container.get();
do
{
const auto input_size = std::min(sizeof(dest), data.size() - offset);
stream.avail_in = static_cast<uInt>(input_size);
stream.next_in = reinterpret_cast<const Bytef*>(data.data()) + offset;
offset += stream.avail_in;
do
{
stream.avail_out = sizeof(dest);
stream.next_out = dest;
ret = inflate(&stream, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END)
{
return {};
}
buffer.insert(buffer.end(), dest, dest + sizeof(dest) - stream.avail_out);
}
while (stream.avail_out == 0);
}
while (ret != Z_STREAM_END);
return buffer;
}
std::string compress(const std::string& data)
{
std::string result{};
auto length = compressBound(static_cast<uLong>(data.size()));
result.resize(length);
if (compress2(reinterpret_cast<Bytef*>(result.data()), &length,
reinterpret_cast<const Bytef*>(data.data()), static_cast<uLong>(data.size()),
Z_BEST_COMPRESSION) != Z_OK)
{
return {};
}
result.resize(length);
return result;
}
}
namespace zip
{
namespace
{
bool add_file(zipFile& zip_file, const std::string& filename, const std::string& data)
{
const auto zip_64 = data.size() > 0xffffffff ? 1 : 0;
if (ZIP_OK != zipOpenNewFileInZip64(zip_file, filename.data(), nullptr, nullptr, 0, nullptr, 0, nullptr,
Z_DEFLATED, Z_BEST_COMPRESSION, zip_64))
{
return false;
}
const auto _ = finally([&zip_file]()
{
zipCloseFileInZip(zip_file);
});
return ZIP_OK == zipWriteInFileInZip(zip_file, data.data(), static_cast<unsigned>(data.size()));
}
}
void archive::add(std::string filename, std::string data)
{
this->files_[std::move(filename)] = std::move(data);
}
bool archive::write(const std::string& filename, const std::string& comment)
{
// Hack to create the directory :3
io::write_file(filename, {});
io::remove_file(filename);
auto* zip_file = zipOpen64(filename.data(), 0);
if (!zip_file)
{
return false;
}
const auto _ = finally([&zip_file, &comment]()
{
zipClose(zip_file, comment.empty() ? nullptr : comment.data());
});
for (const auto& file : this->files_)
{
if (!add_file(zip_file, file.first, file.second))
{
return false;
}
}
return true;
}
namespace
{
std::optional<std::pair<std::string, std::string>> read_zip_file_entry(unzFile& zip_file)
{
char filename[1024]{};
unz_file_info file_info{};
if (unzGetCurrentFileInfo(zip_file, &file_info, filename, sizeof(filename), nullptr, 0, nullptr, 0) !=
UNZ_OK)
{
return {};
}
if (unzOpenCurrentFile(zip_file) != UNZ_OK)
{
return {};
}
auto _ = finally([&zip_file]
{
unzCloseCurrentFile(zip_file);
});
int error = UNZ_OK;
std::string out_buffer{};
static thread_local char buffer[0x2000];
do
{
error = unzReadCurrentFile(zip_file, buffer, sizeof(buffer));
if (error < 0)
{
return {};
}
// Write data to file.
if (error > 0)
{
out_buffer.append(buffer, error);
}
}
while (error > 0);
return std::pair<std::string, std::string>{filename, out_buffer};
}
class memory_file
{
public:
memory_file(const std::string& data)
: data_(data)
{
func_def_.opaque = this;
func_def_.zopen64_file = open_file_static;
func_def_.zseek64_file = seek_file_static;
func_def_.ztell64_file = tell_file_static;
func_def_.zread_file = read_file_static;
func_def_.zwrite_file = write_file_static;
func_def_.zclose_file = close_file_static;
func_def_.zerror_file = testerror_file_static;
}
const char* get_name() const
{
return "blub";
}
zlib_filefunc64_def* get_func_def()
{
return &this->func_def_;
}
private:
const std::string& data_;
size_t offset_{0};
zlib_filefunc64_def func_def_{};
voidpf open_file(const void* filename, const int mode) const
{
if (mode != (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING))
{
return nullptr;
}
if (strcmp(static_cast<const char*>(filename), get_name()) != 0)
{
return nullptr;
}
return reinterpret_cast<voidpf>(1);
}
long seek_file(const voidpf stream, const ZPOS64_T offset, const int origin)
{
if (stream != reinterpret_cast<voidpf>(1))
{
return -1;
}
size_t target_base = this->data_.size();
if (origin == ZLIB_FILEFUNC_SEEK_CUR)
{
target_base = this->offset_;
}
else if (origin == ZLIB_FILEFUNC_SEEK_SET)
{
target_base = 0;
}
const auto target_offset = target_base + offset;
if (target_offset > this->data_.size())
{
return -1;
}
this->offset_ = target_offset;
return 0;
}
ZPOS64_T tell_file(const voidpf stream) const
{
if (stream != reinterpret_cast<voidpf>(1))
{
return static_cast<ZPOS64_T>(-1);
}
return this->offset_;
}
uLong read_file(const voidpf stream, void* buf, const uLong size)
{
if (stream != reinterpret_cast<voidpf>(1))
{
return 0;
}
const auto file_end = this->data_.size();
const auto start = this->offset_;
const auto end = std::min(this->offset_ + size, file_end);
const auto length = end - start;
memcpy(buf, this->data_.data() + start, length);
this->offset_ = end;
return static_cast<uLong>(length);
}
static voidpf open_file_static(const voidpf opaque, const void* filename, const int mode)
{
return static_cast<memory_file*>(opaque)->open_file(filename, mode);
}
static long seek_file_static(const voidpf opaque, const voidpf stream, const ZPOS64_T offset,
const int origin)
{
return static_cast<memory_file*>(opaque)->seek_file(stream, offset, origin);
}
static ZPOS64_T tell_file_static(const voidpf opaque, const voidpf stream)
{
return static_cast<memory_file*>(opaque)->tell_file(stream);
}
static uLong read_file_static(const voidpf opaque, const voidpf stream, void* buf, const uLong size)
{
return static_cast<memory_file*>(opaque)->read_file(stream, buf, size);
}
static uLong write_file_static(voidpf, voidpf, const void*, uLong)
{
return 0;
}
static int close_file_static(voidpf, voidpf)
{
return 0;
}
static int testerror_file_static(voidpf, voidpf)
{
return 0;
}
};
}
std::unordered_map<std::string, std::string> extract(const std::string& data)
{
memory_file mem_file(data);
auto zip_file = unzOpen2_64(mem_file.get_name(), mem_file.get_func_def());
auto _ = finally([&zip_file]
{
if (zip_file)
{
unzClose(zip_file);
}
});
if (!zip_file)
{
return {};
}
unz_global_info global_info{};
if (unzGetGlobalInfo(zip_file, &global_info) != UNZ_OK)
{
return {};
}
std::unordered_map<std::string, std::string> files{};
files.reserve(global_info.number_entry);
for (auto i = 0ul; i < global_info.number_entry; ++i)
{
if (i > 0 && unzGoToNextFile(zip_file) != UNZ_OK)
{
break;
}
auto file = read_zip_file_entry(zip_file);
if (!file)
{
continue;
}
files[std::move(file->first)] = std::move(file->second);
}
return files;
}
}
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <string>
#include <unordered_map>
#define CHUNK 16384u
namespace utils::compression
{
namespace zlib
{
std::string compress(const std::string& data);
std::string decompress(const std::string& data);
}
namespace zip
{
class archive
{
public:
void add(std::string filename, std::string data);
bool write(const std::string& filename, const std::string& comment = {});
private:
std::unordered_map<std::string, std::string> files_;
};
std::unordered_map<std::string, std::string> extract(const std::string& data);
}
};

View File

@ -0,0 +1,46 @@
#pragma once
#include <mutex>
namespace utils::concurrency
{
template <typename T, typename MutexType = std::mutex>
class container
{
public:
template <typename R = void, typename F>
R access(F&& accessor) const
{
std::lock_guard<MutexType> _{mutex_};
return accessor(object_);
}
template <typename R = void, typename F>
R access(F&& accessor)
{
std::lock_guard<MutexType> _{mutex_};
return accessor(object_);
}
template <typename R = void, typename F>
R access_with_lock(F&& accessor) const
{
std::unique_lock<MutexType> lock{mutex_};
return accessor(object_, lock);
}
template <typename R = void, typename F>
R access_with_lock(F&& accessor)
{
std::unique_lock<MutexType> lock{mutex_};
return accessor(object_, lock);
}
T& get_raw() { return object_; }
const T& get_raw() const { return object_; }
private:
mutable MutexType mutex_{};
T object_{};
};
}

View File

@ -0,0 +1,640 @@
#include "string.hpp"
#include "cryptography.hpp"
#include "nt.hpp"
#include "finally.hpp"
#undef max
using namespace std::string_literals;
/// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf
namespace utils::cryptography
{
namespace
{
struct __
{
__()
{
ltc_mp = ltm_desc;
register_cipher(&aes_desc);
register_cipher(&des3_desc);
register_prng(&sprng_desc);
register_prng(&fortuna_desc);
register_prng(&yarrow_desc);
register_hash(&sha1_desc);
register_hash(&sha256_desc);
register_hash(&sha512_desc);
}
} ___;
[[maybe_unused]] const char* cs(const uint8_t* data)
{
return reinterpret_cast<const char*>(data);
}
[[maybe_unused]] char* cs(uint8_t* data)
{
return reinterpret_cast<char*>(data);
}
[[maybe_unused]] const uint8_t* cs(const char* data)
{
return reinterpret_cast<const uint8_t*>(data);
}
[[maybe_unused]] uint8_t* cs(char* data)
{
return reinterpret_cast<uint8_t*>(data);
}
[[maybe_unused]] unsigned long ul(const size_t value)
{
return static_cast<unsigned long>(value);
}
class prng
{
public:
prng(const ltc_prng_descriptor& descriptor, const bool autoseed = true)
: state_(std::make_unique<prng_state>())
, descriptor_(descriptor)
{
this->id_ = register_prng(&descriptor);
if (this->id_ == -1)
{
throw std::runtime_error("PRNG "s + this->descriptor_.name + " could not be registered!");
}
if (autoseed)
{
this->auto_seed();
}
else
{
this->descriptor_.start(this->state_.get());
}
}
~prng()
{
this->descriptor_.done(this->state_.get());
}
prng_state* get_state() const
{
this->descriptor_.ready(this->state_.get());
return this->state_.get();
}
int get_id() const
{
return this->id_;
}
void add_entropy(const void* data, const size_t length) const
{
this->descriptor_.add_entropy(static_cast<const uint8_t*>(data), ul(length), this->state_.get());
}
void read(void* data, const size_t length) const
{
this->descriptor_.read(static_cast<unsigned char*>(data), ul(length), this->get_state());
}
private:
int id_;
std::unique_ptr<prng_state> state_;
const ltc_prng_descriptor& descriptor_;
void auto_seed() const
{
rng_make_prng(128, this->id_, this->state_.get(), nullptr);
int i[4]; // uninitialized data
auto* i_ptr = &i;
this->add_entropy(reinterpret_cast<uint8_t*>(&i), sizeof(i));
this->add_entropy(reinterpret_cast<uint8_t*>(&i_ptr), sizeof(i_ptr));
auto t = time(nullptr);
this->add_entropy(reinterpret_cast<uint8_t*>(&t), sizeof(t));
}
};
const prng prng_(fortuna_desc);
}
ecc::key::key()
{
ZeroMemory(&this->key_storage_, sizeof(this->key_storage_));
}
ecc::key::~key()
{
this->free();
}
ecc::key::key(key&& obj) noexcept
: key()
{
this->operator=(std::move(obj));
}
ecc::key::key(const key& obj)
: key()
{
this->operator=(obj);
}
ecc::key& ecc::key::operator=(key&& obj) noexcept
{
if (this != &obj)
{
std::memmove(&this->key_storage_, &obj.key_storage_, sizeof(this->key_storage_));
ZeroMemory(&obj.key_storage_, sizeof(obj.key_storage_));
}
return *this;
}
ecc::key& ecc::key::operator=(const key& obj)
{
if (this != &obj && obj.is_valid())
{
this->deserialize(obj.serialize(obj.key_storage_.type));
}
return *this;
}
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_;
}
const ecc_key& ecc::key::get() const
{
return this->key_storage_;
}
std::string ecc::key::get_public_key() const
{
uint8_t buffer[512] = {0};
unsigned long length = sizeof(buffer);
if (ecc_ansi_x963_export(&this->key_storage_, buffer, &length) == CRYPT_OK)
{
return std::string(cs(buffer), length);
}
return {};
}
void ecc::key::set(const std::string& pub_key_buffer)
{
this->free();
if (ecc_ansi_x963_import(cs(pub_key_buffer.data()),
ul(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(cs(key.data()), ul(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};
unsigned long length = sizeof(buffer);
if (ecc_export(buffer, &length, type, &this->key_storage_) == CRYPT_OK)
{
return std::string(cs(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));
}
uint64_t ecc::key::get_hash() const
{
const auto hash = sha1::compute(this->get_public_key());
if (hash.size() >= 8)
{
return *reinterpret_cast<const uint64_t*>(hash.data());
}
return 0;
}
ecc::key ecc::generate_key(const int bits)
{
key key;
ecc_make_key(prng_.get_state(), prng_.get_id(), bits / 8, &key.get());
return key;
}
ecc::key ecc::generate_key(const int bits, const std::string& entropy)
{
key key{};
const prng yarrow(yarrow_desc, false);
yarrow.add_entropy(entropy.data(), entropy.size());
ecc_make_key(yarrow.get_state(), yarrow.get_id(), bits / 8, &key.get());
return key;
}
std::string ecc::sign_message(const key& key, const std::string& message)
{
if (!key.is_valid()) return "";
uint8_t buffer[512];
unsigned long length = sizeof(buffer);
ecc_sign_hash(cs(message.data()), ul(message.size()), buffer, &length, prng_.get_state(), prng_.get_id(),
&key.get());
return std::string(cs(buffer), length);
}
bool ecc::verify_message(const key& key, const std::string& message, const std::string& signature)
{
if (!key.is_valid()) return false;
auto result = 0;
return (ecc_verify_hash(cs(signature.data()),
ul(signature.size()),
cs(message.data()),
ul(message.size()), &result,
&key.get()) == CRYPT_OK && result != 0);
}
bool ecc::encrypt(const key& key, std::string& data)
{
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]()
{
return ecc_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len,
prng_.get_state(), prng_.get_id(), find_hash("sha512"), &key.get());
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW)
{
out_data.resize(out_len);
res = crypt();
}
if (res != CRYPT_OK)
{
return false;
}
out_data.resize(out_len);
data = std::move(out_data);
return true;
}
bool ecc::decrypt(const key& key, std::string& data)
{
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]()
{
return ecc_decrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, &key.get());
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW)
{
out_data.resize(out_len);
res = crypt();
}
if (res != CRYPT_OK)
{
return false;
}
out_data.resize(out_len);
data = std::move(out_data);
return true;
}
std::string rsa::encrypt(const std::string& data, const std::string& hash, const std::string& key)
{
rsa_key new_key;
rsa_import(cs(key.data()), ul(key.size()), &new_key);
const auto _ = finally([&]()
{
rsa_free(&new_key);
});
std::string out_data{};
out_data.resize(std::max(ul(data.size() * 3), ul(0x100)));
auto out_len = ul(out_data.size());
auto crypt = [&]()
{
return rsa_encrypt_key(cs(data.data()), ul(data.size()), cs(out_data.data()), &out_len, cs(hash.data()),
ul(hash.size()), prng_.get_state(), prng_.get_id(), find_hash("sha512"), &new_key);
};
auto res = crypt();
if (res == CRYPT_BUFFER_OVERFLOW)
{
out_data.resize(out_len);
res = crypt();
}
if (res == CRYPT_OK)
{
out_data.resize(out_len);
return out_data;
}
return {};
}
std::string des3::encrypt(const std::string& data, const std::string& iv, const std::string& key)
{
std::string enc_data;
enc_data.resize(data.size());
symmetric_CBC cbc;
const auto des3 = find_cipher("3des");
cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()), 0, &cbc);
cbc_encrypt(cs(data.data()), cs(enc_data.data()), ul(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)
{
std::string dec_data;
dec_data.resize(data.size());
symmetric_CBC cbc;
const auto des3 = find_cipher("3des");
cbc_start(des3, cs(iv.data()), cs(key.data()), static_cast<int>(key.size()), 0, &cbc);
cbc_decrypt(cs(data.data()), cs(dec_data.data()), ul(data.size()), &cbc);
cbc_done(&cbc);
return dec_data;
}
std::string tiger::compute(const std::string& data, const bool hex)
{
return compute(cs(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, ul(length));
tiger_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string aes::encrypt(const std::string& data, const std::string& iv, const std::string& key)
{
std::string enc_data;
enc_data.resize(data.size());
symmetric_CBC cbc;
const auto aes = find_cipher("aes");
cbc_start(aes, cs(iv.data()), cs(key.data()),
static_cast<int>(key.size()), 0, &cbc);
cbc_encrypt(cs(data.data()),
cs(enc_data.data()),
ul(data.size()), &cbc);
cbc_done(&cbc);
return enc_data;
}
std::string aes::decrypt(const std::string& data, const std::string& iv, const std::string& key)
{
std::string dec_data;
dec_data.resize(data.size());
symmetric_CBC cbc;
const auto aes = find_cipher("aes");
cbc_start(aes, cs(iv.data()), cs(key.data()),
static_cast<int>(key.size()), 0, &cbc);
cbc_decrypt(cs(data.data()),
cs(dec_data.data()),
ul(data.size()), &cbc);
cbc_done(&cbc);
return dec_data;
}
std::string hmac_sha1::compute(const std::string& data, const std::string& key)
{
std::string buffer;
buffer.resize(20);
hmac_state state;
hmac_init(&state, find_hash("sha1"), cs(key.data()), ul(key.size()));
hmac_process(&state, cs(data.data()), static_cast<int>(data.size()));
auto out_len = ul(buffer.size());
hmac_done(&state, cs(buffer.data()), &out_len);
buffer.resize(out_len);
return buffer;
}
std::string sha1::compute(const std::string& data, const bool hex)
{
return compute(cs(data.data()), data.size(), hex);
}
std::string sha1::compute(const uint8_t* data, const size_t length, const bool hex)
{
uint8_t buffer[20] = {0};
hash_state state;
sha1_init(&state);
sha1_process(&state, data, ul(length));
sha1_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string sha256::compute(const std::string& data, const bool hex)
{
return compute(cs(data.data()), data.size(), hex);
}
std::string sha256::compute(const uint8_t* data, const size_t length, const bool hex)
{
uint8_t buffer[32] = {0};
hash_state state;
sha256_init(&state);
sha256_process(&state, data, ul(length));
sha256_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string sha512::compute(const std::string& data, const bool hex)
{
return compute(cs(data.data()), data.size(), hex);
}
std::string sha512::compute(const uint8_t* data, const size_t length, const bool hex)
{
uint8_t buffer[64] = {0};
hash_state state;
sha512_init(&state);
sha512_process(&state, data, ul(length));
sha512_done(&state, buffer);
std::string hash(cs(buffer), sizeof(buffer));
if (!hex) return hash;
return string::dump_hex(hash, "");
}
std::string base64::encode(const uint8_t* data, const size_t len)
{
std::string result;
result.resize((len + 2) * 2);
auto out_len = ul(result.size());
if (base64_encode(data, ul(len), result.data(), &out_len) != CRYPT_OK)
{
return {};
}
result.resize(out_len);
return result;
}
std::string base64::encode(const std::string& data)
{
return base64::encode(cs(data.data()), static_cast<unsigned>(data.size()));
}
std::string base64::decode(const std::string& data)
{
std::string result;
result.resize((data.size() + 2) * 2);
auto out_len = ul(result.size());
if (base64_decode(data.data(), ul(data.size()), cs(result.data()), &out_len) != CRYPT_OK)
{
return {};
}
result.resize(out_len);
return result;
}
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, const 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;
}
uint32_t random::get_integer()
{
uint32_t result;
random::get_data(&result, sizeof(result));
return result;
}
std::string random::get_challenge()
{
std::string result;
result.resize(sizeof(uint32_t));
random::get_data(result.data(), result.size());
return string::dump_hex(result, "");
}
void random::get_data(void* data, const size_t size)
{
prng_.read(data, size);
}
}

View File

@ -0,0 +1,118 @@
#pragma once
#include <string>
#include <tomcrypt.h>
namespace utils::cryptography
{
namespace ecc
{
class key final
{
public:
key();
~key();
key(key&& obj) noexcept;
key(const key& obj);
key& operator=(key&& obj) noexcept;
key& operator=(const key& obj);
bool is_valid() const;
ecc_key& get();
const ecc_key& get() const;
std::string get_public_key() const;
void set(const std::string& pub_key_buffer);
void deserialize(const std::string& key);
std::string serialize(int type = PK_PRIVATE) const;
void free();
bool operator==(key& key) const;
uint64_t get_hash() const;
private:
ecc_key key_storage_{};
};
key generate_key(int bits);
key generate_key(int bits, const std::string& entropy);
std::string sign_message(const key& key, const std::string& message);
bool verify_message(const key& key, const std::string& message, const std::string& signature);
bool encrypt(const key& key, std::string& data);
bool decrypt(const key& key, std::string& data);
}
namespace rsa
{
std::string encrypt(const std::string& data, const std::string& hash, const std::string& key);
}
namespace des3
{
std::string encrypt(const std::string& data, const std::string& iv, const std::string& key);
std::string decrypt(const std::string& data, const std::string& iv, const std::string& key);
}
namespace tiger
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const uint8_t* data, size_t length, bool hex = false);
}
namespace aes
{
std::string encrypt(const std::string& data, const std::string& iv, const std::string& key);
std::string decrypt(const std::string& data, const std::string& iv, const std::string& key);
}
namespace hmac_sha1
{
std::string compute(const std::string& data, const std::string& key);
}
namespace sha1
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const uint8_t* data, size_t length, bool hex = false);
}
namespace sha256
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const uint8_t* data, size_t length, bool hex = false);
}
namespace sha512
{
std::string compute(const std::string& data, bool hex = false);
std::string compute(const uint8_t* data, size_t length, bool hex = false);
}
namespace base64
{
std::string encode(const uint8_t* data, size_t len);
std::string encode(const std::string& data);
std::string decode(const std::string& data);
}
namespace jenkins_one_at_a_time
{
unsigned int compute(const std::string& data);
unsigned int compute(const char* key, size_t len);
};
namespace random
{
uint32_t get_integer();
std::string get_challenge();
void get_data(void* data, size_t size);
}
}

View File

@ -0,0 +1,54 @@
#pragma once
#include <type_traits>
namespace utils
{
/*
* Copied from here: https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57
*/
template <class F>
class final_action
{
public:
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value &&
!std::is_volatile<F>::value,
"Final_action should store its callable by value");
explicit final_action(F f) noexcept : f_(std::move(f))
{
}
final_action(final_action&& other) noexcept
: f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false))
{
}
final_action(const final_action&) = delete;
final_action& operator=(const final_action&) = delete;
final_action& operator=(final_action&&) = delete;
~final_action() noexcept
{
if (invoke_) f_();
}
// Added by momo5502
void cancel()
{
invoke_ = false;
}
private:
F f_;
bool invoke_{true};
};
template <class F>
final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>
finally(F&& f) noexcept
{
return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>(
std::forward<F>(f));
}
}

View File

@ -0,0 +1,53 @@
#include "flags.hpp"
#include "string.hpp"
#include "nt.hpp"
#include <shellapi.h>
namespace utils::flags
{
void parse_flags(std::vector<std::string>& flags)
{
int num_args;
auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args);
flags.clear();
if (argv)
{
for (auto i = 0; i < num_args; ++i)
{
std::wstring wide_flag(argv[i]);
if (wide_flag[0] == L'-')
{
wide_flag.erase(wide_flag.begin());
flags.emplace_back(string::convert(wide_flag));
}
}
LocalFree(argv);
}
}
bool has_flag(const std::string& flag)
{
static auto parsed = false;
static std::vector<std::string> enabled_flags;
if (!parsed)
{
parse_flags(enabled_flags);
parsed = true;
}
for (const auto& entry : enabled_flags)
{
if (string::to_lower(entry) == string::to_lower(flag))
{
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,8 @@
#pragma once
#include <string>
namespace utils::flags
{
bool has_flag(const std::string& flag);
}

View File

@ -0,0 +1,139 @@
#include "hardware_breakpoint.hpp"
#include "thread.hpp"
namespace utils::hardware_breakpoint
{
namespace
{
void set_bits(uint64_t& value, const uint32_t bit_index, const uint32_t bits, const uint64_t new_value)
{
const uint64_t range_mask = (1ull << bits) - 1ull;
const uint64_t full_mask = ~(range_mask << bit_index);
value = (value & full_mask) | (new_value << bit_index);
}
void validate_index(const uint32_t index)
{
if (index >= 4)
{
throw std::runtime_error("Invalid index");
}
}
uint32_t translate_length(const uint32_t length)
{
if (length != 1 && length != 2 && length != 4)
{
throw std::runtime_error("Invalid length");
}
return length - 1;
}
class debug_context
{
public:
debug_context(uint32_t thread_id)
: handle_(thread_id, THREAD_SET_CONTEXT | THREAD_GET_CONTEXT)
{
if (!this->handle_)
{
throw std::runtime_error("Unable to access thread");
}
this->context_.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (!GetThreadContext(this->handle_, &this->context_))
{
throw std::runtime_error("Unable to get thread context");
}
}
~debug_context()
{
SetThreadContext(this->handle_, &this->context_);
}
debug_context(const debug_context&) = delete;
debug_context& operator=(const debug_context&) = delete;
debug_context(debug_context&& obj) noexcept = delete;
debug_context& operator=(debug_context&& obj) noexcept = delete;
CONTEXT* operator->()
{
return &this->context_;
}
operator CONTEXT&()
{
return this->context_;
}
private:
thread::handle handle_;
CONTEXT context_{};
};
uint32_t find_free_index(const CONTEXT& context)
{
for (uint32_t i = 0; i < 4; ++i)
{
if ((context.Dr7 & (1ull << (i << 1ull))) == 0)
{
return i;
}
}
throw std::runtime_error("No free index");
}
}
uint32_t activate(const uint64_t address, uint32_t length, const condition cond, CONTEXT& context)
{
const auto index = find_free_index(context);
length = translate_length(length);
(&context.Dr0)[index] = address;
set_bits(context.Dr7, 16 + (index << 2ull), 2, cond);
set_bits(context.Dr7, 18 + (index << 2ull), 2, length);
set_bits(context.Dr7, index << 1ull, 1, 1);
return index;
}
uint32_t activate(void* address, const uint32_t length, const condition cond, const uint32_t thread_id)
{
return activate(reinterpret_cast<uint64_t>(address), length, cond, thread_id);
}
uint32_t activate(const uint64_t address, const uint32_t length, const condition cond, const uint32_t thread_id)
{
debug_context context(thread_id);
return activate(address, length, cond, context);
}
void deactivate(const uint32_t index, CONTEXT& context)
{
validate_index(index);
set_bits(context.Dr7, index << 1ull, 1, 0);
}
void deactivate(const uint32_t index, const uint32_t thread_id)
{
debug_context context(thread_id);
deactivate(index, context);
}
void deactivate_all(CONTEXT& context)
{
context.Dr7 = 0;
}
void deactivate_all(const uint32_t thread_id)
{
debug_context context(thread_id);
deactivate_all(context);
}
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <thread>
#include "nt.hpp"
namespace utils::hardware_breakpoint
{
enum condition
{
execute = 0,
write = 1,
read_write = 3
};
uint32_t activate(uint64_t address, uint32_t length, condition cond, CONTEXT& context);
uint32_t activate(void* address, uint32_t length, condition cond, uint32_t thread_id = GetCurrentThreadId());
uint32_t activate(uint64_t address, uint32_t length, condition cond, uint32_t thread_id = GetCurrentThreadId());
void deactivate(uint32_t index, CONTEXT& context);
void deactivate(uint32_t index, uint32_t thread_id = GetCurrentThreadId());
void deactivate_all(CONTEXT& context);
void deactivate_all(uint32_t thread_id = GetCurrentThreadId());
}

View File

@ -0,0 +1,619 @@
#include "hook.hpp"
#include <map>
#include <MinHook.h>
#include "concurrency.hpp"
#include "string.hpp"
#include "nt.hpp"
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace utils::hook
{
namespace
{
uint8_t* allocate_somewhere_near(const void* base_address, const size_t size)
{
size_t offset = 0;
while (true)
{
offset += size;
auto* target_address = static_cast<const uint8_t*>(base_address) - offset;
if (is_relatively_far(base_address, target_address))
{
return nullptr;
}
const auto res = VirtualAlloc(const_cast<uint8_t*>(target_address), size, MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (res)
{
if (is_relatively_far(base_address, target_address))
{
VirtualFree(res, 0, MEM_RELEASE);
return nullptr;
}
return static_cast<uint8_t*>(res);
}
}
}
class memory
{
public:
memory() = default;
memory(const void* ptr)
: memory()
{
this->length_ = 0x1000;
this->buffer_ = allocate_somewhere_near(ptr, this->length_);
if (!this->buffer_)
{
throw std::runtime_error("Failed to allocate");
}
}
~memory()
{
if (this->buffer_)
{
VirtualFree(this->buffer_, 0, MEM_RELEASE);
}
}
memory(memory&& obj) noexcept
: memory()
{
this->operator=(std::move(obj));
}
memory& operator=(memory&& obj) noexcept
{
if (this != &obj)
{
this->~memory();
this->buffer_ = obj.buffer_;
this->length_ = obj.length_;
this->offset_ = obj.offset_;
obj.buffer_ = nullptr;
obj.length_ = 0;
obj.offset_ = 0;
}
return *this;
}
void* allocate(const size_t length)
{
if (!this->buffer_)
{
return nullptr;
}
if (this->offset_ + length > this->length_)
{
return nullptr;
}
const auto ptr = this->get_ptr();
this->offset_ += length;
return ptr;
}
void* get_ptr() const
{
return this->buffer_ + this->offset_;
}
private:
uint8_t* buffer_{};
size_t length_{};
size_t offset_{};
};
void* get_memory_near(const void* address, const size_t size)
{
static concurrency::container<std::vector<memory>> memory_container{};
return memory_container.access<void*>([&](std::vector<memory>& memories)
{
for (auto& memory : memories)
{
if (!is_relatively_far(address, memory.get_ptr()))
{
const auto buffer = memory.allocate(size);
if (buffer)
{
return buffer;
}
}
}
memories.emplace_back(address);
return memories.back().allocate(size);
});
}
concurrency::container<std::map<const void*, uint8_t>>& get_original_data_map()
{
static concurrency::container<std::map<const void*, uint8_t>> og_data{};
return og_data;
}
void store_original_data(const void* /*data*/, size_t /*length*/)
{
/*get_original_data_map().access([data, length](std::map<const void*, uint8_t>& og_map)
{
const auto data_ptr = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < length; ++i)
{
const auto pos = data_ptr + i;
if (!og_map.contains(pos))
{
og_map[pos] = *pos;
}
}
});*/
}
void* initialize_min_hook()
{
static class min_hook_init
{
public:
min_hook_init()
{
if (MH_Initialize() != MH_OK)
{
throw std::runtime_error("Failed to initialize MinHook");
}
}
~min_hook_init()
{
MH_Uninitialize();
}
} min_hook_init;
return &min_hook_init;
}
}
void assembler::pushad64()
{
this->push(rax);
this->push(rcx);
this->push(rdx);
this->push(rbx);
this->push(rsp);
this->push(rbp);
this->push(rsi);
this->push(rdi);
this->sub(rsp, 0x40);
}
void assembler::popad64()
{
this->add(rsp, 0x40);
this->pop(rdi);
this->pop(rsi);
this->pop(rbp);
this->pop(rsp);
this->pop(rbx);
this->pop(rdx);
this->pop(rcx);
this->pop(rax);
}
void assembler::prepare_stack_for_call()
{
const auto reserve_callee_space = this->newLabel();
const auto stack_unaligned = this->newLabel();
this->test(rsp, 0xF);
this->jnz(stack_unaligned);
this->sub(rsp, 0x8);
this->push(rsp);
this->push(rax);
this->mov(rax, ptr(rsp, 8, 8));
this->add(rax, 0x8);
this->mov(ptr(rsp, 8, 8), rax);
this->pop(rax);
this->jmp(reserve_callee_space);
this->bind(stack_unaligned);
this->push(rsp);
this->bind(reserve_callee_space);
this->sub(rsp, 0x40);
}
void assembler::restore_stack_after_call()
{
this->lea(rsp, ptr(rsp, 0x40));
this->pop(rsp);
}
asmjit::Error assembler::call(void* target)
{
return Assembler::call(size_t(target));
}
asmjit::Error assembler::jmp(void* target)
{
return Assembler::jmp(size_t(target));
}
detour::detour()
{
(void)initialize_min_hook();
}
detour::detour(const size_t place, void* target)
: detour(reinterpret_cast<void*>(place), target)
{
}
detour::detour(void* place, void* target)
: detour()
{
this->create(place, target);
}
detour::~detour()
{
this->clear();
}
void detour::enable()
{
MH_EnableHook(this->place_);
if (!this->moved_data_.empty())
{
this->move();
}
}
void detour::disable()
{
this->un_move();
MH_DisableHook(this->place_);
}
void detour::create(void* place, void* target)
{
this->clear();
this->place_ = place;
store_original_data(place, 14);
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK)
{
throw std::runtime_error(string::va("Unable to create hook at location: %p", this->place_));
}
this->enable();
}
void detour::create(const size_t place, void* target)
{
this->create(reinterpret_cast<void*>(place), target);
}
void detour::clear()
{
if (this->place_)
{
this->un_move();
MH_RemoveHook(this->place_);
}
this->place_ = nullptr;
this->original_ = nullptr;
this->moved_data_ = {};
}
void detour::move()
{
this->moved_data_ = move_hook(this->place_);
}
void* detour::get_place() const
{
return this->place_;
}
void* detour::get_original() const
{
return this->original_;
}
void detour::un_move()
{
if (!this->moved_data_.empty())
{
copy(this->place_, this->moved_data_.data(), this->moved_data_.size());
}
}
std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub)
{
if (!library.is_valid()) return {};
auto* const ptr = library.get_iat_entry(target_library, process);
if (!ptr) return {};
store_original_data(ptr, sizeof(*ptr));
DWORD protect;
VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect);
std::swap(*ptr, stub);
VirtualProtect(ptr, sizeof(*ptr), protect, &protect);
return {{ptr, stub}};
}
void nop(void* place, const size_t length)
{
store_original_data(place, length);
DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
std::memset(place, 0x90, length);
VirtualProtect(place, length, old_protect, &old_protect);
FlushInstructionCache(GetCurrentProcess(), place, length);
}
void nop(const size_t place, const size_t length)
{
nop(reinterpret_cast<void*>(place), length);
}
void copy(void* place, const void* data, const size_t length)
{
store_original_data(place, length);
DWORD old_protect{};
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
std::memmove(place, data, length);
VirtualProtect(place, length, old_protect, &old_protect);
FlushInstructionCache(GetCurrentProcess(), place, length);
}
void copy(const size_t place, const void* data, const size_t length)
{
copy(reinterpret_cast<void*>(place), data, length);
}
void copy_string(void* place, const char* str)
{
copy(reinterpret_cast<void*>(place), str, strlen(str) + 1);
}
void copy_string(const size_t place, const char* str)
{
copy_string(reinterpret_cast<void*>(place), str);
}
bool is_relatively_far(const void* pointer, const void* data, const int offset)
{
const int64_t diff = size_t(data) - (size_t(pointer) + offset);
const auto small_diff = int32_t(diff);
return diff != int64_t(small_diff);
}
void call(void* pointer, void* data)
{
if (is_relatively_far(pointer, data))
{
auto* trampoline = get_memory_near(pointer, 14);
if (!trampoline)
{
throw std::runtime_error("Too far away to create 32bit relative branch");
}
call(pointer, trampoline);
jump(trampoline, data, true, true);
return;
}
uint8_t copy_data[5];
copy_data[0] = 0xE8;
*reinterpret_cast<int32_t*>(&copy_data[1]) = int32_t(size_t(data) - (size_t(pointer) + 5));
auto* patch_pointer = PBYTE(pointer);
copy(patch_pointer, copy_data, sizeof(copy_data));
}
void call(const size_t pointer, void* data)
{
return call(reinterpret_cast<void*>(pointer), data);
}
void call(const size_t pointer, const size_t data)
{
return call(pointer, reinterpret_cast<void*>(data));
}
void jump(void* pointer, void* data, const bool use_far, const bool use_safe)
{
static const unsigned char jump_data[] = {
0x48, 0xb8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xff, 0xe0
};
static const unsigned char jump_data_safe[] = {
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00
};
if (!use_far && is_relatively_far(pointer, data))
{
auto* trampoline = get_memory_near(pointer, 14);
if (!trampoline)
{
throw std::runtime_error("Too far away to create 32bit relative branch");
}
jump(pointer, trampoline, false, false);
jump(trampoline, data, true, true);
return;
}
auto* patch_pointer = PBYTE(pointer);
if (use_far)
{
if (use_safe)
{
uint8_t copy_data[sizeof(jump_data_safe) + sizeof(data)];
memcpy(copy_data, jump_data_safe, sizeof(jump_data_safe));
memcpy(copy_data + sizeof(jump_data_safe), &data, sizeof(data));
copy(patch_pointer, copy_data, sizeof(copy_data));
}
else
{
uint8_t copy_data[sizeof(jump_data)];
memcpy(copy_data, jump_data, sizeof(jump_data));
memcpy(copy_data + 2, &data, sizeof(data));
copy(patch_pointer, copy_data, sizeof(copy_data));
}
}
else
{
uint8_t copy_data[5];
copy_data[0] = 0xE9;
*reinterpret_cast<int32_t*>(&copy_data[1]) = int32_t(size_t(data) - (size_t(pointer) + 5));
copy(patch_pointer, copy_data, sizeof(copy_data));
}
}
void jump(const size_t pointer, void* data, const bool use_far, const bool use_safe)
{
return jump(reinterpret_cast<void*>(pointer), data, use_far, use_safe);
}
void jump(const size_t pointer, const size_t data, const bool use_far, const bool use_safe)
{
return jump(pointer, reinterpret_cast<void*>(data), use_far, use_safe);
}
void* assemble(const std::function<void(assembler&)>& asm_function)
{
static asmjit::JitRuntime runtime;
asmjit::CodeHolder code;
code.init(runtime.environment());
assembler a(&code);
asm_function(a);
void* result = nullptr;
runtime.add(&result, &code);
return result;
}
void inject(void* pointer, const void* data)
{
if (is_relatively_far(pointer, data, 4))
{
throw std::runtime_error("Too far away to create 32bit relative branch");
}
set<int32_t>(pointer, int32_t(size_t(data) - (size_t(pointer) + 4)));
}
void inject(const size_t pointer, const void* data)
{
return inject(reinterpret_cast<void*>(pointer), data);
}
std::vector<uint8_t> move_hook(void* pointer)
{
std::vector<uint8_t> original_data{};
auto* data_ptr = static_cast<uint8_t*>(pointer);
if (data_ptr[0] == 0xE9)
{
original_data.resize(6);
memmove(original_data.data(), pointer, original_data.size());
auto* target = follow_branch(data_ptr);
nop(data_ptr, 1);
jump(data_ptr + 1, target);
}
else if (data_ptr[0] == 0xFF && data_ptr[1] == 0x25)
{
original_data.resize(15);
memmove(original_data.data(), pointer, original_data.size());
copy(data_ptr + 1, data_ptr, 14);
nop(data_ptr, 1);
}
else
{
throw std::runtime_error("No branch instruction found");
}
return original_data;
}
std::vector<uint8_t> move_hook(const size_t pointer)
{
return move_hook(reinterpret_cast<void*>(pointer));
}
void* follow_branch(void* address)
{
auto* const data = static_cast<uint8_t*>(address);
if (*data != 0xE8 && *data != 0xE9)
{
throw std::runtime_error("No branch instruction found");
}
return extract<void*>(data + 1);
}
std::vector<uint8_t> query_original_data(const void* data, const size_t length)
{
std::vector<uint8_t> og_data{};
og_data.resize(length);
memcpy(og_data.data(), data, length);
get_original_data_map().access([data, length, &og_data](const std::map<const void*, uint8_t>& og_map)
{
auto* ptr = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < length; ++i)
{
auto entry = og_map.find(ptr + i);
if (entry != og_map.end())
{
og_data[i] = entry->second;
}
}
});
return og_data;
}
}

View File

@ -0,0 +1,216 @@
#pragma once
#include "signature.hpp"
#include <asmjit/core/jitruntime.h>
#include <asmjit/x86/x86assembler.h>
using namespace asmjit::x86;
namespace utils::hook
{
namespace detail
{
template <size_t Entries>
std::vector<size_t(*)()> get_iota_functions()
{
if constexpr (Entries == 0)
{
std::vector<size_t(*)()> functions;
return functions;
}
else
{
auto functions = get_iota_functions<Entries - 1>();
functions.emplace_back([]()
{
return Entries - 1;
});
return functions;
}
}
}
// Gets the pointer to the entry in the v-table.
// It seems otherwise impossible to get this.
// This is ugly as fuck and only safely works on x64
// Example:
// ID3D11Device* device = ...
// auto entry = get_vtable_entry(device, &ID3D11Device::CreateTexture2D);
template <size_t Entries = 100, typename Class, typename T, typename... Args>
void** get_vtable_entry(Class* obj, T (Class::* entry)(Args ...))
{
union
{
decltype(entry) func;
void* pointer;
};
func = entry;
auto iota_functions = detail::get_iota_functions<Entries>();
auto* object = iota_functions.data();
using fake_func = size_t(__thiscall*)(void* self);
auto index = static_cast<fake_func>(pointer)(&object);
void** obj_v_table = *reinterpret_cast<void***>(obj);
return &obj_v_table[index];
}
class assembler : public Assembler
{
public:
using Assembler::Assembler;
using Assembler::call;
using Assembler::jmp;
void pushad64();
void popad64();
void prepare_stack_for_call();
void restore_stack_after_call();
template <typename T>
void call_aligned(T&& target)
{
this->prepare_stack_for_call();
this->call(std::forward<T>(target));
this->restore_stack_after_call();
}
asmjit::Error call(void* target);
asmjit::Error jmp(void* target);
};
class detour
{
public:
detour();
detour(void* place, void* target);
detour(size_t place, void* target);
~detour();
detour(detour&& other) noexcept
{
this->operator=(std::move(other));
}
detour& operator=(detour&& other) noexcept
{
if (this != &other)
{
this->clear();
this->place_ = other.place_;
this->original_ = other.original_;
this->moved_data_ = other.moved_data_;
other.place_ = nullptr;
other.original_ = nullptr;
other.moved_data_ = {};
}
return *this;
}
detour(const detour&) = delete;
detour& operator=(const detour&) = delete;
void enable();
void disable();
void create(void* place, void* target);
void create(size_t place, void* target);
void clear();
void move();
void* get_place() const;
template <typename T>
T* get() const
{
return static_cast<T*>(this->get_original());
}
template <typename T = void, typename... Args>
T invoke(Args ... args)
{
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
}
[[nodiscard]] void* get_original() const;
private:
std::vector<uint8_t> moved_data_{};
void* place_{};
void* original_{};
void un_move();
};
std::optional<std::pair<void*, void*>> iat(const nt::library& library, const std::string& target_library, const std::string& process, void* stub);
void nop(void* place, size_t length);
void nop(size_t place, size_t length);
void copy(void* place, const void* data, size_t length);
void copy(size_t place, const void* data, size_t length);
void copy_string(void* place, const char* str);
void copy_string(size_t place, const char* str);
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
void call(void* pointer, void* data);
void call(size_t pointer, void* data);
void call(size_t pointer, size_t data);
void jump(void* pointer, void* data, bool use_far = false, bool use_safe = false);
void jump(size_t pointer, void* data, bool use_far = false, bool use_safe = false);
void jump(size_t pointer, size_t data, bool use_far = false, bool use_safe = false);
void* assemble(const std::function<void(assembler&)>& asm_function);
void inject(void* pointer, const void* data);
void inject(size_t pointer, const void* data);
std::vector<uint8_t> move_hook(void* pointer);
std::vector<uint8_t> move_hook(size_t pointer);
template <typename T>
T extract(void* address)
{
auto* const data = static_cast<uint8_t*>(address);
const auto offset = *reinterpret_cast<int32_t*>(data);
return reinterpret_cast<T>(data + offset + 4);
}
void* follow_branch(void* address);
template <typename T>
static void set(void* place, T value = false)
{
copy(place, &value, sizeof(value));
}
template <typename T>
static void set(const size_t place, T value = false)
{
return set<T>(reinterpret_cast<void*>(place), value);
}
template <typename T, typename... Args>
static T invoke(size_t func, Args ... args)
{
return reinterpret_cast<T(*)(Args ...)>(func)(args...);
}
template <typename T, typename... Args>
static T invoke(void* func, Args ... args)
{
return static_cast<T(*)(Args ...)>(func)(args...);
}
std::vector<uint8_t> query_original_data(const void* data, size_t length);
}

View File

@ -0,0 +1,48 @@
#include "http.hpp"
#include "nt.hpp"
#include <atlcomcli.h>
namespace utils::http
{
std::optional<std::string> get_data(const std::string& url)
{
CComPtr<IStream> stream;
if (FAILED(URLOpenBlockingStreamA(nullptr, url.data(), &stream, 0, nullptr)))
{
return {};
}
char buffer[0x1000];
std::string result;
HRESULT status{};
do
{
DWORD bytes_read = 0;
status = stream->Read(buffer, sizeof(buffer), &bytes_read);
if (bytes_read > 0)
{
result.append(buffer, bytes_read);
}
}
while (SUCCEEDED(status) && status != S_FALSE);
if (FAILED(status))
{
return {};
}
return {result};
}
std::future<std::optional<std::string>> get_data_async(const std::string& url)
{
return std::async(std::launch::async, [url]()
{
return get_data(url);
});
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
#include <optional>
#include <future>
namespace utils::http
{
std::optional<std::string> get_data(const std::string& url);
std::future<std::optional<std::string>> get_data_async(const std::string& url);
}

View File

@ -0,0 +1,56 @@
#include "image.hpp"
#include <stdexcept>
#pragma warning(push)
#pragma warning(disable: 4100)
#define STBI_ONLY_JPEG
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#pragma warning(pop)
#include "finally.hpp"
namespace utils::image
{
image load_image(const std::string& data)
{
stbi_uc* buffer{};
const auto _ = finally([&]
{
if (buffer)
{
stbi_image_free(buffer);
}
});
constexpr int channels = 4;
int x, y, channels_in_file;
buffer = stbi_load_from_memory(reinterpret_cast<const uint8_t*>(data.data()),
static_cast<int>(data.size()), &x, &y, &channels_in_file, channels);
if (!buffer)
{
throw std::runtime_error("Failed to load image");
}
image res{};
res.width = static_cast<size_t>(x);
res.height = static_cast<size_t>(y);
res.data.assign(reinterpret_cast<const char*>(buffer), res.width * res.height * channels);
return res;
}
object create_bitmap(const image& img)
{
auto copy = img.data;
for (size_t i = 0; i < (img.width * img.height); ++i)
{
auto& r = copy[i * 4 + 0];
auto& b = copy[i * 4 + 2];
std::swap(r, b);
}
return CreateBitmap(static_cast<int>(img.width), static_cast<int>(img.height), 4, 8, copy.data());
}
}

View File

@ -0,0 +1,89 @@
#pragma once
#include <string>
#include "nt.hpp"
namespace utils::image
{
struct image
{
size_t width;
size_t height;
std::string data;
};
class object
{
public:
object() = default;
object(const HGDIOBJ h)
: handle_(h)
{
}
~object()
{
if (*this)
{
DeleteObject(this->handle_);
this->handle_ = nullptr;
}
}
object(const object&) = delete;
object& operator=(const object&) = delete;
object(object&& obj) noexcept
: object()
{
this->operator=(std::move(obj));
}
object& operator=(object&& obj) noexcept
{
if (this != &obj)
{
this->~object();
this->handle_ = obj.handle_;
obj.handle_ = nullptr;
}
return *this;
}
object& operator=(HANDLE h) noexcept
{
this->~object();
this->handle_ = h;
return *this;
}
HGDIOBJ get() const
{
return this->handle_;
}
operator bool() const
{
return this->handle_ != nullptr;
}
operator HGDIOBJ() const
{
return this->handle_;
}
operator LPARAM() const
{
return reinterpret_cast<LPARAM>(this->handle_);
}
private:
HGDIOBJ handle_{nullptr};
};
image load_image(const std::string& data);
object create_bitmap(const image& img);
}

View File

@ -0,0 +1,65 @@
#include "info_string.hpp"
#include "string.hpp"
namespace utils
{
info_string::info_string(const std::string& buffer)
{
this->parse(buffer);
}
info_string::info_string(const std::string_view& buffer)
: info_string(std::string{buffer})
{
}
void info_string::set(const std::string& key, const std::string& value)
{
this->key_value_pairs_[key] = value;
}
std::string info_string::get(const std::string& key) const
{
const auto value = this->key_value_pairs_.find(key);
if (value != this->key_value_pairs_.end())
{
return value->second;
}
return "";
}
void info_string::parse(std::string buffer)
{
if (buffer[0] == '\\')
{
buffer = buffer.substr(1);
}
auto key_values = string::split(buffer, '\\');
for (size_t i = 0; !key_values.empty() && i < (key_values.size() - 1); i += 2)
{
const auto& key = key_values[i];
const auto& value = key_values[i + 1];
this->key_value_pairs_[key] = value;
}
}
std::string info_string::build() const
{
//auto first = true;
std::string info_string;
for (auto i = this->key_value_pairs_.begin(); i != this->key_value_pairs_.end(); ++i)
{
//if (first) first = false;
/*else*/
info_string.append("\\");
info_string.append(i->first); // Key
info_string.append("\\");
info_string.append(i->second); // Value
}
return info_string;
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <string>
#include <unordered_map>
namespace utils
{
class info_string
{
public:
info_string() = default;
info_string(const std::string& buffer);
info_string(const std::string_view& buffer);
void set(const std::string& key, const std::string& value);
std::string get(const std::string& key) const;
std::string build() const;
private:
std::unordered_map<std::string, std::string> key_value_pairs_{};
void parse(std::string buffer);
};
}

View File

@ -0,0 +1,130 @@
#include "io.hpp"
#include "nt.hpp"
#include <fstream>
namespace utils::io
{
bool remove_file(const std::string& file)
{
if(DeleteFileA(file.data()) != FALSE)
{
return true;
}
return GetLastError() == ERROR_FILE_NOT_FOUND;
}
bool move_file(const std::string& src, const std::string& target)
{
return MoveFileA(src.data(), target.data()) == TRUE;
}
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 : 0));
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::filesystem::create_directories(directory);
}
bool directory_exists(const std::string& directory)
{
return std::filesystem::is_directory(directory);
}
bool directory_is_empty(const std::string& directory)
{
return std::filesystem::is_empty(directory);
}
std::vector<std::string> list_files(const std::string& directory)
{
std::vector<std::string> files;
for (auto& file : std::filesystem::directory_iterator(directory))
{
files.push_back(file.path().generic_string());
}
return files;
}
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
{
std::filesystem::copy(src, target,
std::filesystem::copy_options::overwrite_existing |
std::filesystem::copy_options::recursive);
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <vector>
#include <filesystem>
namespace utils::io
{
bool remove_file(const std::string& file);
bool move_file(const std::string& src, const std::string& target);
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);
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
}

View File

@ -0,0 +1,173 @@
#include "memory.hpp"
#include "nt.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;
}
bool memory::allocator::find(const void* data)
{
std::lock_guard _(this->mutex_);
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
return j != this->pool_.end();
}
void* memory::allocate(const size_t length)
{
return calloc(length, 1);
}
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 = static_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;
}
bool memory::is_rdata_ptr(void* pointer)
{
const std::string rdata = ".rdata";
const auto pointer_lib = utils::nt::library::get_by_address(pointer);
for (const auto& section : pointer_lib.get_section_headers())
{
const auto size = sizeof(section->Name);
char name[size + 1];
name[size] = 0;
std::memcpy(name, section->Name, size);
if (name == rdata)
{
const auto target = size_t(pointer);
const size_t source_start = size_t(pointer_lib.get_ptr()) + section->PointerToRawData;
const size_t source_end = source_start + section->SizeOfRawData;
return target >= source_start && target <= source_end;
}
}
return false;
}
memory::allocator* memory::get_allocator()
{
return &memory::mem_allocator_;
}
}

View File

@ -0,0 +1,77 @@
#pragma once
#include <mutex>
#include <vector>
namespace utils
{
class memory final
{
public:
class allocator final
{
public:
~allocator();
void clear();
void free(void* data);
void free(const void* data);
void* allocate(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);
bool find(const void* data);
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 bool is_rdata_ptr(void* ptr);
static allocator* get_allocator();
private:
static allocator mem_allocator_;
};
}

View File

@ -0,0 +1,278 @@
#include "nt.hpp"
namespace utils::nt
{
library library::load(const char* name)
{
return library(LoadLibraryA(name));
}
library library::load(const std::string& name)
{
return library::load(name.data());
}
library library::load(const std::filesystem::path& path)
{
return library::load(path.generic_string());
}
library library::get_by_address(const void* address)
{
HMODULE handle = nullptr;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
static_cast<LPCSTR>(address), &handle);
return library(handle);
}
library::library()
{
this->module_ = GetModuleHandleA(nullptr);
}
library::library(const std::string& name)
{
this->module_ = GetModuleHandleA(name.data());
}
library::library(const HMODULE handle)
{
this->module_ = handle;
}
bool library::operator==(const library& obj) const
{
return this->module_ == obj.module_;
}
library::operator bool() const
{
return this->is_valid();
}
library::operator HMODULE() const
{
return this->get_handle();
}
PIMAGE_NT_HEADERS library::get_nt_headers() const
{
if (!this->is_valid()) return nullptr;
return reinterpret_cast<PIMAGE_NT_HEADERS>(this->get_ptr() + this->get_dos_header()->e_lfanew);
}
PIMAGE_DOS_HEADER library::get_dos_header() const
{
return reinterpret_cast<PIMAGE_DOS_HEADER>(this->get_ptr());
}
PIMAGE_OPTIONAL_HEADER library::get_optional_header() const
{
if (!this->is_valid()) return nullptr;
return &this->get_nt_headers()->OptionalHeader;
}
std::vector<PIMAGE_SECTION_HEADER> library::get_section_headers() const
{
std::vector<PIMAGE_SECTION_HEADER> headers;
auto nt_headers = this->get_nt_headers();
auto section = IMAGE_FIRST_SECTION(nt_headers);
for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section)
{
if (section) headers.push_back(section);
else OutputDebugStringA("There was an invalid section :O");
}
return headers;
}
std::uint8_t* library::get_ptr() const
{
return reinterpret_cast<std::uint8_t*>(this->module_);
}
void library::unprotect() const
{
if (!this->is_valid()) return;
DWORD protection;
VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE,
&protection);
}
size_t library::get_relative_entry_point() const
{
if (!this->is_valid()) return 0;
return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint;
}
void* library::get_entry_point() const
{
if (!this->is_valid()) return nullptr;
return this->get_ptr() + this->get_relative_entry_point();
}
bool library::is_valid() const
{
return this->module_ != nullptr && this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE;
}
std::string library::get_name() const
{
if (!this->is_valid()) return "";
auto path = this->get_path();
const auto pos = path.find_last_of("/\\");
if (pos == std::string::npos) return path;
return path.substr(pos + 1);
}
std::string library::get_path() const
{
if (!this->is_valid()) return "";
char name[MAX_PATH] = {0};
GetModuleFileNameA(this->module_, name, sizeof name);
return name;
}
std::string library::get_folder() const
{
if (!this->is_valid()) return "";
const auto path = std::filesystem::path(this->get_path());
return path.parent_path().generic_string();
}
void library::free()
{
if (this->is_valid())
{
FreeLibrary(this->module_);
this->module_ = nullptr;
}
}
HMODULE library::get_handle() const
{
return this->module_;
}
void** library::get_iat_entry(const std::string& module_name, const std::string& proc_name) const
{
if (!this->is_valid()) return nullptr;
const library other_module(module_name);
if (!other_module.is_valid()) return nullptr;
auto* const target_function = other_module.get_proc<void*>(proc_name);
if (!target_function) return nullptr;
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);
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());
while (original_thunk_data->u1.AddressOfData)
{
if (thunk_data->u1.Function == (uint64_t)target_function)
{
return reinterpret_cast<void**>(&thunk_data->u1.Function);
}
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
if (ordinal_number <= 0xFFFF)
{
if (GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number)) ==
target_function)
{
return reinterpret_cast<void**>(&thunk_data->u1.Function);
}
}
++original_thunk_data;
++thunk_data;
}
//break;
}
++import_descriptor;
}
return nullptr;
}
bool is_shutdown_in_progress()
{
static auto* shutdown_in_progress = []
{
const library ntdll("ntdll.dll");
return ntdll.get_proc<BOOLEAN(*)()>("RtlDllShutdownInProgress");
}();
return shutdown_in_progress();
}
void raise_hard_exception()
{
int data = false;
const library ntdll("ntdll.dll");
ntdll.invoke_pascal<void>("RtlAdjustPrivilege", 19, true, false, &data);
ntdll.invoke_pascal<void>("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data);
}
std::string load_resource(const int id)
{
const auto lib = library::get_by_address(load_resource);
auto* const res = FindResource(lib, MAKEINTRESOURCE(id), RT_RCDATA);
if (!res) return {};
auto* const handle = LoadResource(lib, res);
if (!handle) return {};
return std::string(LPSTR(LockResource(handle)), SizeofResource(lib, res));
}
void relaunch_self()
{
const utils::nt::library self;
STARTUPINFOA startup_info;
PROCESS_INFORMATION process_info;
ZeroMemory(&startup_info, sizeof(startup_info));
ZeroMemory(&process_info, sizeof(process_info));
startup_info.cb = sizeof(startup_info);
char current_dir[MAX_PATH];
GetCurrentDirectoryA(sizeof(current_dir), current_dir);
auto* const command_line = GetCommandLineA();
CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir,
&startup_info, &process_info);
if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread);
if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess);
}
void terminate(const uint32_t code)
{
TerminateProcess(GetCurrentProcess(), code);
}
}

View File

@ -0,0 +1,176 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// min and max is required by gdi, therefore NOMINMAX won't work
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#include <string>
#include <functional>
#include <filesystem>
namespace utils::nt
{
class library final
{
public:
static library load(const char* name);
static library load(const std::string& name);
static library load(const std::filesystem::path& path);
static library get_by_address(const void* address);
library();
explicit library(const std::string& name);
explicit library(HMODULE handle);
library(const library& a) : module_(a.module_)
{
}
bool operator!=(const library& obj) const { return !(*this == obj); };
bool operator==(const library& obj) const;
operator bool() const;
operator HMODULE() const;
void unprotect() const;
void* get_entry_point() const;
size_t get_relative_entry_point() const;
bool is_valid() const;
std::string get_name() const;
std::string get_path() const;
std::string get_folder() const;
std::uint8_t* get_ptr() const;
void free();
HMODULE get_handle() const;
template <typename T>
T get_proc(const std::string& process) const
{
if (!this->is_valid()) T{};
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
}
template <typename T>
std::function<T> get(const std::string& process) const
{
if (!this->is_valid()) return std::function<T>();
return static_cast<T*>(this->get_proc<void*>(process));
}
template <typename T, typename... Args>
T invoke(const std::string& process, Args ... args) const
{
auto method = this->get<T(__cdecl)(Args ...)>(process);
if (method) return method(args...);
return T();
}
template <typename T, typename... Args>
T invoke_pascal(const std::string& process, Args ... args) const
{
auto method = this->get<T(__stdcall)(Args ...)>(process);
if (method) return method(args...);
return T();
}
template <typename T, typename... Args>
T invoke_this(const std::string& process, void* this_ptr, Args ... args) const
{
auto method = this->get<T(__thiscall)(void*, Args ...)>(this_ptr, process);
if (method) return method(args...);
return T();
}
std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const;
PIMAGE_NT_HEADERS get_nt_headers() const;
PIMAGE_DOS_HEADER get_dos_header() const;
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const;
private:
HMODULE module_;
};
template <HANDLE InvalidHandle = nullptr>
class handle
{
public:
handle() = default;
handle(const HANDLE h)
: handle_(h)
{
}
~handle()
{
if (*this)
{
CloseHandle(this->handle_);
this->handle_ = InvalidHandle;
}
}
handle(const handle&) = delete;
handle& operator=(const handle&) = delete;
handle(handle&& obj) noexcept
: handle()
{
this->operator=(std::move(obj));
}
handle& operator=(handle&& obj) noexcept
{
if (this != &obj)
{
this->~handle();
this->handle_ = obj.handle_;
obj.handle_ = InvalidHandle;
}
return *this;
}
handle& operator=(HANDLE h) noexcept
{
this->~handle();
this->handle_ = h;
return *this;
}
operator bool() const
{
return this->handle_ != InvalidHandle;
}
operator HANDLE() const
{
return this->handle_;
}
private:
HANDLE handle_{InvalidHandle};
};
bool is_shutdown_in_progress();
__declspec(noreturn) void raise_hard_exception();
std::string load_resource(int id);
void relaunch_self();
__declspec(noreturn) void terminate(uint32_t code = 0);
}

View File

@ -0,0 +1,45 @@
#include "progress_ui.hpp"
#include <utils/string.hpp>
namespace utils
{
progress_ui::progress_ui()
{
this->dialog_ = utils::com::create_progress_dialog();
if (!this->dialog_)
{
throw std::runtime_error{"Failed to create dialog"};
}
}
progress_ui::~progress_ui()
{
this->dialog_->StopProgressDialog();
}
void progress_ui::show(const bool marquee, HWND parent) const
{
this->dialog_->StartProgressDialog(parent, nullptr, PROGDLG_AUTOTIME | (marquee ? PROGDLG_MARQUEEPROGRESS : 0), nullptr);
}
void progress_ui::set_progress(const size_t current, const size_t max) const
{
this->dialog_->SetProgress64(current, max);
}
void progress_ui::set_line(const int line, const std::string& text) const
{
this->dialog_->SetLine(line, utils::string::convert(text).data(), false, nullptr);
}
void progress_ui::set_title(const std::string& title) const
{
this->dialog_->SetTitle(utils::string::convert(title).data());
}
bool progress_ui::is_cancelled() const
{
return this->dialog_->HasUserCancelled();
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "com.hpp"
namespace utils
{
class progress_ui
{
public:
progress_ui();
~progress_ui();
void show(bool marquee, HWND parent = nullptr) const;
void set_progress(size_t current, size_t max) const;
void set_line(int line, const std::string& text) const;
void set_title(const std::string& title) const;
bool is_cancelled() const;
private:
CComPtr<IProgressDialog> dialog_{};
};
}

View File

@ -0,0 +1,223 @@
#include "signature.hpp"
#include <thread>
#include <mutex>
#include "string.hpp" // dbg
#include <intrin.h>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace utils::hook
{
void signature::load_pattern(const std::string& pattern)
{
this->mask_.clear();
this->pattern_.clear();
uint8_t nibble = 0;
auto has_nibble = false;
for (auto val : pattern)
{
if (val == ' ') continue;
if (val == '?')
{
this->mask_.push_back(val);
this->pattern_.push_back(0);
}
else
{
if ((val < '0' || val > '9') && (val < 'A' || val > 'F') && (val < 'a' || val > 'f'))
{
throw std::runtime_error("Invalid pattern");
}
char str[] = {val, 0};
const auto current_nibble = static_cast<uint8_t>(strtol(str, nullptr, 16));
if (!has_nibble)
{
has_nibble = true;
nibble = current_nibble;
}
else
{
has_nibble = false;
const uint8_t byte = current_nibble | (nibble << 4);
this->mask_.push_back('x');
this->pattern_.push_back(byte);
}
}
}
while (!this->mask_.empty() && this->mask_.back() == '?')
{
this->mask_.pop_back();
this->pattern_.pop_back();
}
if (this->has_sse_support())
{
while (this->pattern_.size() < 16)
{
this->pattern_.push_back(0);
}
}
if (has_nibble)
{
throw std::runtime_error("Invalid pattern");
}
}
signature::signature_result signature::process_range(uint8_t* start, const size_t length) const
{
if (this->has_sse_support()) return this->process_range_vectorized(start, length);
return this->process_range_linear(start, length);
}
signature::signature_result signature::process_range_linear(uint8_t* start, const size_t length) const
{
std::vector<uint8_t*> result;
for (size_t i = 0; i < length; ++i)
{
const auto address = start + i;
size_t j = 0;
for (; j < this->mask_.size(); ++j)
{
if (this->mask_[j] != '?' && this->pattern_[j] != address[j])
{
break;
}
}
if (j == this->mask_.size())
{
result.push_back(address);
}
}
return result;
}
signature::signature_result signature::process_range_vectorized(uint8_t* start, const size_t length) const
{
std::vector<uint8_t*> result;
__declspec(align(16)) char desired_mask[16] = {0};
for (size_t i = 0; i < this->mask_.size(); i++)
{
desired_mask[i / 8] |= (this->mask_[i] == '?' ? 0 : 1) << i % 8;
}
const auto mask = _mm_load_si128(reinterpret_cast<const __m128i*>(desired_mask));
const auto comparand = _mm_loadu_si128(reinterpret_cast<const __m128i*>(this->pattern_.data()));
for (size_t i = 0; i < length; ++i)
{
const auto address = start + i;
const auto value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(address));
const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast<int>(this->mask_.size()),
_SIDD_CMP_EQUAL_EACH);
const auto matches = _mm_and_si128(mask, comparison);
const auto equivalence = _mm_xor_si128(mask, matches);
if (_mm_test_all_zeros(equivalence, equivalence))
{
result.push_back(address);
}
}
return result;
}
signature::signature_result signature::process() const
{
//MessageBoxA(nullptr, utils::string::va("%llX(%llX)%llX", *this->start_ , this->start_, this->length_), "signature::process", MB_OK | MB_ICONINFORMATION);
const auto range = this->length_ - this->mask_.size();
const auto cores = std::max(1u, std::thread::hardware_concurrency());
if (range <= cores * 10ull) return this->process_serial();
return this->process_parallel();
}
signature::signature_result signature::process_serial() const
{
const auto sub = this->has_sse_support() ? 16 : this->mask_.size();
return {this->process_range(this->start_, this->length_ - sub)};
}
signature::signature_result signature::process_parallel() const
{
const auto sub = this->has_sse_support() ? 16 : this->mask_.size();
const auto range = this->length_ - sub;
const auto cores = std::max(1u, std::thread::hardware_concurrency() / 2);
// Only use half of the available cores
const auto grid = range / cores;
std::mutex mutex;
std::vector<uint8_t*> result;
std::vector<std::thread> threads;
for (auto i = 0u; i < cores; ++i)
{
const auto start = this->start_ + (grid * i);
const auto length = (i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid;
threads.emplace_back([&, start, length]()
{
const auto local_result = this->process_range(start, length);
if (local_result.empty()) return;
std::lock_guard _(mutex);
for (const auto& address : local_result)
{
result.push_back(address);
}
});
}
for (auto& t : threads)
{
if (t.joinable())
{
t.join();
}
}
std::sort(result.begin(), result.end());
return {std::move(result)};
}
bool signature::has_sse_support() const
{
if (this->mask_.size() <= 16)
{
int cpu_id[4];
__cpuid(cpu_id, 0);
if (cpu_id[0] >= 1)
{
__cpuidex(cpu_id, 1, 0);
return (cpu_id[2] & (1 << 20)) != 0;
}
}
return false;
}
}
utils::hook::signature::signature_result operator"" _sig(const char* str, const size_t len)
{
return utils::hook::signature(std::string(str, len)).process();
}

View File

@ -0,0 +1,49 @@
#pragma once
#include "nt.hpp"
#include <cstdint>
namespace utils::hook
{
class signature final
{
public:
using signature_result = std::vector<uint8_t*>;
explicit signature(const std::string& pattern, const nt::library& library = {})
: signature(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage)
{
}
signature(const std::string& pattern, void* start, void* end)
: signature(pattern, start, size_t(end) - size_t(start))
{
}
signature(const std::string& pattern, void* start, const size_t length)
: start_(static_cast<uint8_t*>(start)), length_(length)
{
this->load_pattern(pattern);
}
signature_result process() const;
private:
std::string mask_;
std::basic_string<uint8_t> pattern_;
uint8_t* start_;
size_t length_;
void load_pattern(const std::string& pattern);
signature_result process_parallel() const;
signature_result process_serial() const;
signature_result process_range(uint8_t* start, size_t length) const;
signature_result process_range_linear(uint8_t* start, size_t length) const;
signature_result process_range_vectorized(uint8_t* start, size_t length) const;
bool has_sse_support() const;
};
}
utils::hook::signature::signature_result operator"" _sig(const char* str, size_t len);

View File

@ -0,0 +1,94 @@
#include "smbios.hpp"
#include "memory.hpp"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <intrin.h>
namespace utils::smbios
{
namespace
{
#pragma warning(push)
#pragma warning(disable: 4200)
struct RawSMBIOSData
{
BYTE Used20CallingMethod;
BYTE SMBIOSMajorVersion;
BYTE SMBIOSMinorVersion;
BYTE DmiRevision;
DWORD Length;
BYTE SMBIOSTableData[];
};
typedef struct
{
BYTE type;
BYTE length;
WORD handle;
} dmi_header;
#pragma warning(pop)
std::vector<uint8_t> get_smbios_data()
{
DWORD size = 0;
std::vector<uint8_t> data{};
size = GetSystemFirmwareTable('RSMB', 0, nullptr, size);
data.resize(size);
GetSystemFirmwareTable('RSMB', 0, data.data(), size);
return data;
}
std::string parse_uuid(const uint8_t* data)
{
if (utils::memory::is_set(data, 0, 16) || utils::memory::is_set(data, -1, 16))
{
return {};
}
char uuid[16] = {0};
*reinterpret_cast<unsigned long*>(uuid + 0) =
_byteswap_ulong(*reinterpret_cast<const unsigned long*>(data + 0));
*reinterpret_cast<unsigned short*>(uuid + 4) =
_byteswap_ushort(*reinterpret_cast<const unsigned short*>(data + 4));
*reinterpret_cast<unsigned short*>(uuid + 6) =
_byteswap_ushort(*reinterpret_cast<const unsigned short*>(data + 6));
memcpy(uuid + 8, data + 8, 8);
return std::string(uuid, sizeof(uuid));
}
}
std::string get_uuid()
{
auto smbios_data = get_smbios_data();
auto* raw_data = reinterpret_cast<RawSMBIOSData*>(smbios_data.data());
auto* data = raw_data->SMBIOSTableData;
for (DWORD i = 0; i + sizeof(dmi_header) < raw_data->Length;)
{
auto* header = reinterpret_cast<dmi_header*>(data + i);
if (header->length < 4)
{
return {};
}
if (header->type == 0x01 && header->length >= 0x19)
{
return parse_uuid(data + i + 0x8);
}
i += header->length;
while ((i + 1) < raw_data->Length && *reinterpret_cast<uint16_t*>(data + i) != 0)
{
++i;
}
i += 2;
}
return {};
}
}

View File

@ -0,0 +1,8 @@
#pragma once
#include <string>
namespace utils::smbios
{
std::string get_uuid();
}

View File

@ -0,0 +1,179 @@
#include "string.hpp"
#include <sstream>
#include <cstdarg>
#include <algorithm>
#include "nt.hpp"
namespace utils::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::vector<std::string> split(const std::string& s, const char delim)
{
std::stringstream ss(s);
std::string item;
std::vector<std::string> elems;
while (std::getline(ss, item, delim))
{
elems.push_back(item); // elems.push_back(std::move(item)); // if C++11 (based on comment from @mchiasson)
}
return elems;
}
std::string to_lower(std::string text)
{
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
{
return static_cast<char>(tolower(input));
});
return text;
}
std::string to_upper(std::string text)
{
std::transform(text.begin(), text.end(), text.begin(), [](const char input)
{
return static_cast<char>(toupper(input));
});
return text;
}
bool starts_with(const std::string& text, const std::string& substring)
{
return text.find(substring) == 0;
}
bool ends_with(const std::string& text, const std::string& substring)
{
if (substring.size() > text.size()) return false;
return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
}
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;
}
std::string get_clipboard_data()
{
if (OpenClipboard(nullptr))
{
std::string data;
auto* const clipboard_data = GetClipboardData(1u);
if (clipboard_data)
{
auto* const cliptext = static_cast<char*>(GlobalLock(clipboard_data));
if (cliptext)
{
data.append(cliptext);
GlobalUnlock(clipboard_data);
}
}
CloseClipboard();
return data;
}
return {};
}
void strip(const char* in, char* out, int max)
{
if (!in || !out) return;
max--;
auto current = 0;
while (*in != 0 && current < max)
{
const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48);
if (*in == '^' && (color_index != 7 || *(in + 1) == '7'))
{
++in;
}
else
{
*out = *in;
++out;
++current;
}
++in;
}
*out = '\0';
}
#pragma warning(push)
#pragma warning(disable: 4100)
std::string convert(const std::wstring& wstr)
{
std::string result;
result.reserve(wstr.size());
for (const auto& chr : wstr)
{
result.push_back(static_cast<char>(chr));
}
return result;
}
std::wstring convert(const std::string& str)
{
std::wstring result;
result.reserve(str.size());
for (const auto& chr : str)
{
result.push_back(static_cast<wchar_t>(chr));
}
return result;
}
#pragma warning(pop)
std::string replace(std::string str, const std::string& from, const std::string& to)
{
if (from.empty())
{
return str;
}
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
{
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
return str;
}
}

View File

@ -0,0 +1,100 @@
#pragma once
#include "memory.hpp"
#include <cstdint>
#ifndef ARRAYSIZE
template <class Type, size_t n>
size_t ARRAYSIZE(Type (&)[n]) { return n; }
#endif
namespace utils::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:
entry(const 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{nullptr};
};
size_t current_buffer_{};
entry string_pool_[Buffers]{};
};
const char* va(const char* fmt, ...);
std::vector<std::string> split(const std::string& s, char delim);
std::string to_lower(std::string text);
std::string to_upper(std::string text);
bool starts_with(const std::string& text, const std::string& substring);
bool ends_with(const std::string& text, const std::string& substring);
std::string dump_hex(const std::string& data, const std::string& separator = " ");
std::string get_clipboard_data();
void strip(const char* in, char* out, int max);
std::string convert(const std::wstring& wstr);
std::wstring convert(const std::string& str);
std::string replace(std::string str, const std::string& from, const std::string& to);
}

View File

@ -0,0 +1,117 @@
#include "thread.hpp"
#include "string.hpp"
#include "finally.hpp"
#include <TlHelp32.h>
namespace utils::thread
{
bool set_name(const HANDLE t, const std::string& name)
{
const nt::library kernel32("kernel32.dll");
if (!kernel32)
{
return false;
}
const auto set_description = kernel32.get_proc<HRESULT(WINAPI *)(HANDLE, PCWSTR)>("SetThreadDescription");
if (!set_description)
{
return false;
}
return SUCCEEDED(set_description(t, string::convert(name).data()));
}
bool set_name(const DWORD id, const std::string& name)
{
auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id);
if (!t) return false;
const auto _ = utils::finally([t]()
{
CloseHandle(t);
});
return set_name(t, name);
}
bool set_name(std::thread& t, const std::string& name)
{
return set_name(t.native_handle(), name);
}
bool set_name(const std::string& name)
{
return set_name(GetCurrentThread(), name);
}
std::vector<DWORD> get_thread_ids()
{
nt::handle<INVALID_HANDLE_VALUE> h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
if (!h)
{
return {};
}
THREADENTRY32 entry{};
entry.dwSize = sizeof(entry);
if (!Thread32First(h, &entry))
{
return {};
}
std::vector<DWORD> ids{};
do
{
const auto check_size = entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID)
+ sizeof(entry.th32OwnerProcessID);
entry.dwSize = sizeof(entry);
if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId())
{
ids.emplace_back(entry.th32ThreadID);
}
}
while (Thread32Next(h, &entry));
return ids;
}
void for_each_thread(const std::function<void(HANDLE)>& callback, const DWORD access)
{
const auto ids = get_thread_ids();
for (const auto& id : ids)
{
handle thread(id, access);
if (thread)
{
callback(thread);
}
}
}
void suspend_other_threads()
{
for_each_thread([](const HANDLE thread)
{
if (GetThreadId(thread) != GetCurrentThreadId())
{
SuspendThread(thread);
}
});
}
void resume_other_threads()
{
for_each_thread([](const HANDLE thread)
{
if (GetThreadId(thread) != GetCurrentThreadId())
{
ResumeThread(thread);
}
});
}
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <thread>
#include "nt.hpp"
namespace utils::thread
{
bool set_name(HANDLE t, const std::string& name);
bool set_name(DWORD id, const std::string& name);
bool set_name(std::thread& t, const std::string& name);
bool set_name(const std::string& name);
template <typename ...Args>
std::thread create_named_thread(const std::string& name, Args&&... args)
{
auto t = std::thread(std::forward<Args>(args)...);
set_name(t, name);
return t;
}
class handle
{
public:
handle(const DWORD thread_id, const DWORD access = THREAD_ALL_ACCESS)
: handle_(OpenThread(access, FALSE, thread_id))
{
}
operator bool() const
{
return this->handle_;
}
operator HANDLE() const
{
return this->handle_;
}
private:
nt::handle<> handle_{};
};
std::vector<DWORD> get_thread_ids();
void for_each_thread(const std::function<void(HANDLE)>& callback, DWORD access = THREAD_ALL_ACCESS);
void suspend_other_threads();
void resume_other_threads();
}