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

This commit is contained in:
Akash Mozumdar 2019-06-09 00:48:30 -04:00
parent 7964623ec8
commit 5e27de842b
13 changed files with 90 additions and 75 deletions

View File

@ -108,11 +108,14 @@ namespace
{ {
auto info = *(HookFoundNotif*)buffer; auto info = *(HookFoundNotif*)buffer;
auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound; 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; 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; 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; break;
case HOST_NOTIFICATION_RMVHOOK: case HOST_NOTIFICATION_RMVHOOK:

View File

@ -41,15 +41,6 @@ namespace
RCode.erase(0, match[0].length()); 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 // @addr
if (!std::regex_match(RCode, match, std::wregex(L"@([[:xdigit:]]+)"))) return {}; if (!std::regex_match(RCode, match, std::wregex(L"@([[:xdigit:]]+)"))) return {};
hp.address = std::stoull(match[1], nullptr, 16); hp.address = std::stoull(match[1], nullptr, 16);
@ -132,6 +123,13 @@ namespace
HCode.erase(0, match[0].length()); 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 // data_offset
if (!std::regex_search(HCode, match, std::wregex(L"^-?[[:xdigit:]]+"))) return {}; if (!std::regex_search(HCode, match, std::wregex(L"^-?[[:xdigit:]]+"))) return {};
hp.offset = std::stoi(match[0], nullptr, 16); hp.offset = std::stoi(match[0], nullptr, 16);
@ -206,8 +204,6 @@ namespace
RCode << std::uppercase << std::hex; RCode << std::uppercase << std::hex;
if (hp.type & DATA_INDIRECT) RCode << "*" << HexString(hp.index);
RCode << "@" << hp.address; RCode << "@" << hp.address;
return RCode.str(); return RCode.str();
@ -239,6 +235,8 @@ namespace
HCode << std::uppercase << std::hex; HCode << std::uppercase << std::hex;
if (hp.padding) HCode << hp.padding << "+";
if (hp.offset < 0) hp.offset += 4; if (hp.offset < 0) hp.offset += 4;
if (hp.split < 0) hp.split += 4; if (hp.split < 0) hp.split += 4;
@ -338,7 +336,7 @@ namespace Util
assert(StringToWideString(u8"こんにちは").value() == L"こんにちは"), assert(StringToWideString(u8"こんにちは").value() == L"こんにちは"),
assert(ParseCode(L"/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")), assert(ParseCode(L"/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")),
assert(ParseCode(L"HB4@0")), assert(ParseCode(L"HB4@0")),
assert(ParseCode(L"/RS*10@44")), assert(ParseCode(L"/RS65001#@44")),
assert(!ParseCode(L"HQ@4")), assert(!ParseCode(L"HQ@4")),
assert(!ParseCode(L"/RW@44")), assert(!ParseCode(L"/RW@44")),
assert(!ParseCode(L"/HWG@33")) assert(!ParseCode(L"/HWG@33"))

View File

@ -33,6 +33,7 @@ extern const char* SEARCH_DURATION;
extern const char* PATTERN_OFFSET; extern const char* PATTERN_OFFSET;
extern const char* MIN_ADDRESS; extern const char* MIN_ADDRESS;
extern const char* MAX_ADDRESS; extern const char* MAX_ADDRESS;
extern const char* STRING_OFFSET;
extern const char* HOOK_SEARCH_FILTER; extern const char* HOOK_SEARCH_FILTER;
extern const char* START_HOOK_SEARCH; extern const char* START_HOOK_SEARCH;
extern const char* SAVE_SEARCH_RESULTS; extern const char* SAVE_SEARCH_RESULTS;
@ -353,6 +354,7 @@ void MainWindow::FindHooks()
for (auto[value, label] : Array<std::tuple<uintptr_t&, const char*>>{ for (auto[value, label] : Array<std::tuple<uintptr_t&, const char*>>{
{ sp.minAddress = 0, MIN_ADDRESS }, { sp.minAddress = 0, MIN_ADDRESS },
{ sp.maxAddress = -1ULL, MAX_ADDRESS }, { sp.maxAddress = -1ULL, MAX_ADDRESS },
{ sp.padding = 0, STRING_OFFSET }
}) })
{ {
auto input = new QLineEdit(QString::number(value, 16), this); auto input = new QLineEdit(QString::number(value, 16), this);

View File

@ -4,7 +4,7 @@
// 8/23/2013 jichi // 8/23/2013 jichi
// Branch: ITH/common.h, rev 128 // 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 }; 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 };

View File

@ -40,6 +40,7 @@ struct HookParam
DWORD type; // flags DWORD type; // flags
UINT codepage; // text encoding UINT codepage; // text encoding
short length_offset; // index of the string length short length_offset; // index of the string length
uintptr_t padding; // padding
DWORD user_value; // 7/20/2014: jichi additional parameters for PSP games 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); 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 int length, // length of pattern
offset, // offset from start of pattern to add hook offset, // offset from start of pattern to add hook
searchTime; // ms searchTime; // ms
uintptr_t minAddress, maxAddress; uintptr_t padding, minAddress, maxAddress;
}; };
struct InsertHookCmd // From host struct InsertHookCmd // From host

View File

@ -23,19 +23,21 @@ const char* CODE_INFODUMP = u8R"(Search for text
S[codepage#]text S[codepage#]text
OR OR
Enter read code Enter read code
R{S|Q|V}[null_length<][codepage#][*deref_offset]@addr R{S|Q|V}[null_length<][codepage#]@addr
OR OR
Enter hook code 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 All numbers except codepage/null_length in hexadecimal
Default codepage is 932 (Shift-JIS) but this can be changed in settings Default codepage is 932 (Shift-JIS) but this can be changed in settings
A/B: codepage char little/big endian A/B: codepage char little/big endian
W: UTF-16 char W: UTF-16 char
S/Q/V: codepage/UTF-16/UTF-8 string S/Q/V: codepage/UTF-16/UTF-8 string
N: don't use context
null_length: length of null terminator used for string 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 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 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)"; * means dereference pointer+deref_offset)";
const char* SAVE_SETTINGS = u8"Save settings"; 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 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* PATTERN_OFFSET = u8"Offset from pattern start";
const char* MIN_ADDRESS = u8"Minimum address (hex)"; const char* MIN_ADDRESS = u8"Minimum address (hex)";
const char* MAX_ADDRESS = u8"Maximum 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* HOOK_SEARCH_FILTER = u8"Results must match this regex";
const char* START_HOOK_SEARCH = u8"Start hook search"; const char* START_HOOK_SEARCH = u8"Start hook search";
const char* SAVE_SEARCH_RESULTS = u8"Save search results"; const char* SAVE_SEARCH_RESULTS = u8"Save search results";

View File

@ -11,22 +11,32 @@ extern WinMutex viewMutex;
namespace namespace
{ {
SearchParam current;
constexpr int CACHE_SIZE = 500'000; constexpr int CACHE_SIZE = 500'000;
struct HookRecord struct HookRecord
{ {
HookRecord() : address(0) {} ~HookRecord()
~HookRecord() { if (address) NotifyHookFound(address, offset, text); } {
uint64_t address; if (!address) return;
int offset; HookParam hp = {};
wchar_t text[200]; 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<HookRecord[]> records; std::unique_ptr<HookRecord[]> records;
long recordsAvailable; long recordsAvailable;
uint64_t addressCharCache[CACHE_SIZE] = {}; uint64_t signatureCache[CACHE_SIZE] = {};
long sumCache[CACHE_SIZE] = {}; long sumCache[CACHE_SIZE] = {};
#ifndef _WIN64 #ifndef _WIN64
BYTE trampoline[32] = BYTE trampoline[] =
{ {
0x9c, // pushfd 0x9c, // pushfd
0x60, // pushad 0x60, // pushad
@ -43,7 +53,7 @@ namespace
}; };
constexpr int addr_offset = 3, send_offset = 13, original_offset = 25, registers = 8; constexpr int addr_offset = 3, send_offset = 13, original_offset = 25, registers = 8;
#else #else
BYTE trampoline[128] = { BYTE trampoline[] = {
0x9c, // push rflags 0x9c, // push rflags
0x50, // push rax 0x50, // push rax
0x53, // push rbx 0x53, // push rbx
@ -69,7 +79,10 @@ namespace
0x48, 0x8d, 0x8c, 0x24, 0xa8, 0x00, 0x00, 0x00, // lea rcx,[rsp+0xa8] 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, 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, 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 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, 0x6c, 0x24, 0x10, // vmovdqu xmm5,XMMWORD PTR[rsp + 0x10]
0xc5, 0xfa, 0x6f, 0x24, 0x24, // vmovdqu xmm4,XMMWORD PTR[rsp] 0xc5, 0xfa, 0x6f, 0x24, 0x24, // vmovdqu xmm4,XMMWORD PTR[rsp]
0x48, 0x83, 0xc4, 0x20, // add rsp,0x20 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) 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 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 #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) // 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 // 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) for (int i = -registers; i < 6; ++i)
{ {
int length = 0, sum = 0; 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) {} __except (EXCEPTION_EXECUTE_HANDLER) {}
if (length > 7 && length < 199) if (length > STRING && length < 499)
{ {
__try __try
{ {
// many duplicate results with same address and second character will be found: filter them out // many duplicate results with same address, offset, and third/fourth character will be found: filter them out
uint64_t addressAndChar = (((uint64_t)stack[i][1]) << 48) | address; uint64_t signature = ((uint64_t)i << 56) | ((uint64_t)(str[2] + str[3]) << 48) | address;
if (addressCharCache[addressAndChar % CACHE_SIZE] == addressAndChar) continue; if (signatureCache[signature % CACHE_SIZE] == signature) continue;
addressCharCache[addressAndChar % CACHE_SIZE] = addressAndChar; signatureCache[signature % CACHE_SIZE] = signature;
// if there are huge amount of strings that are the same, it's probably garbage: filter them out // 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 // can't store all the strings, so use sum as heuristic instead
if (_InterlockedIncrement(sumCache + (sum % CACHE_SIZE)) > 25) continue; if (_InterlockedIncrement(sumCache + (sum % CACHE_SIZE)) > 25) continue;
@ -127,8 +141,8 @@ void Send(wchar_t** stack, uintptr_t address)
if (n > 0) if (n > 0)
{ {
records[n].address = address; records[n].address = address;
records[n].offset = i * sizeof(wchar_t*); records[n].offset = i * sizeof(char*);
for (int j = 0; j < length; ++j) records[n].text[j] = stack[i][j]; for (int j = 0; j < length; ++j) records[n].text[j] = str[j];
records[n].text[length] = 0; records[n].text[length] = 0;
} }
} }
@ -145,12 +159,11 @@ void SearchForHooks(SearchParam sp)
static std::mutex m; static std::mutex m;
std::scoped_lock lock(m); std::scoped_lock lock(m);
try try { records = std::make_unique<HookRecord[]>(recordsAvailable = CACHE_SIZE); }
{
records = std::make_unique<HookRecord[]>(recordsAvailable = CACHE_SIZE);
}
catch (std::bad_alloc&) { return ConsoleOutput("Textractor: SearchForHooks ERROR (out of memory)"); } catch (std::bad_alloc&) { return ConsoleOutput("Textractor: SearchForHooks ERROR (out of memory)"); }
current = sp;
uintptr_t moduleStartAddress = (uintptr_t)GetModuleHandleW(ITH_DLL); uintptr_t moduleStartAddress = (uintptr_t)GetModuleHandleW(ITH_DLL);
uintptr_t moduleStopAddress = moduleStartAddress; uintptr_t moduleStopAddress = moduleStartAddress;
MEMORY_BASIC_INFORMATION info; MEMORY_BASIC_INFORMATION info;
@ -162,12 +175,9 @@ void SearchForHooks(SearchParam sp)
moduleStopAddress -= info.RegionSize; moduleStopAddress -= info.RegionSize;
ConsoleOutput(STARTING_SEARCH); ConsoleOutput(STARTING_SEARCH);
std::vector<uint64_t> addresses = Util::SearchMemory(sp.pattern, sp.length); std::vector<uint64_t> addresses = Util::SearchMemory(sp.pattern, sp.length, PAGE_EXECUTE, sp.minAddress, sp.maxAddress);
for (auto& addr : addresses) addr += sp.offset; for (auto& addr : addresses) addr += sp.offset;
addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) { return addr > moduleStartAddress && addr < moduleStopAddress; }), addresses.end());
{
return (addr > moduleStartAddress && addr < moduleStopAddress) || addr > sp.maxAddress || addr < sp.minAddress;
}), addresses.end());
*(void**)(trampoline + send_offset) = Send; *(void**)(trampoline + send_offset) = Send;
auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE); auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE);
VirtualProtect(trampolines, addresses.size() * sizeof(trampoline), PAGE_EXECUTE_READWRITE, DUMMY); 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); for (auto addr : addresses) MH_RemoveHook((void*)addr);
records.reset(); records.reset();
VirtualFree(trampolines, 0, MEM_RELEASE); 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); ConsoleOutput(HOOK_SEARCH_FINISHED, CACHE_SIZE - recordsAvailable);
}).detach(); }).detach();
} }

