diff options
| author | auth12 <[email protected]> | 2020-07-22 08:37:58 -0700 |
|---|---|---|
| committer | auth12 <[email protected]> | 2020-07-22 08:37:58 -0700 |
| commit | 7caedef9a8c343b63cef6e971f4f87660520bb82 (patch) | |
| tree | 66477c42a768bf5efb1177130347170c62f6cb60 /client/src/injection | |
| parent | Added game selection. (diff) | |
| download | loader-7caedef9a8c343b63cef6e971f4f87660520bb82.tar.xz loader-7caedef9a8c343b63cef6e971f4f87660520bb82.zip | |
Client injection.
Process class implementation.
Diffstat (limited to 'client/src/injection')
| -rw-r--r-- | client/src/injection/mapper.cpp | 105 | ||||
| -rw-r--r-- | client/src/injection/mapper.h | 9 | ||||
| -rw-r--r-- | client/src/injection/process.cpp | 350 | ||||
| -rw-r--r-- | client/src/injection/process.h | 54 |
4 files changed, 479 insertions, 39 deletions
diff --git a/client/src/injection/mapper.cpp b/client/src/injection/mapper.cpp new file mode 100644 index 0000000..7fcb8b3 --- /dev/null +++ b/client/src/injection/mapper.cpp @@ -0,0 +1,105 @@ +#include "../include.h" +#include "../client/client.h" +#include "../util/util.h" +#include "process.h" +#include "mapper.h" + +void mmap::thread(tcp::client& client) { + while (client.mapper_data.imports.empty()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + util::fetch_processes(); + + auto needle = std::find_if(util::process_list.begin(), util::process_list.end(), [&](util::process& proc) { + return proc.name() == "notepad++.exe"; + }); + + while (needle == util::process_list.end()) { + std::this_thread::sleep_for(std::chrono::seconds(5)); + util::fetch_processes(); + io::logger->info("waiting for process.."); + needle = std::find_if(util::process_list.begin(), util::process_list.end(), [&](util::process& proc) { + return proc.name() == "notepad++.exe"; + }); + } + + needle->open(); + needle->enum_modules(); + + auto image = needle->allocate(client.mapper_data.image_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (!image) { + io::logger->error("failed to allocate memory for image."); + return; + } + + io::logger->info("image base : {:x}", image); + + auto imports = nlohmann::json::parse(client.mapper_data.imports); + + nlohmann::json final_imports; + for (auto& [key, value] : imports.items()) { + auto mod = key; + std::transform(mod.begin(), mod.end(), mod.begin(), ::tolower); + + auto base = needle->load(mod); + if (!base) { + io::logger->error("failed to load {}", mod); + continue; + } + + for (auto& i : value) { + auto name = i.get<std::string>(); + + auto func = needle->module_export(mod, name); + + final_imports[name] = func; + } + } + + nlohmann::json resp; + resp["alloc"] = image; + + client.write(tcp::packet_t(resp.dump(), tcp::packet_type::write, client.session_id, tcp::packet_id::image)); + + auto proc_imports = final_imports.dump(); + client.stream(proc_imports); + + io::logger->info("please wait..."); + while (client.mapper_data.image.empty()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + if (!needle->write(image, client.mapper_data.image.data(), client.mapper_data.image.size())) { + io::logger->error("failed to write image."); + return; + } + + auto entry = image + client.mapper_data.entry; + + io::logger->info("entry : {:x}", entry); + + static std::vector<uint8_t> shellcode = { 0x55, 0x89, 0xE5, 0x6A, 0x00, 0x6A, 0x01, 0x68, 0xEF, 0xBE, + 0xAD, 0xDE, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xFF, 0xD0, 0x89, 0xEC, 0x5D, 0xC3 }; + + *reinterpret_cast<uint32_t*>(&shellcode[8]) = image; + *reinterpret_cast<uint32_t*>(&shellcode[13]) = entry; + + auto code = needle->allocate(shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (!needle->write(code, shellcode.data(), shellcode.size())) { + io::logger->error("failed to write shellcode."); + return; + } + + io::logger->info("shellcode : {:x}", code); + + needle->thread(code); + + needle->free(code, shellcode.size()); + + needle->close(); + + io::logger->info("done"); + + std::cin.get(); +}
\ No newline at end of file diff --git a/client/src/injection/mapper.h b/client/src/injection/mapper.h index d1cfa5c..1c39d8b 100644 --- a/client/src/injection/mapper.h +++ b/client/src/injection/mapper.h @@ -2,13 +2,6 @@ namespace mmap { - void thread(tcp::client& client) { - while (client.mapper_data.imports.empty()) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - - - } + void thread(tcp::client& client); };
\ No newline at end of file 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; } diff --git a/client/src/injection/process.h b/client/src/injection/process.h index 574713a..ddbadf5 100644 --- a/client/src/injection/process.h +++ b/client/src/injection/process.h @@ -1,19 +1,39 @@ #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 +namespace util { + class process { + int m_id; + std::string m_name; + std::unordered_map<std::string, uintptr_t> m_modules; + + HANDLE m_handle; + public: + process() : m_handle{ INVALID_HANDLE_VALUE } {}; + process(const SYSTEM_PROCESS_INFORMATION* info); + ~process(); + + bool open(); + bool read(const uintptr_t addr, void* data, size_t size); + bool write(const uintptr_t addr, void* data, size_t size); + bool free(const uintptr_t addr, size_t size); + bool thread(const uintptr_t start); + bool enum_modules(); + + uintptr_t peb(); + uintptr_t load(const std::string_view mod); + uintptr_t allocate(size_t size, uint32_t type, uint32_t protection); + uintptr_t module_export(const std::string_view name, const std::string_view func); + + bool close(); + + operator bool() const { return m_handle != INVALID_HANDLE_VALUE; } + + auto& name() { return m_name; } + auto& id() { return m_id; } + auto& handle() { return m_handle; } + }; + + extern std::vector<process> process_list; + + bool fetch_processes(); +}; |