aboutsummaryrefslogtreecommitdiff
path: root/client/src/injection/process.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/injection/process.cpp')
-rw-r--r--client/src/injection/process.cpp350
1 files changed, 336 insertions, 14 deletions
diff --git a/client/src/injection/process.cpp b/client/src/injection/process.cpp
index 954e9a8..9b05963 100644
--- a/client/src/injection/process.cpp
+++ b/client/src/injection/process.cpp
@@ -4,21 +4,25 @@
#include "../util/util.h"
#include "process.h"
-process::process(const SYSTEM_PROCESS_INFORMATION* info) {
+util::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());
+ name.assign(name.data());
+
m_name = util::wide_to_multibyte(name);
m_id = int(info->UniqueProcessId);
+
+ m_handle = INVALID_HANDLE_VALUE;
}
-process::~process() {
+util::process::~process() {
m_name.clear();
}
-bool process::open() {
+bool util::process::open() {
CLIENT_ID cid = { HANDLE(m_id), 0 };
OBJECT_ATTRIBUTES oa;
oa.Length = sizeof(oa);
@@ -30,42 +34,360 @@ bool process::open() {
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);
+ auto status = nt_open(&m_handle, PROCESS_ALL_ACCESS, &oa, &cid);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to open handle to {}, status {:#X}.", m_name, (status & 0xFFFFFFFF));
+ return false;
+ }
+
+ io::logger->info("opened handle to {}.", m_name);
+
+ if (!enum_modules()) {
+ io::logger->error("failed to enumerate process modules.");
return false;
}
return true;
}
-bool process::read(const uintptr_t addr, void* data, const size_t size) {
+bool util::process::read(const uintptr_t addr, void* data, size_t size) {
static auto nt_read = g_syscalls.get<native::NtReadVirtualMemory>("NtReadVirtualMemory");
if (!m_handle) {
- io::logger->error("invalid process handle.", m_name);
+ io::logger->error("invalid {} 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);
+ auto status = nt_read(m_handle, reinterpret_cast<void*>(addr), data, size, &read);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to read at {:x}, status {:#X}.", addr, (status & 0xFFFFFFFF));
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");
+bool util::process::write(const uintptr_t addr, void* data, size_t size) {
+ static auto nt_write = g_syscalls.get<native::NtWiteVirtualMemory>("NtWriteVirtualMemory");
if (!m_handle) {
- io::logger->error("invalid process handle.", m_name);
+ io::logger->error("invalid {} 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);
+ auto status = nt_write(m_handle, reinterpret_cast<void*>(addr), data, size, &wrote);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to write to {}, status {:#X}.", m_name, (status & 0xFFFFFFFF));
+ return false;
+ }
+
+ return true;
+}
+
+bool util::process::free(const uintptr_t addr, size_t size) {
+ static auto nt_free = g_syscalls.get<native::NtFreeVirtualMemory>("NtFreeVirtualMemory");
+
+ void* cast_addr = reinterpret_cast<void*>(addr);
+ SIZE_T win_size = size;
+ auto status = nt_free(m_handle, &cast_addr, &win_size, MEM_RELEASE);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to free at {:x}, status {:#X}.", addr, (status & 0xFFFFFFFF));
+ return false;
+ }
+
+ return true;
+}
+
+bool util::process::thread(const uintptr_t start) {
+ static auto nt_create = g_syscalls.get<native::NtCreateThreadEx>("NtCreateThreadEx");
+ static auto nt_wait = g_syscalls.get<native::NtWaitForSingleObject>("NtWaitForSingleObject");
+
+ HANDLE out;
+ auto status = nt_create(&out, THREAD_ALL_ACCESS, nullptr, m_handle, reinterpret_cast<LPTHREAD_START_ROUTINE>(start), 0, 0x4, 0, 0, 0, 0);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to create thread in {}, status {:#X}.", m_name, (status & 0xFFFFFFFF));
+ return false;
+ }
+
+ status = nt_wait(out, false, nullptr);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to wait for handle {}, status {:#X}.", out, (status & 0xFFFFFFFF));
+
+ util::close_handle(out);
+ return false;
+ }
+
+ if (!util::close_handle(out)) {
+ return false;
+ }
+
+ return true;
+}
+
+uintptr_t util::process::load(const std::string_view mod) {
+ auto base = m_modules[mod.data()];
+ if (base) {
+ return base;
+ }
+
+ static auto loaddll = module_export("ntdll.dll", "LdrLoadDll");
+
+ auto name = allocate(0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+
+ std::string path{ "C:\\Windows\\SysWOW64\\" };
+ path.append(mod.data());
+
+ native::unicode_string_t<uint32_t> ustr = { 0 };
+
+ auto wpath = util::multibyte_to_wide(path.data());
+ ustr.Buffer = name + sizeof(ustr);
+ ustr.MaximumLength = ustr.Length = wpath.size() * sizeof(wchar_t);
+
+ if (!write(name, &ustr, sizeof(ustr))) {
+ io::logger->error("failed to write name.");
+ return {};
+ }
+
+ if (!write(name + sizeof(ustr), wpath.data(), wpath.size() * sizeof(wchar_t))) {
+ io::logger->error("failed to write path.");
+ return {};
+ }
+
+ static std::vector<uint8_t> shellcode = { 0x55, 0x89, 0xE5, 0x68, 0xEF, 0xBE, 0xAD,
+ 0xDE, 0x68, 0xEF, 0xBE, 0xAD, 0xDE, 0x6A, 0x00, 0x6A, 0x00, 0xB8,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0x89, 0xEC, 0x5D, 0xC3 };
+ *reinterpret_cast<uint32_t*>(&shellcode[4]) = name + 0x800;
+ *reinterpret_cast<uint32_t*>(&shellcode[9]) = name;
+ *reinterpret_cast<uint32_t*>(&shellcode[18]) = loaddll;
+
+ auto code = allocate(shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+ if (!write(code, shellcode.data(), shellcode.size())) {
+ io::logger->error("failed to write shellcode.");
+ return {};
+ }
+
+ io::logger->info("name : {:x}", name);
+ io::logger->info("shellcode : {:x}", code);
+
+ if (!thread(code)) {
+ io::logger->error("thread creation failed.");
+ return {};
+ }
+
+ if (!free(code, shellcode.size())) {
+ io::logger->error("failed to free shellcode.");
+ return {};
+ }
+
+ if (!free(name, 0x1000)) {
+ io::logger->error("failed to free name.");
+ return {};
+ }
+
+ enum_modules();
+
+ return m_modules[mod.data()];
+}
+
+bool util::process::enum_modules() {
+ static auto peb_addr = peb();
+
+ uint32_t ldr;
+ if (!read(peb_addr + offsetof(native::peb_t<uint32_t>, Ldr), &ldr, sizeof(ldr))) {
return false;
}
+ const auto list_head = ldr + offsetof(native::peb_ldr_data_t<uint32_t>, InLoadOrderModuleList);
+
+ uint32_t load_order_flink;
+ if (!read(list_head, &load_order_flink, sizeof(load_order_flink))) {
+ return false;
+ }
+
+ native::ldr_data_table_entry_t<uint32_t> entry;
+ for (auto list_curr = load_order_flink; list_curr != list_head;) {
+ if (!read(list_curr, &entry, sizeof(entry))) {
+ return false;
+ }
+
+ list_curr = uint32_t(entry.InLoadOrderLinks.Flink);
+
+ std::vector<wchar_t> name_vec(entry.FullDllName.Length);
+
+ if (!read(entry.FullDllName.Buffer, &name_vec[0], name_vec.size())) {
+ continue;
+ }
+
+ auto name = util::wide_to_multibyte(name_vec.data());
+ auto pos = name.rfind('\\');
+ if (pos != std::string::npos) {
+ name = name.substr(pos + 1);
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+
+ m_modules[name] = entry.DllBase;
+ }
+ }
+
+ return true;
+}
+
+uintptr_t util::process::peb() {
+ static auto nt_proc_info = g_syscalls.get<native::NtQueryInformationProcess>("NtQueryInformationProcess");
+
+ uintptr_t addr;
+ auto status = nt_proc_info(m_handle, ProcessWow64Information, &addr, sizeof(addr), nullptr);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to query {} info, status {:#X}.", m_name, (status & 0xFFFFFFFF));
+ return {};
+ }
+
+ return addr;
+}
+
+uintptr_t util::process::allocate(size_t size, uint32_t type, uint32_t protection) {
+ static auto nt_alloc = g_syscalls.get<native::NtAllocateVirtualMemory>("NtAllocateVirtualMemory");
+ if (!m_handle) {
+ io::logger->error("invalid {} handle.", m_name);
+ return {};
+ }
+
+ void* alloc = nullptr;
+ SIZE_T win_size = size;
+ auto status = nt_alloc(m_handle, &alloc, 0, &win_size, type, protection);
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to allocate in {}, status {:#X}.", m_name, (status & 0xFFFFFFFF));
+ return {};
+ }
+
+ return uintptr_t(alloc);
+}
+
+uintptr_t util::process::module_export(const std::string_view name, const std::string_view func) {
+ auto base = m_modules[name.data()];
+ if (!base) {
+ io::logger->error("module {} isnt loaded.", name);
+ return {};
+ }
+
+ IMAGE_DOS_HEADER dos{};
+ if (!read(base, &dos, sizeof(dos))) {
+ io::logger->info("failed to read dos header for {}", name);
+ return {};
+ }
+
+ if (dos.e_magic != IMAGE_DOS_SIGNATURE)
+ return {};
+
+ IMAGE_NT_HEADERS32 nt{};
+ if (!read(base + dos.e_lfanew, &nt, sizeof(nt))) {
+ io::logger->info("failed to read nt header for {}", name);
+ return {};
+ }
+
+ if (nt.Signature != IMAGE_NT_SIGNATURE)
+ return {};
+
+ IMAGE_EXPORT_DIRECTORY exp_dir{};
+ auto exp_va = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
+ .VirtualAddress;
+ auto exp_dir_size =
+ nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
+
+ auto exp_dir_start = base + exp_va;
+ auto exp_dir_end = exp_dir_start + exp_dir_size;
+
+ if (!read(exp_dir_start, &exp_dir, sizeof(exp_dir))) {
+ io::logger->info("failed to read export dir for {}", name);
+ return {};
+ }
+
+ auto funcs = base + exp_dir.AddressOfFunctions;
+ auto ords = base + exp_dir.AddressOfNameOrdinals;
+ auto names = base + exp_dir.AddressOfNames;
+
+ for (int i = 0; i < exp_dir.NumberOfFunctions; ++i) {
+ uint32_t name_rva{};
+ uint32_t func_rva{};
+ uint16_t ordinal{};
+
+ if (!read(names + (i * sizeof(uint32_t)), &name_rva, sizeof(uint32_t))) {
+ continue;
+ }
+ std::string name;
+ name.resize(func.size());
+
+ if (!read(base + name_rva, &name[0], name.size())) {
+ continue;
+ }
+
+ if (name == func) {
+ if (!read(ords + (i * sizeof(uint16_t)), &ordinal, sizeof(uint16_t))) {
+ return {};
+ }
+
+ if (!read(funcs + (ordinal * sizeof(uint32_t)), &func_rva, sizeof(uint32_t))) {
+ return {};
+ }
+
+ auto proc_addr = base + func_rva;
+ if (proc_addr >= exp_dir_start && proc_addr < exp_dir_end) {
+ std::array<char, 255> forwarded_name;
+ read(proc_addr, &forwarded_name[0], forwarded_name.size());
+
+ std::string name_str(forwarded_name.data());
+
+ size_t delim = name_str.find('.');
+ if (delim == std::string::npos) return {};
+
+ std::string fwd_mod_name = name_str.substr(0, delim + 1);
+ fwd_mod_name += "dll";
+
+ std::transform(fwd_mod_name.begin(), fwd_mod_name.end(), fwd_mod_name.begin(), ::tolower);
+
+ std::string fwd_func_name = name_str.substr(delim + 1);
+
+ return module_export(fwd_mod_name, fwd_func_name);
+ }
+
+ return proc_addr;
+ }
+ }
+
+ return {};
+}
+
+bool util::process::close() {
+ auto ret = util::close_handle(m_handle);
+ if (ret) {
+ io::logger->info("closed handle to {}.", m_name);
+ }
+ m_handle = INVALID_HANDLE_VALUE;
+ return ret;
+}
+
+std::vector<util::process> util::process_list;
+
+bool util::fetch_processes() {
+ auto info = g_syscalls.get<native::NtQuerySystemInformation>("NtQuerySystemInformation");
+
+ std::vector<char> buf(1);
+ ULONG size_needed = 0;
+ NTSTATUS status;
+ while ((status = info(SystemProcessInformation, buf.data(), buf.size(), &size_needed)) == STATUS_INFO_LENGTH_MISMATCH) {
+ buf.resize(size_needed);
+ };
+
+ if (!NT_SUCCESS(status)) {
+ io::logger->error("failed to get system process info, status {:#X}.", (status & 0xFFFFFFFF));
+ return false;
+ }
+
+ 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)) {
+
+ process_list.emplace_back(util::process(pi));
+ }
+
return true;
}