forked from Public-Mirror/Textractor
implement searching for read codes
This commit is contained in:
parent
b06dc8ff82
commit
f5d8d43149
36
GUI/misc.cpp
36
GUI/misc.cpp
@ -18,10 +18,10 @@ namespace
|
|||||||
case L'S':
|
case L'S':
|
||||||
break;
|
break;
|
||||||
case L'Q':
|
case L'Q':
|
||||||
hp.type |= USING_STRING | USING_UNICODE;
|
hp.type |= USING_UNICODE;
|
||||||
break;
|
break;
|
||||||
case L'V':
|
case L'V':
|
||||||
hp.type |= USING_STRING | USING_UTF8;
|
hp.type |= USING_UTF8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
@ -36,7 +36,7 @@ namespace
|
|||||||
RCode.remove(0, codepage.captured(0).length());
|
RCode.remove(0, codepage.captured(0).length());
|
||||||
}
|
}
|
||||||
|
|
||||||
// [*deref_offset|0]
|
// [*deref_offset]
|
||||||
if (RCode.at(0).unicode() == L'0') RCode.remove(0, 1); // Legacy
|
if (RCode.at(0).unicode() == L'0') RCode.remove(0, 1); // Legacy
|
||||||
QRegularExpressionMatch deref = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(RCode);
|
QRegularExpressionMatch deref = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(RCode);
|
||||||
if (deref.hasMatch())
|
if (deref.hasMatch())
|
||||||
@ -53,6 +53,24 @@ namespace
|
|||||||
return hp;
|
return hp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<HookParam> 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<MAX_MODULE_SIZE>(hp.text, S(SCode).c_str());
|
||||||
|
|
||||||
|
return hp;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<HookParam> ParseHCode(QString HCode)
|
std::optional<HookParam> ParseHCode(QString HCode)
|
||||||
{
|
{
|
||||||
HookParam hp = {};
|
HookParam hp = {};
|
||||||
@ -157,7 +175,7 @@ namespace
|
|||||||
|
|
||||||
QString GenerateRCode(HookParam hp)
|
QString GenerateRCode(HookParam hp)
|
||||||
{
|
{
|
||||||
QString RCode = "/R";
|
QString RCode = "R";
|
||||||
QTextStream codeBuilder(&RCode);
|
QTextStream codeBuilder(&RCode);
|
||||||
|
|
||||||
if (hp.type & USING_UNICODE) codeBuilder << "Q";
|
if (hp.type & USING_UNICODE) codeBuilder << "Q";
|
||||||
@ -178,7 +196,7 @@ namespace
|
|||||||
|
|
||||||
QString GenerateHCode(HookParam hp, DWORD processId)
|
QString GenerateHCode(HookParam hp, DWORD processId)
|
||||||
{
|
{
|
||||||
QString HCode = "/H";
|
QString HCode = "H";
|
||||||
QTextStream codeBuilder(&HCode);
|
QTextStream codeBuilder(&HCode);
|
||||||
|
|
||||||
if (hp.type & USING_UNICODE)
|
if (hp.type & USING_UNICODE)
|
||||||
@ -229,8 +247,10 @@ namespace
|
|||||||
|
|
||||||
std::optional<HookParam> ParseCode(QString code)
|
std::optional<HookParam> ParseCode(QString code)
|
||||||
{
|
{
|
||||||
if (code.startsWith("/H")) return ParseHCode(code.remove(0, 2));
|
if (code.startsWith("/")) code.remove(0, 1); // legacy/AGTH compatibility
|
||||||
else if (code.startsWith("/R")) return ParseRCode(code.remove(0, 2));
|
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 {};
|
else return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +262,7 @@ QString GenerateCode(HookParam hp, DWORD processId)
|
|||||||
|
|
||||||
TEST(
|
TEST(
|
||||||
assert(ParseCode("/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")),
|
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("/RS*10@44")),
|
||||||
assert(!ParseCode("HQ@4")),
|
assert(!ParseCode("HQ@4")),
|
||||||
assert(!ParseCode("/RW@44")),
|
assert(!ParseCode("/RW@44")),
|
||||||
|
@ -35,6 +35,7 @@ enum HookParamType : unsigned long
|
|||||||
MODULE_OFFSET = 0x40, // address is relative to module
|
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,
|
USING_UTF8 = 0x100,
|
||||||
|
READ_SEARCH = 0x200, // unspecified address: search for text instead
|
||||||
NO_CONTEXT = 0x400,
|
NO_CONTEXT = 0x400,
|
||||||
HOOK_EMPTY = 0x800,
|
HOOK_EMPTY = 0x800,
|
||||||
FIXING_SPLIT = 0x1000,
|
FIXING_SPLIT = 0x1000,
|
||||||
|
@ -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"
|
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";
|
u8"You can also type in the process id";
|
||||||
constexpr auto CODE_INFODUMP = u8"Enter hook code\r\n"
|
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"OR\r\n"
|
||||||
u8"Enter read code\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"All numbers except codepage in hexadecimal\r\n"
|
||||||
u8"A/B: Shift-JIS char little/big endian\r\n"
|
u8"A/B: Shift-JIS char little/big endian\r\n"
|
||||||
u8"W: UTF-16 char\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 REMOVING_HOOK = u8"Textractor: removing hook: %s";
|
||||||
constexpr auto HOOK_FAILED = u8"Textractor: failed to insert hook";
|
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 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 FUNC_MISSING = u8"Textractor: function not present";
|
||||||
constexpr auto MODULE_MISSING = u8"Textractor: module not present";
|
constexpr auto MODULE_MISSING = u8"Textractor: module not present";
|
||||||
constexpr auto GARBAGE_MEMORY = u8"Textractor: memory constantly changing, useless to read";
|
constexpr auto GARBAGE_MEMORY = u8"Textractor: memory constantly changing, useless to read";
|
||||||
|
@ -54,7 +54,11 @@ struct HookParam
|
|||||||
index, // deref_offset1
|
index, // deref_offset1
|
||||||
split, // offset of the split character
|
split, // offset of the split character
|
||||||
split_index; // deref_offset2
|
split_index; // deref_offset2
|
||||||
|
union
|
||||||
|
{
|
||||||
wchar_t module[MAX_MODULE_SIZE];
|
wchar_t module[MAX_MODULE_SIZE];
|
||||||
|
wchar_t text[MAX_MODULE_SIZE];
|
||||||
|
};
|
||||||
char function[MAX_MODULE_SIZE];
|
char function[MAX_MODULE_SIZE];
|
||||||
DWORD type; // flags
|
DWORD type; // flags
|
||||||
UINT codepage; // text encoding
|
UINT codepage; // text encoding
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "MinHook.h"
|
#include "MinHook.h"
|
||||||
#include "engine/match.h"
|
#include "engine/match.h"
|
||||||
#include "texthook.h"
|
#include "texthook.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
std::unique_ptr<WinMutex> viewMutex;
|
std::unique_ptr<WinMutex> viewMutex;
|
||||||
|
|
||||||
@ -133,11 +134,35 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID)
|
|||||||
|
|
||||||
void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
|
void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
|
||||||
{
|
{
|
||||||
|
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<std::tuple<std::vector<uint64_t>, 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 (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS);
|
||||||
if (lpname && *lpname) strcpy_s<HOOK_NAME_SIZE>(hp.name, lpname);
|
if (lpname && *lpname) strcpy_s<HOOK_NAME_SIZE>(hp.name, lpname);
|
||||||
ConsoleOutput(INSERTING_HOOK, hp.name);
|
ConsoleOutput(INSERTING_HOOK, hp.name);
|
||||||
RemoveHook(hp.address, 0);
|
RemoveHook(hp.address, 0);
|
||||||
if (!hooks[currentHook].Insert(hp, flag)) ConsoleOutput(HOOK_FAILED);
|
if (!hooks[currentHook].Insert(hp, flag)) ConsoleOutput(HOOK_FAILED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveHook(uint64_t addr, int maxOffset)
|
void RemoveHook(uint64_t addr, int maxOffset)
|
||||||
|
@ -302,7 +302,7 @@ bool SearchResourceString(LPCWSTR str)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint64_t> SearchMemory(const BYTE* bytes, short length, DWORD protect)
|
std::vector<uint64_t> SearchMemory(const void* bytes, short length, DWORD protect)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<uint64_t, uint64_t>> validMemory;
|
std::vector<std::pair<uint64_t, uint64_t>> validMemory;
|
||||||
for (BYTE* probe = NULL; (uint64_t)probe < 0x80000000;) // end of user memory space
|
for (BYTE* probe = NULL; (uint64_t)probe < 0x80000000;) // end of user memory space
|
||||||
@ -323,7 +323,7 @@ std::vector<uint64_t> SearchMemory(const BYTE* bytes, short length, DWORD protec
|
|||||||
std::vector<uint64_t> ret;
|
std::vector<uint64_t> ret;
|
||||||
for (auto memory : validMemory)
|
for (auto memory : validMemory)
|
||||||
for (uint64_t addr = memory.first; true;)
|
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++);
|
ret.push_back(addr++);
|
||||||
else break;
|
else break;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ bool CheckFile(LPCWSTR name);
|
|||||||
|
|
||||||
bool SearchResourceString(LPCWSTR str);
|
bool SearchResourceString(LPCWSTR str);
|
||||||
|
|
||||||
std::vector<uint64_t> SearchMemory(const BYTE* bytes, short length, DWORD protect = PAGE_EXECUTE);
|
std::vector<uint64_t> SearchMemory(const void* bytes, short length, DWORD protect = PAGE_EXECUTE);
|
||||||
|
|
||||||
} // namespace Util
|
} // namespace Util
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user