From e0a2eb55659978ee3f0b28c4ad1bd687242ca933 Mon Sep 17 00:00:00 2001 From: Kolyah35 Date: Mon, 6 Jan 2025 11:19:20 +0300 Subject: [PATCH] c string patch w/o zydis :sunglasses: --- api/PageManager.cpp | 76 ++++++++++++++++++++ api/PageManager.hpp | 46 ++++++++++++ api/stringPatch.cpp | 172 +++++--------------------------------------- libs/utf8 | 2 +- libs/zydis | 2 +- src/patches.cpp | 4 +- 6 files changed, 144 insertions(+), 158 deletions(-) create mode 100644 api/PageManager.cpp create mode 100644 api/PageManager.hpp diff --git a/api/PageManager.cpp b/api/PageManager.cpp new file mode 100644 index 0000000..f97db2f --- /dev/null +++ b/api/PageManager.cpp @@ -0,0 +1,76 @@ +#include + +using namespace geode::prelude; + +void PageManager::freeAll() { + for (auto& page : m_pages) { + page.free(); + } +} + +PageManager::Page& PageManager::getPageForSize(size_t size) { + if (m_pages.size() > 0 && m_pages.back().canFit(size)) { + return m_pages.back(); + } else { + return allocNewPage(); + } +} + +uint8_t* PageManager::getMemoryForSize(size_t size) { + auto& page = getPageForSize(size); + auto ret = page.getOffsetAddress(); + page.reserve(size); + return ret; +} + +PageManager::Page& PageManager::allocNewPage() { + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + + const uint64_t PAGE_SIZE = sysInfo.dwPageSize; + auto targetAddr = base::get() + 0x251000; // approximately the middle of the exe file + void* newPageAddr = nullptr; + uint64_t startAddr = (uint64_t(targetAddr) & ~(PAGE_SIZE - 1)); // round down to nearest page boundary + uint64_t minAddr = std::min(startAddr - 0x7FFFFF00, (uint64_t)sysInfo.lpMinimumApplicationAddress); + uint64_t maxAddr = std::max(startAddr + 0x7FFFFF00, (uint64_t)sysInfo.lpMaximumApplicationAddress); + uint64_t startPage = (startAddr - (startAddr % PAGE_SIZE)); + uint64_t pageOffset = 1; + + while (1) { + uint64_t byteOffset = pageOffset * PAGE_SIZE; + uint64_t highAddr = startPage + byteOffset; + uint64_t lowAddr = (startPage > byteOffset) ? startPage - byteOffset : 0; + bool needsExit = highAddr > maxAddr && lowAddr < minAddr; + + if (highAddr < maxAddr) { + void* outAddr = VirtualAlloc((void*)highAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (outAddr) { + newPageAddr = outAddr; + break; + } + } + + if (lowAddr > minAddr) { + void* outAddr = VirtualAlloc((void*)lowAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (outAddr != nullptr) { + newPageAddr = outAddr; + break; + } + } + + pageOffset++; + + if (needsExit) { + break; + } + } + + if (newPageAddr == nullptr) { + log::error("page failed to alloc (what?)"); + throw std::runtime_error("gdl: page failed to alloc"); + } + + m_pages.push_back(Page {.m_address = (uint8_t*)newPageAddr, .m_totalSize = PAGE_SIZE, .m_offset = 0, .m_id = m_pages.size()}); + + return m_pages.back(); +} \ No newline at end of file diff --git a/api/PageManager.hpp b/api/PageManager.hpp new file mode 100644 index 0000000..7cc82d8 --- /dev/null +++ b/api/PageManager.hpp @@ -0,0 +1,46 @@ +#pragma once +#include + +#if defined(GEODE_IS_WINDOWS64) + +class PageManager { + struct Page { + uint8_t* m_address; + size_t m_totalSize; + size_t m_offset; // aka used size + size_t m_id; + + inline uint8_t* getOffsetAddress() { return m_address + m_offset; } + inline bool canFit(size_t len) { return m_totalSize - m_offset >= len; } + inline void reserve(size_t len) { m_offset = std::min(m_offset + len, m_totalSize); } + void free() { + if (m_address) { + VirtualFree(m_address, m_totalSize, MEM_RELEASE); + m_address = nullptr; + m_totalSize = 0; + m_offset = 0; + } + } + }; + + public: + static PageManager& get() { + static PageManager inst; + return inst; + } + + ~PageManager() { freeAll(); } + + void freeAll(); + + // if the last page can fit those bytes, return it. otherwise, alloc a new page + Page& getPageForSize(size_t size); + uint8_t* getMemoryForSize(size_t size); + + private: + Page& allocNewPage(); + + std::vector m_pages; +}; + +#endif \ No newline at end of file diff --git a/api/stringPatch.cpp b/api/stringPatch.cpp index 8c67054..a2594aa 100644 --- a/api/stringPatch.cpp +++ b/api/stringPatch.cpp @@ -3,178 +3,42 @@ #include #include #include +#include using namespace geode::prelude; -#if defined(GEODE_IS_WINDOWS64) - -class PageManager { - struct Page { - uint8_t* m_address; - size_t m_totalSize; - size_t m_offset; // aka used size - size_t m_id; - - inline uint8_t* getOffsetAddress() { return m_address + m_offset; } - inline bool canFit(size_t len) { return m_totalSize - m_offset >= len; } - inline void reserve(size_t len) { m_offset = std::min(m_offset + len, m_totalSize); } - void free() { - if (m_address) { - VirtualFree(m_address, m_totalSize, MEM_RELEASE); - m_address = nullptr; - m_totalSize = 0; - m_offset = 0; - } - } - }; - - public: - static PageManager& get() { - static PageManager inst; - return inst; - } - - ~PageManager() { freeAll(); } - - void freeAll() { - for (auto& page : m_pages) - page.free(); - } - - // if the last page can fit those bytes, return it. otherwise, alloc a new page - Page& getPageForSize(size_t size) { - if (m_pages.size() > 0 && m_pages.back().canFit(size)) { - return m_pages.back(); - } else { - return allocNewPage(); - } - } - - uint8_t* getMemoryForSize(size_t size) { - auto& page = getPageForSize(size); - auto ret = page.getOffsetAddress(); - page.reserve(size); - return ret; - } - - private: - Page& allocNewPage() { - SYSTEM_INFO sysInfo; - GetSystemInfo(&sysInfo); - const uint64_t PAGE_SIZE = sysInfo.dwPageSize; - - auto targetAddr = base::get() + 0x251000; // approximately the middle of the exe file - - void* newPageAddr = nullptr; - - uint64_t startAddr = (uint64_t(targetAddr) & ~(PAGE_SIZE - 1)); // round down to nearest page boundary - uint64_t minAddr = std::min(startAddr - 0x7FFFFF00, (uint64_t)sysInfo.lpMinimumApplicationAddress); - uint64_t maxAddr = std::max(startAddr + 0x7FFFFF00, (uint64_t)sysInfo.lpMaximumApplicationAddress); - - uint64_t startPage = (startAddr - (startAddr % PAGE_SIZE)); - - uint64_t pageOffset = 1; - while (1) { - uint64_t byteOffset = pageOffset * PAGE_SIZE; - uint64_t highAddr = startPage + byteOffset; - uint64_t lowAddr = (startPage > byteOffset) ? startPage - byteOffset : 0; - - bool needsExit = highAddr > maxAddr && lowAddr < minAddr; - - if (highAddr < maxAddr) { - void* outAddr = VirtualAlloc((void*)highAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (outAddr) { - newPageAddr = outAddr; - break; - } - } - - if (lowAddr > minAddr) { - void* outAddr = VirtualAlloc((void*)lowAddr, PAGE_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (outAddr != nullptr) { - newPageAddr = outAddr; - break; - } - } - - pageOffset++; - - if (needsExit) { - break; - } - } - - if (newPageAddr == nullptr) { - log::error("page failed to alloc (what?)"); - throw std::runtime_error("gdl: page failed to alloc"); - } - - m_pages.push_back(Page {.m_address = (uint8_t*)newPageAddr, .m_totalSize = PAGE_SIZE, .m_offset = 0, .m_id = m_pages.size()}); - return m_pages.back(); - } - - std::vector m_pages; -}; - -#endif - namespace gdl { #if defined(GEODE_IS_WINDOWS64) bool patchCString(uintptr_t srcAddr, const char* str) { - ZydisDisassembledInstruction instruction; - if (ZYAN_SUCCESS(ZydisDisassembleIntel(ZYDIS_MACHINE_MODE_LONG_64, srcAddr, (void*)srcAddr, ZYDIS_MAX_INSTRUCTION_LENGTH, &instruction))) { - if (instruction.info.opcode != 0x8d) { - log::error("instruction is not lea!"); - return false; - } - - if (instruction.info.length != 7) { - log::error("wtf instruction isnt 7 bytes long"); - return false; - } - } else { - log::error("Failed to decomp the orig instruction!"); + uint8_t* arr = (uint8_t*)srcAddr; + if(arr[1] != 0x8D) { + log::error("0x{:X}: instruction isn't lea!", srcAddr); return false; } - // mov , - ZydisEncoderRequest req; - memset(&req, 0, sizeof(req)); - - req.mnemonic = ZYDIS_MNEMONIC_MOV; - req.machine_mode = ZYDIS_MACHINE_MODE_LONG_64; - req.operand_count = 2; - - req.operands[0].type = ZYDIS_OPERAND_TYPE_REGISTER; - req.operands[0].reg.value = instruction.operands[0].reg.value; + uint8_t reg = arr[2] >> 3; - req.operands[1].type = ZYDIS_OPERAND_TYPE_IMMEDIATE; - req.operands[1].imm.u = (uintptr_t)str; + std::array patch = { + static_cast(0x48 | (reg > 0x07)), // prefix 64 bit operation + static_cast(0xB8 | (reg & 0xF7)), // mov , + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // <64-bit value> + 0xC3 // ret + }; - ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH]; - ZyanUSize encoded_length = sizeof(encoded_instruction); - - if (ZYAN_FAILED(ZydisEncoderEncodeInstruction(&req, encoded_instruction, &encoded_length))) { - log::error("Failed to encode instruction!"); - return false; - } + *(uintptr_t*)(patch.data() + 2) = (uintptr_t)str; // because our instruction takes 10 bytes and the original one takes 7 bytes, we need to place a `call` in place of the original instruction that points to a trampoline // that we allocate which, in its turn, will contain the `mov` and `ret` instructions - auto tramp = PageManager::get().getMemoryForSize(encoded_length + 1); // + 1 because of `ret` - - memcpy(tramp, encoded_instruction, encoded_length); - tramp[encoded_length] = 0xC3; // ret - - // now, the call instruction + auto tramp = PageManager::get().getMemoryForSize(11); // + 1 because of `ret` + std::copy(patch.begin(), patch.end(), tramp); - uint8_t callBytes[] = {0xE8, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90}; // call and 2 NOPs + std::array callBytes = {0xE8, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90}; // call and 2 NOPs auto relAddr = (uintptr_t)tramp - ((uintptr_t)srcAddr + 5); // call is 5 bytes long - *(int32_t*)(callBytes + 1) = (int32_t)relAddr; + *(int32_t*)(callBytes.data() + 1) = (int32_t)relAddr; - if (Mod::get()->patch((void*)srcAddr, ByteVector(callBytes, callBytes + sizeof(callBytes))).isErr()) { - log::error("failed to patch!"); + if (Mod::get()->patch((void*)srcAddr, ByteVector(callBytes.begin(), callBytes.end())).isErr()) { + log::error("0x{:X}: failed to patch!", srcAddr); return false; } diff --git a/libs/utf8 b/libs/utf8 index b26a5f7..aed5828 160000 --- a/libs/utf8 +++ b/libs/utf8 @@ -1 +1 @@ -Subproject commit b26a5f718f4f370af1852a0d5c6ae8fa031ba7d0 +Subproject commit aed58281cf45838bdb7296e3109bd5a633d677ed diff --git a/libs/zydis b/libs/zydis index bffbb61..5a68f63 160000 --- a/libs/zydis +++ b/libs/zydis @@ -1 +1 @@ -Subproject commit bffbb610cfea643b98e87658b9058382f7522807 +Subproject commit 5a68f639e4f01604cc7bfc8d313f583a8137e3d3 diff --git a/src/patches.cpp b/src/patches.cpp index a1733e8..78764e1 100644 --- a/src/patches.cpp +++ b/src/patches.cpp @@ -39,8 +39,8 @@ void patches::patchStrings() { patches::fixCyrillicP(); #ifdef GEODE_IS_WINDOWS64 -// bool res3; -// res3 = gdl::patchCString(base::get() + 0x3151D5, "Привет, мир!"); + bool res3; + res3 = gdl::patchCString(base::get() + 0x320CB5, "Настройки"); // log::debug("{}", res3); // res3 = gdl::patchCString(base::get() + 0x350598, "Привет, мир 2!"); // log::debug("{}", res3);