mirror of
https://github.com/Artikash/Textractor.git
synced 2024-12-23 17:04:12 +08:00
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:
parent
7964623ec8
commit
5e27de842b
@ -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:
|
||||||
|
@ -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"))
|
||||||
|
@ -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);
|
||||||
|
@ -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 };
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
9
text.cpp
9
text.cpp
@ -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";
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user