diff options
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | driver-kmem.cpp | 62 | ||||
-rw-r--r-- | ht2.bat | 27 | ||||
-rw-r--r-- | hunt.cpp | 13 | ||||
-rw-r--r-- | hunt2.cpp | 204 | ||||
-rw-r--r-- | memory.cpp | 71 | ||||
-rw-r--r-- | memory.hpp | 47 |
8 files changed, 435 insertions, 5 deletions
@@ -20,19 +20,25 @@ HUNT_NAME = ht HUNT_OBJECTS = hunt.o memory.o HUNT_TARGET = $(HUNT_NAME).sys +HUNT1896_NAME = ht2 +HUNT1896_OBJECTS = hunt2.o memory.o +HUNT1896_TARGET = $(HUNT1896_NAME).sys + BF4_NAME = bf4 BF4_OBJECTS = bf4.o memory.o BF4_TARGET = $(BF4_NAME).sys -all: $(DRIVER_TARGET) $(TARKOV_TARGET) $(HUNT_TARGET) $(BF4_TARGET) +all: $(DRIVER_TARGET) $(TARKOV_TARGET) $(HUNT_TARGET) $(HUNT1896_TARGET) $(BF4_TARGET) -install: $(DRIVER_TARGET) $(TARKOV_TARGET) $(HUNT_TARGET) $(BF4_TARGET) +install: $(DRIVER_TARGET) $(TARKOV_TARGET) $(HUNT_TARGET) $(HUNT1896_TARGET) $(BF4_TARGET) $(call INSTALL_EXEC_SIGN,$(DRIVER_TARGET)) $(INSTALL) $(DRIVER_NAME).bat $(DESTDIR) $(call INSTALL_EXEC_SIGN,$(TARKOV_TARGET)) $(INSTALL) $(TARKOV_NAME).bat $(DESTDIR) $(call INSTALL_EXEC_SIGN,$(HUNT_TARGET)) $(INSTALL) $(HUNT_NAME).bat $(DESTDIR) + $(call INSTALL_EXEC_SIGN,$(HUNT1896_TARGET)) + $(INSTALL) $(HUNT1896_NAME).bat $(DESTDIR) $(call INSTALL_EXEC_SIGN,$(BF4_TARGET)) $(INSTALL) $(BF4_NAME).bat $(DESTDIR) @@ -40,6 +46,7 @@ clean: rm -f $(DRIVER_OBJECTS) $(DRIVER_TARGET) rm -f $(TARKOV_OBJECTS) $(TARKOV_TARGET) rm -f $(HUNT_OBJECTS) $(HUNT_TARGET) + rm -f $(HUNT1896_OBJECTS) $(HUNT1896_TARGET) rm -f $(BF4_OBJECTS) $(BF4_TARGET) memory-experimental.o: memory.cpp @@ -57,6 +64,9 @@ $(TARKOV_TARGET): $(TARKOV_OBJECTS) $(HUNT_TARGET): $(HUNT_OBJECTS) $(call LINK_CPP_KERNEL_TARGET,$(HUNT_OBJECTS),$@) +$(HUNT1896_TARGET): $(HUNT1896_OBJECTS) + $(call LINK_CPP_KERNEL_TARGET,$(HUNT1896_OBJECTS),$@) + $(BF4_TARGET): $(BF4_OBJECTS) $(call LINK_CPP_KERNEL_TARGET,$(BF4_OBJECTS),$@) @@ -34,4 +34,6 @@ Yet another game hack that does nothing but: ## Hunt: Showdown (ht.sys) +This cheat won't work after 2024-08-15 (CryEngine 5.11 update). + * chams diff --git a/driver-kmem.cpp b/driver-kmem.cpp index 60f7374..3b8fafe 100644 --- a/driver-kmem.cpp +++ b/driver-kmem.cpp @@ -17,6 +17,53 @@ NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject, UNREFERENCED_PARAMETER(DriverObject); UNREFERENCED_PARAMETER(RegistryPath); + const eastl::array<uint8_t, 10> buffer = {0x41, 0xDE, 0xAD, 0xC0, 0xDE, + 0xCA, 0xFE, 0xCA, 0xFE, 0x41}; + const eastl::array<uint8_t, 2> pattern_00 = {0xCA, 0xFE}; + const eastl::array<uint8_t, 2> pattern_01 = {0xFE, 0x41}; + const eastl::array<uint8_t, 1> pattern_02 = {0x41}; + const eastl::array<uint8_t, 10> pattern_03 = {0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41}; + eastl::vector<size_t> found_offsets; + + auto found = PatternScanner::SearchWithMask( + buffer.data(), eastl::size(buffer), pattern_00.data(), + eastl::size(pattern_00), "xx", found_offsets); + if (!found) { + DbgPrint("%s\n", "First pattern not found!"); + return STATUS_UNSUCCESSFUL; + } + found = PatternScanner::SearchWithMask( + buffer.data(), eastl::size(buffer), pattern_01.data(), + eastl::size(pattern_01), "xx", found_offsets); + if (!found) { + DbgPrint("%s\n", "Second pattern not found!"); + return STATUS_UNSUCCESSFUL; + } + found = PatternScanner::SearchWithMask( + buffer.data(), eastl::size(buffer), pattern_02.data(), + eastl::size(pattern_02), "x", found_offsets); + if (!found) { + DbgPrint("%s\n", "Third pattern not found!"); + return STATUS_UNSUCCESSFUL; + } + found = PatternScanner::SearchWithMask(buffer, pattern_03, "x????????x", + found_offsets); + if (!found) { + DbgPrint("%s\n", "Fourth pattern not found!"); + return STATUS_UNSUCCESSFUL; + } + found = PatternScanner::SearchWithMask( + buffer, {0xDE, 0xAD, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xFE}, "xx????xx", + found_offsets); + if (!found) { + DbgPrint("%s\n", "Fifth pattern not found!"); + return STATUS_UNSUCCESSFUL; + } + for (const auto offset : found_offsets) { + DbgPrint("Offset: %zu\n", offset); + } + DbgPrint("%s\n", "Starting thread.."); auto args = eastl::make_shared<ThreadArgs>(); thread.Start( @@ -156,9 +203,24 @@ NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject, DbgPrint("%s\n", page.toString().c_str()); } + PatternScanner::ProcessModule scanner(pep, obj, {0x4D, 0x5A, 0x90}, + "xxx"); + eastl::vector<size_t> results; + auto found = scanner.Scan(L"Explorer.EXE", results); + if (!found) + DbgPrint("%s\n", "PatternScanner::ProcessModule was unsuccessful"); + if (results.size() != 1) + DbgPrint( + "PatternScanner::ProcessModule was unsuccessful: %zu results\n", + results.size()); + else + DbgPrint("PatternScanner::ProcessModule found address for 'MZ\\x90': %p\n", results[0]); + ::CloseProcess(&pep, &obj); } + DbgPrint("%s\n", "Done."); + return STATUS_SUCCESS; }, args); @@ -0,0 +1,27 @@ +@echo off +set SERVICE_NAME=ht2 +set DRIVER="%~dp0\ht2.sys" + +net session >nul 2>&1 +if NOT %ERRORLEVEL% EQU 0 ( + echo ERROR: This script requires Administrator privileges! + pause + exit /b 1 +) + +echo --------------------------------------- +echo -- Service Name: %SERVICE_NAME% +echo -- Driver......: %DRIVER% +echo --------------------------------------- + +sc create %SERVICE_NAME% binPath= %DRIVER% type= kernel +echo --------------------------------------- +sc start %SERVICE_NAME% +echo --------------------------------------- +sc query %SERVICE_NAME% +echo [PRESS A KEY TO STOP THE DRIVER] +pause +sc stop %SERVICE_NAME% +sc delete %SERVICE_NAME% +echo Done. +timeout /t 3 @@ -1,3 +1,6 @@ +/* + * Won't work after CryEngine 5.11 update (2024-08-15) + */ #include <ntddk.h> #include <EASTL/array.h> @@ -9,6 +12,9 @@ #include "memory.hpp" #include "stringify.hpp" +#define STRNCMP_CR(haystack, needle) \ + (strncmp(haystack, skCrypt(needle), sizeof(needle) - 1)) + using namespace DriverThread; static Thread thread; @@ -104,7 +110,7 @@ NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject, // Offsets stolen from: https://www.unknowncheats.me/forum/3809820-post343.html Memory memory(pep); - auto sys_global_env = memory.Read<uint64_t>(base + 0x5EF2FA0); + auto sys_global_env = memory.Read<uint64_t>(base + 0x5EFCF90); auto entity_system = memory.Read<uint64_t>(sys_global_env + 0xA8); uint16_t number_of_objects = memory.Read<uint16_t>(entity_system + 0x4006A); @@ -120,8 +126,9 @@ NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject, char entity_name[128] = {}; memory.ReadString<sizeof(entity_name)>(entity_name_ptr, entity_name); - if (strncmp(entity_name, skCrypt("ShootingRange_Target"), sizeof("ShootingRange_Target") - 1) == 0 || - strncmp(entity_name, skCrypt("HunterBasic"), sizeof("HunterBasic") - 1) == 0) { + if (STRNCMP_CR(entity_name, "ShootingRange_Target") == 0 || + STRNCMP_CR(entity_name, "HunterBasic") == 0) + { hunters++; auto slots_ptr = memory.Read<uint64_t>(entity + 0xA8); diff --git a/hunt2.cpp b/hunt2.cpp new file mode 100644 index 0000000..4663973 --- /dev/null +++ b/hunt2.cpp @@ -0,0 +1,204 @@ +/* + * Won't work after CryEngine 5.11 update (2024-08-15) + */ +#include <ntddk.h> + +#include <EASTL/array.h> +#include <EASTL/unordered_map.h> +#include <eastl_compat.hpp> +#include <except.h> +#include <DriverThread.hpp> +#include <obfuscate.hpp> + +#include "memory.hpp" +#include "stringify.hpp" + +#define STRNCMP_CR(haystack, needle) \ + (strncmp(haystack, skCrypt(needle), sizeof(needle) - 1)) + +using namespace DriverThread; + +static Thread thread; +static Event shutdown_event; +static auto targetProcess = skCrypt(L"HuntGame.exe"); +static auto targetModule = skCrypt(L"GameHunt.dll"); + +enum ColorType : uint32_t { + Pink = 0xFFA0FFFF, + Red = 0xFF000080, + Green = 0x00FF00FF, + Blue = 0x0000FF60, + Cyan = 0x00FFFFFF, + Orange = 0xFFA500FF, + Yellow = 0xFFFF0060, + White = 0xFFFFFFFF +}; + +static uint64_t SearchHuntProcess(void) { + const auto &procs = ::GetProcesses(); + const auto &found = eastl::find_if(procs.begin(), procs.end(), + [](const auto &item) { + if (item.ProcessName == targetProcess) + return true; + return false; + }); + + if (found == procs.end()) { + return 0; + } + + return found->UniqueProcessId; +} + +extern "C" { +DRIVER_INITIALIZE DriverEntry; +DRIVER_UNLOAD DriverUnload; + +int mainloop_exception_handler(_In_ EXCEPTION_POINTERS * lpEP) +{ + (void)lpEP; + return EXCEPTION_EXECUTE_HANDLER; +} + +NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject, + _In_ PUNICODE_STRING RegistryPath) { + UNREFERENCED_PARAMETER(DriverObject); + UNREFERENCED_PARAMETER(RegistryPath); + + auto args = eastl::make_shared<ThreadArgs>(); + thread.Start( + [](eastl::shared_ptr<ThreadArgs> args) { + UNREFERENCED_PARAMETER(args); + + HANDLE hunt_pid = NULL; + PEPROCESS pep = NULL; + HANDLE obj = NULL; + uint64_t base = 0; + LONGLONG wait_timeout = (-1LL) * 10LL * 1000LL * 250LL; + + DbgPrint("%s\n", "start"); + __dpptry(mainloop_exception_handler, mainloop_seh) { + while (shutdown_event.Wait(wait_timeout) == STATUS_TIMEOUT) { + if (!hunt_pid) { + wait_timeout = (-1LL) * 10LL * 1000LL * 1000LL; + hunt_pid = reinterpret_cast<HANDLE>(SearchHuntProcess()); + if (hunt_pid == NULL) { + continue; + } + DbgPrint(skCrypt("pid: %p\n"), hunt_pid); + + if (!NT_SUCCESS(::OpenProcess(hunt_pid, &pep, &obj))) { + hunt_pid = NULL; + continue; + } + + base = 0; + while (!base && hunt_pid) { + LARGE_INTEGER wait = {.QuadPart = (-1LL) * 10LL * 1000LL * 5000LL}; + KeDelayExecutionThread(KernelMode, FALSE, &wait); + + const auto mods = ::GetModules(pep, FALSE); + for (const auto &mod : mods) { + if (mod.BaseDllName == targetModule) { + base = mod.DllBase; + break; + } + } + + hunt_pid = reinterpret_cast<HANDLE>(SearchHuntProcess()); + } + + if (!hunt_pid) { + LARGE_INTEGER wait = {.QuadPart = (-1LL) * 10LL * 1000LL * 5000LL}; + KeDelayExecutionThread(KernelMode, FALSE, &wait); + ::CloseProcess(&pep, &obj); + continue; + } + } + + // Offsets stolen from: https://www.unknowncheats.me/forum/other-fps-games/350352-hunt-showdown-51.html + Memory memory(pep); + auto sys_global_env = memory.Read<uint64_t>(base + 0x23582C0); + + auto entity_system = memory.Read<uint64_t>(sys_global_env + 0xC0); + uint16_t number_of_objects = memory.Read<uint16_t>(entity_system + 0x40092); + uint64_t entity_list = entity_system + 0x40098; + + for (decltype(number_of_objects) i = 0; i < number_of_objects; ++i) { + auto entity = memory.Read<uint64_t>(entity_list + i * sizeof(uint64_t)); + if (!entity) + continue; + + auto entity_name_ptr = memory.Read<uint64_t>(entity + 0x10); + char entity_name[128] = {}; + memory.ReadString<sizeof(entity_name)>(entity_name_ptr, entity_name); + + if (STRNCMP_CR(entity_name, "ShootingRange_Target") == 0 || + STRNCMP_CR(entity_name, "HunterBasic") == 0) + { + auto slots_ptr = memory.Read<uint64_t>(entity + 0xA8); + auto slot_ptr = memory.Read<uint64_t>(slots_ptr + 0); + auto render_node_ptr = memory.Read<uint64_t>(slot_ptr + 0xA0); + + auto rgba_color = memory.Read<uint32_t>(render_node_ptr + 0x2C); + if (rgba_color != ColorType::Cyan /* team */) { + memory.Write<uint32_t>(render_node_ptr + 0x10, 0x80018); + memory.Write<uint32_t>(render_node_ptr + 0x2C, ColorType::Pink); + } + } else if (STRNCMP_CR(entity_name, "Target_Butcher") == 0 || + STRNCMP_CR(entity_name, "Target_Assassin") == 0 || + STRNCMP_CR(entity_name, "Spider_target") == 0 || + STRNCMP_CR(entity_name, "Target_Scrapbeak") == 0 || + STRNCMP_CR(entity_name, "Grunts.specials") == 0 || + STRNCMP_CR(entity_name, "immolator_el") == 0) + { + auto slots_ptr = memory.Read<uint64_t>(entity + 0xA8); + auto slot_ptr = memory.Read<uint64_t>(slots_ptr + 0); + auto render_node_ptr = memory.Read<uint64_t>(slot_ptr + 0xA0); + + memory.Write<uint32_t>(render_node_ptr + 0x10, 0x80018); + memory.Write<uint32_t>(render_node_ptr + 0x2C, ColorType::Red); + } else if (STRNCMP_CR(entity_name, "currency_collection") == 0 || + STRNCMP_CR(entity_name, "cash_register") == 0 || + STRNCMP_CR(entity_name, "cash") == 0) + { + auto slots_ptr = memory.Read<uint64_t>(entity + 0xA8); + auto slot_ptr = memory.Read<uint64_t>(slots_ptr + 0); + auto render_node_ptr = memory.Read<uint64_t>(slot_ptr + 0xA0); + + memory.Write<uint32_t>(render_node_ptr + 0x10, 0x80018); + memory.Write<uint32_t>(render_node_ptr + 0x2C, ColorType::Yellow); + } else if (STRNCMP_CR(entity_name, "TraitCharm") == 0) + { + auto slots_ptr = memory.Read<uint64_t>(entity + 0xA8); + auto slot_ptr = memory.Read<uint64_t>(slots_ptr + 0); + auto render_node_ptr = memory.Read<uint64_t>(slot_ptr + 0xA0); + + memory.Write<uint32_t>(render_node_ptr + 0x10, 0x80018); + memory.Write<uint32_t>(render_node_ptr + 0x2C, ColorType::Blue); + } + } + } + } __dppexcept(mainloop_seh) { + return STATUS_UNSUCCESSFUL; + } __dpptryend(mainloop_seh); + + return STATUS_SUCCESS; + }, + args); + + return STATUS_SUCCESS; +} + +VOID DriverUnload(_In_ struct _DRIVER_OBJECT *DriverObject) { + UNREFERENCED_PARAMETER(DriverObject); + + DbgPrint("%s\n", "Waiting for thread termination.."); + __dpptry(mainloop_exception_handler, unload_seh) + { + shutdown_event.Notify(); + thread.WaitForTermination(); + } __dppexcept(unload_seh) { + } __dpptryend(unload_seh); +} +} @@ -1,4 +1,5 @@ #include <EASTL/finally.h> +#include <EASTL/shared_ptr.h> #include <eastl_compat.hpp> #include <except.h> @@ -601,3 +602,73 @@ NTSTATUS WriteVirtualMemory(_In_ PEPROCESS pep, _In_ const UCHAR *sourceAddress, return status; } + +bool PatternScanner::SearchWithMask(const uint8_t *buffer, size_t buffer_size, + const uint8_t *pattern, size_t pattern_size, + const eastl::string_view &mask, + eastl::vector<size_t> &results) { + bool found_some = false; + + if (mask.length() != pattern_size) + return false; + + for (size_t i = 0; i < buffer_size - pattern_size + 1; ++i) { + bool match = true; + for (size_t j = 0; j < pattern_size; ++j) { + if (mask[j] == 'x' && buffer[i + j] != pattern[j]) { + match = false; + break; + } + } + if (match) { + found_some = true; + results.push_back(i); + } + } + + return found_some; +} + +bool PatternScanner::ProcessModule::Scan(const eastl::wstring_view &module_name, + eastl::vector<size_t> &results, + size_t max_results) { + if (eastl::size(m_pattern) > 64) + return false; + + const auto mods = ::GetModules(m_pep, FALSE); + const auto mod = eastl::find_if(mods.begin(), mods.end(), + [&module_name](const Module &mod) { + return mod.BaseDllName == module_name; + }); + if (mod == mods.end()) + return false; + + auto copy_buffer = eastl::make_shared<eastl::array<uint8_t, 4096 * 8>>(); + const auto pages = ::GetPages(m_obj, m_max_pages); + for (const auto page : pages) { + if (page.BaseAddress < mod->DllBase || + page.BaseAddress + page.RegionSize > mod->DllBase + mod->SizeOfImage) + continue; + SIZE_T offset = 0; + while (offset < page.RegionSize - eastl::size(m_pattern) + 1) { + SIZE_T in_out_size = + eastl::min(eastl::size(*copy_buffer), page.RegionSize - offset); + const auto ret = + ::ReadVirtualMemory(m_pep, page.BaseAddress + offset, + eastl::begin(*copy_buffer), &in_out_size); + if (!NT_SUCCESS(ret) || in_out_size == 0) + break; + + if (SearchWithMask(eastl::begin(*copy_buffer), in_out_size, + m_pattern.begin(), eastl::size(m_pattern), m_mask, + results)) { + if (results.size() >= max_results) + return false; + } + + offset += in_out_size - eastl::size(m_pattern) + 1; + } + } + + return true; +} @@ -1,6 +1,8 @@ #ifndef MEMORY_H #define MEMORY_H 1 +#include <EASTL/array.h> +#include <EASTL/initializer_list.h> #include <EASTL/string.h> #include <EASTL/vector.h> #include <cstdint> @@ -175,4 +177,49 @@ private: NTSTATUS m_last_error; SIZE_T m_last_size; }; + +namespace PatternScanner { +bool SearchWithMask(const uint8_t *buffer, size_t buffer_size, + const uint8_t *pattern, size_t pattern_size, + const eastl::string_view &mask, + eastl::vector<size_t> &results); + +template <size_t PM, size_t N> +SearchWithMask(const eastl::array<uint8_t, N> &buffer, + const eastl::array<uint8_t, PM> &pattern, + const eastl::string_view &mask, eastl::vector<size_t> &results) { + return SearchWithMask(buffer.data(), eastl::size(buffer), pattern.data(), + eastl::size(pattern), mask, results); +} + +template <size_t N> +SearchWithMask(const eastl::array<uint8_t, N> &buffer, + const std::initializer_list<uint8_t> &pattern, + const eastl::string_view &mask, eastl::vector<size_t> &results) { + return SearchWithMask(buffer.data(), eastl::size(buffer), pattern.begin(), + eastl::size(pattern), mask, results); +} + +class ProcessModule { +public: + ProcessModule(_In_ PEPROCESS pep, _In_ HANDLE obj, + const std::initializer_list<uint8_t> &pattern, + const eastl::string_view &mask) + : m_max_pages(8192), m_pep(pep), m_obj(obj), m_pattern(pattern), + m_mask(mask), m_offset(0) {} + ProcessModule(const ProcessModule &) = delete; + + void SetMaxPages(SIZE_T new_max_pages) { m_max_pages = new_max_pages; } + bool Scan(const eastl::wstring_view &module_name, + eastl::vector<size_t> &results, size_t max_results = 128); + +private: + SIZE_T m_max_pages; + PEPROCESS m_pep; + HANDLE m_obj; + const std::initializer_list<uint8_t> &m_pattern; + const eastl::string_view &m_mask; + size_t m_offset; +}; +} // namespace PatternScanner #endif |