aboutsummaryrefslogtreecommitdiff
path: root/client/src
diff options
context:
space:
mode:
authorauth12 <[email protected]>2020-08-01 11:15:55 -0700
committerauth12 <[email protected]>2020-08-01 11:15:55 -0700
commit5bbda279685f52693d4f5d9cb1500e295e06fc1e (patch)
tree87cc4aa993afe879f8b5dffbbe7013dcf8e5dc44 /client/src
parentAdded server support for both x64 and x32 images with automatic selection. (diff)
downloadloader-5bbda279685f52693d4f5d9cb1500e295e06fc1e.tar.xz
loader-5bbda279685f52693d4f5d9cb1500e295e06fc1e.zip
Started security.
Diffstat (limited to 'client/src')
-rw-r--r--client/src/client/client.h28
-rw-r--r--client/src/include.h1
-rw-r--r--client/src/injection/mapper.cpp112
-rw-r--r--client/src/injection/pe.h145
-rw-r--r--client/src/injection/process.cpp44
-rw-r--r--client/src/injection/process.h15
-rw-r--r--client/src/main.cpp151
-rw-r--r--client/src/security/security.cpp102
-rw-r--r--client/src/security/security.h13
-rw-r--r--client/src/util/apiset.h2
-rw-r--r--client/src/util/io.cpp6
-rw-r--r--client/src/util/io.h3
-rw-r--r--client/src/util/native.h158
-rw-r--r--client/src/util/syscalls.cpp4
-rw-r--r--client/src/util/syscalls.h4
-rw-r--r--client/src/util/util.cpp25
16 files changed, 518 insertions, 295 deletions
diff --git a/client/src/client/client.h b/client/src/client/client.h
index 91cb494..930a17d 100644
--- a/client/src/client/client.h
+++ b/client/src/client/client.h
@@ -15,20 +15,14 @@ struct mapper_data_t {
};
struct game_data_t {
- std::string name;
- std::string version;
- std::string process_name;
bool x64;
uint8_t id;
+ uint8_t version;
+ std::string name;
+ std::string process_name;
};
namespace tcp {
- struct version_t {
- uint8_t major;
- uint8_t minor;
- uint8_t patch;
- };
-
enum client_state {
idle = 0, logged_in, waiting, imports_ready, image_ready, injected
};
@@ -58,6 +52,8 @@ namespace tcp {
event<packet_t&> receive_event;
event<> connect_event;
+ uint16_t ver = 4640;
+
client() : m_socket{ -1 }, m_active{ false }, state{ client_state::idle }, m_server_ssl{ nullptr }, m_ssl_ctx{ nullptr } {}
void start(const std::string_view server_ip, const uint16_t port);
@@ -97,14 +93,17 @@ namespace tcp {
__forceinline void shutdown() {
m_active.store(false);
- closesocket(m_socket);
- wolfSSL_shutdown(m_server_ssl);
- wolfSSL_free(m_server_ssl);
+ if (m_server_ssl) {
+ closesocket(m_socket);
+ wolfSSL_shutdown(m_server_ssl);
+ wolfSSL_free(m_server_ssl);
+
+ m_socket = -1;
+ m_server_ssl = nullptr;
+ }
}
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());
@@ -114,6 +113,7 @@ namespace tcp {
}
io::log_error("connection lost.");
+ client.shutdown();
break;
}
std::string msg(buf.data(), ret);
diff --git a/client/src/include.h b/client/src/include.h
index 4119f0e..4fda470 100644
--- a/client/src/include.h
+++ b/client/src/include.h
@@ -1,6 +1,5 @@
#pragma once
-
#include <windows.h>
#include <stdio.h>
#include <winsock2.h>
diff --git a/client/src/injection/mapper.cpp b/client/src/injection/mapper.cpp
index c7f771c..28b5409 100644
--- a/client/src/injection/mapper.cpp
+++ b/client/src/injection/mapper.cpp
@@ -5,46 +5,74 @@
#include "mapper.h"
void mmap::thread(tcp::client& client) {
- while (client.state != tcp::client_state::imports_ready) {
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
+ while (client) {
+ if (client.state != tcp::client_state::imports_ready) {
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ continue;
+ }
- if (client.selected_game.x64) {
- map64(client);
+ if (client.selected_game.x64) {
+ map64(client);
- return;
- }
+ break;
+ }
- map32(client);
+ map32(client);
+ break;
+ }
}
void mmap::map32(tcp::client& client) {
- util::system_data_t dat;
- util::fetch_system_data(dat);
+ std::vector<util::process_data_t> dat;
+ if (!util::fetch_processes(dat)) {
+ io::log_error("failed to fetch processes.");
+ client.shutdown();
+ return;
+ }
- auto needle = std::find_if(dat.processes.begin(), dat.processes.end(), [&](util::process_data_t& dat) {
+ auto needle = std::find_if(dat.begin(), dat.end(), [&](util::process_data_t& dat) {
return dat.name == client.selected_game.process_name;
+ });
+
+ io::log("waiting for {}.", client.selected_game.process_name);
+ while (needle == dat.end()) {
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ if (!client) {
+ return;
+ }
+
+ if (!util::fetch_processes(dat)) {
+ io::log_error("failed to fetch processes.");
+ client.shutdown();
+ return;
+ }
+
+ needle = std::find_if(dat.begin(), dat.end(), [&](util::process_data_t& dat) {
+ return dat.name == client.selected_game.process_name;
});
- if (needle == dat.processes.end()) {
- io::log_error("failed to find process.");
- return;
+ io::log(".");
}
+ io::log("found!");
+
util::process<uint32_t> proc(*needle);
if (!proc.open()) {
+ client.shutdown();
return;
}
if (!proc.enum_modules()) {
io::log_error("failed to enum {} modules", proc.name());
+ client.shutdown();
return;
}
auto image = proc.allocate(client.mapper_data.image_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!image) {
io::log_error("failed to allocate memory for image.");
+ client.shutdown();
return;
}
@@ -75,11 +103,16 @@ void mmap::map32(tcp::client& client) {
io::log("please wait...");
while (client.state != tcp::client_state::image_ready) {
+ if (!client) {
+ return;
+ }
+
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (!proc.write(image, client.mapper_data.image.data(), client.mapper_data.image.size())) {
io::log_error("failed to write image.");
+ client.shutdown();
return;
}
client.mapper_data.image.clear();
@@ -97,6 +130,8 @@ void mmap::map32(tcp::client& client) {
auto code = proc.allocate(shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!proc.write(code, shellcode.data(), shellcode.size())) {
io::log_error("failed to write shellcode.");
+ client.shutdown();
+
return;
}
@@ -111,35 +146,64 @@ void mmap::map32(tcp::client& client) {
client.state = tcp::client_state::injected;
io::log("done");
+
+ client.shutdown();
}
void mmap::map64(tcp::client& client) {
- util::system_data_t dat;
- util::fetch_system_data(dat);
+ std::vector<util::process_data_t> dat;
+ if (!util::fetch_processes(dat)) {
+ io::log_error("failed to fetch processes.");
+ client.shutdown();
+ return;
+ }
- auto needle = std::find_if(dat.processes.begin(), dat.processes.end(), [&](util::process_data_t& dat) {
+ auto needle = std::find_if(dat.begin(), dat.end(), [&](util::process_data_t& dat) {
return dat.name == client.selected_game.process_name;
});
- if (needle == dat.processes.end()) {
- io::log_error("failed to find process.");
- return;
+ io::log("waiting for {}.", client.selected_game.process_name);
+ while (needle == dat.end()) {
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+
+ if (!client) {
+ return;
+ }
+
+ if (!util::fetch_processes(dat)) {
+ io::log_error("failed to fetch processes.");
+ client.shutdown();
+ return;
+ }
+
+ needle = std::find_if(dat.begin(), dat.end(), [&](util::process_data_t& dat) {
+ return dat.name == client.selected_game.process_name;
+ });
+
+ io::log(".");
}
+ io::log("found!");
+
util::process<uint64_t> proc(*needle);
if (!proc.open()) {
+ client.shutdown();
return;
}
if (!proc.enum_modules()) {
io::log_error("failed to enum {} modules", proc.name());
+
+ client.shutdown();
+
return;
}
auto image = proc.allocate(client.mapper_data.image_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!image) {
io::log_error("failed to allocate memory for image.");
+ client.shutdown();
return;
}
@@ -170,11 +234,16 @@ void mmap::map64(tcp::client& client) {
io::log("please wait...");
while (client.state != tcp::client_state::image_ready) {
+ if (!client) {
+ return;
+ }
+
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (!proc.write(image, client.mapper_data.image.data(), client.mapper_data.image.size())) {
io::log_error("failed to write image.");
+ client.shutdown();
return;
}
client.mapper_data.image.clear();
@@ -193,6 +262,7 @@ void mmap::map64(tcp::client& client) {
auto code = proc.allocate(shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!proc.write(code, shellcode.data(), shellcode.size())) {
io::log_error("failed to write shellcode.");
+ client.shutdown();
return;
}
@@ -207,4 +277,6 @@ void mmap::map64(tcp::client& client) {
client.state = tcp::client_state::injected;
io::log("done");
+
+ client.shutdown();
} \ No newline at end of file
diff --git a/client/src/injection/pe.h b/client/src/injection/pe.h
index f246108..fab41bb 100644
--- a/client/src/injection/pe.h
+++ b/client/src/injection/pe.h
@@ -3,14 +3,93 @@
#include "../util/native.h"
namespace pe {
+
+#pragma pack(push, 4)
+ struct reloc_entry_t {
+ uint16_t offset : 12;
+ uint16_t type : 4;
+ };
+
+ struct reloc_block_t {
+ uint32_t base_rva;
+ uint32_t size_block;
+ reloc_entry_t entries[1]; // Variable length array
+
+
+ inline reloc_block_t* get_next() { return (reloc_block_t*)((char*)this + this->size_block); }
+ inline uint32_t num_entries() { return (reloc_entry_t*)get_next() - &entries[0]; }
+ };
+
+ struct image_named_import_t
+ {
+ uint16_t hint;
+ char name[1];
+ };
+
+#pragma pack(push, 8)
+ struct image_thunk_data_x64_t
+ {
+ union
+ {
+ uint64_t forwarder_string;
+ uint64_t function;
+ uint64_t address; // -> image_named_import_t
+ struct
+ {
+ uint64_t ordinal : 16;
+ uint64_t _reserved0 : 47;
+ uint64_t is_ordinal : 1;
+ };
+ };
+ };
+#pragma pack(pop)
+
+ struct image_thunk_data_x86_t
+ {
+ union
+ {
+ uint32_t forwarder_string;
+ uint32_t function;
+ uint32_t address; // -> image_named_import_t
+ struct
+ {
+ uint32_t ordinal : 16;
+ uint32_t _reserved0 : 15;
+ uint32_t is_ordinal : 1;
+ };
+ };
+ };
+#pragma pack(pop)
+
+ template<bool x64,
+ typename base_type = typename std::conditional<x64, image_thunk_data_x64_t, image_thunk_data_x86_t>::type>
+ struct image_thunk_data_t : base_type {};
+
+ template<bool x64, typename base_type = typename std::conditional<x64, IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32>::type>
+ struct nt_headers_t : base_type {};
+
+ struct import_t {
+ std::string name;
+ uint32_t rva;
+ };
+
+ struct section_t {
+ std::string name;
+ size_t size;
+ size_t v_size;
+ uint32_t rva;
+ uint32_t va;
+ };
+
class virtual_image {
std::unordered_map<std::string, uintptr_t> m_exports;
+ std::vector<section_t> m_sections;
IMAGE_NT_HEADERS64* m_nt;
uintptr_t m_base;
public:
virtual_image() : m_nt{ nullptr }, m_base{ 0 } {};
- virtual_image(const std::string_view mod) : m_base{0}, m_nt{nullptr} {
+ virtual_image(const std::string_view mod) : m_base{ 0 }, m_nt{ nullptr } {
auto peb = util::peb();
if (!peb) return;
@@ -30,7 +109,7 @@ namespace pe {
m_base = uintptr_t(entry->DllBase);
auto dos = reinterpret_cast<IMAGE_DOS_HEADER*>(m_base);
- m_nt = reinterpret_cast<native::nt_headers_t<true>*>(m_base + dos->e_lfanew);
+ m_nt = reinterpret_cast<nt_headers_t<true>*>(m_base + dos->e_lfanew);
parse_exports();
break;
@@ -38,6 +117,24 @@ namespace pe {
}
}
+ virtual_image(const uintptr_t base) : m_base{ base }, m_nt{ nullptr } {
+ auto dos = reinterpret_cast<IMAGE_DOS_HEADER*>(m_base);
+
+ m_nt = reinterpret_cast<nt_headers_t<true>*>(m_base + dos->e_lfanew);
+ }
+
+ void parse_sections() {
+ auto secs = IMAGE_FIRST_SECTION(m_nt);
+ const size_t n = m_nt->FileHeader.NumberOfSections;
+
+ for (size_t i = 0; i < n; i++) {
+ auto sec = secs[i];
+
+ auto name = reinterpret_cast<const char*>(sec.Name);
+ m_sections.emplace_back(section_t{ name, sec.SizeOfRawData, sec.Misc.VirtualSize, sec.PointerToRawData, sec.VirtualAddress });
+ }
+ };
+
void parse_exports() {
auto dir = m_nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
auto exp =
@@ -60,20 +157,11 @@ namespace pe {
}
auto& exports() { return m_exports; }
- operator bool() { return m_base != 0; }
- };
-
- struct import_t {
- std::string name;
- uint32_t rva;
- };
+ auto &base() { return m_base; }
+ auto& nt() { return m_nt; }
+ auto& sections() { return m_sections; }
- struct section_t {
- std::string name;
- size_t size;
- size_t v_size;
- uint32_t rva;
- uint32_t va;
+ operator bool() { return m_base != 0; }
};
template <bool x64 = false>
@@ -83,9 +171,9 @@ namespace pe {
std::unordered_map<std::string, std::vector<import_t>> m_imports;
std::vector<section_t> m_sections;
- native::nt_headers_t<x64>* m_nt;
+ nt_headers_t<x64>* m_nt;
IMAGE_DOS_HEADER* m_dos;
- std::vector<std::pair<uint32_t, native::reloc_entry_t>> m_relocs;
+ std::vector<std::pair<uint32_t, reloc_entry_t>> m_relocs;
public:
image() = default;
@@ -94,7 +182,7 @@ namespace pe {
m_dos = reinterpret_cast<IMAGE_DOS_HEADER*>(m_buffer.data());
- m_nt = reinterpret_cast<native::nt_headers_t<x64>*>(m_buffer.data() + m_dos->e_lfanew);
+ m_nt = reinterpret_cast<nt_headers_t<x64>*>(m_buffer.data() + m_dos->e_lfanew);
load();
}
@@ -134,8 +222,8 @@ namespace pe {
if (!reloc_dir.Size) return;
const auto ptr = rva_to_ptr(reloc_dir.VirtualAddress);
- auto block = reinterpret_cast<native::reloc_block_t*>(ptr);
-
+ auto block = reinterpret_cast<reloc_block_t*>(ptr);
+
while (block->base_rva) {
for (size_t i = 0; i < block->num_entries(); ++i) {
auto entry = block->entries[i];
@@ -156,11 +244,11 @@ namespace pe {
for (uint32_t i = 0; i < table->Name; i = table->Name, ++table) {
auto mod_name = std::string(reinterpret_cast<char*>(rva_to_ptr(table->Name)));
- auto thunk = reinterpret_cast<native::image_thunk_data_t<x64>*>(rva_to_ptr(table->OriginalFirstThunk));
+ auto thunk = reinterpret_cast<image_thunk_data_t<x64>*>(rva_to_ptr(table->OriginalFirstThunk));
auto step = x64 ? sizeof(uint64_t) : sizeof(uint32_t);
for (uint32_t index = 0; thunk->address; index += step, ++thunk) {
- auto named_import = reinterpret_cast<native::image_named_import_t*>(rva_to_ptr(thunk->address));
+ auto named_import = reinterpret_cast<image_named_import_t*>(rva_to_ptr(thunk->address));
if (!thunk->is_ordinal) {
import_t data;
@@ -178,7 +266,7 @@ namespace pe {
void copy(std::vector<char>& out) {
out.resize(m_nt->OptionalHeader.SizeOfImage);
- std::memcpy(&out[0], &m_buffer[0], 4096);
+ std::memcpy(&out[0], &m_buffer[0], m_nt->OptionalHeader.SizeOfHeaders);
for (auto& sec : m_sections) {
std::memcpy(&out[sec.va], &m_buffer[sec.rva], sec.size);
@@ -209,4 +297,15 @@ namespace pe {
auto& relocs() const { return m_relocs; }
auto& sections() const { return m_sections; }
};
+
+
+ static virtual_image &ntdll() {
+ static virtual_image img{};
+ if (!img) {
+ img = virtual_image("ntdll.dll");
+ }
+ return img;
+ }
+
+ void get_all_modules(std::unordered_map<std::string, virtual_image>& modules);
}; // namespace pe \ No newline at end of file
diff --git a/client/src/injection/process.cpp b/client/src/injection/process.cpp
index 38a676b..3f3c96e 100644
--- a/client/src/injection/process.cpp
+++ b/client/src/injection/process.cpp
@@ -42,7 +42,7 @@ bool util::base_process::read(const uintptr_t addr, void* data, size_t size) {
}
bool util::base_process::write(const uintptr_t addr, void* data, size_t size) {
- static auto nt_write = g_syscalls.get<native::NtWiteVirtualMemory>("NtWriteVirtualMemory");
+ static auto nt_write = g_syscalls.get<native::NtWriteVirtualMemory>("NtWriteVirtualMemory");
ULONG wrote;
auto status = nt_write(m_handle, reinterpret_cast<void*>(addr), data, size, &wrote);
@@ -175,7 +175,7 @@ bool util::process<T>::enum_modules() {
template<typename T>
uintptr_t util::process<T>::peb() {
- constexpr bool is64 = sizeof(T) == sizeof(uint64_t);
+ constexpr bool is64 = std::is_same_v<T, uint64_t>;
if (is64) {
native::PROCESS_EXTENDED_BASIC_INFORMATION proc_info;
proc_info.Size = sizeof(proc_info);
@@ -209,8 +209,8 @@ uintptr_t util::process<T>::module_export(const uintptr_t base, const std::strin
if (dos.e_magic != IMAGE_DOS_SIGNATURE)
return {};
- constexpr bool is64 = sizeof(T) == sizeof(uint64_t);
- native::nt_headers_t<is64> nt{};
+ constexpr bool is64 = std::is_same_v<T, uint64_t>;
+ pe::nt_headers_t<is64> nt{};
if (!read(base + dos.e_lfanew, &nt, sizeof(nt))) {
io::log_error("failed to read nt header for {}", m_name);
return {};
@@ -291,7 +291,7 @@ uintptr_t util::process<T>::module_export(const uintptr_t base, const std::strin
template<typename T>
uintptr_t util::process<T>::map(const std::string_view module_name) {
std::string mod{ module_name };
- if (g_apiset(mod)) {
+ if (g_apiset.find(mod)) {
io::log("resolved {} -> {}", module_name, mod);
}
@@ -302,7 +302,7 @@ uintptr_t util::process<T>::map(const std::string_view module_name) {
io::log("mapping {}", module_name);
- constexpr bool is64 = sizeof(T) == sizeof(uint64_t);
+ constexpr bool is64 = std::is_same_v<T, uint64_t>;
std::string path{ is64 ? "C:\\Windows\\System32\\" : "C:\\Windows\\SysWOW64\\" };
path.append(mod);
@@ -331,7 +331,9 @@ uintptr_t util::process<T>::map(const std::string_view module_name) {
for (auto& [mod, funcs] : img.imports()) {
for (auto& func : funcs) {
auto addr = module_export(map(mod), func.name);
+
//io::log("{}:{}->{:x}", mod, func.name, addr);
+
*reinterpret_cast<T*>(&remote_image[func.rva]) = addr;
}
}
@@ -343,6 +345,7 @@ uintptr_t util::process<T>::map(const std::string_view module_name) {
}
io::log("{}->{:x}", mod, base);
+
m_modules[mod] = base;
return base;
@@ -352,14 +355,15 @@ uintptr_t util::process<T>::map(const std::string_view module_name) {
template class util::process<uint64_t>;
template class util::process<uint32_t>;
-bool util::fetch_system_data(system_data_t& out) {
+bool util::fetch_processes(std::vector<process_data_t>& out, bool threads /*= false*/) {
static auto info = g_syscalls.get<native::NtQuerySystemInformation>("NtQuerySystemInformation");
+ out.clear();
std::vector<uint8_t> buf(1);
ULONG size_needed = 0;
NTSTATUS status;
- while ((status = info(native::SystemProcessInformation, buf.data(), buf.size(), &size_needed)) == STATUS_INFO_LENGTH_MISMATCH) {
+ while ((status = info(SystemProcessInformation, buf.data(), buf.size(), &size_needed)) == STATUS_INFO_LENGTH_MISMATCH) {
buf.resize(size_needed);
};
@@ -368,28 +372,29 @@ bool util::fetch_system_data(system_data_t& out) {
return false;
}
- std::vector<thread_data_t> threads;
- std::vector<process_data_t> processes;
auto pi = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(buf.data());
while (pi->NextEntryOffset) {
std::wstring name(pi->ImageName.Buffer, pi->ImageName.Length / sizeof(wchar_t));
- processes.emplace_back(process_data_t{ util::wide_to_multibyte(name), int(pi->UniqueProcessId) });
+ process_data_t data{int(pi->UniqueProcessId), util::wide_to_multibyte(name)};
+
+ if (!threads) {
+ out.emplace_back(data);
+
+ pi = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(uintptr_t(pi) + pi->NextEntryOffset);
+ continue;
+ }
+ std::vector<thread_data_t> threads;
auto ti = reinterpret_cast<SYSTEM_THREAD_INFORMATION*>(uintptr_t(pi) + sizeof(SYSTEM_PROCESS_INFORMATION));
-
for (auto i = 0; i < pi->NumberOfThreads; ++i) {
- auto dat = ti[i];
- threads.emplace_back(thread_data_t{ int(dat.ClientId.UniqueProcess), uintptr_t(dat.ClientId.UniqueThread), dat.ThreadState });
+ auto thread = ti[i];
+ threads.emplace_back(thread_data_t{ thread.ClientId.UniqueThread, thread.ThreadState });
}
pi = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(uintptr_t(pi) + pi->NextEntryOffset);
}
-
- out.processes = std::move(processes);
- out.threads = std::move(threads);
-
return true;
}
@@ -400,7 +405,8 @@ bool util::fetch_process_handles(const int pid, std::vector<handle_info_t>& out)
ULONG size_needed = 0;
NTSTATUS status;
- while ((status = info(native::SystemHandleInformation, buf.data(), buf.size(), &size_needed)) == STATUS_INFO_LENGTH_MISMATCH) {
+ /* SystemHandleInformation */
+ while ((status = info(static_cast<SYSTEM_INFORMATION_CLASS>(16), buf.data(), buf.size(), &size_needed)) == STATUS_INFO_LENGTH_MISMATCH) {
buf.resize(size_needed);
};
diff --git a/client/src/injection/process.h b/client/src/injection/process.h
index e5d3f18..74cfccd 100644
--- a/client/src/injection/process.h
+++ b/client/src/injection/process.h
@@ -25,19 +25,14 @@ namespace util {
auto& id() { return m_id; }
};
- struct process_data_t {
- std::string name;
- int id;
- };
-
struct thread_data_t {
- int id;
- uintptr_t handle;
+ HANDLE handle;
uint32_t state;
};
- struct system_data_t {
- std::vector<process_data_t> processes;
+ struct process_data_t {
+ int id;
+ std::string name;
std::vector<thread_data_t> threads;
};
@@ -64,6 +59,6 @@ namespace util {
uint32_t obj_type;
};
- bool fetch_system_data(system_data_t& out);
+ bool fetch_processes(std::vector<process_data_t>& out, bool threads = false);
bool fetch_process_handles(const int pid, std::vector<handle_info_t>& out);
};
diff --git a/client/src/main.cpp b/client/src/main.cpp
index 71a064f..8737196 100644
--- a/client/src/main.cpp
+++ b/client/src/main.cpp
@@ -7,19 +7,37 @@
#include "injection/mapper.h"
#include "hwid/hwid.h"
#include "util/apiset.h"
+#include "security/security.h"
int main(int argc, char* argv[]) {
tcp::client client;
- std::thread t{ tcp::client::monitor, std::ref(client) };
- t.detach();
-
- std::thread t1{ mmap::thread, std::ref(client) };
+ std::thread sec_thread{ security::thread, std::ref(client) };
+ sec_thread.join();
client.start("127.0.0.1", 6666);
+ if (!client) {
+ io::log_error("failed to start client.");
+
+ io::log("press enter...");
+
+ std::cin.get();
+
+ return 0;
+ }
+
client.connect_event.add([&]() { io::log("connected."); });
+ std::thread t{ tcp::client::monitor, std::ref(client) };
+ t.detach();
+
+ std::thread mapper_thread{ mmap::thread, std::ref(client) };
+ mapper_thread.detach();
+
+ /*std::thread sec_thread{ security::thread, std::ref(client) };
+ sec_thread.detach();*/
+
client.receive_event.add([&](tcp::packet_t& packet) {
if (!packet) return;
auto message = packet();
@@ -28,20 +46,23 @@ int main(int argc, char* argv[]) {
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::log("current server version {}.", message);
+ uint16_t ver{0};
+ for (int i = 0; i < message.size(); ++i) {
+ if (i % 2) { // skip characters in between
+ continue;
+ }
+
+ ver += static_cast<uint8_t>(message[i]) << 5;
+ }
- if (version != message) {
+ if (client.ver != ver) {
io::log_error("please update your client.");
client.shutdown();
return;
}
auto hwid = hwid::fetch();
- int ret =
- client.write(tcp::packet_t(hwid, tcp::packet_type::write,
- client.session_id, tcp::packet_id::hwid));
+ int ret = client.write(tcp::packet_t(hwid, tcp::packet_type::write, client.session_id, tcp::packet_id::hwid));
if (ret <= 0) {
io::log_error("failed to send hwid.");
client.shutdown();
@@ -81,12 +102,12 @@ int main(int argc, char* argv[]) {
if (res == tcp::login_result::login_success) {
auto games = j["games"];
for (auto& [key, value] : games.items()) {
- std::string version = value["version"];
+ uint8_t version = value["version"];
std::string process = value["process"];
uint8_t id = value["id"];
bool x64 = value["x64"];
- client.games.emplace_back(game_data_t{ key, version, process, x64, id });
+ client.games.emplace_back(game_data_t{ x64, id, version, key, process });
}
io::log("logged in.");
@@ -125,64 +146,84 @@ int main(int argc, char* argv[]) {
io::log("{}:{}->{} {}", 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 u;
+ getline(std::cin, u);
- std::string p;
- getline(std::cin, p);
+ std::string p;
+ getline(std::cin, p);
- if (client.state == tcp::client_state::logged_in)
- continue;
+ auto l = fmt::format("{},{}", u, 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));
- int ret = client.write(tcp::packet_t(l, tcp::packet_type::write,
- client.session_id,
- tcp::packet_id::login_req));
+ if (ret <= 0) {
+ io::log_error("failed to send login req packet.");
+ client.shutdown();
- if (ret <= 0) {
- client.shutdown();
- break;
- }
+ io::log("press enter...");
+
+ std::cin.get();
+
+ return 0;
+ }
+
+ while (client.state != tcp::client_state::logged_in) {
+ if (!client) {
+ io::log("press enter...");
+
+ std::cin.get();
+
+ return 0;
}
- if (client.state == tcp::client_state::logged_in) {
- for (auto& dat : client.games) {
- io::log("[{}]{} : {}", dat.id, dat.name, dat.version);
- }
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ }
- io::log("please select a game :");
+ for (auto& dat : client.games) {
+ io::log("[{}]{} : {}", dat.id, dat.name, dat.version);
+ }
- int id;
- std::cin >> id;
+ io::log("please select a game :");
- auto it = std::find_if(client.games.begin(), client.games.end(), [&](game_data_t& dat) {
- return dat.id == id;
- });
- client.selected_game = *it;
+ int id;
+ std::cin >> id;
+ std::cin.ignore();
- nlohmann::json j;
- j["id"] = client.selected_game.process_name;
- j["x64"] = client.selected_game.x64;
+ auto it = std::find_if(client.games.begin(), client.games.end(), [&](game_data_t& dat) {
+ return dat.id == id;
+ });
+ client.selected_game = *it;
- int ret = client.write(tcp::packet_t(j.dump(), tcp::packet_type::write,
- client.session_id,
- tcp::packet_id::game_select));
+ nlohmann::json j;
+ j["id"] = client.selected_game.process_name;
+ j["x64"] = client.selected_game.x64;
- if (ret <= 0) {
- client.shutdown();
- break;
- }
+ ret = client.write(tcp::packet_t(j.dump(), tcp::packet_type::write,
+ client.session_id,
+ tcp::packet_id::game_select));
- client.state = tcp::client_state::waiting;
- break;
- }
+ if (ret <= 0) {
+ io::log_error("failed to send game select packet.");
+ client.shutdown();
+
+ io::log("press enter...");
+
+ std::cin.get();
+
+ return 0;
}
- t1.join();
+ client.state = tcp::client_state::waiting;
+
+ while (client) {
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ }
+
+ io::log("press enter...");
std::cin.get();
- std::cin.get();
+
+ return 0;
}
diff --git a/client/src/security/security.cpp b/client/src/security/security.cpp
new file mode 100644
index 0000000..c4408af
--- /dev/null
+++ b/client/src/security/security.cpp
@@ -0,0 +1,102 @@
+#include "../include.h"
+#include "../util/util.h"
+#include "../client/client.h"
+#include "../injection/process.h"
+#include "../util/apiset.h"
+#include "security.h"
+
+void security::thread(tcp::client& client) {
+ std::unordered_map<std::string, pe::image<true>> raw_images;
+ std::unordered_map<std::string, std::vector<char>> parsed_images;
+
+ std::unordered_map<std::string, pe::virtual_image> images;
+ pe::get_all_modules(images);
+ for (auto& [name, vi] : images) {
+ std::vector<char> raw;
+ char path[MAX_PATH];
+ GetModuleFileNameA(GetModuleHandleA(name.c_str()), path, MAX_PATH);
+
+ if (!io::read_file(path, raw)) {
+ io::log("failed to read {}.", name);
+ continue;
+ }
+
+ raw_images[name] = pe::image<true>(raw);
+ }
+
+ for (auto& [name, image] : raw_images) {
+ std::vector<char> mem;
+
+ image.copy(mem);
+ image.relocate(mem, uintptr_t(GetModuleHandleA(name.c_str())));
+
+ for (auto& [mod, funcs] : image.imports()) {
+ std::string mod_name{ mod };
+ g_apiset.find(mod_name);
+
+ for (auto& func : funcs) {
+ *reinterpret_cast<uintptr_t*>(&mem[func.rva]) = uintptr_t(GetProcAddress(GetModuleHandleA(mod_name.c_str()), func.name.c_str()));
+ }
+ }
+
+ parsed_images[name] = mem;
+ }
+
+ raw_images.clear();
+ images.clear();
+
+ while (1) {
+ std::unordered_map<std::string, pe::virtual_image> loaded_images;
+ pe::get_all_modules(loaded_images);
+
+ std::vector<patch_t> patches;
+ for (auto& [name, limage] : loaded_images) {
+ auto& parsed = parsed_images[name];
+ if (parsed.empty()) {
+ continue;
+ }
+
+ auto start = limage.base();
+ auto len = limage.nt()->OptionalHeader.SizeOfImage;
+
+
+ limage.parse_sections();
+ for (auto& sec : limage.sections()) {
+ if (sec.name != ".text") {
+ continue;
+ }
+
+
+ int ret = std::memcmp(&parsed[sec.va], reinterpret_cast<void*>(start + sec.va), sec.size);
+ if (ret != 0) {
+ io::log("found patch in {}.", name);
+ }
+
+ /*auto sec_start = reinterpret_cast<uint8_t*>(start + sec.va);
+ auto sec_len = sec.size;
+
+ for (size_t i = 0; i < sec_len; ++i) {
+ auto va = start + sec.va + i;
+ auto og_op = uint8_t(parsed[sec.va + i]);
+ auto cur_op = sec_start[i];
+
+ if (og_op != cur_op) {
+ patch_t patch;
+ patch.va = va;
+ patch.original_op = og_op;
+ patch.patched_op = cur_op;
+ patch.module = name;
+
+ patches.emplace_back(patch);
+ }
+ }*/
+ }
+ }
+
+ for (auto& patch : patches) {
+ io::log("found patch in {} at {:x}.", patch.module, patch.va);
+ }
+
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ }
+}
diff --git a/client/src/security/security.h b/client/src/security/security.h
new file mode 100644
index 0000000..6a765d9
--- /dev/null
+++ b/client/src/security/security.h
@@ -0,0 +1,13 @@
+#pragma once
+
+
+namespace security {
+ struct patch_t {
+ uintptr_t va;
+ uint8_t original_op;
+ uint8_t patched_op;
+ std::string module;
+ };
+
+ void thread(tcp::client &client);
+}; \ No newline at end of file
diff --git a/client/src/util/apiset.h b/client/src/util/apiset.h
index a13f3bb..9fad501 100644
--- a/client/src/util/apiset.h
+++ b/client/src/util/apiset.h
@@ -5,7 +5,7 @@ class apiset {
public:
apiset();
- bool operator()(std::string &mod) {
+ bool find(std::string &mod) {
auto it = std::find_if(m_apimap.begin(), m_apimap.end(), [&](const std::pair<std::string, std::string>& pair) {
return mod.find(pair.first) != std::string::npos;
});
diff --git a/client/src/util/io.cpp b/client/src/util/io.cpp
index 7e783c2..47d9dbe 100644
--- a/client/src/util/io.cpp
+++ b/client/src/util/io.cpp
@@ -1,10 +1,10 @@
#include "../include.h"
#include "io.h"
-bool io::read_file(const std::string_view name, std::vector<char>& out) {
- std::ifstream file(name.data(), std::ios::binary);
+bool io::read_file(const std::string_view path, std::vector<char>& out) {
+ std::ifstream file(path.data(), std::ios::binary);
if (!file.good()) {
- log_error("{} isnt valid.", name);
+ log_error("{} isnt valid.", path);
return false;
}
diff --git a/client/src/util/io.h b/client/src/util/io.h
index 03d6964..adb63f7 100644
--- a/client/src/util/io.h
+++ b/client/src/util/io.h
@@ -3,7 +3,6 @@
#include <fmt/format.h>
#include <fmt/color.h>
-
namespace io {
template<typename... Args>
void log(const std::string_view str, Args... params) {
@@ -31,5 +30,5 @@ namespace io {
fmt::print(msg, std::forward<Args>(params)...);
}
- bool read_file(const std::string_view name, std::vector<char>& out);
+ bool read_file(const std::string_view path, std::vector<char>& out);
}; // namespace io
diff --git a/client/src/util/native.h b/client/src/util/native.h
index 9c8cef7..31348ee 100644
--- a/client/src/util/native.h
+++ b/client/src/util/native.h
@@ -60,12 +60,11 @@ namespace native {
UNICODE_STRING BaseDllName;
};
- template<bool x64, typename base_type = typename std::conditional<x64, IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32>::type>
- struct nt_headers_t : base_type {};
-
template<class P>
struct peb_t {
- std::uint8_t _ignored[4];
+ uint8_t _ignored[2];
+ uint8_t being_debugged;
+ uint8_t bitfield;
P _ignored2[2];
P Ldr;
};
@@ -86,8 +85,8 @@ namespace native {
template<class P>
struct unicode_string_t {
- std::uint16_t Length;
- std::uint16_t MaximumLength;
+ uint16_t Length;
+ uint16_t MaximumLength;
P Buffer;
};
@@ -105,141 +104,6 @@ namespace native {
unicode_string_t<P> FullDllName;
};
-
-#pragma pack(push, 4)
- struct reloc_entry_t {
- uint16_t offset : 12;
- uint16_t type : 4;
- };
-
- struct reloc_block_t {
- uint32_t base_rva;
- uint32_t size_block;
- reloc_entry_t entries[ 1 ]; // Variable length array
-
-
- inline reloc_block_t* get_next() { return ( reloc_block_t* ) ( ( char* ) this + this->size_block ); }
- inline uint32_t num_entries() { return ( reloc_entry_t* ) get_next() - &entries[ 0 ]; }
- };
-
- struct image_named_import_t
- {
- uint16_t hint;
- char name[ 1 ];
- };
-
-#pragma pack(push, 8)
- struct image_thunk_data_x64_t
- {
- union
- {
- uint64_t forwarder_string;
- uint64_t function;
- uint64_t address; // -> image_named_import_t
- struct
- {
- uint64_t ordinal : 16;
- uint64_t _reserved0 : 47;
- uint64_t is_ordinal : 1;
- };
- };
- };
-#pragma pack(pop)
-
- struct image_thunk_data_x86_t
- {
- union
- {
- uint32_t forwarder_string;
- uint32_t function;
- uint32_t address; // -> image_named_import_t
- struct
- {
- uint32_t ordinal : 16;
- uint32_t _reserved0 : 15;
- uint32_t is_ordinal : 1;
- };
- };
- };
-#pragma pack(pop)
-
- template<bool x64,
- typename base_type = typename std::conditional<x64, image_thunk_data_x64_t, image_thunk_data_x86_t>::type>
- struct image_thunk_data_t : base_type {};
-
- typedef struct _PROCESS_EXTENDED_BASIC_INFORMATION
- {
- SIZE_T Size; // set to sizeof structure on input
- PROCESS_BASIC_INFORMATION BasicInfo;
- union
- {
- ULONG Flags;
- struct
- {
- ULONG IsProtectedProcess : 1;
- ULONG IsWow64Process : 1;
- ULONG IsProcessDeleting : 1;
- ULONG IsCrossSessionCreate : 1;
- ULONG IsFrozen : 1;
- ULONG IsBackground : 1;
- ULONG IsStronglyNamed : 1;
- ULONG IsSecureProcess : 1;
- ULONG IsSubsystemProcess : 1;
- ULONG SpareBits : 23;
- };
- };
- } PROCESS_EXTENDED_BASIC_INFORMATION, *PPROCESS_EXTENDED_BASIC_INFORMATION;
-
-
- typedef enum _SYSTEM_INFORMATION_CLASS {
- SystemBasicInformation,
- SystemProcessorInformation,
- SystemPerformanceInformation,
- SystemTimeOfDayInformation,
- SystemPathInformation,
- SystemProcessInformation,
- SystemCallCountInformation,
- SystemDeviceInformation,
- SystemProcessorPerformanceInformation,
- SystemFlagsInformation,
- SystemCallTimeInformation,
- SystemModuleInformation,
- SystemLocksInformation,
- SystemStackTraceInformation,
- SystemPagedPoolInformation,
- SystemNonPagedPoolInformation,
- SystemHandleInformation,
- SystemObjectInformation,
- SystemPageFileInformation,
- SystemVdmInstemulInformation,
- SystemVdmBopInformation,
- SystemFileCacheInformation,
- SystemPoolTagInformation,
- SystemInterruptInformation,
- SystemDpcBehaviorInformation,
- SystemFullMemoryInformation,
- SystemLoadGdiDriverInformation,
- SystemUnloadGdiDriverInformation,
- SystemTimeAdjustmentInformation,
- SystemSummaryMemoryInformation,
- SystemNextEventIdInformation,
- SystemEventIdsInformation,
- SystemCrashDumpInformation,
- SystemExceptionInformation,
- SystemCrashDumpStateInformation,
- SystemKernelDebuggerInformation,
- SystemContextSwitchInformation,
- SystemRegistryQuotaInformation,
- SystemExtendServiceTableInformation,
- SystemPrioritySeperation,
- SystemPlugPlayBusInformation,
- SystemDockInformation,
- SystemPowerInformation,
- SystemProcessorSpeedInformation,
- SystemCurrentTimeZoneInformation,
- SystemLookasideInformation
- } SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;
-
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
USHORT UniqueProcessId;
USHORT CreatorBackTraceIndex;
@@ -287,15 +151,23 @@ namespace native {
ULONG Unk[2];
};
- using NtQuerySystemInformation = NTSTATUS(__stdcall*)(native::SYSTEM_INFORMATION_CLASS, PVOID, SIZE_T, PULONG);
+ struct PROCESS_EXTENDED_BASIC_INFORMATION {
+ SIZE_T Size; // set to sizeof structure on input
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ uint8_t Flags;
+ };
+
+ using NtQuerySystemInformation = NTSTATUS(__stdcall*)(SYSTEM_INFORMATION_CLASS, PVOID, SIZE_T, PULONG);
using NtOpenProcess = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, CLIENT_ID*);
+ using NtOpenThread = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, CLIENT_ID*);
using NtReadVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID, PVOID, SIZE_T, PULONG);
using NtAllocateVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG);
- using NtWiteVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID, PVOID, ULONG, PULONG);
+ using NtWriteVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID, PVOID, ULONG, PULONG);
using NtClose = NTSTATUS(__stdcall*)(HANDLE);
using NtFreeVirtualMemory = NTSTATUS(__stdcall*)(HANDLE, PVOID*, PSIZE_T, ULONG);
using NtQueryInformationProcess = NTSTATUS(__stdcall*)(HANDLE, PROCESSINFOCLASS, PVOID, SIZE_T, PULONG);
using NtWaitForSingleObject = NTSTATUS(__stdcall*)(HANDLE, BOOLEAN, PLARGE_INTEGER);
using NtCreateThreadEx = NTSTATUS(__stdcall*)(PHANDLE, ACCESS_MASK, PVOID, HANDLE, LPTHREAD_START_ROUTINE, PVOID, ULONG, ULONG_PTR, SIZE_T, SIZE_T, PVOID);
+ using NtGetContextThread = NTSTATUS(__stdcall*)(HANDLE, PCONTEXT);
}; // namespace native \ No newline at end of file
diff --git a/client/src/util/syscalls.cpp b/client/src/util/syscalls.cpp
index 96036a3..a755d22 100644
--- a/client/src/util/syscalls.cpp
+++ b/client/src/util/syscalls.cpp
@@ -9,9 +9,9 @@ syscalls::syscalls() {
m_call_table = VirtualAlloc(0, 0x100000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
std::memset(m_call_table, 0x90, 0x100000);
- io::log("syscalls call table : {:#x}", uintptr_t(m_call_table));
+ io::log("syscalls call table : {:x}", uintptr_t(m_call_table));
- static auto nt = pe::virtual_image("ntdll.dll");
+ static auto nt = pe::ntdll();
for (auto& exp : nt.exports()) {
auto addr = exp.second;
diff --git a/client/src/util/syscalls.h b/client/src/util/syscalls.h
index 713e24c..0855d18 100644
--- a/client/src/util/syscalls.h
+++ b/client/src/util/syscalls.h
@@ -17,6 +17,10 @@ public:
return reinterpret_cast<T>(uintptr_t(m_call_table) + (m_indexes[func.data()].first * m_stub.size()));
};
+ uintptr_t operator()(const std::string_view func) {
+ return uintptr_t(m_call_table) + (m_indexes[func.data()].first * m_stub.size());
+ }
+
uintptr_t operator()() {
return uintptr_t(m_call_table);
}
diff --git a/client/src/util/util.cpp b/client/src/util/util.cpp
index 1847780..fab886e 100644
--- a/client/src/util/util.cpp
+++ b/client/src/util/util.cpp
@@ -15,7 +15,7 @@ std::string util::wide_to_multibyte(const std::wstring& str) {
str_len = WideCharToMultiByte(CP_UTF8, 0, &str[0], str.size(), 0, 0, 0, 0);
// setup return value
- ret = std::string(str_len, 0);
+ ret.resize(str_len);
// final conversion
WideCharToMultiByte(CP_UTF8, 0, &str[0], str.size(), &ret[0], str_len, 0, 0);
@@ -40,7 +40,7 @@ std::wstring util::multibyte_to_wide(const std::string& str) {
bool util::close_handle(HANDLE handle) {
if (!handle) {
- io::log_error("invalid handle specified to close.");
+ io::log_error("invalid handle to close.");
return false;
}
@@ -54,3 +54,24 @@ bool util::close_handle(HANDLE handle) {
return true;
}
+
+
+void pe::get_all_modules(std::unordered_map<std::string, virtual_image>& modules) {
+ auto peb = util::peb();
+ if (!peb) return;
+
+ if (!peb->Ldr->InMemoryOrderModuleList.Flink) return;
+
+ 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;
+
+ auto name = util::wide_to_multibyte(entry->BaseDllName.Buffer);
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+
+ modules[name] = virtual_image(entry->DllBase);
+ }
+} \ No newline at end of file