diff options
| author | auth12 <[email protected]> | 2020-08-01 11:15:55 -0700 |
|---|---|---|
| committer | auth12 <[email protected]> | 2020-08-01 11:15:55 -0700 |
| commit | 5bbda279685f52693d4f5d9cb1500e295e06fc1e (patch) | |
| tree | 87cc4aa993afe879f8b5dffbbe7013dcf8e5dc44 /client | |
| parent | Added server support for both x64 and x32 images with automatic selection. (diff) | |
| download | loader-5bbda279685f52693d4f5d9cb1500e295e06fc1e.tar.xz loader-5bbda279685f52693d4f5d9cb1500e295e06fc1e.zip | |
Started security.
Diffstat (limited to 'client')
| -rw-r--r-- | client/client.vcxproj | 2 | ||||
| -rw-r--r-- | client/client.vcxproj.filters | 9 | ||||
| -rw-r--r-- | client/src/client/client.h | 28 | ||||
| -rw-r--r-- | client/src/include.h | 1 | ||||
| -rw-r--r-- | client/src/injection/mapper.cpp | 112 | ||||
| -rw-r--r-- | client/src/injection/pe.h | 145 | ||||
| -rw-r--r-- | client/src/injection/process.cpp | 44 | ||||
| -rw-r--r-- | client/src/injection/process.h | 15 | ||||
| -rw-r--r-- | client/src/main.cpp | 151 | ||||
| -rw-r--r-- | client/src/security/security.cpp | 102 | ||||
| -rw-r--r-- | client/src/security/security.h | 13 | ||||
| -rw-r--r-- | client/src/util/apiset.h | 2 | ||||
| -rw-r--r-- | client/src/util/io.cpp | 6 | ||||
| -rw-r--r-- | client/src/util/io.h | 3 | ||||
| -rw-r--r-- | client/src/util/native.h | 158 | ||||
| -rw-r--r-- | client/src/util/syscalls.cpp | 4 | ||||
| -rw-r--r-- | client/src/util/syscalls.h | 4 | ||||
| -rw-r--r-- | client/src/util/util.cpp | 25 |
18 files changed, 529 insertions, 295 deletions
diff --git a/client/client.vcxproj b/client/client.vcxproj index 170107c..f5c6722 100644 --- a/client/client.vcxproj +++ b/client/client.vcxproj @@ -160,6 +160,7 @@ <ClInclude Include="src\include.h" /> <ClInclude Include="src\injection\pe.h" /> <ClInclude Include="src\injection\process.h" /> + <ClInclude Include="src\security\security.h" /> <ClInclude Include="src\util\apiset.h" /> <ClInclude Include="src\util\events.h" /> <ClInclude Include="src\util\io.h" /> @@ -172,6 +173,7 @@ <ClCompile Include="src\injection\mapper.cpp" /> <ClCompile Include="src\injection\process.cpp" /> <ClCompile Include="src\main.cpp" /> + <ClCompile Include="src\security\security.cpp" /> <ClCompile Include="src\util\apiset.cpp" /> <ClCompile Include="src\util\io.cpp" /> <ClCompile Include="src\util\syscalls.cpp" /> diff --git a/client/client.vcxproj.filters b/client/client.vcxproj.filters index a064f8c..4adf81f 100644 --- a/client/client.vcxproj.filters +++ b/client/client.vcxproj.filters @@ -28,6 +28,9 @@ <Filter Include="src\util\apiset"> <UniqueIdentifier>{970f013c-b1c0-4229-9278-d3a305fec8e2}</UniqueIdentifier> </Filter> + <Filter Include="src\security"> + <UniqueIdentifier>{5927052f-0078-4cad-a301-620d081c18da}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClInclude Include="src\include.h"> @@ -75,6 +78,9 @@ <ClInclude Include="src\hwid\hwid.h"> <Filter>src\hwid</Filter> </ClInclude> + <ClInclude Include="src\security\security.h"> + <Filter>src\security</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="src\main.cpp"> @@ -101,5 +107,8 @@ <ClCompile Include="src\util\apiset.cpp"> <Filter>src\util\apiset</Filter> </ClCompile> + <ClCompile Include="src\security\security.cpp"> + <Filter>src\security</Filter> + </ClCompile> </ItemGroup> </Project>
\ No newline at end of file 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 |