View File

@ -102,12 +102,8 @@ void ConsoleOutput(LPCSTR text, ...)
WriteFile(hookPipe, &buffer, sizeof(buffer), DUMMY, nullptr); 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); HookFoundNotif buffer(hp, text);
WriteFile(hookPipe, &buffer, sizeof(buffer), DUMMY, nullptr); WriteFile(hookPipe, &buffer, sizeof(buffer), DUMMY, nullptr);
} }

View File

@ -9,7 +9,7 @@
void TextOutput(ThreadParam tp, BYTE* text, int len); void TextOutput(ThreadParam tp, BYTE* text, int len);
void ConsoleOutput(LPCSTR text, ...); 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 NotifyHookRemove(uint64_t addr, LPCSTR name);
void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE); void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
void RemoveHook(uint64_t addr, int maxOffset = 9); void RemoveHook(uint64_t addr, int maxOffset = 9);

View File

@ -63,7 +63,10 @@ namespace { // unnamed
0x48, 0x8d, 0x94, 0x24, 0xa8, 0x00, 0x00, 0x00, // lea rdx,[rsp+0xa8] 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, 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, 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 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, 0x6c, 0x24, 0x10, // vmovdqu xmm5,XMMWORD PTR[rsp + 0x10]
0xc5, 0xfa, 0x6f, 0x24, 0x24, // vmovdqu xmm4,XMMWORD PTR[rsp] 0xc5, 0xfa, 0x6f, 0x24, 0x24, // vmovdqu xmm4,XMMWORD PTR[rsp]
0x48, 0x83, 0xc4, 0x20, // add rsp,0x20 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) 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 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 #endif
bool trigger = false; 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 & SPLIT_INDIRECT) dwSplit = *(DWORD *)(dwSplit + hp.split_index);
} }
if (hp.type & DATA_INDIRECT) dwDataIn = *(DWORD *)(dwDataIn + hp.index); if (hp.type & DATA_INDIRECT) dwDataIn = *(DWORD *)(dwDataIn + hp.index);
dwDataIn += hp.padding;
dwCount = GetLength(dwDataBase, dwDataIn); 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); if (hp.type & DATA_INDIRECT) data = *(uintptr_t*)(data + hp.index);
data += hp.padding;
count = GetLength(dwDataBase, data); count = GetLength(dwDataBase, data);
if (count == 0) return; if (count == 0) return;
if (count > TEXT_BUFFER_SIZE) count = TEXT_BUFFER_SIZE; if (count > TEXT_BUFFER_SIZE) count = TEXT_BUFFER_SIZE;
@ -225,18 +230,15 @@ bool TextHook::InsertHookCode()
return MH_EnableHook(location) == MH_OK; return MH_EnableHook(location) == MH_OK;
} }
DWORD WINAPI TextHook::Reader(LPVOID hookPtr) void TextHook::Read()
{ {
TextHook* This = (TextHook*)hookPtr;
BYTE buffer[TEXT_BUFFER_SIZE] = {}; BYTE buffer[TEXT_BUFFER_SIZE] = {};
int changeCount = 0, dataLen = 1; int changeCount = 0, dataLen = 1;
__try __try
{ {
uint64_t currentAddress = This->address; while (WaitForSingleObject(readerEvent, 500) == WAIT_TIMEOUT)
while (WaitForSingleObject(This->readerEvent, 500) == WAIT_TIMEOUT)
{ {
if (This->hp.type & DATA_INDIRECT) currentAddress = *(uintptr_t*)This->address + This->hp.index; if (memcmp(buffer, location, dataLen) == 0)
if (memcmp(buffer, (void*)currentAddress, dataLen) == 0)
{ {
changeCount = 0; changeCount = 0;
continue; continue;
@ -244,26 +246,25 @@ DWORD WINAPI TextHook::Reader(LPVOID hookPtr)
if (++changeCount > 10) if (++changeCount > 10)
{ {
ConsoleOutput(GARBAGE_MEMORY); ConsoleOutput(GARBAGE_MEMORY);
This->Clear(); Clear();
break; break;
} }
dataLen = min(This->HookStrlen((BYTE*)currentAddress), TEXT_BUFFER_SIZE); dataLen = min(HookStrlen((BYTE*)location), TEXT_BUFFER_SIZE);
memcpy(buffer, (void*)currentAddress, dataLen); memcpy(buffer, location, dataLen);
TextOutput({ GetCurrentProcessId(), This->address, 0, 0 }, buffer, dataLen); TextOutput({ GetCurrentProcessId(), address, 0, 0 }, buffer, dataLen);
} }
} }
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{ {
ConsoleOutput(READ_ERROR); ConsoleOutput(READ_ERROR);
This->Clear(); Clear();
} }
return 0;
} }
bool TextHook::InsertReadCode() 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); readerEvent = CreateEventW(nullptr, FALSE, FALSE, NULL);
return true; return true;
} }

