diff --git a/GUI/misc.cpp b/GUI/misc.cpp index f276a6a..6db111a 100644 --- a/GUI/misc.cpp +++ b/GUI/misc.cpp @@ -18,10 +18,10 @@ namespace case L'S': break; case L'Q': - hp.type |= USING_STRING | USING_UNICODE; + hp.type |= USING_UNICODE; break; case L'V': - hp.type |= USING_STRING | USING_UTF8; + hp.type |= USING_UTF8; break; default: return {}; @@ -36,7 +36,7 @@ namespace RCode.remove(0, codepage.captured(0).length()); } - // [*deref_offset|0] + // [*deref_offset] if (RCode.at(0).unicode() == L'0') RCode.remove(0, 1); // Legacy QRegularExpressionMatch deref = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(RCode); if (deref.hasMatch()) @@ -53,6 +53,24 @@ namespace return hp; } + std::optional ParseSCode(QString SCode) + { + HookParam hp = {}; + hp.type |= READ_SEARCH; + + // [codepage#] + QRegularExpressionMatch codepage = QRegularExpression("^([0-9]+)#").match(SCode); + if (codepage.hasMatch()) + { + hp.codepage = codepage.captured(1).toInt(); + SCode.remove(0, codepage.captured(0).length()); + } + + wcscpy_s(hp.text, S(SCode).c_str()); + + return hp; + } + std::optional ParseHCode(QString HCode) { HookParam hp = {}; @@ -157,7 +175,7 @@ namespace QString GenerateRCode(HookParam hp) { - QString RCode = "/R"; + QString RCode = "R"; QTextStream codeBuilder(&RCode); if (hp.type & USING_UNICODE) codeBuilder << "Q"; @@ -178,7 +196,7 @@ namespace QString GenerateHCode(HookParam hp, DWORD processId) { - QString HCode = "/H"; + QString HCode = "H"; QTextStream codeBuilder(&HCode); if (hp.type & USING_UNICODE) @@ -229,8 +247,10 @@ namespace std::optional ParseCode(QString code) { - if (code.startsWith("/H")) return ParseHCode(code.remove(0, 2)); - else if (code.startsWith("/R")) return ParseRCode(code.remove(0, 2)); + if (code.startsWith("/")) code.remove(0, 1); // legacy/AGTH compatibility + if (code.startsWith("R")) return ParseRCode(code.remove(0, 1)); + else if (code.startsWith("S")) return ParseSCode(code.remove(0, 1)); + else if (code.startsWith("H")) return ParseHCode(code.remove(0, 1)); else return {}; } @@ -242,7 +262,7 @@ QString GenerateCode(HookParam hp, DWORD processId) TEST( assert(ParseCode("/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")), - assert(ParseCode("/HB4@0")), + assert(ParseCode("HB4@0")), assert(ParseCode("/RS*10@44")), assert(!ParseCode("HQ@4")), assert(!ParseCode("/RW@44")), diff --git a/include/const.h b/include/const.h index f3cfb8c..0fef39b 100644 --- a/include/const.h +++ b/include/const.h @@ -33,8 +33,9 @@ enum HookParamType : unsigned long USING_SPLIT = 0x10, // aware of split time? SPLIT_INDIRECT = 0x20, MODULE_OFFSET = 0x40, // address is relative to module - FUNCTION_OFFSET = 0x80, // address is relative to function + FUNCTION_OFFSET = 0x80, // address is relative to function USING_UTF8 = 0x100, + READ_SEARCH = 0x200, // unspecified address: search for text instead NO_CONTEXT = 0x400, HOOK_EMPTY = 0x800, FIXING_SPLIT = 0x1000, diff --git a/include/text.h b/include/text.h index a792278..250f1cd 100644 --- a/include/text.h +++ b/include/text.h @@ -14,10 +14,13 @@ constexpr auto SELECT_PROCESS = u8"Select Process"; constexpr auto ATTACH_INFO = u8"If you don't see the process you want to attach, try running with admin rights\r\n" u8"You can also type in the process id"; constexpr auto CODE_INFODUMP = u8"Enter hook code\r\n" -u8"/H{A|B|W|S|Q|V}[N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]]\r\n" +u8"H{A|B|W|S|Q|V}[N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]]\r\n" u8"OR\r\n" u8"Enter read code\r\n" -u8"/R{S|Q|V}[codepage#][*deref_offset|0]@addr\r\n" +u8"R{S|Q|V}[codepage#][*deref_offset]@addr\r\n" +u8"OR\r\n" +u8"Search for read code\r\n" +u8"S[codepage#]text\r\n" u8"All numbers except codepage in hexadecimal\r\n" u8"A/B: Shift-JIS char little/big endian\r\n" u8"W: UTF-16 char\r\n" @@ -50,6 +53,7 @@ constexpr auto INSERTING_HOOK = u8"Textractor: inserting hook: %s"; constexpr auto REMOVING_HOOK = u8"Textractor: removing hook: %s"; constexpr auto HOOK_FAILED = u8"Textractor: failed to insert hook"; constexpr auto TOO_MANY_HOOKS = u8"Textractor: too many hooks: can't insert"; +constexpr auto NOT_ENOUGH_TEXT = u8"Textractor: not enough text to search accurately"; constexpr auto FUNC_MISSING = u8"Textractor: function not present"; constexpr auto MODULE_MISSING = u8"Textractor: module not present"; constexpr auto GARBAGE_MEMORY = u8"Textractor: memory constantly changing, useless to read"; diff --git a/include/types.h b/include/types.h index fed342f..b09000a 100644 --- a/include/types.h +++ b/include/types.h @@ -54,7 +54,11 @@ struct HookParam index, // deref_offset1 split, // offset of the split character split_index; // deref_offset2 - wchar_t module[MAX_MODULE_SIZE]; + union + { + wchar_t module[MAX_MODULE_SIZE]; + wchar_t text[MAX_MODULE_SIZE]; + }; char function[MAX_MODULE_SIZE]; DWORD type; // flags UINT codepage; // text encoding diff --git a/vnrhook/main.cc b/vnrhook/main.cc index ef12b5b..4eacee4 100644 --- a/vnrhook/main.cc +++ b/vnrhook/main.cc @@ -9,6 +9,7 @@ #include "MinHook.h" #include "engine/match.h" #include "texthook.h" +#include "util.h" std::unique_ptr viewMutex; @@ -133,11 +134,35 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID) void NewHook(HookParam hp, LPCSTR lpname, DWORD flag) { - if (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS); - if (lpname && *lpname) strcpy_s(hp.name, lpname); - ConsoleOutput(INSERTING_HOOK, hp.name); - RemoveHook(hp.address, 0); - if (!hooks[currentHook].Insert(hp, flag)) ConsoleOutput(HOOK_FAILED); + if (hp.type & READ_SEARCH) + { + char utf8Text[MAX_MODULE_SIZE * 4] = {}; + WideCharToMultiByte(CP_UTF8, 0, hp.text, MAX_MODULE_SIZE, utf8Text, MAX_MODULE_SIZE * 4, nullptr, nullptr); + char codepageText[MAX_MODULE_SIZE * 4] = {}; + WideCharToMultiByte(hp.codepage ? hp.codepage : SHIFT_JIS, 0, hp.text, MAX_MODULE_SIZE, codepageText, MAX_MODULE_SIZE * 4, nullptr, nullptr); + if (strlen(utf8Text) < 8 || strlen(codepageText) < 8 || wcslen(hp.text) < 4) return ConsoleOutput(NOT_ENOUGH_TEXT); + for (auto[addrs, type] : Array, HookParamType>>{ + { Util::SearchMemory(utf8Text, strlen(utf8Text), PAGE_READWRITE), USING_UTF8 }, + { Util::SearchMemory(codepageText, strlen(codepageText), PAGE_READWRITE), (HookParamType)0 }, + { Util::SearchMemory(hp.text, wcslen(hp.text) * 2, PAGE_READWRITE), USING_UNICODE } + }) + for (auto addr : addrs) + { + HookParam h = {}; + h.type = DIRECT_READ | type; + h.address = addr; + h.codepage = hp.codepage; + NewHook(h, lpname, 0); + } + } + else + { + if (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS); + if (lpname && *lpname) strcpy_s(hp.name, lpname); + ConsoleOutput(INSERTING_HOOK, hp.name); + RemoveHook(hp.address, 0); + if (!hooks[currentHook].Insert(hp, flag)) ConsoleOutput(HOOK_FAILED); + } } void RemoveHook(uint64_t addr, int maxOffset) diff --git a/vnrhook/util/util.cc b/vnrhook/util/util.cc index a33736a..1ec0166 100644 --- a/vnrhook/util/util.cc +++ b/vnrhook/util/util.cc @@ -302,7 +302,7 @@ bool SearchResourceString(LPCWSTR str) return false; } -std::vector SearchMemory(const BYTE* bytes, short length, DWORD protect) +std::vector SearchMemory(const void* bytes, short length, DWORD protect) { std::vector> validMemory; for (BYTE* probe = NULL; (uint64_t)probe < 0x80000000;) // end of user memory space @@ -323,7 +323,7 @@ std::vector SearchMemory(const BYTE* bytes, short length, DWORD protec std::vector ret; for (auto memory : validMemory) for (uint64_t addr = memory.first; true;) - if (addr = SafeSearchMemory(addr, memory.first + memory.second, bytes, length)) + if (addr = SafeSearchMemory(addr, memory.first + memory.second, (const BYTE*)bytes, length)) ret.push_back(addr++); else break; diff --git a/vnrhook/util/util.h b/vnrhook/util/util.h index a009944..b3acef5 100644 --- a/vnrhook/util/util.h +++ b/vnrhook/util/util.h @@ -22,7 +22,7 @@ bool CheckFile(LPCWSTR name); bool SearchResourceString(LPCWSTR str); -std::vector SearchMemory(const BYTE* bytes, short length, DWORD protect = PAGE_EXECUTE); +std::vector SearchMemory(const void* bytes, short length, DWORD protect = PAGE_EXECUTE); } // namespace Util