aboutsummaryrefslogtreecommitdiff
path: root/client/src
diff options
context:
space:
mode:
authorauth12 <[email protected]>2020-07-21 13:07:42 -0700
committerauth12 <[email protected]>2020-07-21 13:07:42 -0700
commitf09669dd5846d95b063712571ccb7519910a0d6e (patch)
tree902f5ad201651f2d96ccf619e90b76cfa06a7b9b /client/src
parentSyscalls. (diff)
downloadloader-f09669dd5846d95b063712571ccb7519910a0d6e.tar.xz
loader-f09669dd5846d95b063712571ccb7519910a0d6e.zip
Added game selection.
Started process wrapper. Removed asmjit.
Diffstat (limited to 'client/src')
-rw-r--r--client/src/client/ca.h37
-rw-r--r--client/src/client/client.cpp168
-rw-r--r--client/src/client/client.h212
-rw-r--r--client/src/client/enc.cpp56
-rw-r--r--client/src/client/enc.h4
-rw-r--r--client/src/include.h1
-rw-r--r--client/src/injection/mapper.h19
-rw-r--r--client/src/injection/process.cpp71
-rw-r--r--client/src/injection/process.h19
-rw-r--r--client/src/main.cpp305
-rw-r--r--client/src/shellcode/shellcode.cpp31
-rw-r--r--client/src/shellcode/shellcode.h35
-rw-r--r--client/src/util/events.h28
-rw-r--r--client/src/util/io.cpp8
-rw-r--r--client/src/util/io.h4
-rw-r--r--client/src/util/native.h6
-rw-r--r--client/src/util/pe.h74
-rw-r--r--client/src/util/syscalls.cpp8
-rw-r--r--client/src/util/syscalls.h10
-rw-r--r--client/src/util/util.cpp54
-rw-r--r--client/src/util/util.h22
21 files changed, 637 insertions, 535 deletions
diff --git a/client/src/client/ca.h b/client/src/client/ca.h
new file mode 100644
index 0000000..fca03c4
--- /dev/null
+++ b/client/src/client/ca.h
@@ -0,0 +1,37 @@
+#pragma once
+
+
+const static std::string root_cert = R"(
+-----BEGIN CERTIFICATE-----
+MIIFgTCCA2mgAwIBAgIUI1iNRm7wAiOk64UxFSCPziYMdyAwDQYJKoZIhvcNAQEL
+BQAwUDELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1ZWJlYzERMA8GA1UEBwwITW9u
+dHJlYWwxDDAKBgNVBAoMA2FscDEPMA0GA1UECwwGbG9hZGVyMB4XDTIwMDYyOTA4
+NDgxMloXDTIzMDQxOTA4NDgxMlowUDELMAkGA1UEBhMCQ0ExDzANBgNVBAgMBlF1
+ZWJlYzERMA8GA1UEBwwITW9udHJlYWwxDDAKBgNVBAoMA2FscDEPMA0GA1UECwwG
+bG9hZGVyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvPv6+6+Nn3D5
+YPa6SwvS7dzZNe8kPqkAwZy4PMCQQ9MmQQILJMjou/6i4xN0YL2bZ24VoVhOAmVa
+sZDLV1Qmra+zDJa4no5bBIxsA/HZc3QAHRzkye9Pw8avBAw67X7AzWH5z7i2Ce+l
+F4ezNG6i7PJ3QC+jkJRqzm0gh/BIjHl7takuBunNS7l1tP+M4YUIqN6AV/9sLodE
+YS9PL8fP9Gk6Lds90OSj4DBmsYJdYLfxdPrQ609ijKOjQtphUZ8/JnaED7KGnFV3
+FIQV7L/usmxUKUbwxa+36TXpC+qYGKXTdVpu++VKcgHU0u8H03X/4LlR0DUkby/P
+nFxJFL1WWzbYIH3CBCx2YeaBneT6EUZvCBNHwL1fl8sG8FOiRoV1G2sy7eBadofi
+R+l7Pm4iTzsl1zF9ODCsE3EvJdZ/WxbSk5OU0oLJyeCJszhA/jPXu9ogKjFG1LVd
+5JEYGvg4SKKiEUD6G/luCTV9WKcajm4v+BtzTy/ftl1e4zbQ/Vod44BE/0Rj0Z+l
+yK7XI3RYZwO+McBjT3/bUAED1dB3u8kOyHf4k+boIYaI3LImXVGZue7tB+qQv6ZB
+NY9m3FM6yVdV4peWKBsuSucGDVElBD0SgaeOvmLPFgVaQjmabRVUReYHvWoVHLDZ
+WgPZe72SkPJ1Lu85Ux52PMDeKrIigC0CAwEAAaNTMFEwHQYDVR0OBBYEFE4IyL3j
+3kdYSMpOfxRGmUSmLbGAMB8GA1UdIwQYMBaAFE4IyL3j3kdYSMpOfxRGmUSmLbGA
+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAIilajIQyW05OGcH
+PY8ePZupbnOsjvHjHk+CtvrMcf6CAXZkbkgfkAddJcS4D7HoZG9v5GONLZSIfVqT
+E7iKSQm4uYaAE/MqaHIdZwPrE9Z/mp5sGP1zdNFPuqil4JvSusP2aV6D5++1//P3
+Hw9t588U5phGalL6MP9NezXnuuRNC6Nz5CIkawlO2O4bg2BX7wxFiF5YeZEtdKI4
+CKAyoTlVPgc0VkzSPTUGwK2MYOq3BqVrtmvNthItcXp6ML5dhRv2Grk9pEU/xirv
+ahIxwusFXiSmW2vgfPFGdNb3elnHGs7H4291oNNvtQjlHvjZXOSRjeBnLMunUsyn
+81zDbaRC91TZPPogUFiKq3Avh80d/QnoypdjhPmhsOGwPikjolfJ45jYO/f27CVT
+AqexIUOuKaJudeejWX3Vj3xlyl4x8kxPk6zNnBLY9fAur5i/UTLOfZtbMAP1T1ZJ
+vQZ5ncJFy3SkN4yyzeOXAXXZMh3xIEALtPqH4UFWdvLcMUIHUmCa22zYOK9zcKFm
+qwSN/mNqYvYFS9nT2CkmjcXzbXazijfjnie94nHDtCyZzBwKz76xuldDcE+NMXy2
+xf2P4NoAmulUDxgHLhsXJVA5OS9kGuJNQ6i2axZpRhPBN6kUFfT14VjZ/Co/Fkty
+hTbB0ZS9vX7ug3j2KF0WPHyJaXcS
+-----END CERTIFICATE-----
+)"; \ No newline at end of file
diff --git a/client/src/client/client.cpp b/client/src/client/client.cpp
index 8e71e81..3fa55fc 100644
--- a/client/src/client/client.cpp
+++ b/client/src/client/client.cpp
@@ -1,106 +1,108 @@
#include "../include.h"
#include "client.h"
+#include "ca.h"
+
void tcp::client::start(const std::string_view server_ip, const uint16_t port) {
- wolfSSL_library_init();
-
- m_ssl_ctx = wolfSSL_CTX_new(wolfTLS_client_method());
-
- int ret = wolfSSL_CTX_load_verify_locations(m_ssl_ctx, "ssl/rootCA.crt", nullptr);
- if (ret != 1) {
- io::logger->error("failed to load ca.");
- return;
- }
- wolfSSL_CTX_set_verify(m_ssl_ctx, SSL_VERIFY_PEER, 0);
-
- WSADATA data;
- ret = WSAStartup(MAKEWORD(2, 2), &data);
- if (ret != 0) {
- io::logger->error("failed to initialize WSA.");
- return;
- }
-
- m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (m_socket == -1) {
- io::logger->error("failed to create socket.");
- return;
- }
-
- sockaddr_in server_addr;
-
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = inet_addr(server_ip.data());
- server_addr.sin_port = htons(port);
-
- ret = connect(m_socket, reinterpret_cast<sockaddr*>(&server_addr),
- sizeof(server_addr));
- if (ret < 0) {
- io::logger->error("failed to connect to server.");
- return;
- }
-
- m_server_ssl = wolfSSL_new(m_ssl_ctx);
- wolfSSL_set_fd(m_server_ssl, m_socket);
-
- ret = wolfSSL_connect(m_server_ssl);
-
- if (ret != 1) {
- ret = wolfSSL_get_error(m_server_ssl, ret);
- io::logger->error("secure connection failed, code {}", ret);
- return;
- }
-
- m_active = true;
-
- connect_event.call();
+ wolfSSL_library_init();
+
+ m_ssl_ctx = wolfSSL_CTX_new(wolfTLS_client_method());
+
+ int ret = wolfSSL_CTX_load_verify_buffer(m_ssl_ctx, reinterpret_cast<const unsigned char*>(root_cert.data()), root_cert.size(), SSL_FILETYPE_PEM);
+ if (ret != 1) {
+ io::logger->error("failed to load ca.");
+ return;
+ }
+ wolfSSL_CTX_set_verify(m_ssl_ctx, SSL_VERIFY_PEER, 0);
+
+ WSADATA data;
+ ret = WSAStartup(MAKEWORD(2, 2), &data);
+ if (ret != 0) {
+ io::logger->error("failed to initialize WSA.");
+ return;
+ }
+
+ m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (m_socket == -1) {
+ io::logger->error("failed to create socket.");
+ return;
+ }
+
+ sockaddr_in server_addr;
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_addr.s_addr = inet_addr(server_ip.data());
+ server_addr.sin_port = htons(port);
+
+ ret = connect(m_socket, reinterpret_cast<sockaddr*>(&server_addr),
+ sizeof(server_addr));
+ if (ret < 0) {
+ io::logger->error("failed to connect to server.");
+ return;
+ }
+
+ m_server_ssl = wolfSSL_new(m_ssl_ctx);
+ wolfSSL_set_fd(m_server_ssl, m_socket);
+
+ ret = wolfSSL_connect(m_server_ssl);
+
+ if (ret != 1) {
+ ret = wolfSSL_get_error(m_server_ssl, ret);
+ io::logger->error("secure connection failed, code {}", ret);
+ return;
+ }
+
+ m_active = true;
+
+ connect_event.call();
}
int tcp::client::read_stream(std::vector<char>& out) {
- size_t size;
- read(&size, sizeof(size));
+ size_t size;
+ read(&size, sizeof(size));
- size = ntohl(size);
- out.resize(size);
+ size = ntohl(size);
+ out.resize(size);
- constexpr size_t chunk_size = 4096;
- size_t total = 0;
+ constexpr size_t chunk_size = 4096;
+ size_t total = 0;
- while (size > 0) {
- auto to_read = std::min(size, chunk_size);
+ while (size > 0) {
+ auto to_read = std::min(size, chunk_size);
- int ret = read(&out[total], to_read);
- if (ret <= 0) {
- break;
- }
+ int ret = read(&out[total], to_read);
+ if (ret <= 0) {
+ break;
+ }
- size -= ret;
- total += ret;
- }
+ size -= ret;
+ total += ret;
+ }
- return total;
+ return total;
}
int tcp::client::stream(std::vector<char>& data) {
- auto size = data.size();
+ auto size = data.size();
- auto networked_size = htonl(size);
- write(&networked_size, sizeof(networked_size));
+ auto networked_size = htonl(size);
+ write(&networked_size, sizeof(networked_size));
- // with 4kb chunk size, speed peaks at 90mb/s
- constexpr size_t chunk_size = 4096;
- size_t sent = 0;
+ // with 4kb chunk size, speed peaks at 90mb/s
+ constexpr size_t chunk_size = 4096;
+ size_t sent = 0;
- while (size > 0) {
- auto to_send = std::min(size, chunk_size);
+ while (size > 0) {
+ auto to_send = std::min(size, chunk_size);
- int ret = write(&data[sent], to_send);
- if (ret <= 0) {
- break;
- }
+ int ret = write(&data[sent], to_send);
+ if (ret <= 0) {
+ break;
+ }
- sent += ret;
- size -= ret;
- }
+ sent += ret;
+ size -= ret;
+ }
- return sent;
+ return sent;
}
diff --git a/client/src/client/client.h b/client/src/client/client.h
index 8d5b80a..cdc00d6 100644
--- a/client/src/client/client.h
+++ b/client/src/client/client.h
@@ -1,115 +1,123 @@
#pragma once
+#include <wolfssl/IDE/WIN/user_settings.h>
#include <wolfssl/ssl.h>
#include "../util/io.h"
#include "../util/events.h"
-#include "../injection/mapper.h"
#include "packet.h"
-namespace tcp {
-
-struct version_t {
- uint8_t major;
- uint8_t minor;
- uint8_t patch;
-};
-
-struct game_data_t {
- std::string name;
- std::string version;
- int id;
-};
-
-enum client_state {
- idle = 0, logged_in, waiting
-};
-
-enum login_result {
- login_fail = 15494,
- hwid_mismatch = 11006,
- login_success = 61539,
- banned = 28618,
- server_error = 98679
+struct mapper_data_t {
+ size_t image_size;
+ uint32_t entry;
+ uint32_t base;
+ std::string imports;
+ std::vector<char> image;
};
-class client {
- int m_socket;
- std::atomic<bool> m_active;
-
- WOLFSSL* m_server_ssl;
- WOLFSSL_CTX* m_ssl_ctx;
-
- public:
- int state;
- mmap::mapper_data_t mapper_data;
- std::vector<game_data_t> games;
-
- std::string session_id;
- event<packet_t&> receive_event;
- event<> connect_event;
-
- client() : m_socket{-1}, m_active{false}, state{client_state::idle} {}
-
- void start(const std::string_view server_ip, const uint16_t port);
-
- int write(const packet_t& packet) {
- if (!packet) return 0;
- return write(packet.message.data(),
- packet.message.size());
- }
-
- int write(const void* data, size_t size) {
- return wolfSSL_write(m_server_ssl, data, size);
- }
-
- int read(void* data, size_t size) {
- return wolfSSL_read(m_server_ssl, data, size);
- }
-
- int read_stream(std::vector<char>& out);
- int stream(std::vector<char>& data);
-
- int stream(std::string &str) {
- std::vector<char> vec(str.begin(), str.end());
- return stream(vec);
- }
-
- int read_stream(std::string &str) {
- std::vector<char> out;
- int ret = read_stream(out);
- str.assign(out.begin(), out.end());
- return ret;
- }
-
- int get_socket() { return m_socket; }
-
- operator bool() const { return m_active; }
-
- void shutdown() {
- closesocket(m_socket);
- wolfSSL_shutdown(m_server_ssl);
- wolfSSL_free(m_server_ssl);
-
- m_active = false;
- }
-
- static void monitor(client& client) {
- while (!client) std::this_thread::sleep_for(std::chrono::milliseconds(100));
-
- std::array<char, message_len> buf;
- while (client) {
- int ret = client.read(&buf[0], buf.size());
- if (ret <= 0) {
- io::logger->error("connection lost.");
- break;
- }
- std::string msg(buf.data(), ret);
- packet_t packet(msg, packet_type::read);
+namespace tcp {
- client.receive_event.call(packet);
- }
- }
-};
+ struct version_t {
+ uint8_t major;
+ uint8_t minor;
+ uint8_t patch;
+ };
+
+ struct game_data_t {
+ std::string name;
+ std::string version;
+ int id;
+ };
+
+ enum client_state {
+ idle = 0, logged_in, waiting
+ };
+
+ enum login_result {
+ login_fail = 15494,
+ hwid_mismatch = 11006,
+ login_success = 61539,
+ banned = 28618,
+ server_error = 98679
+ };
+
+ class client {
+ int m_socket;
+ std::atomic<bool> m_active;
+
+ WOLFSSL* m_server_ssl;
+ WOLFSSL_CTX* m_ssl_ctx;
+
+ public:
+ int state;
+ mapper_data_t mapper_data;
+ std::vector<game_data_t> games;
+
+ std::string session_id;
+ event<packet_t&> receive_event;
+ event<> connect_event;
+
+ client() : m_socket{ -1 }, m_active{ false }, state{ client_state::idle } {}
+
+ void start(const std::string_view server_ip, const uint16_t port);
+
+ int write(const packet_t& packet) {
+ if (!packet) return 0;
+ return write(packet.message.data(),
+ packet.message.size());
+ }
+
+ int write(const void* data, int size) {
+ return wolfSSL_write(m_server_ssl, data, size);
+ }
+
+ int read(void* data, int size) {
+ return wolfSSL_read(m_server_ssl, data, size);
+ }
+
+ int read_stream(std::vector<char>& out);
+ int stream(std::vector<char>& data);
+
+ int stream(std::string& str) {
+ std::vector<char> vec(str.begin(), str.end());
+ return stream(vec);
+ }
+
+ int read_stream(std::string& str) {
+ std::vector<char> out;
+ int ret = read_stream(out);
+ str.assign(out.begin(), out.end());
+ return ret;
+ }
+
+ int get_socket() { return m_socket; }
+
+ operator bool() const { return m_active; }
+
+ void shutdown() {
+ closesocket(m_socket);
+ wolfSSL_shutdown(m_server_ssl);
+ wolfSSL_free(m_server_ssl);
+
+ m_active = false;
+ }
+
+ static void monitor(client& client) {
+ while (!client) std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ std::array<char, message_len> buf;
+ while (client) {
+ int ret = client.read(&buf[0], buf.size());
+ if (ret <= 0) {
+ io::logger->error("connection lost.");
+ break;
+ }
+ std::string msg(buf.data(), ret);
+ packet_t packet(msg, packet_type::read);
+
+ client.receive_event.call(packet);
+ }
+ }
+ };
} // namespace tcp
diff --git a/client/src/client/enc.cpp b/client/src/client/enc.cpp
index 97e1c29..b1287aa 100644
--- a/client/src/client/enc.cpp
+++ b/client/src/client/enc.cpp
@@ -3,33 +3,33 @@
namespace enc {
-std::random_device r;
-
-void encrypt_message(std::string &str) {
- std::default_random_engine e1(r());
- std::uniform_int_distribution<int> gen(0, 255);
-
- char k1 = static_cast<char>(gen(e1));
- char k2 = static_cast<char>(gen(e1));
- for (int i = 0; i < str.size(); i++) {
- char k = (i % 2) ? k1 : k2;
- str[i] ^= k;
- }
- str.insert(str.begin(), k1);
- str.insert(str.end(), k2);
-}
-
-void decrypt_message(std::string &str) {
- char k1 = str[0];
- char k2 = str[str.size() - 1];
-
- str.erase(str.begin());
- str.erase(str.end() - 1);
-
- for (int i = 0; i < str.size(); i++) {
- char k = (i % 2) ? k1 : k2;
- str[i] ^= k;
- }
-}
+ std::random_device r;
+
+ void encrypt_message(std::string& str) {
+ std::default_random_engine e1(r());
+ std::uniform_int_distribution<int> gen(0, 255);
+
+ char k1 = static_cast<char>(gen(e1));
+ char k2 = static_cast<char>(gen(e1));
+ for (int i = 0; i < str.size(); i++) {
+ char k = (i % 2) ? k1 : k2;
+ str[i] ^= k;
+ }
+ str.insert(str.begin(), k1);
+ str.insert(str.end(), k2);
+ }
+
+ void decrypt_message(std::string& str) {
+ char k1 = str[0];
+ char k2 = str[str.size() - 1];
+
+ str.erase(str.begin());
+ str.erase(str.end() - 1);
+
+ for (int i = 0; i < str.size(); i++) {
+ char k = (i % 2) ? k1 : k2;
+ str[i] ^= k;
+ }
+ }
}; // namespace enc \ No newline at end of file
diff --git a/client/src/client/enc.h b/client/src/client/enc.h
index ae8d5a6..e85e296 100644
--- a/client/src/client/enc.h
+++ b/client/src/client/enc.h
@@ -2,7 +2,7 @@
namespace enc {
-void encrypt_message(std::string &str);
-void decrypt_message(std::string &str);
+ void encrypt_message(std::string& str);
+ void decrypt_message(std::string& str);
}; // namespace enc \ No newline at end of file
diff --git a/client/src/include.h b/client/src/include.h
index 4955913..d378325 100644
--- a/client/src/include.h
+++ b/client/src/include.h
@@ -7,6 +7,7 @@
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <winternl.h>
+#include <ntstatus.h>
#include <algorithm>
#include <array>
diff --git a/client/src/injection/mapper.h b/client/src/injection/mapper.h
index 0d9026e..d1cfa5c 100644
--- a/client/src/injection/mapper.h
+++ b/client/src/injection/mapper.h
@@ -2,12 +2,13 @@
namespace mmap {
-struct mapper_data_t {
- size_t image_size;
- uint32_t entry;
- uint32_t base;
- std::string imports;
- std::vector<char> image;
-};
-
-}; // namespace mmap \ No newline at end of file
+ void thread(tcp::client& client) {
+ while (client.mapper_data.imports.empty()) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+
+
+
+ }
+
+}; \ No newline at end of file
diff --git a/client/src/injection/process.cpp b/client/src/injection/process.cpp
new file mode 100644
index 0000000..954e9a8
--- /dev/null
+++ b/client/src/injection/process.cpp
@@ -0,0 +1,71 @@
+#include "../include.h"
+#include "../util/io.h"
+#include "../util/syscalls.h"
+#include "../util/util.h"
+#include "process.h"
+
+process::process(const SYSTEM_PROCESS_INFORMATION* info) {
+ std::wstring name;
+ name.resize(info->ImageName.Length);
+
+ std::memcpy(&name[0], &info->ImageName.Buffer[0], name.size());
+
+ m_name = util::wide_to_multibyte(name);
+ m_id = int(info->UniqueProcessId);
+}
+
+process::~process() {
+ m_name.clear();
+}
+
+bool process::open() {
+ CLIENT_ID cid = { HANDLE(m_id), 0 };
+ OBJECT_ATTRIBUTES oa;
+ oa.Length = sizeof(oa);
+ oa.Attributes = 0;
+ oa.RootDirectory = 0;
+ oa.SecurityDescriptor = 0;
+ oa.ObjectName = 0;
+ oa.SecurityQualityOfService = 0;
+
+ static auto nt_open = g_syscalls.get<native::NtOpenProcess>("NtOpenProcess");
+
+ if (!NT_SUCCESS(nt_open(&m_handle, PROCESS_ALL_ACCESS, &oa, &cid))) {
+ io::logger->error("failed to open handle to {}.", m_name);
+ return false;
+ }
+
+ return true;
+}
+
+bool process::read(const uintptr_t addr, void* data, const size_t size) {
+ static auto nt_read = g_syscalls.get<native::NtReadVirtualMemory>("NtReadVirtualMemory");
+ if (!m_handle) {
+ io::logger->error("invalid process handle.", m_name);
+ return false;
+ }
+
+ ULONG read;
+ if (!NT_SUCCESS(nt_read(m_handle, reinterpret_cast<void*>(addr), data, size, &read))) {
+ io::logger->error("failed to read to {}.", m_name);
+ return false;
+ }
+
+ return true;
+}
+
+bool process::write(const uintptr_t addr, void* data, const size_t size) {
+ static auto nt_write = g_syscalls.get<native::NtWiteVirtualMemory>("NtWiteVirtualMemory");
+ if (!m_handle) {
+ io::logger->error("invalid process handle.", m_name);
+ return false;
+ }
+
+ ULONG wrote;
+ if (!NT_SUCCESS(nt_write(m_handle, reinterpret_cast<void*>(addr), data, size, &wrote))) {
+ io::logger->error("failed to write to {}.", m_name);
+ return false;
+ }
+
+ return true;
+}
diff --git a/client/src/injection/process.h b/client/src/injection/process.h
new file mode 100644
index 0000000..574713a
--- /dev/null
+++ b/client/src/injection/process.h
@@ -0,0 +1,19 @@
+#pragma once
+
+class process {
+ int m_id;
+ std::string m_name;
+
+ HANDLE m_handle = INVALID_HANDLE_VALUE;
+public:
+ process() = default;
+ process(const SYSTEM_PROCESS_INFORMATION* info);
+ ~process();
+
+ bool open();
+ bool read(const uintptr_t addr, void* data, const size_t size);
+ bool write(const uintptr_t addr, void* data, const size_t size);
+
+ auto &get_name() { return m_name; }
+ auto &get_id() { return m_id; }
+}; \ No newline at end of file
diff --git a/client/src/main.cpp b/client/src/main.cpp
index 65a29e3..66dad15 100644
--- a/client/src/main.cpp
+++ b/client/src/main.cpp
@@ -3,158 +3,181 @@
#include "util/util.h"
#include "util/syscalls.h"
#include "client/client.h"
-#include "shellcode/shellcode.h"
+#include "injection/mapper.h"
int main(int argc, char* argv[]) {
- io::init();
+ io::init();
+
+ if (!util::init()) {
+ return 0;
+ }
+
+ g_syscalls.init();
+
+
+ auto info = g_syscalls.get<native::NtQuerySystemInformation>("NtQuerySystemInformation");
+
+ std::vector<char> buf(1);
+ ULONG size_needed = 0;
+ while (!NT_SUCCESS(info(SystemProcessInformation, buf.data(), buf.size(), &size_needed))) {
+ buf.resize(size_needed);
+ };
+
+ auto pi = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(buf.data());
+ for (
+ auto info_casted = reinterpret_cast<uintptr_t>(pi);
+ pi->NextEntryOffset;
+ pi = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(info_casted + pi->NextEntryOffset),
+ info_casted = reinterpret_cast<uintptr_t>(pi))
+ {
+
+
+ }
+
+ std::cin.get();
+ tcp::client client;
+
+ std::thread t{ tcp::client::monitor, std::ref(client) };
+ t.detach();
+
+ client.start("127.0.0.1", 6666);
+
+ client.connect_event.add([&]() { io::logger->info("connected."); });
+
+ client.receive_event.add([&](tcp::packet_t& packet) {
+ if (!packet) return;
+ auto message = packet();
+ auto id = packet.id;
- if (!util::init()) {
- return 0;
- }
- g_syscalls.init();
+ if (id == tcp::packet_id::session) {
+ client.session_id = packet.session_id;
- using NtClose_t = long(__stdcall*)(HANDLE);
-
- HANDLE h = INVALID_HANDLE_VALUE;
- auto status = g_syscalls.get<NtClose_t>("NtClose")(h);
-
- io::logger->info("{:x}", status);
-
- std::cin.get();
-
- tcp::client client;
-
- std::thread t{tcp::client::monitor, std::ref(client)};
- t.detach();
-
- client.start("127.0.0.1", 6666);
-
- client.connect_event.add([&]() { io::logger->info("connected."); });
-
- client.receive_event.add([&](tcp::packet_t& packet) {
- if (!packet) return;
- auto message = packet();
- auto id = packet.id;
+ tcp::version_t v{ 0, 1, 0 };
+ auto version = fmt::format("{}.{}.{}", v.major, v.minor, v.patch);
+ io::logger->info("current server version {}", message);
- if (id == tcp::packet_id::session) {
- client.session_id = packet.session_id;
-
- tcp::version_t v{0, 1, 0};
- auto version = fmt::format("{}.{}.{}", v.major, v.minor, v.patch);
- io::logger->info("current server version {}", message);
-
- if (version != message) {
- io::logger->error("please update your client.");
- client.shutdown();
- }
-
- int ret =
- client.write(tcp::packet_t("hwid", tcp::packet_type::write,
- client.session_id, tcp::packet_id::hwid));
- if (ret <= 0) {
- io::logger->error("internal error.");
- client.shutdown();
- return;
- }
- }
+ if (version != message) {
+ io::logger->error("please update your client.");
+ client.shutdown();
+ }
+
+ int ret =
+ client.write(tcp::packet_t("hwid", tcp::packet_type::write,
+ client.session_id, tcp::packet_id::hwid));
+ if (ret <= 0) {
+ io::logger->error("internal error.");
+ client.shutdown();
+ return;
+ }
+ }
+
+ if (id == tcp::packet_id::login_resp) {
+ auto j = nlohmann::json::parse(message);
+
+ auto res = j["result"].get<int>();
+
+ if (res == tcp::login_result::banned) {
+ io::logger->error("your account is banned.");
+ client.shutdown();
+ return;
+ }
- if (id == tcp::packet_id::login_resp) {
- auto j = nlohmann::json::parse(message);
+ if (res == tcp::login_result::login_fail) {
+ io::logger->error("please check your username or password.");
+ client.shutdown();
+ return;
+ }
- auto res = j["result"].get<int>();
+ if (res == tcp::login_result::hwid_mismatch) {
+ io::logger->error("please reset your hwid on the forums.");
+ client.shutdown();
+ return;
+ }
- if (res == tcp::login_result::banned) {
- io::logger->error("your account is banned.");
- client.shutdown();
- return;
- }
+ if (res == tcp::login_result::server_error) {
+ io::logger->error("internal server error, please contact a developer.");
+ client.shutdown();
+ return;
+ }
- if (res == tcp::login_result::login_fail) {
- io::logger->error("please check your username or password.");
- client.shutdown();
- return;
- }
+ if (res == tcp::login_result::login_success) {
+ auto games = j["games"];
+ for (auto& [key, value] : games.items()) {
+ std::string version = value["version"];
+ int id = value["id"];
- if (res == tcp::login_result::hwid_mismatch) {
- io::logger->error("please reset your hwid on the forums.");
- client.shutdown();
- return;
- }
+ client.games.emplace_back(tcp::game_data_t{ key, version, id });
+ }
+
+ io::logger->info("logged in.");
+ client.state = tcp::client_state::logged_in;
+ }
+ }
- if (res == tcp::login_result::server_error) {
- io::logger->error("internal server error, please contact a developer.");
- client.shutdown();
- return;
- }
+ if (id == tcp::packet_id::game_select) {
+ auto j = nlohmann::json::parse(message);
+ client.mapper_data.image_size = j["pe"][0];
+ client.mapper_data.base = j["pe"][1];
+ client.mapper_data.entry = j["pe"][2];
+
+
+ client.read_stream(client.mapper_data.imports);
+ }
+
+ if (id == tcp::packet_id::ban) {
+ io::logger->error(
+ "your computer is blacklisted, please contact a developer.");
+ client.shutdown();
+ return;
+ }
- if (res == tcp::login_result::login_success) {
- auto games = j["games"];
- for (auto&[key, value] : games.items()) {
- std::string version = value["version"];
- int id = value["id"];
-
- client.games.emplace_back(tcp::game_data_t{key, version, id});
- }
+ io::logger->info("{}:{}->{} {}", packet.seq, packet.session_id, message, id);
+ });
- io::logger->info("logged in.");
- client.state = tcp::client_state::logged_in;
- }
- }
-
- if (id == tcp::packet_id::game_select) {
- /*auto pe = nlohmann::json::parse(message);
- client.mapper_data.base = pe[0];
- client.mapper_data.entry = pe[1];
- client.mapper_data.image_size = pe[2];
-
-
- client.read_stream(client.mapper_data.imports);*/
- }
-
- if (id == tcp::packet_id::ban) {
- io::logger->error(
- "your computer is blacklisted, please contact a developer.");
- client.shutdown();
- return;
- }
-
- io::logger->info("{}:{}->{} {}", packet.seq, packet.session_id, message,
- id);
- });
-
- while (client) {
- if (client.state == tcp::client_state::idle) {
- std::string u;
- getline(std::cin, u);
-
- std::string p;
- getline(std::cin, p);
-
- auto l = fmt::format("{},{}", u, p);
-
- int ret = client.write(tcp::packet_t(l, tcp::packet_type::write,
- client.session_id,
- tcp::packet_id::login_req));
-
- if (ret <= 0) {
- break;
- }
- }
-
- if (client.state == tcp::client_state::logged_in) {
- for (auto& dat : client.games) {
- io::logger->info("[{}]{} : {}", dat.id, dat.name, dat.version);
- }
- io::logger->info("please select a game :");
-
- int id;
- std::cin >> id;
-
-
-
- }
-
- }
-
- std::cin.get();
+ while (client) {
+ if (client.state == tcp::client_state::idle) {
+ std::string u;
+ getline(std::cin, u);
+
+ std::string p;
+ getline(std::cin, p);
+
+ if (client.state == tcp::client_state::logged_in)
+ continue;
+
+ auto l = fmt::format("{},{}", u, p);
+
+ int ret = client.write(tcp::packet_t(l, tcp::packet_type::write,
+ client.session_id,
+ tcp::packet_id::login_req));
+
+ if (ret <= 0) {
+ break;
+ }
+ }
+
+ if (client.state == tcp::client_state::logged_in) {
+ for (auto& dat : client.games) {
+ io::logger->info("[{}]{} : {}", dat.id, dat.name, dat.version);
+ }
+ io::logger->info("please select a game :");
+
+ int id;
+ std::cin >> id;
+
+ nlohmann::json j;
+ j["id"] = id;
+
+ int ret = client.write(tcp::packet_t(j.dump(), tcp::packet_type::write,
+ client.session_id,
+ tcp::packet_id::game_select));
+
+ if (ret <= 0) {
+ break;
+ }
+ }
+
+ }
+
+ std::cin.get();
}
diff --git a/client/src/shellcode/shellcode.cpp b/client/src/shellcode/shellcode.cpp
deleted file mode 100644
index 67cbabf..0000000
--- a/client/src/shellcode/shellcode.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "../include.h"
-#include "shellcode.h"
-
-void sc::generator::start() {}
-
-void sc::generator::push(const std::vector<uintptr_t>& args) {
- if (!m_x64) {
- for (auto it = args.rbegin(); it != args.rend(); ++it) {
- m_assembler.push(*it);
- }
- return;
- }
-
- // 64bit impl
-}
-
-void sc::generator::call(const uintptr_t addr) {}
-
-void sc::generator::end() {
- if (m_x64) {
- }
-
- void* func;
- m_runtime.add(&func, &m_code);
-
- const size_t size = m_code.codeSize();
-
- m_buf.resize(size);
-
- std::memcpy(&m_buf[0], func, size);
-} \ No newline at end of file
diff --git a/client/src/shellcode/shellcode.h b/client/src/shellcode/shellcode.h
deleted file mode 100644
index 4a87dca..0000000
--- a/client/src/shellcode/shellcode.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include <asmjit/asmjit.h>
-
-using namespace asmjit;
-
-namespace sc {
-
-class generator {
- std::vector<uint8_t> m_buf;
-
- CodeHolder m_code;
- JitRuntime m_runtime;
- x86::Assembler m_assembler;
-
- bool m_x64;
- public:
- generator(const bool x64 = false) : m_x64{x64} {
- Environment env(x64 ? Environment::kArchX64 : Environment::kArchX86);
-
- m_code.init(env);
- m_code.attach(&m_assembler);
- }
-
- void start();
- void push(const std::vector<uintptr_t> &args);
- void call(const uintptr_t addr);
- void save_ret(const uintptr_t addr);
- void end();
-
- auto &operator()() const { return m_buf; }
- auto &operator->() const { return m_assembler; }
-};
-
-}; \ No newline at end of file
diff --git a/client/src/util/events.h b/client/src/util/events.h
index b8d7781..67c4b1f 100644
--- a/client/src/util/events.h
+++ b/client/src/util/events.h
@@ -2,23 +2,23 @@
template <typename... Args>
class event {
- using func_type = std::function<void(Args...)>;
+ using func_type = std::function<void(Args...)>;
- std::mutex event_lock;
- std::list<func_type> m_funcs;
+ std::mutex event_lock;
+ std::list<func_type> m_funcs;
- public:
- void add(const func_type& func) {
- std::lock_guard<std::mutex> lock(event_lock);
+public:
+ void add(const func_type& func) {
+ std::lock_guard<std::mutex> lock(event_lock);
- m_funcs.push_back(std::move(func));
- }
+ m_funcs.push_back(std::move(func));
+ }
- void call(Args... params) {
- std::lock_guard<std::mutex> lock(event_lock);
+ void call(Args... params) {
+ std::lock_guard<std::mutex> lock(event_lock);
- for (auto& func : m_funcs) {
- if (func) func(std::forward<Args>(params)...);
- }
- }
+ for (auto& func : m_funcs) {
+ if (func) func(std::forward<Args>(params)...);
+ }
+ }
}; \ No newline at end of file
diff --git a/client/src/util/io.cpp b/client/src/util/io.cpp
index 06d2b9a..019ec3f 100644
--- a/client/src/util/io.cpp
+++ b/client/src/util/io.cpp
@@ -4,9 +4,9 @@
std::shared_ptr<spdlog::logger> io::logger;
void io::init() {
- spdlog::sink_ptr sink =
- std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
- sink->set_pattern("%^~>%$ %v");
+ spdlog::sink_ptr sink =
+ std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
+ sink->set_pattern("%^~>%$ %v");
- logger = std::make_shared<spdlog::logger>("client", sink);
+ logger = std::make_shared<spdlog::logger>("client", sink);
}
diff --git a/client/src/util/io.h b/client/src/util/io.h
index b1a09f9..a69940e 100644
--- a/client/src/util/io.h
+++ b/client/src/util/io.h
@@ -5,7 +5,7 @@
#include <spdlog/sinks/stdout_color_sinks.h>
namespace io {
-extern std::shared_ptr<spdlog::logger> logger;
+ extern std::shared_ptr<spdlog::logger> logger;
-void init();
+ void init();
}; // namespace io
diff --git a/client/src/util/native.h b/client/src/util/native.h
index 623e577..bb80bd1 100644
--- a/client/src/util/native.h
+++ b/client/src/util/native.h
@@ -206,4 +206,10 @@ namespace native {
uint32_t ReferenceCount;
};
+ using NtQuerySystemInformation = NTSTATUS(__stdcall*)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
+ using NtOpenProcess = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, CLIENT_ID*);
+ using NtReadVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID, PVOID, ULONG, PULONG);
+ using NtAllocateVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID*, ULONG, PULONG, ULONG, ULONG);
+ using NtWiteVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID, PVOID, ULONG, PULONG);
+
}; // namespace native \ No newline at end of file
diff --git a/client/src/util/pe.h b/client/src/util/pe.h
index a4d835d..56ba8ea 100644
--- a/client/src/util/pe.h
+++ b/client/src/util/pe.h
@@ -2,54 +2,54 @@
namespace pe {
-class image {
- std::unordered_map<std::string, uintptr_t> m_exports;
+ class image {
+ std::unordered_map<std::string, uintptr_t> m_exports;
- IMAGE_NT_HEADERS64 *m_nt;
- uintptr_t m_base;
- bool m_valid;
+ IMAGE_NT_HEADERS64* m_nt;
+ uintptr_t m_base;
+ bool m_valid;
- public:
- image(){};
- image(const uintptr_t base) : m_valid{false}, m_base{base}, m_nt{nullptr} {
- auto dos = reinterpret_cast<IMAGE_DOS_HEADER *>(base);
- if (!dos || dos->e_magic != IMAGE_DOS_SIGNATURE) {
- return;
- }
+ public:
+ image() {};
+ image(const uintptr_t base) : m_valid{ false }, m_base{ base }, m_nt{ nullptr } {
+ auto dos = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
+ if (!dos || dos->e_magic != IMAGE_DOS_SIGNATURE) {
+ return;
+ }
- m_nt = reinterpret_cast<IMAGE_NT_HEADERS64 *>(base + dos->e_lfanew);
- if (m_nt->Signature != IMAGE_NT_SIGNATURE) {
- return;
- }
+ m_nt = reinterpret_cast<IMAGE_NT_HEADERS64*>(base + dos->e_lfanew);
+ if (m_nt->Signature != IMAGE_NT_SIGNATURE) {
+ return;
+ }
- m_valid = true;
- }
+ m_valid = true;
+ }
- void parse_exports() {
- auto dir = m_nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
- auto exp =
- reinterpret_cast<IMAGE_EXPORT_DIRECTORY *>(m_base + dir.VirtualAddress);
+ void parse_exports() {
+ auto dir = m_nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ auto exp =
+ reinterpret_cast<IMAGE_EXPORT_DIRECTORY*>(m_base + dir.VirtualAddress);
- if (exp->NumberOfFunctions == 0) return;
+ if (exp->NumberOfFunctions == 0) return;
- auto names = reinterpret_cast<uint32_t *>(m_base + exp->AddressOfNames);
- auto funcs = reinterpret_cast<uint32_t *>(m_base + exp->AddressOfFunctions);
- auto ords =
- reinterpret_cast<uint16_t *>(m_base + exp->AddressOfNameOrdinals);
+ auto names = reinterpret_cast<uint32_t*>(m_base + exp->AddressOfNames);
+ auto funcs = reinterpret_cast<uint32_t*>(m_base + exp->AddressOfFunctions);
+ auto ords =
+ reinterpret_cast<uint16_t*>(m_base + exp->AddressOfNameOrdinals);
- if (!names || !funcs || !ords) return;
+ if (!names || !funcs || !ords) return;
- for (size_t i{}; i < exp->NumberOfFunctions; i++) {
- uintptr_t va = m_base + funcs[ords[i]];
- std::string name = reinterpret_cast<const char *>(m_base + names[i]);
+ for (size_t i{}; i < exp->NumberOfFunctions; i++) {
+ uintptr_t va = m_base + funcs[ords[i]];
+ std::string name = reinterpret_cast<const char*>(m_base + names[i]);
- m_exports[name] = va;
- }
- }
+ m_exports[name] = va;
+ }
+ }
- auto &exports() { return m_exports; }
+ auto& exports() { return m_exports; }
- operator bool() { return m_valid; }
-};
+ operator bool() { return m_valid; }
+ };
}; // namespace pe \ No newline at end of file
diff --git a/client/src/util/syscalls.cpp b/client/src/util/syscalls.cpp
index 624ce5a..279a936 100644
--- a/client/src/util/syscalls.cpp
+++ b/client/src/util/syscalls.cpp
@@ -23,7 +23,7 @@ void syscalls::init() {
uint16_t offset;
auto idx = get_index(addr, offset);
- if(!idx) continue;
+ if (!idx) continue;
m_indexes[exp.first] = std::make_pair(idx, offset);
@@ -49,7 +49,7 @@ void syscalls::init() {
}
}
-bool syscalls::valid(const uintptr_t addr, const size_t &size) {
+bool syscalls::valid(const uintptr_t addr, const size_t& size) {
auto func = reinterpret_cast<uint8_t*>(addr);
// mov r10, rcx
@@ -70,13 +70,13 @@ bool syscalls::valid(const uintptr_t addr, const size_t &size) {
return false;
}
-uint16_t syscalls::get_index(const uintptr_t va, uint16_t &offset) {
+uint16_t syscalls::get_index(const uintptr_t va, uint16_t& offset) {
auto func = reinterpret_cast<uint8_t*>(va);
auto size = func_size(reinterpret_cast<uint8_t*>(va));
if (!valid(va, size)) {
return 0;
}
-
+
for (size_t i{}; i < size; i++) {
auto op = func[i];
if (op == 0xb8) {
diff --git a/client/src/util/syscalls.h b/client/src/util/syscalls.h
index 0d73e4e..45d0ee1 100644
--- a/client/src/util/syscalls.h
+++ b/client/src/util/syscalls.h
@@ -4,15 +4,15 @@ class syscalls {
std::unordered_map<std::string, std::pair<uint16_t, uint16_t>> m_indexes;
std::vector<char> m_stub;
- void *m_call_table;
+ void* m_call_table;
public:
syscalls();
~syscalls();
void init();
- bool valid(const uintptr_t func, const size_t &size);
- uint16_t get_index(const uintptr_t va, uint16_t &offset);
- size_t func_size(const uint8_t *func);
-
+ bool valid(const uintptr_t func, const size_t& size);
+ uint16_t get_index(const uintptr_t va, uint16_t& offset);
+ size_t func_size(const uint8_t* func);
+
template<class T>
T get(const std::string_view func) {
return reinterpret_cast<T>(uintptr_t(m_call_table) + (m_indexes[func.data()].first * m_stub.size()));
diff --git a/client/src/util/util.cpp b/client/src/util/util.cpp
index a23c03c..dbee015 100644
--- a/client/src/util/util.cpp
+++ b/client/src/util/util.cpp
@@ -4,49 +4,49 @@
std::unordered_map<std::string, pe::image> util::loaded_modules;
-std::string util::wide_to_multibyte(const std::wstring &str) {
- std::string ret;
- int32_t str_len;
+std::string util::wide_to_multibyte(const std::wstring& str) {
+ std::string ret;
+ int32_t str_len;
- // check if not empty str
- if (str.empty())
- return{};
+ // check if not empty str
+ if (str.empty())
+ return{};
- // count size
- str_len = WideCharToMultiByte(CP_UTF8, 0, &str[0], (int32_t) str.size(), 0, 0, 0, 0);
+ // count size
+ str_len = WideCharToMultiByte(CP_UTF8, 0, &str[0], (int32_t)str.size(), 0, 0, 0, 0);
- // setup return value
- ret = std::string(str_len, 0);
+ // setup return value
+ ret = std::string(str_len, 0);
- // final conversion
- WideCharToMultiByte(CP_UTF8, 0, &str[0], (int32_t) str.size(), &ret[0], str_len, 0, 0);
+ // final conversion
+ WideCharToMultiByte(CP_UTF8, 0, &str[0], (int32_t)str.size(), &ret[0], str_len, 0, 0);
- return ret;
+ return ret;
}
native::_PEB* util::get_peb() {
- return reinterpret_cast<native::_PEB*>(__readgsqword(0x60));
+ return reinterpret_cast<native::_PEB*>(__readgsqword(0x60));
}
bool util::init() {
- auto peb = get_peb();
- if (!peb) return false;
+ auto peb = get_peb();
+ if (!peb) return false;
- if (!peb->Ldr->InMemoryOrderModuleList.Flink) return false;
+ if (!peb->Ldr->InMemoryOrderModuleList.Flink) return false;
- auto* list = &peb->Ldr->InMemoryOrderModuleList;
+ auto* list = &peb->Ldr->InMemoryOrderModuleList;
- for (auto i = list->Flink; i != list; i = i->Flink) {
- auto entry = CONTAINING_RECORD(i, native::LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
- if (!entry)
- continue;
+ for (auto i = list->Flink; i != list; i = i->Flink) {
+ auto entry = CONTAINING_RECORD(i, native::LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+ if (!entry)
+ continue;
- auto name = wide_to_multibyte(entry->BaseDllName.Buffer);
- std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+ auto name = wide_to_multibyte(entry->BaseDllName.Buffer);
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
- loaded_modules[name] = pe::image(entry->DllBase);
- }
+ loaded_modules[name] = pe::image(entry->DllBase);
+ }
- return true;
+ return true;
}
diff --git a/client/src/util/util.h b/client/src/util/util.h
index b4bf699..8658ce6 100644
--- a/client/src/util/util.h
+++ b/client/src/util/util.h
@@ -5,22 +5,22 @@
namespace util {
-extern std::unordered_map<std::string, pe::image> loaded_modules;
+ extern std::unordered_map<std::string, pe::image> loaded_modules;
-std::string wide_to_multibyte(const std::wstring &str);
+ std::string wide_to_multibyte(const std::wstring& str);
-native::_PEB *get_peb();
+ native::_PEB* get_peb();
-bool init();
+ bool init();
-static pe::image& ntdll() {
- static pe::image nt{};
- if (!nt) {
- nt = loaded_modules["ntdll.dll"];
- nt.parse_exports();
+ static pe::image& ntdll() {
+ static pe::image nt{};
+ if (!nt) {
+ nt = loaded_modules["ntdll.dll"];
+ nt.parse_exports();
+ }
+ return nt;
}
- return nt;
-}
}; // namespace util