implement searching for read codes

This commit is contained in:
Akash Mozumdar 2019-01-03 17:52:16 -05:00
parent b06dc8ff82
commit f5d8d43149
7 changed files with 74 additions and 20 deletions

View File

@ -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<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)
{
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<HookParam> 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")),

View File

@ -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,

View File

@ -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";

View File

@ -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

View File

@ -9,6 +9,7 @@
#include "MinHook.h"
#include "engine/match.h"
#include "texthook.h"
#include "util.h"
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)
{
if (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS);
if (lpname && *lpname) strcpy_s<HOOK_NAME_SIZE>(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<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 (lpname && *lpname) strcpy_s<HOOK_NAME_SIZE>(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)

View File

@ -302,7 +302,7 @@ bool SearchResourceString(LPCWSTR str)
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;
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;
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;

View File

@ -22,7 +22,7 @@ bool CheckFile(LPCWSTR name);
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