From 5e27de842bb5faa868363218e69119555d6cfa16 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Sun, 9 Jun 2019 00:48:30 -0400 Subject: [PATCH] added string offsets to hook codes and searches, fixed stack alignment bug, fixed inaccurate documentation on x64 registers, fixed bug with search signature cache, imrpvoed hook search string detection --- GUI/host/host.cpp | 9 ++++-- GUI/host/util.cpp | 22 +++++++------- GUI/mainwindow.cpp | 2 ++ include/const.h | 2 +- include/types.h | 3 +- text.cpp | 9 ++++-- texthook/hookfinder.cc | 66 ++++++++++++++++++++++++------------------ texthook/main.cc | 6 +--- texthook/main.h | 2 +- texthook/texthook.cc | 29 ++++++++++--------- texthook/texthook.h | 4 +-- texthook/util/util.cc | 9 +++--- texthook/util/util.h | 2 +- 13 files changed, 90 insertions(+), 75 deletions(-) diff --git a/GUI/host/host.cpp b/GUI/host/host.cpp index d3afe8e..a65517b 100644 --- a/GUI/host/host.cpp +++ b/GUI/host/host.cpp @@ -108,11 +108,14 @@ namespace { auto info = *(HookFoundNotif*)buffer; auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound; - OnHookFound(info.hp, processId, info.text); + std::wstring wide = info.text; + if (wide.size() > STRING) OnHookFound(info.hp, processId, info.text); info.hp.type = USING_STRING; - if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage)) if (converted->size() > 12) OnHookFound(info.hp, processId, converted.value()); + if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage)) + if (converted->size() > STRING) OnHookFound(info.hp, processId, converted.value()); info.hp.codepage = CP_UTF8; - if (auto converted = Util::StringToWideString((char*)info.text, CP_UTF8)) if (converted->size() > 12) OnHookFound(info.hp, processId, converted.value()); + if (auto converted = Util::StringToWideString((char*)info.text, CP_UTF8)) + if (converted->size() > STRING) OnHookFound(info.hp, processId, converted.value()); } break; case HOST_NOTIFICATION_RMVHOOK: diff --git a/GUI/host/util.cpp b/GUI/host/util.cpp index 3b77ab5..3c4a6ab 100644 --- a/GUI/host/util.cpp +++ b/GUI/host/util.cpp @@ -41,15 +41,6 @@ namespace RCode.erase(0, match[0].length()); } - // [*deref_offset] - if (RCode[0] == L'0') RCode.erase(0, 1); // Legacy - if (std::regex_search(RCode, match, std::wregex(L"^\\*(-?[[:xdigit:]]+)"))) - { - hp.type |= DATA_INDIRECT; - hp.index = std::stoi(match[1], nullptr, 16); - RCode.erase(0, match[0].length()); - } - // @addr if (!std::regex_match(RCode, match, std::wregex(L"@([[:xdigit:]]+)"))) return {}; hp.address = std::stoull(match[1], nullptr, 16); @@ -132,6 +123,13 @@ namespace HCode.erase(0, match[0].length()); } + // [padding+] + if (std::regex_search(HCode, match, std::wregex(L"^([[:xdigit:]]+)\\+"))) + { + hp.padding = std::stoull(match[1], nullptr, 16); + HCode.erase(0, match[0].length()); + } + // data_offset if (!std::regex_search(HCode, match, std::wregex(L"^-?[[:xdigit:]]+"))) return {}; hp.offset = std::stoi(match[0], nullptr, 16); @@ -206,8 +204,6 @@ namespace RCode << std::uppercase << std::hex; - if (hp.type & DATA_INDIRECT) RCode << "*" << HexString(hp.index); - RCode << "@" << hp.address; return RCode.str(); @@ -239,6 +235,8 @@ namespace HCode << std::uppercase << std::hex; + if (hp.padding) HCode << hp.padding << "+"; + if (hp.offset < 0) hp.offset += 4; if (hp.split < 0) hp.split += 4; @@ -338,7 +336,7 @@ namespace Util assert(StringToWideString(u8"こんにちは").value() == L"こんにちは"), assert(ParseCode(L"/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")), assert(ParseCode(L"HB4@0")), - assert(ParseCode(L"/RS*10@44")), + assert(ParseCode(L"/RS65001#@44")), assert(!ParseCode(L"HQ@4")), assert(!ParseCode(L"/RW@44")), assert(!ParseCode(L"/HWG@33")) diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 485c599..eae35a8 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -33,6 +33,7 @@ extern const char* SEARCH_DURATION; extern const char* PATTERN_OFFSET; extern const char* MIN_ADDRESS; extern const char* MAX_ADDRESS; +extern const char* STRING_OFFSET; extern const char* HOOK_SEARCH_FILTER; extern const char* START_HOOK_SEARCH; extern const char* SAVE_SEARCH_RESULTS; @@ -353,6 +354,7 @@ void MainWindow::FindHooks() for (auto[value, label] : Array>{ { sp.minAddress = 0, MIN_ADDRESS }, { sp.maxAddress = -1ULL, MAX_ADDRESS }, + { sp.padding = 0, STRING_OFFSET } }) { auto input = new QLineEdit(QString::number(value, 16), this); diff --git a/include/const.h b/include/const.h index 9779867..62bd90d 100644 --- a/include/const.h +++ b/include/const.h @@ -4,7 +4,7 @@ // 8/23/2013 jichi // Branch: ITH/common.h, rev 128 -enum Misc { MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 2000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, HOOK_NAME_SIZE = 30, FIXED_SPLIT_VALUE = 0x10001 }; +enum Misc { STRING = 12, MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 2000, SHIFT_JIS = 932, MAX_MODULE_SIZE = 120, HOOK_NAME_SIZE = 30, FIXED_SPLIT_VALUE = 0x10001 }; enum HostCommandType { HOST_COMMAND_NEW_HOOK, HOST_COMMAND_REMOVE_HOOK, HOST_COMMAND_FIND_HOOK, HOST_COMMAND_MODIFY_HOOK, HOST_COMMAND_HIJACK_PROCESS, HOST_COMMAND_DETACH }; diff --git a/include/types.h b/include/types.h index 7249fd4..73c369b 100644 --- a/include/types.h +++ b/include/types.h @@ -40,6 +40,7 @@ struct HookParam DWORD type; // flags UINT codepage; // text encoding short length_offset; // index of the string length + uintptr_t padding; // padding DWORD user_value; // 7/20/2014: jichi additional parameters for PSP games void(*text_fun)(DWORD stack, HookParam* hp, BYTE obsoleteAlwaysZero, DWORD* data, DWORD* split, DWORD* len); @@ -64,7 +65,7 @@ struct SearchParam int length, // length of pattern offset, // offset from start of pattern to add hook searchTime; // ms - uintptr_t minAddress, maxAddress; + uintptr_t padding, minAddress, maxAddress; }; struct InsertHookCmd // From host diff --git a/text.cpp b/text.cpp index 4e18071..f745bc3 100644 --- a/text.cpp +++ b/text.cpp @@ -23,19 +23,21 @@ const char* CODE_INFODUMP = u8R"(Search for text S[codepage#]text OR Enter read code -R{S|Q|V}[null_length<][codepage#][*deref_offset]@addr +R{S|Q|V}[null_length<][codepage#]@addr OR Enter hook code -H{A|B|W|S|Q|V}[null_length<][N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]] +H{A|B|W|S|Q|V}[null_length<][N][codepage#][padding+]data_offset[*deref_offset][:split_offset[*deref_offset]]@addr[:module[:func]] All numbers except codepage/null_length in hexadecimal Default codepage is 932 (Shift-JIS) but this can be changed in settings A/B: codepage char little/big endian W: UTF-16 char S/Q/V: codepage/UTF-16/UTF-8 string +N: don't use context null_length: length of null terminator used for string +padding: length of padding data before string (C struct { int64_t size; char string[500]; } needs padding = 8) Negatives for data_offset/split_offset refer to registers -4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI --4 for RAX, -C for RBX, -14 for RCX, -1C for RDX, and so on for RSP, RBP, RSI, RDI, R8-R15 +-C for RAX, -14 for RBX, -1C for RCX, -24 for RDX, and so on for RSP, RBP, RSI, RDI, R8-R15 * means dereference pointer+deref_offset)"; const char* SAVE_SETTINGS = u8"Save settings"; const char* EXTEN_WINDOW_INSTRUCTIONS = u8R"(Drag and drop extension (.dll) files here from your computer to add them @@ -53,6 +55,7 @@ const char* SEARCH_DURATION = u8"Search duration (ms)"; const char* PATTERN_OFFSET = u8"Offset from pattern start"; const char* MIN_ADDRESS = u8"Minimum address (hex)"; const char* MAX_ADDRESS = u8"Maximum address (hex)"; +const char* STRING_OFFSET = u8"String offset (hex)"; const char* HOOK_SEARCH_FILTER = u8"Results must match this regex"; const char* START_HOOK_SEARCH = u8"Start hook search"; const char* SAVE_SEARCH_RESULTS = u8"Save search results"; diff --git a/texthook/hookfinder.cc b/texthook/hookfinder.cc index 833846b..53ce36f 100644 --- a/texthook/hookfinder.cc +++ b/texthook/hookfinder.cc @@ -11,22 +11,32 @@ extern WinMutex viewMutex; namespace { + SearchParam current; + constexpr int CACHE_SIZE = 500'000; struct HookRecord { - HookRecord() : address(0) {} - ~HookRecord() { if (address) NotifyHookFound(address, offset, text); } - uint64_t address; - int offset; - wchar_t text[200]; + ~HookRecord() + { + if (!address) return; + HookParam hp = {}; + hp.offset = offset; + hp.type = USING_UNICODE | USING_STRING; + hp.address = address; + hp.padding = current.padding; + NotifyHookFound(hp, (wchar_t*)text); + } + uint64_t address = 0; + int offset = 0; + char text[500] = {}; }; std::unique_ptr records; long recordsAvailable; - uint64_t addressCharCache[CACHE_SIZE] = {}; + uint64_t signatureCache[CACHE_SIZE] = {}; long sumCache[CACHE_SIZE] = {}; #ifndef _WIN64 - BYTE trampoline[32] = + BYTE trampoline[] = { 0x9c, // pushfd 0x60, // pushad @@ -43,7 +53,7 @@ namespace }; constexpr int addr_offset = 3, send_offset = 13, original_offset = 25, registers = 8; #else - BYTE trampoline[128] = { + BYTE trampoline[] = { 0x9c, // push rflags 0x50, // push rax 0x53, // push rbx @@ -69,7 +79,10 @@ namespace 0x48, 0x8d, 0x8c, 0x24, 0xa8, 0x00, 0x00, 0x00, // lea rcx,[rsp+0xa8] 0x48, 0xba, 0,0,0,0,0,0,0,0, // mov rcx,@addr 0x48, 0xb8, 0,0,0,0,0,0,0,0, // mov rax,@Send + 0x48, 0x89, 0xe3, // mov rbx,rsp + 0x48, 0x83, 0xe4, 0xf0, // and rsp,0xfffffffffffffff0 ; align stack 0xff, 0xd0, // call rax + 0x48, 0x89, 0xdc, // mov rsp,rbx 0xc5, 0xfa, 0x6f, 0x6c, 0x24, 0x10, // vmovdqu xmm5,XMMWORD PTR[rsp + 0x10] 0xc5, 0xfa, 0x6f, 0x24, 0x24, // vmovdqu xmm4,XMMWORD PTR[rsp] 0x48, 0x83, 0xc4, 0x20, // add rsp,0x20 @@ -93,11 +106,11 @@ namespace 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [0] ; relative to next instruction (i.e. jmp @original) 0,0,0,0,0,0,0,0 // @original }; - constexpr int addr_offset = 50, send_offset = 60, original_offset = 116, registers = 16; + constexpr int addr_offset = 50, send_offset = 60, original_offset = 126, registers = 16; #endif } -void Send(wchar_t** stack, uintptr_t address) +void Send(char** stack, uintptr_t address) { // it is unsafe to call ANY external functions from this, as they may have been hooked (if called the hook would call this function making an infinite loop) // the exceptions are compiler intrinsics like _InterlockedDecrement @@ -105,16 +118,17 @@ void Send(wchar_t** stack, uintptr_t address) for (int i = -registers; i < 6; ++i) { int length = 0, sum = 0; - __try { for (wchar_t* str = stack[i]; str[length] && length < 200; ++length) sum += str[length]; } + char* str = stack[i] + current.padding; + __try { for (; (str[length] || str[length + 1]) && length < 500; length += 2) sum += str[length] + str[length + 1]; } __except (EXCEPTION_EXECUTE_HANDLER) {} - if (length > 7 && length < 199) + if (length > STRING && length < 499) { __try { - // many duplicate results with same address and second character will be found: filter them out - uint64_t addressAndChar = (((uint64_t)stack[i][1]) << 48) | address; - if (addressCharCache[addressAndChar % CACHE_SIZE] == addressAndChar) continue; - addressCharCache[addressAndChar % CACHE_SIZE] = addressAndChar; + // many duplicate results with same address, offset, and third/fourth character will be found: filter them out + uint64_t signature = ((uint64_t)i << 56) | ((uint64_t)(str[2] + str[3]) << 48) | address; + if (signatureCache[signature % CACHE_SIZE] == signature) continue; + signatureCache[signature % CACHE_SIZE] = signature; // if there are huge amount of strings that are the same, it's probably garbage: filter them out // can't store all the strings, so use sum as heuristic instead if (_InterlockedIncrement(sumCache + (sum % CACHE_SIZE)) > 25) continue; @@ -127,8 +141,8 @@ void Send(wchar_t** stack, uintptr_t address) if (n > 0) { records[n].address = address; - records[n].offset = i * sizeof(wchar_t*); - for (int j = 0; j < length; ++j) records[n].text[j] = stack[i][j]; + records[n].offset = i * sizeof(char*); + for (int j = 0; j < length; ++j) records[n].text[j] = str[j]; records[n].text[length] = 0; } } @@ -145,12 +159,11 @@ void SearchForHooks(SearchParam sp) static std::mutex m; std::scoped_lock lock(m); - try - { - records = std::make_unique(recordsAvailable = CACHE_SIZE); - } + try { records = std::make_unique(recordsAvailable = CACHE_SIZE); } catch (std::bad_alloc&) { return ConsoleOutput("Textractor: SearchForHooks ERROR (out of memory)"); } + current = sp; + uintptr_t moduleStartAddress = (uintptr_t)GetModuleHandleW(ITH_DLL); uintptr_t moduleStopAddress = moduleStartAddress; MEMORY_BASIC_INFORMATION info; @@ -162,12 +175,9 @@ void SearchForHooks(SearchParam sp) moduleStopAddress -= info.RegionSize; ConsoleOutput(STARTING_SEARCH); - std::vector addresses = Util::SearchMemory(sp.pattern, sp.length); + std::vector addresses = Util::SearchMemory(sp.pattern, sp.length, PAGE_EXECUTE, sp.minAddress, sp.maxAddress); for (auto& addr : addresses) addr += sp.offset; - addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) - { - return (addr > moduleStartAddress && addr < moduleStopAddress) || addr > sp.maxAddress || addr < sp.minAddress; - }), addresses.end()); + addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) { return addr > moduleStartAddress && addr < moduleStopAddress; }), addresses.end()); *(void**)(trampoline + send_offset) = Send; auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE); VirtualProtect(trampolines, addresses.size() * sizeof(trampoline), PAGE_EXECUTE_READWRITE, DUMMY); @@ -189,7 +199,7 @@ void SearchForHooks(SearchParam sp) for (auto addr : addresses) MH_RemoveHook((void*)addr); records.reset(); VirtualFree(trampolines, 0, MEM_RELEASE); - for (int i = 0; i < CACHE_SIZE; ++i) addressCharCache[i] = sumCache[i] = 0; + for (int i = 0; i < CACHE_SIZE; ++i) signatureCache[i] = sumCache[i] = 0; ConsoleOutput(HOOK_SEARCH_FINISHED, CACHE_SIZE - recordsAvailable); }).detach(); } diff --git a/texthook/main.cc b/texthook/main.cc index 7f5c921..c5aade7 100644 --- a/texthook/main.cc +++ b/texthook/main.cc @@ -102,12 +102,8 @@ void ConsoleOutput(LPCSTR text, ...) WriteFile(hookPipe, &buffer, sizeof(buffer), DUMMY, nullptr); } -void NotifyHookFound(uint64_t addr, int offset, wchar_t* text) +void NotifyHookFound(HookParam hp, wchar_t* text) { - HookParam hp = {}; - hp.offset = offset; - hp.type = USING_UNICODE | USING_STRING; - hp.address = addr; HookFoundNotif buffer(hp, text); WriteFile(hookPipe, &buffer, sizeof(buffer), DUMMY, nullptr); } diff --git a/texthook/main.h b/texthook/main.h index 3342912..6e07e60 100644 --- a/texthook/main.h +++ b/texthook/main.h @@ -9,7 +9,7 @@ void TextOutput(ThreadParam tp, BYTE* text, int len); void ConsoleOutput(LPCSTR text, ...); -void NotifyHookFound(uint64_t addr, int offset, wchar_t* text); +void NotifyHookFound(HookParam hp, wchar_t* text); void NotifyHookRemove(uint64_t addr, LPCSTR name); void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE); void RemoveHook(uint64_t addr, int maxOffset = 9); diff --git a/texthook/texthook.cc b/texthook/texthook.cc index 0e2ecd0..ca13886 100644 --- a/texthook/texthook.cc +++ b/texthook/texthook.cc @@ -63,7 +63,10 @@ namespace { // unnamed 0x48, 0x8d, 0x94, 0x24, 0xa8, 0x00, 0x00, 0x00, // lea rdx,[rsp+0xa8] 0x48, 0xb9, 0,0,0,0,0,0,0,0, // mov rcx,@this 0x48, 0xb8, 0,0,0,0,0,0,0,0, // mov rax,@TextHook::Send + 0x48, 0x89, 0xe3, // mov rbx,rsp + 0x48, 0x83, 0xe4, 0xf0, // and rsp,0xfffffffffffffff0 ; align stack 0xff, 0xd0, // call rax + 0x48, 0x89, 0xdc, // mov rsp,rbx 0xc5, 0xfa, 0x6f, 0x6c, 0x24, 0x10, // vmovdqu xmm5,XMMWORD PTR[rsp + 0x10] 0xc5, 0xfa, 0x6f, 0x24, 0x24, // vmovdqu xmm4,XMMWORD PTR[rsp] 0x48, 0x83, 0xc4, 0x20, // add rsp,0x20 @@ -87,7 +90,7 @@ namespace { // unnamed 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword ptr [0] ; relative to next instruction (i.e. jmp @original) 0,0,0,0,0,0,0,0 // @original }; - int this_offset = 50, send_offset = 60, original_offset = 116; + int this_offset = 50, send_offset = 60, original_offset = 126; #endif bool trigger = false; @@ -142,6 +145,7 @@ void TextHook::Send(uintptr_t dwDataBase) if (hp.type & SPLIT_INDIRECT) dwSplit = *(DWORD *)(dwSplit + hp.split_index); } if (hp.type & DATA_INDIRECT) dwDataIn = *(DWORD *)(dwDataIn + hp.index); + dwDataIn += hp.padding; dwCount = GetLength(dwDataBase, dwDataIn); } @@ -173,6 +177,7 @@ void TextHook::Send(uintptr_t dwDataBase) } if (hp.type & DATA_INDIRECT) data = *(uintptr_t*)(data + hp.index); + data += hp.padding; count = GetLength(dwDataBase, data); if (count == 0) return; if (count > TEXT_BUFFER_SIZE) count = TEXT_BUFFER_SIZE; @@ -225,18 +230,15 @@ bool TextHook::InsertHookCode() return MH_EnableHook(location) == MH_OK; } -DWORD WINAPI TextHook::Reader(LPVOID hookPtr) +void TextHook::Read() { - TextHook* This = (TextHook*)hookPtr; BYTE buffer[TEXT_BUFFER_SIZE] = {}; int changeCount = 0, dataLen = 1; __try { - uint64_t currentAddress = This->address; - while (WaitForSingleObject(This->readerEvent, 500) == WAIT_TIMEOUT) + while (WaitForSingleObject(readerEvent, 500) == WAIT_TIMEOUT) { - if (This->hp.type & DATA_INDIRECT) currentAddress = *(uintptr_t*)This->address + This->hp.index; - if (memcmp(buffer, (void*)currentAddress, dataLen) == 0) + if (memcmp(buffer, location, dataLen) == 0) { changeCount = 0; continue; @@ -244,26 +246,25 @@ DWORD WINAPI TextHook::Reader(LPVOID hookPtr) if (++changeCount > 10) { ConsoleOutput(GARBAGE_MEMORY); - This->Clear(); + Clear(); break; } - dataLen = min(This->HookStrlen((BYTE*)currentAddress), TEXT_BUFFER_SIZE); - memcpy(buffer, (void*)currentAddress, dataLen); - TextOutput({ GetCurrentProcessId(), This->address, 0, 0 }, buffer, dataLen); + dataLen = min(HookStrlen((BYTE*)location), TEXT_BUFFER_SIZE); + memcpy(buffer, location, dataLen); + TextOutput({ GetCurrentProcessId(), address, 0, 0 }, buffer, dataLen); } } __except (EXCEPTION_EXECUTE_HANDLER) { ConsoleOutput(READ_ERROR); - This->Clear(); + Clear(); } - return 0; } bool TextHook::InsertReadCode() { - readerThread = CreateThread(nullptr, 0, Reader, this, 0, nullptr); + readerThread = CreateThread(nullptr, 0, [](void* This) { ((TextHook*)This)->Read(); return 0UL; }, this, 0, nullptr); readerEvent = CreateEventW(nullptr, FALSE, FALSE, NULL); return true; } diff --git a/texthook/texthook.h b/texthook/texthook.h index e6dd620..c286099 100644 --- a/texthook/texthook.h +++ b/texthook/texthook.h @@ -29,7 +29,7 @@ public: void Clear(); private: - static DWORD WINAPI Reader(LPVOID hookPtr); + void Read(); bool InsertHookCode(); bool InsertReadCode(); void Send(uintptr_t dwDatabase); @@ -40,7 +40,7 @@ private: HANDLE readerThread, readerEvent; bool err; - BYTE trampoline[130]; + BYTE trampoline[x64 ? 140 : 40]; }; diff --git a/texthook/util/util.cc b/texthook/util/util.cc index b19bd62..f569fe9 100644 --- a/texthook/util/util.cc +++ b/texthook/util/util.cc @@ -301,7 +301,7 @@ bool SearchResourceString(LPCWSTR str) return false; } -std::vector SearchMemory(const void* bytes, short length, DWORD protect) +std::vector SearchMemory(const void* bytes, short length, DWORD protect, uintptr_t minAddr, uintptr_t maxAddr) { SYSTEM_INFO systemInfo; GetNativeSystemInfo(&systemInfo); @@ -316,15 +316,16 @@ std::vector SearchMemory(const void* bytes, short length, DWORD protec } else { - if (info.Protect >= protect && !(info.Protect & PAGE_GUARD)) validMemory.push_back({ (uint64_t)info.BaseAddress, info.RegionSize }); + if ((uint64_t)info.BaseAddress + info.RegionSize >= minAddr && info.Protect >= protect && !(info.Protect & PAGE_GUARD)) + validMemory.push_back({ (uint64_t)info.BaseAddress, info.RegionSize }); probe += info.RegionSize; } } std::vector ret; for (auto memory : validMemory) - for (uint64_t addr = memory.first; true;) - if (addr = SafeSearchMemory(addr, memory.first + memory.second, (const BYTE*)bytes, length)) + for (uint64_t addr = max(memory.first, minAddr); true;) + if (addr < maxAddr && (addr = SafeSearchMemory(addr, memory.first + memory.second, (const BYTE*)bytes, length))) ret.push_back(addr++); else break; diff --git a/texthook/util/util.h b/texthook/util/util.h index b3acef5..e37eb69 100644 --- a/texthook/util/util.h +++ b/texthook/util/util.h @@ -22,7 +22,7 @@ bool CheckFile(LPCWSTR name); bool SearchResourceString(LPCWSTR str); -std::vector SearchMemory(const void* bytes, short length, DWORD protect = PAGE_EXECUTE); +std::vector SearchMemory(const void* bytes, short length, DWORD protect = PAGE_EXECUTE, uintptr_t minAddr = 0, uintptr_t maxAddr = -1ULL); } // namespace Util