LunaHook-mirror/include/hookcode.cpp

460 lines
9.4 KiB
C++
Raw Normal View History

2024-05-06 22:56:24 +08:00

2024-02-07 20:59:24 +08:00
namespace
{
std::optional<HookParam> ParseRCode(std::wstring RCode)
{
2024-07-21 21:07:05 +08:00
RCode.erase(0, 1);
2024-02-07 20:59:24 +08:00
std::wsmatch match;
HookParam hp;
hp.type |= DIRECT_READ;
// {S|Q|V|M}
switch (RCode[0])
{
case L'S':
break;
case L'Q':
hp.type |= CODEC_UTF16;
break;
case L'U':
hp.type |= CODEC_UTF32;
break;
case L'V':
hp.type |= CODEC_UTF8;
break;
default:
return {};
}
RCode.erase(0, 1);
// [codepage#]
if (std::regex_search(RCode, match, std::wregex(L"^([0-9]+)#")))
{
hp.codepage = std::stoi(match[1]);
RCode.erase(0, match[0].length());
}
// @addr
2024-07-21 21:07:05 +08:00
if (!std::regex_match(RCode, match, std::wregex(L"@([[:xdigit:]]+)")))
return {};
2024-02-07 20:59:24 +08:00
hp.address = std::stoull(match[1], nullptr, 16);
return hp;
}
2024-07-21 21:07:05 +08:00
std::optional<HookParam> ParseHCode(std::wstring HCode, std::optional<HookParam> hpo = {})
2024-02-07 20:59:24 +08:00
{
2024-07-21 21:07:05 +08:00
auto hp = hpo ? hpo.value() : HookParam{};
if (HCode[0] == 'L')
{
hp.type |= HOOK_RETURN;
HCode.erase(0, 1);
2024-05-10 15:39:46 +08:00
}
2024-04-01 16:42:31 +08:00
switch (HCode[0])
{
2024-07-21 21:07:05 +08:00
case L'B':
hp.type |= BREAK_POINT;
case L'H':
break;
default:
return {};
}
2024-07-21 21:07:05 +08:00
HCode.erase(0, 1);
if (endWith(HCode, L":JIT:YUZU"))
hp.jittype = JITTYPE::YUZU;
else if (endWith(HCode, L":JIT:PPSSPP"))
hp.jittype = JITTYPE::PPSSPP;
else if (endWith(HCode, L":JIT:VITA3K"))
hp.jittype = JITTYPE::VITA3K;
else if (endWith(HCode, L":JIT:RPCS3"))
hp.jittype = JITTYPE::RPCS3;
else if (endWith(HCode, L":JIT:UNITY"))
hp.jittype = JITTYPE::UNITY;
2024-04-01 16:42:31 +08:00
2024-02-07 20:59:24 +08:00
// {A|B|W|H|S|Q|V|M}
switch (HCode[0])
{
case L'A':
hp.type |= CODEC_ANSI_BE;
break;
case L'B':
2024-07-21 21:07:05 +08:00
// ANSI LE
2024-02-07 20:59:24 +08:00
break;
case L'W':
hp.type |= CODEC_UTF16;
2024-11-04 09:46:14 +08:00
case L'C':
hp.type |= CODEC_UTF8;
2024-02-07 20:59:24 +08:00
break;
case L'I':
hp.type |= CODEC_UTF32;
break;
case L'S':
hp.type |= USING_STRING;
break;
case L'Q':
hp.type |= USING_STRING | CODEC_UTF16;
break;
2024-05-10 02:11:33 +08:00
case L'M':
2024-11-04 09:46:14 +08:00
hp.type |= USING_STRING | CODEC_UTF16 | SPECIAL_JIT_STRING;
2024-05-10 02:11:33 +08:00
break;
2024-02-07 20:59:24 +08:00
case L'U':
hp.type |= USING_STRING | CODEC_UTF32;
break;
case L'V':
hp.type |= USING_STRING | CODEC_UTF8;
break;
default:
return {};
}
HCode.erase(0, 1);
if (hp.type & USING_STRING)
{
if (HCode[0] == L'F')
{
hp.type |= FULL_STRING;
HCode.erase(0, 1);
}
}
// [N]
if (HCode[0] == L'N')
{
hp.type |= NO_CONTEXT;
HCode.erase(0, 1);
}
2024-07-21 21:07:05 +08:00
2024-04-01 16:42:31 +08:00
std::wsmatch match;
2024-02-07 20:59:24 +08:00
// [codepage#]
if (std::regex_search(HCode, match, std::wregex(L"^([0-9]+)#")))
{
hp.codepage = std::stoi(match[1]);
HCode.erase(0, match[0].length());
}
// [padding+]
if (std::regex_search(HCode, match, std::wregex(L"^([[:xdigit:]]+)\\+")))
{
2024-10-30 15:49:25 +08:00
hp.padding = std::stoll(match[1], nullptr, 16);
2024-02-07 20:59:24 +08:00
HCode.erase(0, match[0].length());
}
auto ConsumeHexInt = [&HCode]
{
size_t size = 0;
int value = 0;
2024-07-21 21:07:05 +08:00
try
{
value = std::stoi(HCode, &size, 16);
}
catch (std::invalid_argument)
{
}
2024-02-07 20:59:24 +08:00
HCode.erase(0, size);
return value;
};
// data_offset
hp.offset = ConsumeHexInt();
// [*deref_offset1]
if (HCode[0] == L'*')
{
hp.type |= DATA_INDIRECT;
HCode.erase(0, 1);
hp.index = ConsumeHexInt();
}
// [:split_offset[*deref_offset2]]
if (HCode[0] == L':')
{
hp.type |= USING_SPLIT;
HCode.erase(0, 1);
hp.split = ConsumeHexInt();
if (HCode[0] == L'*')
{
hp.type |= SPLIT_INDIRECT;
HCode.erase(0, 1);
hp.split_index = ConsumeHexInt();
}
}
2024-07-21 21:07:05 +08:00
if (hp.jittype == JITTYPE::UNITY)
{
if (HCode[0] != L'@')
return {};
HCode.erase(0, 1);
HCode = HCode.substr(0, HCode.size() - wcslen(L":JIT:UNITY"));
hp.argidx = hp.offset;
hp.offset = 0;
hp.address = 0;
2024-05-09 07:23:06 +08:00
hp.type &= ~MODULE_OFFSET;
hp.type &= ~FUNCTION_OFFSET;
2024-07-21 21:07:05 +08:00
strcpy(hp.function, "");
wcscpy(hp.module, L"");
strcpy(hp.unityfunctioninfo, wcasta(HCode).c_str());
2024-02-07 20:59:24 +08:00
}
2024-07-21 21:07:05 +08:00
else
2024-02-07 20:59:24 +08:00
{
2024-07-21 21:07:05 +08:00
// @addr[:module[:func]]
if (!std::regex_match(HCode, match, std::wregex(L"^@([[:xdigit:]]+)(:.+?)?(:.+)?")))
return {};
hp.address = std::stoull(match[1], nullptr, 16);
if (match[2].matched)
{
hp.type |= MODULE_OFFSET;
wcsncpy_s(hp.module, match[2].str().erase(0, 1).c_str(), MAX_MODULE_SIZE - 1);
}
if (match[3].matched)
{
hp.type |= FUNCTION_OFFSET;
std::wstring func = match[3];
strncpy_s(hp.function, std::string(func.begin(), func.end()).erase(0, 1).c_str(), MAX_MODULE_SIZE - 1);
}
// ITH has registers offset by 4 vs AGTH: need this to correct
if (hp.offset < 0)
hp.offset -= 4;
if (hp.split < 0)
hp.split -= 4;
if (hp.jittype != JITTYPE::PC)
{
hp.emu_addr = hp.address;
hp.argidx = hp.offset;
hp.offset = 0;
hp.address = 0;
hp.type &= ~MODULE_OFFSET;
hp.type &= ~FUNCTION_OFFSET;
strcpy(hp.function, "");
wcscpy(hp.module, L"");
}
2024-05-09 07:23:06 +08:00
}
2024-02-07 20:59:24 +08:00
return hp;
}
std::optional<HookParam> ParseECode(std::wstring code)
2024-07-21 21:07:05 +08:00
{
code.erase(0, 1);
2024-04-01 16:42:31 +08:00
HookParam hp;
2024-07-21 21:07:05 +08:00
hp.type |= EMBED_ABLE;
2024-02-07 20:59:24 +08:00
2024-07-21 21:07:05 +08:00
if (code[0] == L'D')
2024-02-07 20:59:24 +08:00
{
2024-07-21 21:07:05 +08:00
hp.type |= EMBED_DYNA_SJIS;
code.erase(0, 1);
2024-02-07 20:59:24 +08:00
}
2024-07-21 21:07:05 +08:00
if (code[0] == L'S')
2024-02-07 20:59:24 +08:00
{
2024-07-21 21:07:05 +08:00
code.erase(0, 1);
hp.type |= EMBED_BEFORE_SIMPLE;
2024-02-07 20:59:24 +08:00
2024-07-21 21:07:05 +08:00
if (code[0] == L'N')
hp.type |= EMBED_AFTER_NEW;
else if (code[0] == L'O')
hp.type |= EMBED_AFTER_OVERWRITE;
2024-02-07 20:59:24 +08:00
else
2024-07-21 21:07:05 +08:00
return ParseHCode(code, hp);
code.erase(0, 1);
2024-02-07 20:59:24 +08:00
}
2024-07-21 21:07:05 +08:00
return ParseHCode(code, hp);
2024-02-07 20:59:24 +08:00
}
std::wstring HexString(int64_t num)
{
2024-07-21 21:07:05 +08:00
if (num < 0)
return FormatString(L"-%I64X", -num);
2024-02-07 20:59:24 +08:00
return FormatString(L"%I64X", num);
}
std::wstring GenerateRCode(HookParam hp)
{
std::wstring RCode = L"R";
2024-07-21 21:07:05 +08:00
if (hp.type & CODEC_UTF16)
2024-03-27 19:33:28 +08:00
RCode += L'Q';
2024-07-21 21:07:05 +08:00
else if (hp.type & CODEC_UTF32)
2024-03-27 19:33:28 +08:00
RCode += L'U';
2024-07-21 21:07:05 +08:00
else if (hp.type & CODEC_UTF8)
2024-03-27 19:33:28 +08:00
RCode += L'V';
2024-02-07 20:59:24 +08:00
else
{
RCode += L'S';
2024-07-21 21:07:05 +08:00
if (hp.codepage != 0)
RCode += std::to_wstring(hp.codepage) + L'#';
2024-02-07 20:59:24 +08:00
}
RCode += L'@' + HexString(hp.address);
return RCode;
}
std::wstring GenerateHCode(HookParam hp, DWORD processId)
{
std::wstring HCode;
2024-07-21 21:07:05 +08:00
if (hp.type & EMBED_ABLE)
2024-02-07 20:59:24 +08:00
{
2024-07-21 21:07:05 +08:00
HCode += L"E";
if (hp.hook_before || hp.hook_after)
HCode += L'X';
2024-02-07 20:59:24 +08:00
else
{
2024-07-21 21:07:05 +08:00
if (hp.type & EMBED_DYNA_SJIS)
HCode += L"D";
if (hp.type & EMBED_BEFORE_SIMPLE)
HCode += L"S";
if (hp.type & EMBED_AFTER_NEW)
HCode += L"N";
else if (hp.type & EMBED_AFTER_OVERWRITE)
HCode += L"O";
2024-02-07 20:59:24 +08:00
}
}
2024-07-21 21:07:05 +08:00
if (hp.type & BREAK_POINT)
HCode += L"B";
else
HCode += L"H";
2024-02-07 20:59:24 +08:00
2024-07-21 21:07:05 +08:00
if (hp.type & USING_STRING)
{
2024-07-21 21:07:05 +08:00
if (hp.type & SPECIAL_JIT_STRING)
HCode += L'M';
else if (hp.type & CODEC_UTF16)
HCode += L'Q';
2024-07-21 21:07:05 +08:00
else if (hp.type & CODEC_UTF8)
HCode += L'V';
2024-07-21 21:07:05 +08:00
else if (hp.type & CODEC_UTF32)
HCode += L'U';
2024-07-21 21:07:05 +08:00
else
HCode += L'S';
}
2024-03-27 19:33:28 +08:00
else
2024-02-07 20:59:24 +08:00
{
2024-07-21 21:07:05 +08:00
if (hp.type & CODEC_UTF16)
HCode += L'W';
2024-11-04 09:46:14 +08:00
else if (hp.type & CODEC_UTF8)
HCode += L'C';
2024-07-21 21:07:05 +08:00
else if (hp.type & CODEC_UTF32)
HCode += L'I';
2024-07-21 21:07:05 +08:00
else if (hp.type & CODEC_ANSI_BE)
HCode += L'A';
2024-07-21 21:07:05 +08:00
else
HCode += L'B';
2024-03-27 19:33:28 +08:00
}
2024-02-07 20:59:24 +08:00
2024-07-21 21:07:05 +08:00
if (hp.text_fun || hp.filter_fun)
HCode += L'X';
if (hp.type & FULL_STRING)
HCode += L'F';
2024-02-07 20:59:24 +08:00
2024-07-21 21:07:05 +08:00
if (hp.type & NO_CONTEXT)
HCode += L'N';
2024-02-07 20:59:24 +08:00
2024-07-21 21:07:05 +08:00
if (hp.codepage != 0 && !(hp.type & CODEC_UTF8) && !(hp.type & CODEC_UTF16) && !(hp.type & CODEC_UTF32))
HCode += std::to_wstring(hp.codepage) + L'#';
2024-02-07 20:59:24 +08:00
2024-07-21 21:07:05 +08:00
if (hp.padding)
HCode += HexString(hp.padding) + L'+';
if (hp.offset < 0)
hp.offset += 4;
if (hp.split < 0)
hp.split += 4;
if (hp.jittype == JITTYPE::PC)
{
HCode += HexString(hp.offset);
2024-04-01 00:56:15 +08:00
}
2024-07-21 21:07:05 +08:00
else
{
2024-04-01 00:56:15 +08:00
HCode += HexString(hp.argidx);
}
2024-07-21 21:07:05 +08:00
if (hp.type & DATA_INDIRECT)
HCode += L'*' + HexString(hp.index);
if (hp.type & USING_SPLIT)
HCode += L':' + HexString(hp.split);
if (hp.type & SPLIT_INDIRECT)
HCode += L'*' + HexString(hp.split_index);
2024-04-01 00:56:15 +08:00
// Attempt to make the address relative
2024-07-21 21:07:05 +08:00
if (hp.jittype == JITTYPE::PC)
2024-04-01 00:56:15 +08:00
{
if (processId && !(hp.type & MODULE_OFFSET))
if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))
if (MEMORY_BASIC_INFORMATION info = {}; VirtualQueryEx(process, (LPCVOID)hp.address, &info, sizeof(info)))
2024-05-12 16:48:32 +08:00
if (auto moduleName = getModuleFilename(processId, (HMODULE)info.AllocationBase))
{
hp.type |= MODULE_OFFSET;
hp.address -= (uint64_t)info.AllocationBase;
wcsncpy_s(hp.module, moduleName->c_str() + moduleName->rfind(L'\\') + 1, MAX_MODULE_SIZE - 1);
}
HCode += L'@' + HexString(hp.address);
2024-07-21 21:07:05 +08:00
if (hp.type & MODULE_OFFSET)
HCode += L':' + std::wstring(hp.module);
if (hp.type & FUNCTION_OFFSET)
2024-07-26 19:30:04 +08:00
HCode += L':' + acastw(hp.function);
}
2024-04-01 00:56:15 +08:00
else
2024-07-21 21:07:05 +08:00
{
if (hp.jittype == JITTYPE::UNITY)
{
HCode += L'@';
HCode += acastw(hp.unityfunctioninfo);
HCode += L":JIT:UNITY";
2024-05-09 07:23:06 +08:00
}
2024-07-21 21:07:05 +08:00
else
{
2024-05-09 07:23:06 +08:00
HCode += L'@' + HexString(hp.emu_addr);
switch (hp.jittype)
{
2024-07-21 21:07:05 +08:00
case JITTYPE::YUZU:
HCode += L":JIT:YUZU";
2024-05-09 07:23:06 +08:00
break;
case JITTYPE::PPSSPP:
2024-07-21 21:07:05 +08:00
HCode += L":JIT:PPSSPP";
2024-05-09 07:23:06 +08:00
break;
case JITTYPE::VITA3K:
2024-07-21 21:07:05 +08:00
HCode += L":JIT:VITA3K";
2024-05-09 07:23:06 +08:00
break;
case JITTYPE::RPCS3:
2024-07-21 21:07:05 +08:00
HCode += L":JIT:RPCS3";
2024-05-09 07:23:06 +08:00
break;
}
}
}
2024-02-07 20:59:24 +08:00
return HCode;
}
}
namespace HookCode
{
std::optional<HookParam> Parse(std::wstring code)
{
2024-07-21 21:07:05 +08:00
if (code[0] == L'/')
code.erase(0, 1);
2024-02-07 20:59:24 +08:00
code.erase(std::find(code.begin(), code.end(), L'/'), code.end()); // legacy/AGTH compatibility
Trim(code);
2024-07-21 21:07:05 +08:00
if (code[0] == L'R')
return ParseRCode(code);
else if (code[0] == L'E')
return ParseECode(code);
else
return ParseHCode(code);
2024-02-07 20:59:24 +08:00
}
std::wstring Generate(HookParam hp, DWORD processId)
{
2024-07-21 21:07:05 +08:00
std::wstring HCode = L"";
return HCode += (hp.type & DIRECT_READ ? GenerateRCode(hp) : GenerateHCode(hp, processId));
2024-02-07 20:59:24 +08:00
}
}