View File

@ -29,7 +29,7 @@ public:
void Clear(); void Clear();
private: private:
static DWORD WINAPI Reader(LPVOID hookPtr); void Read();
bool InsertHookCode(); bool InsertHookCode();
bool InsertReadCode(); bool InsertReadCode();
void Send(uintptr_t dwDatabase); void Send(uintptr_t dwDatabase);
@ -40,7 +40,7 @@ private:
HANDLE readerThread, readerEvent; HANDLE readerThread, readerEvent;
bool err; bool err;
BYTE trampoline[130]; BYTE trampoline[x64 ? 140 : 40];
}; };

View File

@ -301,7 +301,7 @@ bool SearchResourceString(LPCWSTR str)
return false; return false;
} }
std::vector<uint64_t> SearchMemory(const void* bytes, short length, DWORD protect) std::vector<uint64_t> SearchMemory(const void* bytes, short length, DWORD protect, uintptr_t minAddr, uintptr_t maxAddr)
{ {
SYSTEM_INFO systemInfo; SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo); GetNativeSystemInfo(&systemInfo);
@ -316,15 +316,16 @@ std::vector<uint64_t> SearchMemory(const void* bytes, short length, DWORD protec
} }
else 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; probe += info.RegionSize;
} }
} }
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 = max(memory.first, minAddr); true;)
if (addr = SafeSearchMemory(addr, memory.first + memory.second, (const BYTE*)bytes, length)) if (addr < maxAddr && (addr = SafeSearchMemory(addr, memory.first + memory.second, (const BYTE*)bytes, length)))
ret.push_back(addr++); ret.push_back(addr++);
else break; else break;

View File

@ -22,7 +22,7 @@ bool CheckFile(LPCWSTR name);
bool SearchResourceString(LPCWSTR str); bool SearchResourceString(LPCWSTR str);
std::vector<uint64_t> SearchMemory(const void* bytes, short length, DWORD protect = PAGE_EXECUTE); std::vector<uint64_t> SearchMemory(const void* bytes, short length, DWORD protect = PAGE_EXECUTE, uintptr_t minAddr = 0, uintptr_t maxAddr = -1ULL);
} // namespace Util } // namespace Util