From 964868b2d2ded0d5525004982dd4d7bc49c7ef0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=81=8D=E5=85=AE=E6=83=9A=E5=85=AE?= <101191390+HIllya51@users.noreply.github.com> Date: Sun, 31 Mar 2024 19:00:26 +0800 Subject: [PATCH] jit hook search 1 1 Update texthook.cc 1 Update hookfinder.cc yuzu/ppsspp hook search Update hookfinder.cc Update hookfinder.cc 1 1 1 --- LunaHook/engine64/yuzusuyu.cpp | 162 +++++----- LunaHook/engines/emujitarg.hpp | 52 ++++ LunaHook/engines/ppsspp/ppsspp.cpp | 32 +- LunaHook/engines/ppsspp/psputils.hpp | 2 + LunaHook/engines/ppsspp/specialgames.hpp | 132 +++------ LunaHook/hookfinder.cc | 358 ++++++++++++++--------- LunaHook/main.cc | 114 +++++++- LunaHook/main.h | 10 + LunaHook/texthook.cc | 93 +++--- LunaHook/util/util.cc | 10 + LunaHook/util/util.h | 2 +- LunaHook/veh_hook.cpp | 85 +++--- LunaHook/veh_hook.h | 10 +- include/hookcode.cpp | 131 +++++---- include/stringutils.cpp | 20 ++ include/stringutils.h | 2 + include/texthook.h | 4 +- include/types.h | 20 +- 18 files changed, 755 insertions(+), 484 deletions(-) create mode 100644 LunaHook/engines/emujitarg.hpp diff --git a/LunaHook/engine64/yuzusuyu.cpp b/LunaHook/engine64/yuzusuyu.cpp index 8178b8f..20f5aa9 100644 --- a/LunaHook/engine64/yuzusuyu.cpp +++ b/LunaHook/engine64/yuzusuyu.cpp @@ -1,7 +1,8 @@ #include"yuzusuyu.h" #include"mages/mages.h" +#include"hookfinder.h" +#include"emujitarg.hpp" namespace{ - auto isFastMem = true; auto isVirtual = true;//Process.arch === 'x64' && Process.platform === 'windows'; @@ -68,18 +69,10 @@ uintptr_t* argidx(hook_stack* stack,int idx){ return (uintptr_t*)((uintptr_t)stack+sizeof(hook_stack)-sizeof(uintptr_t)+offset); } -class emu_arg{ - hook_stack* stack; -public: - emu_arg(hook_stack* stack_):stack(stack_){}; - uintptr_t operator [](int idx){ - auto base=stack->r13; - auto args=(uintptr_t*)stack->r15; - return base+args[idx]; - } -}; struct emfuncinfo{ const char* hookname; + uint64_t type; + int argidx;int padding; void* hookfunc; void* filterfun; const wchar_t* _id; @@ -95,49 +88,35 @@ bool checkiscurrentgame(const emfuncinfo& em){ } return false; } -template -void simpleutf32getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto address=emu_arg(stack)[index]; - auto s=utf32_to_utf16((uint32_t*)address,u32strlen((uint32_t*)address)); - write_string_new(data,len,s); -} - -template -void simpleutf8getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto address=emu_arg(stack)[index]+offset; - hp->type=USING_STRING|CODEC_UTF8|NO_CONTEXT|BREAK_POINT; - *data=address;*len=strlen((char*)address); -} -template -void simpleutf16getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto address=emu_arg(stack)[index]; - hp->type=USING_STRING|CODEC_UTF16|NO_CONTEXT|BREAK_POINT|_type; - *data=address;*len=wcslen((wchar_t*)address)*2; -} - } bool yuzusuyu::attach_function() { - ConsoleOutput("[Compatibility] Yuzu 1616+"); + ConsoleOutput("[Compatibility] Yuzu 1616+"); auto DoJitPtr=getDoJitAddress(); if(DoJitPtr==0)return false; ConsoleOutput("DoJitPtr %p",DoJitPtr); + jitaddrclear(); HookParam hp; hp.address=DoJitPtr; hp.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ auto descriptor = *argidx(stack,idxDescriptor); // r8 auto entrypoint = *argidx(stack,idxEntrypoint); // r9 auto em_address = *(uintptr_t*)descriptor; - em_address-=0x80004000; - if(emfunctionhooks.find(em_address)==emfunctionhooks.end() || !entrypoint)return; - auto op=emfunctionhooks.at(em_address); + jitaddraddr(em_address,entrypoint,JITTYPE::YUZU); + auto em_address_off=em_address- 0x80004000; + if(emfunctionhooks.find(em_address_off)==emfunctionhooks.end() || !entrypoint)return; + auto op=emfunctionhooks.at(em_address_off); if(!(checkiscurrentgame(op)))return; - + HookParam hpinternal; hpinternal.address=entrypoint; - hpinternal.type=CODEC_UTF16|USING_STRING|NO_CONTEXT|BREAK_POINT; + hpinternal.emu_addr=em_address;//用于生成hcode + hpinternal.type=USING_STRING|NO_CONTEXT|BREAK_POINT|op.type; hpinternal.text_fun=(decltype(hpinternal.text_fun))op.hookfunc; hpinternal.filter_fun=(decltype(hpinternal.filter_fun))op.filterfun; + hpinternal.argidx=op.argidx; + hpinternal.padding=op.padding; + hpinternal.jittype=JITTYPE::YUZU; NewHook(hpinternal,op.hookname); }; @@ -145,7 +124,7 @@ bool yuzusuyu::attach_function() } void _0100978013276000(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto s=mages::readString(emu_arg(stack)[0],0); + auto s=mages::readString(YUZU::emu_arg(stack)[0],0); write_string_new(data,len,s); } @@ -174,7 +153,7 @@ bool F010045C0109F2000(void* data, size_t* len, HookParam* hp){ template void T0100A1E00BFEA000(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto address=emu_arg(stack)[index]; + auto address=YUZU::emu_arg(stack)[index]; *len=(*(WORD*)(address+0x10))*2; *data=address+0x14; } @@ -365,7 +344,7 @@ bool F01000200194AE000(void* data, size_t* len, HookParam* hp){ return write_string_overwrite(data,len,s); } bool F0100EA001A626000(void* data, size_t* len, HookParam* hp){ - auto s=std::wstring((wchar_t*)data,*len/2); + auto s=utf32_to_utf16((uint32_t*)data,*len/4); if (s == L"  ") { return false; } @@ -377,20 +356,22 @@ bool F0100EA001A626000(void* data, size_t* len, HookParam* hp){ s = std::regex_replace(s, std::wregex(L"#T2[^#]+"), L""); s = std::regex_replace(s, std::wregex(L"#T\\d"), L""); } - return write_string_overwrite(data,len,s); + auto u32=utf16_to_utf32(s.c_str(),s.size()); + return write_string_overwrite(data,len,u32); } bool F0100F7E00DFC8000(void* data, size_t* len, HookParam* hp){ - auto s=std::wstring((wchar_t*)data,*len/2); + auto s=utf32_to_utf16((uint32_t*)data,*len/4); s = std::regex_replace(s, std::wregex(L"[\\s]"), L" "); s = std::regex_replace(s, std::wregex(L"#KW"), L""); s = std::regex_replace(s, std::wregex(L"#C\\(TR,0xff0000ff\\)"), L""); s = std::regex_replace(s, std::wregex(L"#P\\(.*\\)"), L""); - return write_string_overwrite(data,len,s); + auto u32=utf16_to_utf32(s.c_str(),s.size()); + return write_string_overwrite(data,len,u32); } void T0100982015606000(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto address=emu_arg(stack)[1]; + auto address=YUZU::emu_arg(stack)[1]; *len=(*(DWORD*)(address+0x10))*2; *data=address+0x14; } @@ -410,79 +391,80 @@ bool F0100925014864000(void* data, size_t* len, HookParam* hp){ } bool F0100936018EB4000(void* data, size_t* len, HookParam* hp){ - auto s=std::wstring((wchar_t*)data,*len/2); + auto s=utf32_to_utf16((uint32_t*)data,*len/4); s = std::regex_replace(s, std::wregex(L"<[^>]+>"), L""); s = std::regex_replace(s, std::wregex(L"\n+"), L" "); - return write_string_overwrite(data,len,s); + auto u32=utf16_to_utf32(s.c_str(),s.size()); + return write_string_overwrite(data,len,u32); } namespace{ auto _=[](){ emfunctionhooks={ - {0x8003eeac - 0x80004000,{"Memories Off",_0100978013276000,0,L"0100978013276000",L"1.0.0"}}, - {0x8003eebc - 0x80004000,{"Memories Off",_0100978013276000,0,L"0100978013276000",L"1.0.1"}}, + {0x8003eeac - 0x80004000,{"Memories Off",CODEC_UTF16,0,0,_0100978013276000,0,L"0100978013276000",L"1.0.0"}}, + {0x8003eebc - 0x80004000,{"Memories Off",CODEC_UTF16,0,0,_0100978013276000,0,L"0100978013276000",L"1.0.1"}}, // Shiro to Kuro no Alice - {0x80013f20 - 0x80004000,{"Shiro to Kuro no Alice",simpleutf8getter<0>,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, - {0x80013f94 - 0x80004000,{"Shiro to Kuro no Alice",simpleutf8getter<0>,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, - {0x8001419c - 0x80004000,{"Shiro to Kuro no Alice",simpleutf8getter<0>,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, + {0x80013f20 - 0x80004000,{"Shiro to Kuro no Alice",CODEC_UTF8,0,0,0,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, + {0x80013f94 - 0x80004000,{"Shiro to Kuro no Alice",CODEC_UTF8,0,0,0,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, + {0x8001419c - 0x80004000,{"Shiro to Kuro no Alice",CODEC_UTF8,0,0,0,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, // Shiro to Kuro no Alice -Twilight line- - {0x80014260 - 0x80004000,{"Shiro to Kuro no Alice -Twilight line-",simpleutf8getter<0>,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, - {0x800142d4 - 0x80004000,{"Shiro to Kuro no Alice -Twilight line-",simpleutf8getter<0>,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, - {0x800144dc - 0x80004000,{"Shiro to Kuro no Alice -Twilight line-",simpleutf8getter<0>,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, + {0x80014260 - 0x80004000,{"Shiro to Kuro no Alice -Twilight line-",CODEC_UTF8,0,0,0,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, + {0x800142d4 - 0x80004000,{"Shiro to Kuro no Alice -Twilight line-",CODEC_UTF8,0,0,0,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, + {0x800144dc - 0x80004000,{"Shiro to Kuro no Alice -Twilight line-",CODEC_UTF8,0,0,0,NewLineCharFilterW,L"0100A460141B8000",L"1.0.0"}}, - {0x80072d00 - 0x80004000,{"CLANNAD",simpleutf16getter<1,FULL_STRING>,F0100A3A00CC7E000,L"0100A3A00CC7E000",L"1.0.0"}}, - {0x80072d30 - 0x80004000,{"CLANNAD",simpleutf16getter<1,FULL_STRING>,F0100A3A00CC7E000,L"0100A3A00CC7E000",L"1.0.7"}}, + {0x80072d00 - 0x80004000,{"CLANNAD",CODEC_UTF16|FULL_STRING,1,0,0, F0100A3A00CC7E000,L"0100A3A00CC7E000",L"1.0.0"}}, + {0x80072d30 - 0x80004000,{"CLANNAD",CODEC_UTF16|FULL_STRING,1,0,0,F0100A3A00CC7E000,L"0100A3A00CC7E000",L"1.0.7"}}, - {0x800e3424 - 0x80004000,{"VARIABLE BARRICADE NS",simpleutf8getter<0>,F010045C0109F2000,L"010045C0109F2000",L"1.0.1"}},//"System Messages + Choices"), //Also includes the names of characters, - {0x800fb080 - 0x80004000,{"VARIABLE BARRICADE NS",simpleutf8getter<3>,F010045C0109F2000,L"010045C0109F2000",L"1.0.1"}},//Main Text + {0x800e3424 - 0x80004000,{"VARIABLE BARRICADE NS",CODEC_UTF8,0,0,0,F010045C0109F2000,L"010045C0109F2000",L"1.0.1"}},//"System Messages + Choices"), //Also includes the names of characters, + {0x800fb080 - 0x80004000,{"VARIABLE BARRICADE NS",CODEC_UTF8,3,0,0,F010045C0109F2000,L"010045C0109F2000",L"1.0.1"}},//Main Text - {0x805bba5c - 0x80004000,{"AMNESIA for Nintendo Switch",T0100A1E00BFEA000<2>,F0100A1E00BFEA000,L"0100A1E00BFEA000",L"1.0.1"}},//dialogue - {0x805e9930 - 0x80004000,{"AMNESIA for Nintendo Switch",T0100A1E00BFEA000<2>,F0100A1E00BFEA000,L"0100A1E00BFEA000",L"1.0.1"}},//choice - {0x805e7fd8 - 0x80004000,{"AMNESIA for Nintendo Switch",T0100A1E00BFEA000<2>,F0100A1E00BFEA000,L"0100A1E00BFEA000",L"1.0.1"}},//name + {0x805bba5c - 0x80004000,{"AMNESIA for Nintendo Switch",CODEC_UTF16,0,0,T0100A1E00BFEA000<2>,F0100A1E00BFEA000,L"0100A1E00BFEA000",L"1.0.1"}},//dialogue + {0x805e9930 - 0x80004000,{"AMNESIA for Nintendo Switch",CODEC_UTF16,0,0,T0100A1E00BFEA000<2>,F0100A1E00BFEA000,L"0100A1E00BFEA000",L"1.0.1"}},//choice + {0x805e7fd8 - 0x80004000,{"AMNESIA for Nintendo Switch",CODEC_UTF16,0,0,T0100A1E00BFEA000<2>,F0100A1E00BFEA000,L"0100A1E00BFEA000",L"1.0.1"}},//name - {0x80095010 - 0x80004000,{"Chou no Doku Hana no Kusari Taishou Tsuya Koi Ibun",simpleutf16getter<1>,F0100A1200CA3C000,L"0100A1200CA3C000",L"2.0.1"}},//Main Text + Names + {0x80095010 - 0x80004000,{"Chou no Doku Hana no Kusari Taishou Tsuya Koi Ibun",CODEC_UTF16,1,0,0,F0100A1200CA3C000,L"0100A1200CA3C000",L"2.0.1"}},//Main Text + Names - {0x80a05170 - 0x80004000,{"Live a Live",simpleutf16getter<0>,F0100C29017106000,L"0100C29017106000",L"1.0.0"}}, + {0x80a05170 - 0x80004000,{"Live a Live",CODEC_UTF16,0,0,0,F0100C29017106000,L"0100C29017106000",L"1.0.0"}}, - {0x8049d968 - 0x80004000,{"Sakura no Kumo * Scarlet no Koi",simpleutf8getter<0,1>,F01006590155AC000,L"01006590155AC000",L"1.0.0"}},//name - {0x8049d980 - 0x80004000,{"Sakura no Kumo * Scarlet no Koi",simpleutf8getter<0>,F01006590155AC000,L"01006590155AC000",L"1.0.0"}},//dialogue + {0x8049d968 - 0x80004000,{"Sakura no Kumo * Scarlet no Koi",CODEC_UTF8,0,1,0,F01006590155AC000,L"01006590155AC000",L"1.0.0"}},//name + {0x8049d980 - 0x80004000,{"Sakura no Kumo * Scarlet no Koi",CODEC_UTF8,0,0,0,F01006590155AC000,L"01006590155AC000",L"1.0.0"}},//dialogue - {0x80557408 - 0x80004000,{"Majestic Majolical",simpleutf8getter<0>,F01000200194AE000,L"01000200194AE000",L"1.0.0"}},//name - {0x8059ee94 - 0x80004000,{"Majestic Majolical",simpleutf8getter<3>,F01000200194AE000,L"01000200194AE000",L"1.0.0"}},//player name - {0x80557420 - 0x80004000,{"Majestic Majolical",simpleutf8getter<0>,F01000200194AE000,L"01000200194AE000",L"1.0.0"}},//dialogue + {0x80557408 - 0x80004000,{"Majestic Majolical",CODEC_UTF8,0,0,0,F01000200194AE000,L"01000200194AE000",L"1.0.0"}},//name + {0x8059ee94 - 0x80004000,{"Majestic Majolical",CODEC_UTF8,3,0,0,F01000200194AE000,L"01000200194AE000",L"1.0.0"}},//player name + {0x80557420 - 0x80004000,{"Majestic Majolical",CODEC_UTF8,0,0,0,F01000200194AE000,L"01000200194AE000",L"1.0.0"}},//dialogue - {0x8017ad54 - 0x80004000,{"Matsurika no Kei",simpleutf32getter<1>,F0100EA001A626000,L"0100EA001A626000",L"1.0.0"}},// text - {0x80174d4c - 0x80004000,{"Matsurika no Kei",simpleutf32getter<1>,F0100EA001A626000,L"0100EA001A626000",L"1.0.0"}},// name + {0x8017ad54 - 0x80004000,{"Matsurika no Kei",CODEC_UTF32,1,0,0,F0100EA001A626000,L"0100EA001A626000",L"1.0.0"}},// text + {0x80174d4c - 0x80004000,{"Matsurika no Kei",CODEC_UTF32,1,0,0,F0100EA001A626000,L"0100EA001A626000",L"1.0.0"}},// name - {0x80057910 - 0x80004000,{"Cupid Parasite",simpleutf32getter<2>,F0100F7E00DFC8000,L"0100F7E00DFC8000",L"1.0.1"}},// name + text - {0x80169df0 - 0x80004000,{"Cupid Parasite",simpleutf32getter<0>,F0100F7E00DFC8000,L"0100F7E00DFC8000",L"1.0.1"}},// choice + {0x80057910 - 0x80004000,{"Cupid Parasite",CODEC_UTF32,2,0,0,F0100F7E00DFC8000,L"0100F7E00DFC8000",L"1.0.1"}},// name + text + {0x80169df0 - 0x80004000,{"Cupid Parasite",CODEC_UTF32,0,0,0,F0100F7E00DFC8000,L"0100F7E00DFC8000",L"1.0.1"}},// choice - {0x80075190 - 0x80004000,{"Radiant Tale",simpleutf8getter<1>,F0100925014864000,L"0100925014864000",L"1.0.0"}},// prompt - {0x8002fb18 - 0x80004000,{"Radiant Tale",simpleutf8getter<0>,F0100925014864000,L"0100925014864000",L"1.0.0"}},// name - {0x8002fd7c - 0x80004000,{"Radiant Tale",simpleutf8getter<0>,F0100925014864000,L"0100925014864000",L"1.0.0"}},// text + {0x80075190 - 0x80004000,{"Radiant Tale",CODEC_UTF8,1,0,0,F0100925014864000,L"0100925014864000",L"1.0.0"}},// prompt + {0x8002fb18 - 0x80004000,{"Radiant Tale",CODEC_UTF8,0,0,0,F0100925014864000,L"0100925014864000",L"1.0.0"}},// name + {0x8002fd7c - 0x80004000,{"Radiant Tale",CODEC_UTF8,0,0,0,F0100925014864000,L"0100925014864000",L"1.0.0"}},// text - {0x80462DD4 - 0x80004000,{"MUSICUS",simpleutf8getter<0,1>,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// name - {0x80462DEC - 0x80004000,{"MUSICUS",simpleutf8getter<0>,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// dialogue 1 - {0x80480d4c - 0x80004000,{"MUSICUS",simpleutf8getter<0>,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// dialogue 2 - {0x804798e0 - 0x80004000,{"MUSICUS",simpleutf8getter<0>,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// choice + {0x80462DD4 - 0x80004000,{"MUSICUS",CODEC_UTF8,0,1,0,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// name + {0x80462DEC - 0x80004000,{"MUSICUS",CODEC_UTF8,0,0,0,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// dialogue 1 + {0x80480d4c - 0x80004000,{"MUSICUS",CODEC_UTF8,0,0,0,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// dialogue 2 + {0x804798e0 - 0x80004000,{"MUSICUS",CODEC_UTF8,0,0,0,F01006590155AC000,L"01000130150FA000",L"1.0.0"}},// choice - {0x80046700 - 0x80004000,{"CHAOS;HEAD NOAH",_0100978013276000,0,L"0100957016B90000",L"1.0.0"}}, - {0x8003A2c0 - 0x80004000,{"CHAOS;HEAD NOAH",_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// choice - {0x8003EAB0 - 0x80004000,{"CHAOS;HEAD NOAH",_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// TIPS list (menu) - {0x8004C648 - 0x80004000,{"CHAOS;HEAD NOAH",_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// system message - {0x80050374 - 0x80004000,{"CHAOS;HEAD NOAH",_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// TIPS (red) + {0x80046700 - 0x80004000,{"CHAOS;HEAD NOAH",CODEC_UTF16,0,0,_0100978013276000,0,L"0100957016B90000",L"1.0.0"}}, + {0x8003A2c0 - 0x80004000,{"CHAOS;HEAD NOAH",CODEC_UTF16,0,0,_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// choice + {0x8003EAB0 - 0x80004000,{"CHAOS;HEAD NOAH",CODEC_UTF16,0,0,_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// TIPS list (menu) + {0x8004C648 - 0x80004000,{"CHAOS;HEAD NOAH",CODEC_UTF16,0,0,_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// system message + {0x80050374 - 0x80004000,{"CHAOS;HEAD NOAH",CODEC_UTF16,0,0,_0100978013276000,0,L"0100957016B90000",L"1.0.0"}},// TIPS (red) - {0x80ac4d88 - 0x80004000,{"Story of Seasons a Wonderful Life",simpleutf32getter<0>,F0100936018EB4000,L"0100936018EB4000",L"1.0.3"}},// Main text - {0x808f7e84 - 0x80004000,{"Story of Seasons a Wonderful Life",simpleutf32getter<0>,F0100936018EB4000,L"0100936018EB4000",L"1.0.3"}},// Item name - {0x80bdf804 - 0x80004000,{"Story of Seasons a Wonderful Life",simpleutf32getter<0>,F0100936018EB4000,L"0100936018EB4000",L"1.0.3"}},// Item description + {0x80ac4d88 - 0x80004000,{"Story of Seasons a Wonderful Life",CODEC_UTF32,0,0,F0100936018EB4000,L"0100936018EB4000",L"1.0.3"}},// Main text + {0x808f7e84 - 0x80004000,{"Story of Seasons a Wonderful Life",CODEC_UTF32,0,0,F0100936018EB4000,L"0100936018EB4000",L"1.0.3"}},// Item name + {0x80bdf804 - 0x80004000,{"Story of Seasons a Wonderful Life",CODEC_UTF32,0,0,F0100936018EB4000,L"0100936018EB4000",L"1.0.3"}},// Item description - {0x81e75940 - 0x80004000,{"Hamefura Pirates",T0100982015606000,F0100982015606000,L"0100982015606000",L"1.0.0"}},// Hamekai.TalkPresenter$$AddMessageBacklog - {0x81c9ae60 - 0x80004000,{"Hamefura Pirates",T0100982015606000,F0100982015606000,L"0100982015606000",L"1.0.0"}},// Hamekai.ChoicesText$$SetText - {0x81eb7dc0 - 0x80004000,{"Hamefura Pirates",T0100982015606000,F0100982015606000,L"0100982015606000",L"1.0.0"}},// Hamekai.ShortStoryTextView$$AddText + {0x81e75940 - 0x80004000,{"Hamefura Pirates",CODEC_UTF16,0,0,T0100982015606000,F0100982015606000,L"0100982015606000",L"1.0.0"}},// Hamekai.TalkPresenter$$AddMessageBacklog + {0x81c9ae60 - 0x80004000,{"Hamefura Pirates",CODEC_UTF16,0,0,T0100982015606000,F0100982015606000,L"0100982015606000",L"1.0.0"}},// Hamekai.ChoicesText$$SetText + {0x81eb7dc0 - 0x80004000,{"Hamefura Pirates",CODEC_UTF16,0,0,T0100982015606000,F0100982015606000,L"0100982015606000",L"1.0.0"}},// Hamekai.ShortStoryTextView$$AddText }; return 1; diff --git a/LunaHook/engines/emujitarg.hpp b/LunaHook/engines/emujitarg.hpp new file mode 100644 index 0000000..7066561 --- /dev/null +++ b/LunaHook/engines/emujitarg.hpp @@ -0,0 +1,52 @@ +#pragma once +#ifdef _WIN64 +namespace YUZU +{ +class emu_arg{ + hook_stack* stack; +public: + emu_arg(hook_stack* stack_):stack(stack_){}; + uintptr_t operator [](int idx){ + auto base=stack->r13; + auto args=(uintptr_t*)stack->r15; + return base+args[idx]; + } +}; +} +#endif +namespace PPSSPP{ + inline DWORD x86_baseaddr; +class emu_addr{ + hook_stack* stack; + DWORD addr; +public: + emu_addr(hook_stack* stack_,DWORD addr_):stack(stack_),addr(addr_){}; + operator uintptr_t(){ + #ifndef _WIN64 + auto base=x86_baseaddr; + #else + auto base=stack->rbx; + #endif + return base+addr; + } + operator DWORD*(){ + return (DWORD*)(uintptr_t)*this; + } +}; +class emu_arg{ + hook_stack* stack; +public: + + emu_arg(hook_stack* stack_):stack(stack_){}; + uintptr_t operator [](int idx){ + #ifndef _WIN64 + auto args=stack->ebp; + #else + auto args=stack->r14; + #endif + auto offR = -0x80; + auto offset = offR + 0x10 + idx * 4; + return (uintptr_t)emu_addr(stack,*(uint32_t*)(args+offset)); + } +}; +} \ No newline at end of file diff --git a/LunaHook/engines/ppsspp/ppsspp.cpp b/LunaHook/engines/ppsspp/ppsspp.cpp index 37f626d..11694a9 100644 --- a/LunaHook/engines/ppsspp/ppsspp.cpp +++ b/LunaHook/engines/ppsspp/ppsspp.cpp @@ -283,25 +283,29 @@ bool hookPPSSPPDoJit(){ HookParam hp; hp.address=DoJitPtr;//Jit::DoJit ConsoleOutput("DoJitPtr %p",DoJitPtr); - + jitaddrclear(); + hp.user_value=(uintptr_t)new uintptr_t; hp.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ auto em_address=stack->THISCALLARG1; - if(emfunctionhooks.find(em_address)==emfunctionhooks.end())return; - - if(!(checkiscurrentgame(emfunctionhooks.at(em_address))))return; + *(uintptr_t*)(hp->user_value)=em_address; HookParam hpinternal; - hpinternal.user_value=em_address; + hpinternal.user_value=hp->user_value; hpinternal.address=stack->retaddr; hpinternal.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - hp->text_fun=nullptr;hp->type=HOOK_EMPTY; - + auto ret=stack->RETADDR; if(breakpoints.find(ret)!=breakpoints.end())return; breakpoints.insert(ret); - auto em_address=hp->user_value; + auto em_address=*(uintptr_t*)(hp->user_value); + + jitaddraddr(em_address,ret,JITTYPE::PPSSPP); + + if(emfunctionhooks.find(em_address)==emfunctionhooks.end())return; + if(!(checkiscurrentgame(emfunctionhooks.at(em_address))))return; + auto op=emfunctionhooks.at(em_address); ConsoleOutput("jit function addr %p",ret); #ifndef _WIN64 @@ -317,17 +321,21 @@ bool hookPPSSPPDoJit(){ findbase=MemDbg::findBytes(sig,sizeof(sig),ret-0x1000,ret+0x1000); if(!findbase) ConsoleOutput("can't find emu_baseaddr"); - ppsspp::x86_baseaddr=(*(DWORD*)(findbase+12))&0xffff0000; - ConsoleOutput("x86 base addr %p",x86_baseaddr); + PPSSPP::x86_baseaddr=(*(DWORD*)(findbase+12))&0xffff0000; + ConsoleOutput("x86 base addr %p",PPSSPP::x86_baseaddr); #endif HookParam hpinternal; hpinternal.address=ret; - hpinternal.user_value=em_address; + hpinternal.emu_addr=em_address;//用于生成hcode + hpinternal.type=USING_STRING|NO_CONTEXT|BREAK_POINT|op.type; hpinternal.text_fun=(decltype(hpinternal.text_fun))op.hookfunc; hpinternal.filter_fun=(decltype(hpinternal.filter_fun))op.filterfun; + hpinternal.argidx=op.argidx; + hpinternal.padding=op.padding; + hpinternal.jittype=JITTYPE::PPSSPP; NewHook(hpinternal,op.hookname); }; - NewHook(hpinternal,"DoJitPtrRet"); + static auto once=NewHook(hpinternal,"DoJitPtrRet"); }; return NewHook(hp,"PPSSPPDoJit"); diff --git a/LunaHook/engines/ppsspp/psputils.hpp b/LunaHook/engines/ppsspp/psputils.hpp index 4cf5e1a..f694c9e 100644 --- a/LunaHook/engines/ppsspp/psputils.hpp +++ b/LunaHook/engines/ppsspp/psputils.hpp @@ -5,6 +5,8 @@ namespace ppsspp struct emfuncinfo{ const char* hookname; + uint64_t type; + int argidx;int padding; void* hookfunc; void* filterfun; const wchar_t* _id; diff --git a/LunaHook/engines/ppsspp/specialgames.hpp b/LunaHook/engines/ppsspp/specialgames.hpp index 26ad531..a0da34c 100644 --- a/LunaHook/engines/ppsspp/specialgames.hpp +++ b/LunaHook/engines/ppsspp/specialgames.hpp @@ -1,63 +1,5 @@ #include -namespace ppsspp{ - inline DWORD x86_baseaddr; -} -namespace -{ - -template -void simple932getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - hp->type=USING_STRING|NO_CONTEXT; - hp->codepage=932; - auto address= emu_arg(stack)[index]+offset; - *data=address;*len=strlen((char*)address); -} -template -void simpleutf8getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto address=emu_arg(stack)[index]; - hp->type=USING_STRING|CODEC_UTF8|NO_CONTEXT; - *data=address;*len=strlen((char*)address); -} -template -void simpleutf16getter(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - auto address=emu_arg(stack)[index]; - hp->type=USING_STRING|CODEC_UTF16|NO_CONTEXT|_type; - *data=address;*len=wcslen((wchar_t*)address)*2; -} -class emu_addr{ - hook_stack* stack; - DWORD addr; -public: - emu_addr(hook_stack* stack_,DWORD addr_):stack(stack_),addr(addr_){}; - operator uintptr_t(){ - #ifndef _WIN64 - auto base=ppsspp::x86_baseaddr; - #else - auto base=stack->rbx; - #endif - return base+addr; - } - operator DWORD*(){ - return (DWORD*)(uintptr_t)*this; - } -}; -class emu_arg{ - hook_stack* stack; -public: - - emu_arg(hook_stack* stack_):stack(stack_){}; - uintptr_t operator [](int idx){ - #ifndef _WIN64 - auto args=stack->ebp; - #else - auto args=stack->r14; - #endif - auto offR = -0x80; - auto offset = offR + 0x10 + idx * 4; - return (uintptr_t)emu_addr(stack,*(uint32_t*)(args+offset)); - } -}; -} +#include"emujitarg.hpp" bool ULJS00403_filter(void* data, size_t* len, HookParam* hp){ std::string result = std::string((char*)data,*len); @@ -70,21 +12,19 @@ bool ULJS00403_filter(void* data, size_t* len, HookParam* hp){ void ULJS00339(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - hp->type=USING_STRING|NO_CONTEXT; - hp->codepage=932; - auto a2= emu_arg(stack)[0]; + auto a2= PPSSPP::emu_arg(stack)[0]; auto vm = *(DWORD*)(a2+(0x28)); - vm=*(DWORD*)emu_addr(stack,vm); - vm=*(DWORD*)emu_addr(stack,vm+8); - uintptr_t address=emu_addr(stack,vm); + vm=*(DWORD*)PPSSPP::emu_addr(stack,vm); + vm=*(DWORD*)PPSSPP::emu_addr(stack,vm+8); + uintptr_t address=PPSSPP::emu_addr(stack,vm); auto len1=*(DWORD*)(address+4); auto p=address+0x20; if(len1>4 && *(WORD*)(p+2)==0){ auto p1=*(DWORD*)(address+8); - vm=*(DWORD*)emu_addr(stack,vm); - vm=*(DWORD*)emu_addr(stack,vm+0xC); - p=emu_addr(stack,vm); + vm=*(DWORD*)PPSSPP::emu_addr(stack,vm); + vm=*(DWORD*)PPSSPP::emu_addr(stack,vm+0xC); + p=PPSSPP::emu_addr(stack,vm); } static int fm=0; static std::string pre; @@ -215,9 +155,7 @@ namespace Corda{ } void ULJM05428(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - hp->type=USING_STRING|NO_CONTEXT; - hp->codepage=932; - auto address= emu_arg(stack)[1]; + auto address= PPSSPP::emu_arg(stack)[1]; bool haveNamve; auto s=Corda::readBinaryString(address,&haveNamve); *split=haveNamve; @@ -225,14 +163,12 @@ void ULJM05428(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* spl } void ULJM05054(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - hp->type=USING_STRING|NO_CONTEXT; - hp->codepage=932; - if (hp->user_value != 0x886162c) { - auto addr=emu_arg(stack)[0]+0x3c; + if (hp->emu_addr != 0x886162c) { + auto addr=PPSSPP::emu_arg(stack)[0]+0x3c; *data=addr;*len=strlen((char*)addr); return; } - auto address= emu_arg(stack)[1]; + auto address= PPSSPP::emu_arg(stack)[1]; bool haveNamve; auto s=Corda::readBinaryString(address,&haveNamve); *split=haveNamve; @@ -296,9 +232,7 @@ bool NPJH50505F(void* data, size_t* len, HookParam* hp){ } void QNPJH50909(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ - hp->type=USING_STRING|NO_CONTEXT; - hp->codepage=932; - uintptr_t addr = emu_addr(stack,0x08975110); + uintptr_t addr = PPSSPP::emu_addr(stack,0x08975110); auto adr=addr+0x20; auto len1=*(DWORD*)(addr+0x14)*2; @@ -308,27 +242,27 @@ void QNPJH50909(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* sp } namespace ppsspp{ std::unordered_mapemfunctionhooks= { - {0x883bf34,{"Shinigami to Shoujo",simple932getter<1>,ULJS00403_filter,L"ULJS00403"}}, - {0x0886775c,{"Amagami",ULJS00339,0,L"ULJS00339"}},// String.length() - {0x8814adc,{"Sekai de Ichiban Dame na Koi",simple932getter<0>,NPJH50909_filter,L"ULJM05878"}},// name + dialouge - {0x8850b2c,{"Sekai de Ichiban Dame na Koi",simple932getter<0>,NPJH50909_filter,L"ULJM05878"}},// onscreen toast - {0x0891D72C,{"Dunamis15",simpleutf8getter<0>,ULJM06119_filter,L"ULJM06119"}}, - {0x88506d0,{"Princess Evangile Portable",simpleutf16getter<2>,ULJM06036_filter,L"ULJM06036"}},// [0x88506d0(2)...0x088507C0(?)] // name text text (line doubled) - {0x89b59dc,{"Kin'iro no Corda 2f",ULJM05428,0,L"ULJM05428"}}, - {0x886162c,{"Kin'iro no Corda",ULJM05054,0,L"ULJM05054"}},// dialogue: 0x886162c (x1), 0x889d5fc-0x889d520(a2) fullLine - {0x8899e90,{"Kin'iro no Corda",ULJM05054,0,L"ULJM05054"}},// name 0x88da57c, 0x8899ca4 (x0, oneTime), 0x8899e90 - {0x8952cfc,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//dialog - {0x884aad4,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//description - {0x882e1b0,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//system - {0x88bb108,{"Sol Trigger",simpleutf8getter<2>,NPJH50619F,L"NPJH50619"}},//battle tutorial - {0x89526a0,{"Sol Trigger",simpleutf8getter<0>,NPJH50619F,L"NPJH50619"}},//battle info - {0x88bcef8,{"Sol Trigger",simpleutf8getter<1>,NPJH50619F,L"NPJH50619"}},//battle talk - {0x8958490,{"Fate/EXTRA CCC",simple932getter<0>,NPJH50505F,L"NPJH50505"}}, - {0x088630f8,{"Kamigami no Asobi InFinite",QNPJH50909,0,L"NPJH50909"}}, // text, choice (debounce trailing 400ms), TODO: better hook - {0x0887813c,{"Kamigami no Asobi InFinite",simple932getter<3,4>,0,L"NPJH50909"}}, // Question YN + {0x883bf34,{"Shinigami to Shoujo",0,1,0,0,ULJS00403_filter,L"ULJS00403"}}, + {0x0886775c,{"Amagami",0,0,0,ULJS00339,0,L"ULJS00339"}},// String.length() + {0x8814adc,{"Sekai de Ichiban Dame na Koi",0,0,0,0,NPJH50909_filter,L"ULJM05878"}},// name + dialouge + {0x8850b2c,{"Sekai de Ichiban Dame na Koi",0,0,0,0,NPJH50909_filter,L"ULJM05878"}},// onscreen toast + {0x0891D72C,{"Dunamis15",CODEC_UTF8,0,0,0,ULJM06119_filter,L"ULJM06119"}}, + {0x88506d0,{"Princess Evangile Portable",CODEC_UTF16,2,0,0,ULJM06036_filter,L"ULJM06036"}},// [0x88506d0(2)...0x088507C0(?)] // name text text (line doubled) + {0x89b59dc,{"Kin'iro no Corda 2f",0,0,0,ULJM05428,0,L"ULJM05428"}}, + {0x886162c,{"Kin'iro no Corda",0,0,0,ULJM05054,0,L"ULJM05054"}},// dialogue: 0x886162c (x1), 0x889d5fc-0x889d520(a2) fullLine + {0x8899e90,{"Kin'iro no Corda",0,0,0,ULJM05054,0,L"ULJM05054"}},// name 0x88da57c, 0x8899ca4 (x0, oneTime), 0x8899e90 + {0x8952cfc,{"Sol Trigger",CODEC_UTF8,0,0,0,NPJH50619F,L"NPJH50619"}},//dialog + {0x884aad4,{"Sol Trigger",CODEC_UTF8,0,0,0,NPJH50619F,L"NPJH50619"}},//description + {0x882e1b0,{"Sol Trigger",CODEC_UTF8,0,0,0,NPJH50619F,L"NPJH50619"}},//system + {0x88bb108,{"Sol Trigger",CODEC_UTF8,2,0,0,NPJH50619F,L"NPJH50619"}},//battle tutorial + {0x89526a0,{"Sol Trigger",CODEC_UTF8,0,0,0,NPJH50619F,L"NPJH50619"}},//battle info + {0x88bcef8,{"Sol Trigger",CODEC_UTF8,1,0,0,NPJH50619F,L"NPJH50619"}},//battle talk + {0x8958490,{"Fate/EXTRA CCC",0,0,0,0,NPJH50505F,L"NPJH50505"}}, + {0x088630f8,{"Kamigami no Asobi InFinite",0,0,0,QNPJH50909,0,L"NPJH50909"}}, // text, choice (debounce trailing 400ms), TODO: better hook + {0x0887813c,{"Kamigami no Asobi InFinite",0,3,4,0,0,L"NPJH50909"}}, // Question YN - {0x88eeba4,{"Gekka Ryouran Romance",simple932getter<0,0>,ULJM05943F,L"ULJM05943"}},// a0 - monologue text - {0x8875e0c,{"Gekka Ryouran Romance",simple932getter<1,6>,ULJM05943F,L"ULJM05943"}},// a1 - dialogue text + {0x88eeba4,{"Gekka Ryouran Romance",0,0,0,0,ULJM05943F,L"ULJM05943"}},// a0 - monologue text + {0x8875e0c,{"Gekka Ryouran Romance",0,1,6,0,ULJM05943F,L"ULJM05943"}},// a1 - dialogue text }; } \ No newline at end of file diff --git a/LunaHook/hookfinder.cc b/LunaHook/hookfinder.cc index e48e00b..98a9da1 100644 --- a/LunaHook/hookfinder.cc +++ b/LunaHook/hookfinder.cc @@ -4,6 +4,8 @@ #include "util.h" #include "MinHook.h" #include"Lang/Lang.h" +#include"veh_hook.h" +#include"engines/emujitarg.hpp" namespace { SearchParam sp; @@ -12,8 +14,11 @@ namespace struct HookRecord { uint64_t address = 0; + uint64_t em_addr=0; + int argidx=0; uintptr_t padding = 0; int offset = 0; + JITTYPE jittype; char text[MAX_STRING_SIZE] = {}; }; std::unique_ptr records; @@ -122,7 +127,51 @@ bool IsBadReadPtr(void* data) } return cacheEntry == BAD_PAGE; } - +void DoSend(int i,uintptr_t address,char* str,uintptr_t padding,JITTYPE jittype=JITTYPE::PC,uintptr_t em_addr=0) +{ + str += padding; + if (IsBadReadPtr(str) || IsBadReadPtr(str + MAX_STRING_SIZE)) return; + __try + { + int length = 0, sum = 0; + for (; (str[length] || str[length + 1]) && length < MAX_STRING_SIZE; length += 2) sum += *(uint16_t*)(str + length); + if (length > STRING && length < MAX_STRING_SIZE - 1) + { + // many duplicate results with same address, offset, and third/fourth character will be found: filter them out + uint64_t signature = ((uint64_t)i << 56) | ((uint64_t)(str[2] + str[3]) << 48) | address; + if (signatureCache[signature % CACHE_SIZE] == signature) return; + signatureCache[signature % CACHE_SIZE] = signature; + // 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 + if (_InterlockedIncrement(sumCache + (sum % CACHE_SIZE)) > 25) return; + long n = sp.maxRecords - _InterlockedDecrement(&recordsAvailable); + if (n < sp.maxRecords) + { + records[n].jittype = jittype; + records[n].padding = padding; + if(jittype==JITTYPE::PC) + { + records[n].address = address; + records[n].offset = i * sizeof(char*); + } + else + { + records[n].em_addr=em_addr; + records[n].argidx=i; + } + + for (int j = 0; j < length; ++j) records[n].text[j] = str[j]; + records[n].text[length] = 0; + } + if (n == sp.maxRecords) + { + spDefault.maxRecords = sp.maxRecords * 2; + ConsoleOutput(OUT_OF_RECORDS_RETRY); + } + } + } + __except (EXCEPTION_EXECUTE_HANDLER) {} +} 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) @@ -130,41 +179,46 @@ void Send(char** stack, uintptr_t address) if (recordsAvailable <= 0) return; for (int i = -registers; i < 10; ++i) for (auto padding : { uintptr_t{}, sp.padding }) { - char* str = stack[i] + padding; - if (IsBadReadPtr(str) || IsBadReadPtr(str + MAX_STRING_SIZE)) continue; - __try - { - int length = 0, sum = 0; - for (; (str[length] || str[length + 1]) && length < MAX_STRING_SIZE; length += 2) sum += *(uint16_t*)(str + length); - if (length > STRING && length < MAX_STRING_SIZE - 1) - { - // many duplicate results with same address, offset, and third/fourth character will be found: filter them out - uint64_t signature = ((uint64_t)i << 56) | ((uint64_t)(str[2] + str[3]) << 48) | address; - if (signatureCache[signature % CACHE_SIZE] == signature) continue; - signatureCache[signature % CACHE_SIZE] = signature; - // 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 - if (_InterlockedIncrement(sumCache + (sum % CACHE_SIZE)) > 25) continue; - long n = sp.maxRecords - _InterlockedDecrement(&recordsAvailable); - if (n < sp.maxRecords) - { - records[n].address = address; - records[n].padding = padding; - records[n].offset = i * sizeof(char*); - for (int j = 0; j < length; ++j) records[n].text[j] = str[j]; - records[n].text[length] = 0; - } - if (n == sp.maxRecords) - { - spDefault.maxRecords = sp.maxRecords * 2; - ConsoleOutput(OUT_OF_RECORDS_RETRY); - } - } - } - __except (EXCEPTION_EXECUTE_HANDLER) {} + DoSend(i,address,stack[i],padding); } + +} +void SafeSendJitVeh(hook_stack* stack,uintptr_t address,uintptr_t em_addr,JITTYPE jittype){ + __try + { + for (int i = 0;i<16;i++) + { + char* str=0; + switch (jittype) + { + #ifdef _WIN64 + case JITTYPE::YUZU: + str=(char*)YUZU::emu_arg(stack)[i]; + break; + #endif + case JITTYPE::PPSSPP: + str=(char*)PPSSPP::emu_arg(stack)[i]; + break; + default: + return; + } + DoSend(i,address,str,0,jittype,em_addr); + } + }__except (EXCEPTION_EXECUTE_HANDLER) {} +} +std::unordered_mapaddresscalledtime; +bool SendJitVeh(PCONTEXT context,uintptr_t address,uintptr_t em_addr,JITTYPE jittype){ + + if (recordsAvailable <= 0) return false; + if(addresscalledtime.find(address)==addresscalledtime.end())addresscalledtime[address]=0; + auto tm=GetTickCount64(); + if(tm-addresscalledtime[address]<100)return false; + addresscalledtime[address]=tm; + auto stack=std::make_unique(); + context_get(stack.get(),context); + SafeSendJitVeh(stack.get(),address,em_addr,jittype); + return true; } - std::vector GetFunctions(uintptr_t module) { if (!module) return {}; @@ -190,6 +244,43 @@ void mergevector(std::vector &v1,std::vector &v2){ } } } +void SearchForHooks_Return(){ + ConsoleOutput(HOOK_SEARCH_FINISHED, sp.maxRecords - recordsAvailable); + for (int i = 0, results = 0; i < sp.maxRecords; ++i) + { + HookParam hp; + hp.codepage = sp.codepage; + hp.jittype=records[i].jittype; + hp.padding = records[i].padding; + + if(records[i].jittype==JITTYPE::PC) + { + if (!records[i].address) continue; + hp.offset = records[i].offset; + hp.type = CODEC_UTF16 | USING_STRING; + hp.address = records[i].address; + } + else + { + if (!records[i].em_addr) continue; + hp.emu_addr=records[i].em_addr; + hp.type = CODEC_UTF16 | USING_STRING|BREAK_POINT; + hp.argidx=records[i].argidx; + } + if (sp.hookPostProcessor) sp.hookPostProcessor(hp); //actuall useless because of jit + NotifyHookFound(hp, (wchar_t*)records[i].text); + if (++results % 100'000 == 0) ConsoleOutput(ResultsNum, results); + } + records.reset(); + for (int i = 0; i < CACHE_SIZE; ++i) signatureCache[i] = sumCache[i] = 0; +} +void initrecords(){ + do + try { records = std::make_unique(recordsAvailable = sp.maxRecords); } + catch (std::bad_alloc) { ConsoleOutput(SearchForHooks_ERROR, sp.maxRecords /= 2); } + while (!records && sp.maxRecords); + +} void SearchForHooks(SearchParam spUser) { std::thread([=] @@ -197,113 +288,118 @@ void SearchForHooks(SearchParam spUser) static std::mutex m; std::scoped_lock lock(m); *(void**)(trampoline + send_offset) = Send; + ConsoleOutput(HOOK_SEARCH_INITIALIZING, 0.); sp = spUser.length == 0 ? spDefault : spUser; sp.codepage=spUser.codepage; - ConsoleOutput(HOOK_SEARCH_INITIALIZING, 0.); - do - try { records = std::make_unique(recordsAvailable = sp.maxRecords); } - catch (std::bad_alloc) { ConsoleOutput(SearchForHooks_ERROR, sp.maxRecords /= 2); } - while (!records && sp.maxRecords); + initrecords(); std::vector addresses; - if (*sp.boundaryModule) { - auto [minaddr,maxaddr]=Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule)); - if(sp.address_method==0){ - sp.minAddress=min(max(minaddr,sp.minAddress),maxaddr); - sp.maxAddress=max(min(maxaddr,sp.maxAddress),minaddr); - } - else if(sp.address_method==1){ - auto maxoff=maxaddr-minaddr; - sp.minAddress=minaddr+min(sp.minAddress,maxoff); - sp.maxAddress=minaddr+min(sp.maxAddress,maxoff); - } - //std::tie(sp.minAddress, sp.maxAddress) = Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule)); - } - if (*sp.exportModule) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.exportModule)); - if (*sp.boundaryModule){ - auto _addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.boundaryModule)); - mergevector(addresses,_addresses); - } - std::vector addresses1; - if(sp.search_method==0){ - for (auto& addr : addresses1 = Util::SearchMemory(sp.pattern, sp.length, PAGE_EXECUTE, sp.minAddress, sp.maxAddress)) - addr += sp.offset; - } - else if(sp.search_method==1){ - for(uintptr_t addr=sp.minAddress;addrfuncaddr){ - auto it = std::find(addresses1.begin(), addresses1.end(), funcaddr); - addresses1.push_back(funcaddr); - } - } - } - } - mergevector(addresses,addresses1); - - auto limits = Util::QueryModuleLimits(GetModuleHandleW(LUNA_HOOK_DLL)); - addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](auto addr) { return addr > limits.first && addr < limits.second; }), addresses.end()); - - auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE); - VirtualProtect(trampolines, addresses.size() * sizeof(trampoline), PAGE_EXECUTE_READWRITE, DUMMY); - std::vectormherroridx; - for (int i = 0; i < addresses.size(); ++i) + if(jitaddr2emuaddr.empty() || spUser.length !=0) { - void* original; + if (*sp.boundaryModule) { + auto [minaddr,maxaddr]=Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule)); + if(sp.address_method==0){ + sp.minAddress=min(max(minaddr,sp.minAddress),maxaddr); + sp.maxAddress=max(min(maxaddr,sp.maxAddress),minaddr); + } + else if(sp.address_method==1){ + auto maxoff=maxaddr-minaddr; + sp.minAddress=minaddr+min(sp.minAddress,maxoff); + sp.maxAddress=minaddr+min(sp.maxAddress,maxoff); + } + //std::tie(sp.minAddress, sp.maxAddress) = Util::QueryModuleLimits(GetModuleHandleW(sp.boundaryModule)); + } + if (*sp.exportModule) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.exportModule)); + if (*sp.boundaryModule){ + auto _addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.boundaryModule)); + mergevector(addresses,_addresses); + } + std::vector addresses1; + if(sp.search_method==0){ + for (auto& addr : addresses1 = Util::SearchMemory(sp.pattern, sp.length, PAGE_EXECUTE, sp.minAddress, sp.maxAddress)) + addr += sp.offset; + } + else if(sp.search_method==1){ + for(uintptr_t addr=sp.minAddress;addrfuncaddr){ + auto it = std::find(addresses1.begin(), addresses1.end(), funcaddr); + addresses1.push_back(funcaddr); + } + } + } + } + + mergevector(addresses,addresses1); + + auto limits = Util::QueryModuleLimits(GetModuleHandleW(LUNA_HOOK_DLL)); + addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](auto addr) { return addr > limits.first && addr < limits.second; }), addresses.end()); + + + auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE); + VirtualProtect(trampolines, addresses.size() * sizeof(trampoline), PAGE_EXECUTE_READWRITE, DUMMY); + std::vectormherroridx; + for (int i = 0; i < addresses.size(); ++i) + { + void* original; + //避免MH_RemoveHook时移除原本已有hook + if(MH_CreateHook((void*)addresses[i], trampolines[i], &original)!=MH_OK){ + mherroridx.push_back(i); + } + MH_QueueEnableHook((void*)addresses[i]); + memcpy(trampolines[i], trampoline, sizeof(trampoline)); + *(uintptr_t*)(trampolines[i] + addr_offset) = addresses[i]; + *(void**)(trampolines[i] + original_offset) = original; + if (i % 2500 == 0) ConsoleOutput(HOOK_SEARCH_INITIALIZING, 1 + 98. * i / addresses.size()); + } //避免MH_RemoveHook时移除原本已有hook - if(MH_CreateHook((void*)addresses[i], trampolines[i], &original)!=MH_OK){ - mherroridx.push_back(i); + for(int i=0;isuccessaddr;int i=0; + for(auto addr:jitaddr2emuaddr){ + i+=1; + //addresses.push_back(addr.first); + if(add_veh_hook((void*)addr.first,std::bind(SendJitVeh,std::placeholders::_1,addr.first,addr.second.second,addr.second.first))) + successaddr.push_back(addr.first); + if (i % 2500 == 0) ConsoleOutput(HOOK_SEARCH_INITIALIZING, 1 + 98. * i / jitaddr2emuaddr.size()); + } + ConsoleOutput(MAKE_GAME_PROCESS_TEXT, sp.searchTime / 1000); + Sleep(sp.searchTime); + for(auto addr:successaddr){ + remove_veh_hook((void*)addr); + } + SearchForHooks_Return(); } - records.reset(); - VirtualFree(trampolines, 0, MEM_RELEASE); - for (int i = 0; i < CACHE_SIZE; ++i) signatureCache[i] = sumCache[i] = 0; }).detach(); } diff --git a/LunaHook/main.cc b/LunaHook/main.cc index f4bfb24..94695bc 100644 --- a/LunaHook/main.cc +++ b/LunaHook/main.cc @@ -161,8 +161,35 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID) } return TRUE; } -bool NewHook(HookParam hp, LPCSTR lpname) +int HookStrLen(HookParam* hp,BYTE* data){ + if(data==0)return 0; + + if(hp->type&CODEC_UTF16) + return wcslen((wchar_t*)data)*2; + else if(hp->type&CODEC_UTF32) + return u32strlen((uint32_t*)data)*4; + else + return strlen((char*)data); + +} +static std::mutex maplock; +void jitaddrclear(){ + std::lock_guard _(maplock); + emuaddr2jitaddr.clear(); + jitaddr2emuaddr.clear(); +} +void jitaddraddr(uintptr_t em_addr,uintptr_t jitaddr,JITTYPE jittype){ + std::lock_guard _(maplock); + if(emuaddr2jitaddr.find(em_addr)==emuaddr2jitaddr.end()) + emuaddr2jitaddr[em_addr]={jittype,{}}; + emuaddr2jitaddr[em_addr].second.insert(jitaddr); + jitaddr2emuaddr[jitaddr]={jittype,em_addr}; +} +bool NewHook_1(HookParam& hp, LPCSTR lpname) { + if(hp.emu_addr) + ConsoleOutput("%p => %p",hp.emu_addr,hp.address); + if (++currentHook >= MAX_HOOK){ ConsoleOutput(TOO_MANY_HOOKS); return false; @@ -182,7 +209,22 @@ bool NewHook(HookParam hp, LPCSTR lpname) return true; } } - +bool NewHook(HookParam hp, LPCSTR name){ + if(hp.address) + return NewHook_1(hp,name); + if(emuaddr2jitaddr.find(hp.emu_addr)==emuaddr2jitaddr.end())return false; + strcpy(hp.function,""); + wcscpy(hp.module,L""); + hp.type &= ~MODULE_OFFSET; + hp.type &= ~FUNCTION_OFFSET; + auto succ=false; + for(auto __x: emuaddr2jitaddr[hp.emu_addr].second){ + hp.address=__x; + hp.jittype=emuaddr2jitaddr[hp.emu_addr].first; + succ|=NewHook_1(hp,name); + } + return succ; +} void RemoveHook(uint64_t addr, int maxOffset) { for (auto& hook : *hooks) if (abs((long long)(hook.address - addr)) <= maxOffset) return hook.Clear(); @@ -205,4 +247,70 @@ std::string LoadResData(LPCWSTR pszResID,LPCWSTR _type) GlobalFree(m_hMem); FreeResource(lpRsrc); return data; -} \ No newline at end of file +} + + +void context_get(hook_stack* stack,PCONTEXT context){ + #ifndef _WIN64 + stack->eax=context->Eax; + stack->ecx=context->Ecx; + stack->edx=context->Edx; + stack->ebx=context->Ebx; + stack->esp=context->Esp; + stack->ebp=context->Ebp; + stack->esi=context->Esi; + stack->edi=context->Edi; + stack->eflags=context->EFlags; + stack->retaddr=*(DWORD*)context->Esp; + #else + stack->rax=context->Rax; + stack->rbx=context->Rbx; + stack->rcx=context->Rcx; + stack->rdx=context->Rdx; + stack->rsp=context->Rsp; + stack->rbp=context->Rbp; + stack->rsi=context->Rsi; + stack->rdi=context->Rdi; + stack->r8=context->R8; + stack->r9=context->R9; + stack->r10=context->R10; + stack->r11=context->R11; + stack->r12=context->R12; + stack->r13=context->R13; + stack->r14=context->R14; + stack->r15=context->R15; + stack->eflags=context->EFlags; + stack->retaddr=*(DWORD64*)context->Rsp; + #endif +} +void context_set(hook_stack* stack,PCONTEXT context){ + #ifndef _WIN64 + context->Eax=stack->eax; + context->Ecx=stack->ecx; + context->Edx=stack->edx; + context->Ebx=stack->ebx; + context->Esp=stack->esp; + context->Ebp=stack->ebp; + context->Esi=stack->esi; + context->Edi=stack->edi; + context->EFlags=stack->eflags; + #else + context->Rax=stack->rax; + context->Rbx=stack->rbx; + context->Rcx=stack->rcx; + context->Rdx=stack->rdx; + context->Rsp=stack->rsp; + context->Rbp=stack->rbp; + context->Rsi=stack->rsi; + context->Rdi=stack->rdi; + context->R8=stack->r8; + context->R9=stack->r9; + context->R10=stack->r10; + context->R11=stack->r11; + context->R12=stack->r12; + context->R13=stack->r13; + context->R14=stack->r14; + context->R15=stack->r15; + context->EFlags=stack->eflags; + #endif +} diff --git a/LunaHook/main.h b/LunaHook/main.h index 54a3a85..daab251 100644 --- a/LunaHook/main.h +++ b/LunaHook/main.h @@ -11,8 +11,18 @@ void ConsoleOutput(LPCSTR text, ...); void NotifyHookFound(HookParam hp, wchar_t* text); void NotifyHookRemove(uint64_t addr, LPCSTR name); bool NewHook(HookParam hp, LPCSTR name); +bool NewHookJit(HookParam hp, LPCSTR name); + void RemoveHook(uint64_t addr, int maxOffset = 9); std::string LoadResData(LPCWSTR pszResID,LPCWSTR _type); inline SearchParam spDefault; // EOF +int HookStrLen(HookParam*,BYTE* data); +inline std::unordered_map > >emuaddr2jitaddr; +inline std::unordered_map>jitaddr2emuaddr; +void jitaddrclear(); +void jitaddraddr(uintptr_t em_addr,uintptr_t jitaddr,JITTYPE); + +void context_get(hook_stack*,PCONTEXT); +void context_set(hook_stack*,PCONTEXT); diff --git a/LunaHook/texthook.cc b/LunaHook/texthook.cc index 1dda77d..1caf454 100644 --- a/LunaHook/texthook.cc +++ b/LunaHook/texthook.cc @@ -9,6 +9,7 @@ #include "MinHook.h" #include"Lang/Lang.h" #include"veh_hook.h" +#include"engines/emujitarg.hpp" extern WinMutex viewMutex; // - Unnamed helpers - @@ -102,12 +103,12 @@ uintptr_t getasbaddr(const HookParam &hp){ if (hp.type & FUNCTION_OFFSET) { if (FARPROC function = GetProcAddress(GetModuleHandleW(hp.module), hp.function)) address += (uint64_t)function; - else return ConsoleOutput(FUNC_MISSING), false; + else return ConsoleOutput(FUNC_MISSING), 0; } else { if (HMODULE moduleBase = GetModuleHandleW(hp.module)) address += (uint64_t)moduleBase; - else return ConsoleOutput(MODULE_MISSING), false; + else return ConsoleOutput(MODULE_MISSING), 0; } } return address; @@ -116,6 +117,8 @@ bool TextHook::Insert(HookParam hp) { auto addr=getasbaddr(hp); + if(!addr)return false; + RemoveHook(addr, 0); local_buffer=new BYTE[PIPE_BUFFER_SIZE]; @@ -124,8 +127,7 @@ bool TextHook::Insert(HookParam hp) this->hp = hp; address = addr; } - - + savetypeforremove=hp.type; if (hp.type & DIRECT_READ) return InsertReadCode(); if (hp.type & BREAK_POINT) return InsertBreakPoint(); return InsertHookCode(); @@ -142,6 +144,23 @@ uintptr_t queryrelativeret(uintptr_t retaddr){ re.insert(std::make_pair(retaddr,relative)); return relative; } + +void jitfunctiontext_fun(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ + switch (hp->jittype) + { + #ifdef _WIN64 + case JITTYPE::YUZU: + *data=YUZU::emu_arg(stack)[hp->argidx]+hp->padding; + break; + #endif + case JITTYPE::PPSSPP: + *data=PPSSPP::emu_arg(stack)[hp->argidx]+hp->padding; + break; + default: + *data=0; + } + *len=HookStrLen(hp,(BYTE*)*data); +} void TextHook::Send(uintptr_t lpDataBase) { auto buffer =(TextOutput_T*) local_buffer; @@ -149,7 +168,7 @@ void TextHook::Send(uintptr_t lpDataBase) _InterlockedIncrement((long*)&useCount); __try { - auto stack=(hook_stack*)(lpDataBase-sizeof(hook_stack)+sizeof(uintptr_t)); + auto stack=get_hook_stack(lpDataBase); #ifndef _WIN64 if (auto current_trigger_fun = trigger_fun.exchange(nullptr)) @@ -177,6 +196,10 @@ void TextHook::Send(uintptr_t lpDataBase) isstring=true; hp.text_fun(stack, &hp, &lpDataIn, &lpSplit, &lpCount); } + else if(hp.jittype!=JITTYPE::PC){ + isstring=true; + jitfunctiontext_fun(stack, &hp, &lpDataIn, &lpSplit, &lpCount); + } else { if (hp.type & FIXING_SPLIT) lpSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating @@ -273,52 +296,18 @@ void TextHook::Send(uintptr_t lpDataBase) _InterlockedDecrement((long*) & useCount); } -void TextHook::breakpointcontext(PCONTEXT context){ -#ifdef _WIN64 +bool TextHook::breakpointcontext(PCONTEXT context){ auto stack=std::make_unique(); - stack->rax=context->Rax; - stack->rbx=context->Rbx; - stack->rcx=context->Rcx; - stack->rdx=context->Rdx; - stack->rsp=context->Rsp; - stack->rbp=context->Rbp; - stack->rsi=context->Rsi; - stack->rdi=context->Rdi; - stack->r8=context->R8; - stack->r9=context->R9; - stack->r10=context->R10; - stack->r11=context->R11; - stack->r12=context->R12; - stack->r13=context->R13; - stack->r14=context->R14; - stack->r15=context->R15; - stack->eflags=context->EFlags; - stack->retaddr=*(DWORD64*)context->Rsp; - auto lpDataBase=(uintptr_t)stack.get()+sizeof(hook_stack)-sizeof(uintptr_t); + context_get(stack.get(),context); + auto lpDataBase=stack->get_base(); Send(lpDataBase); - context->Rax=stack->rax; - context->Rbx=stack->rbx; - context->Rcx=stack->rcx; - context->Rdx=stack->rdx; - context->Rsp=stack->rsp; - context->Rbp=stack->rbp; - context->Rsi=stack->rsi; - context->Rdi=stack->rdi; - context->R8=stack->r8; - context->R9=stack->r9; - context->R10=stack->r10; - context->R11=stack->r11; - context->R12=stack->r12; - context->R13=stack->r13; - context->R14=stack->r14; - context->R15=stack->r15; - context->EFlags=stack->eflags; -#endif + context_set(stack.get(),context); + return true; } bool TextHook::InsertBreakPoint() { //MH_CreateHook 64位unity/yuzu-emu经常 MH_ERROR_MEMORY_ALLOC - return add_veh_hook(location,std::bind(&TextHook::breakpointcontext,this,std::placeholders::_1), 0); + return add_veh_hook(location,std::bind(&TextHook::breakpointcontext,this,std::placeholders::_1)); } bool TextHook::RemoveBreakPoint() { @@ -393,8 +382,8 @@ void TextHook::RemoveReadCode() void TextHook::Clear() { if (address == 0) return; - if (hp.type & DIRECT_READ) RemoveReadCode(); - else if (hp.type & BREAK_POINT) RemoveBreakPoint(); + if (savetypeforremove & DIRECT_READ) RemoveReadCode(); + else if (savetypeforremove & BREAK_POINT) RemoveBreakPoint(); else RemoveHookCode(); NotifyHookRemove(address, hp.name); std::scoped_lock lock(viewMutex); @@ -451,15 +440,7 @@ int TextHook::GetLength(hook_stack* stack, uintptr_t in) int TextHook::HookStrlen(BYTE* data) { - if(data==0)return 0; - - if(hp.type&CODEC_UTF16) - return wcslen((wchar_t*)data)*2; - else if(hp.type&CODEC_UTF32) - return u32strlen((uint32_t*)data)*4; - else - return strlen((char*)data); - + return HookStrLen(&hp,data); } // EOF diff --git a/LunaHook/util/util.cc b/LunaHook/util/util.cc index 06e15ce..668ee91 100644 --- a/LunaHook/util/util.cc +++ b/LunaHook/util/util.cc @@ -591,6 +591,15 @@ bool Engine::isAddressWritable(const wchar_t *p, size_t count) namespace{ + uint32_t *Xstrcpy(uint32_t *s, const uint32_t *r){ + while(*r){ + *s=*r; + s+=1; + r+=1; + } + *s=0; + return s; + } wchar_t *Xstrcpy(wchar_t *s, const wchar_t *r){return wcscpy(s,r);} char *Xstrcpy(char *s, const char *r){return strcpy(s,r);} template @@ -611,6 +620,7 @@ namespace{ void write_string_new(uintptr_t* data, size_t* len,const std::wstring& s){write_string_new_impl(data,len,s);} void write_string_new(uintptr_t* data, size_t* len,const std::string& s){write_string_new_impl(data,len,s);} +bool write_string_overwrite(void* data, size_t* len,const std::basic_string& s){return write_string_overwrite_impl(data,len,s);} bool write_string_overwrite(void* data, size_t* len,const std::wstring& s){return write_string_overwrite_impl(data,len,s);} bool write_string_overwrite(void* data, size_t* len,const std::string& s){return write_string_overwrite_impl(data,len,s);} diff --git a/LunaHook/util/util.h b/LunaHook/util/util.h index 8da6cfa..27fa373 100644 --- a/LunaHook/util/util.h +++ b/LunaHook/util/util.h @@ -81,7 +81,7 @@ void write_string_new(uintptr_t* data, size_t* len,const std::wstring& s); void write_string_new(uintptr_t* data, size_t* len,const std::string& s); bool write_string_overwrite(void* data, size_t* len,const std::wstring& s); bool write_string_overwrite(void* data, size_t* len,const std::string& s); - +bool write_string_overwrite(void* data, size_t* len,const std::basic_string& s); struct WindowInfo { HWND handle; diff --git a/LunaHook/veh_hook.cpp b/LunaHook/veh_hook.cpp index 85178e8..6dd6a09 100644 --- a/LunaHook/veh_hook.cpp +++ b/LunaHook/veh_hook.cpp @@ -17,58 +17,62 @@ bool add_veh_hook(void* origFunc, newFuncType newFunc, DWORD hook_type) DWORD oldProtect; if (list == NULL) list = new_veh_list(); if (list == NULL) return false; + if(get_veh_node(list,origFunc))return false; void* handle = AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)veh_dispatch); - veh_node_t* newnode = insert_veh_node(list, origFunc, newFunc, handle, hook_type); - + auto newnode = create_veh_node(origFunc, newFunc, handle, hook_type); + if(newnode==NULL)return false; // For memory hooks especially, we need to know the address of the start of the relevant page. MEMORY_BASIC_INFORMATION mem_info; VirtualQuery(origFunc, &mem_info, sizeof(MEMORY_BASIC_INFORMATION)); newnode->baseAddr = mem_info.BaseAddress; - - VirtualProtect(origFunc, sizeof(int), PAGE_EXECUTE_READWRITE, &newnode->OldProtect); + if(!VirtualProtect(origFunc, sizeof(int), PAGE_EXECUTE_READWRITE, &newnode->OldProtect)) + { + delete newnode; + return false; + } memcpy((void*)(&newnode->origBaseByte), (const void*)origFunc, sizeof (BYTE)); memcpy((void*)origFunc, (const void*)&int3bp, sizeof (BYTE)); VirtualProtect(origFunc, sizeof(int), newnode->OldProtect, &oldProtect); + insert_veh_node(list, newnode); return true; } - +void repair_origin(veh_node_t *node){ + DWORD _p; + VirtualProtect(node->origFunc, sizeof(int), PAGE_EXECUTE_READWRITE, &_p); + memcpy((void*)node->origFunc, (const void*)(&node->origBaseByte), sizeof(char)); + VirtualProtect(node->origFunc, sizeof(int), node->OldProtect, &_p); +} bool remove_veh_hook(void* origFunc) { std::lock_guard _(vehlistlock); if (list == NULL) return false; veh_node_t* node = get_veh_node(list, origFunc); if (node == NULL) return false; - DWORD _p; - VirtualProtect(node->origFunc, sizeof(int), PAGE_EXECUTE_READWRITE, &_p); - memcpy((void*)node->origFunc, (const void*)(&node->origBaseByte), sizeof(char)); - VirtualProtect(node->origFunc, sizeof(int), node->OldProtect, &_p); + repair_origin(node); RemoveVectoredExceptionHandler(node->handle); return remove_veh_node(list, origFunc); } bool remove_veh_node(veh_list_t* list, void* origFunc) { - veh_node_t* searchnode; - veh_node_t* lastsearchnode = NULL; - searchnode = list->head; + veh_node_t* searchnode = list->head; while (searchnode != NULL) { if (searchnode->origFunc == origFunc) { - if (lastsearchnode == NULL) - { - list->head = searchnode->next; - if (list->tail == searchnode) list->tail = searchnode->next; - } - else - { - lastsearchnode->next = searchnode->next; - } + if(list->tail==searchnode) + list->tail=searchnode->last; + if(list->head==searchnode) + list->head=searchnode->next; + if(searchnode->last) + searchnode->last->next=searchnode->next; + if(searchnode->next) + searchnode->next->last=searchnode->last; + delete (searchnode); return true; } - lastsearchnode = searchnode; searchnode = searchnode->next; } return false; @@ -82,21 +86,29 @@ LONG CALLBACK veh_dispatch(PEXCEPTION_POINTERS ExceptionInfo) if (Code != STATUS_BREAKPOINT && Code != STATUS_SINGLE_STEP) return EXCEPTION_CONTINUE_SEARCH; // Try to find the node associated with the address of the current exception, continue searching for handlers if not found; - std::lock_guard _(vehlistlock); + if (Code == STATUS_BREAKPOINT )//&& hooktype == VEH_HK_INT3) { - veh_node_t* currnode = get_veh_node(list, Addr); + veh_node_t* currnode ; + { + std::lock_guard _(vehlistlock); + currnode = get_veh_node(list, Addr); + } if (currnode == NULL) return EXCEPTION_CONTINUE_SEARCH; - - VirtualProtect(Addr, sizeof(int), PAGE_EXECUTE_READWRITE, &currnode->OldProtect); - memcpy((void*)Addr, (const void*)(&currnode->origBaseByte), sizeof (char)); - currnode->newFunc(ExceptionInfo->ContextRecord); - VirtualProtect(Addr, sizeof(int), currnode->OldProtect, &oldProtect); - ExceptionInfo->ContextRecord->EFlags |= 0x100; + if(currnode->newFunc(ExceptionInfo->ContextRecord)) + { + repair_origin(currnode); + ExceptionInfo->ContextRecord->EFlags |= 0x100; + } + else + { + remove_veh_hook(Addr); + } } else if (Code == STATUS_SINGLE_STEP )//&& hooktype == VEH_HK_INT3) { + std::lock_guard _(vehlistlock); veh_node_t* currnode = get_veh_node(list, Addr, 0x10); if (currnode == NULL) return EXCEPTION_CONTINUE_SEARCH; @@ -126,18 +138,22 @@ veh_list_t* new_veh_list() newlist->tail = NULL; return newlist; } -veh_node_t* insert_veh_node(veh_list_t* list, void* origFunc, newFuncType newFunc, void* handle, DWORD hook_type) +veh_node_t* create_veh_node(void* origFunc, newFuncType newFunc, void* handle, DWORD hook_type) { - if (list == NULL) return NULL; - /* create a new node and fill in the blanks */ veh_node_t* newnode = new veh_node_t; if (newnode == NULL) return NULL; + newnode->last=NULL; newnode->origFunc = origFunc; newnode->newFunc = newFunc; newnode->handle = handle; newnode->OldProtect = PAGE_EXECUTE_READWRITE; newnode->next = NULL; newnode->hooktype=hook_type; + return newnode; +} +void insert_veh_node(veh_list_t* list, veh_node_t* newnode) +{ + if (list == NULL) return; if (list->head == NULL) { list->head = newnode; @@ -146,11 +162,10 @@ veh_node_t* insert_veh_node(veh_list_t* list, void* origFunc, newFuncType newFun else { list->tail->next = newnode; + newnode->last=list->tail; list->tail = newnode; } - return newnode; } - veh_node_t* get_veh_node(veh_list_t* list, void* origFunc, int range) { veh_node_t* newnode; diff --git a/LunaHook/veh_hook.h b/LunaHook/veh_hook.h index 92b3566..d4dc3ed 100644 --- a/LunaHook/veh_hook.h +++ b/LunaHook/veh_hook.h @@ -20,10 +20,12 @@ Version: 24-March-2008 //typedef void (*pfvoid)(); //typedef void (*newFuncType)(PCONTEXT); -using newFuncType = std::function; +using newFuncType = std::function; typedef struct veh_node { + struct veh_node* last; + struct veh_node* next; void* origFunc; newFuncType newFunc; void* handle; @@ -31,7 +33,6 @@ typedef struct veh_node void* baseAddr; // Address of the page in which origFunc resides. BYTE origBaseByte; DWORD OldProtect; - struct veh_node* next; } veh_node_t; typedef struct @@ -41,7 +42,7 @@ typedef struct } veh_list_t; // VEH hook interface functions for creating and removing hooks. -bool add_veh_hook(void* origFunc, newFuncType newFunc, DWORD hook_type); +bool add_veh_hook(void* origFunc, newFuncType newFunc, DWORD hook_type=VEH_HK_INT3); bool remove_veh_hook(void* origFunc); // The VEH dispathing function is called by Windows every time an exception is encountered. @@ -50,7 +51,8 @@ LONG CALLBACK veh_dispatch(PEXCEPTION_POINTERS ExceptionInfo); // Functions used internally by the library. veh_list_t* new_veh_list(); -veh_node_t* insert_veh_node(veh_list_t* list, void* origFunc, newFuncType newFunc, void* handle, DWORD hook_type); +veh_node_t* create_veh_node(void* origFunc, newFuncType newFunc, void* handle, DWORD hook_type); +void insert_veh_node(veh_list_t* list, veh_node_t*); bool remove_veh_node(veh_list_t* list, void* origFunc); veh_node_t* get_veh_node(veh_list_t* list, void* origFunc, int range=0); diff --git a/include/hookcode.cpp b/include/hookcode.cpp index aa8d781..cff1392 100644 --- a/include/hookcode.cpp +++ b/include/hookcode.cpp @@ -44,7 +44,12 @@ namespace { std::wsmatch match; HookParam hp; - + if(endWith(HCode,L":JIT:YUZU")){ + hp.jittype=JITTYPE::YUZU; + } + else if(endWith(HCode,L":JIT:PPSSPP")){ + hp.jittype=JITTYPE::PPSSPP; + } // {A|B|W|H|S|Q|V|M} switch (HCode[0]) { @@ -161,7 +166,18 @@ namespace // 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.padding=0; + hp.offset=0; + hp.address=0; + hp.type &= ~MODULE_OFFSET; + hp.type &= ~FUNCTION_OFFSET; + strcpy(hp.function,""); + wcscpy(hp.module,L""); + } return hp; } @@ -258,34 +274,31 @@ namespace else HCode += L"H"; - if (hp.text_fun || hp.filter_fun) - HCode += L'X'; + + if(hp.type & USING_STRING) + { + if(hp.type&CODEC_UTF16) + HCode += L'Q'; + else if(hp.type&CODEC_UTF8) + HCode += L'V'; + else if(hp.type&CODEC_UTF32) + HCode += L'U'; + else + HCode += L'S'; + } else { - if(hp.type & USING_STRING) - { - if(hp.type&CODEC_UTF16) - HCode += L'Q'; - else if(hp.type&CODEC_UTF8) - HCode += L'V'; - else if(hp.type&CODEC_UTF32) - HCode += L'U'; - else - HCode += L'S'; - } - else - { - if(hp.type&CODEC_UTF16) - HCode += L'W'; - else if(hp.type&CODEC_UTF32) - HCode += L'I'; - else if (hp.type & CODEC_ANSI_BE) - HCode += L'A'; - else - HCode += L'B'; - } - + if(hp.type&CODEC_UTF16) + HCode += L'W'; + else if(hp.type&CODEC_UTF32) + HCode += L'I'; + else if (hp.type & CODEC_ANSI_BE) + HCode += L'A'; + else + HCode += L'B'; } + + if (hp.text_fun || hp.filter_fun) HCode += L'X'; if (hp.type & FULL_STRING) HCode += L'F'; @@ -295,31 +308,51 @@ namespace if (hp.padding) HCode += HexString(hp.padding) + L'+'; - if (hp.offset < 0) hp.offset += 4; - if (hp.split < 0) hp.split += 4; + switch (hp.jittype) + { + case JITTYPE::PC: + { + + if (hp.offset < 0) hp.offset += 4; + if (hp.split < 0) hp.split += 4; - HCode += HexString(hp.offset); - 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); - + HCode += HexString(hp.offset); + 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); + // Attempt to make the address relative + 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))) + 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); + if (hp.type & MODULE_OFFSET) HCode += L':' + std::wstring(hp.module); + if (hp.type & FUNCTION_OFFSET) HCode += L':' + StringToWideString(hp.function); + } + break; + default: + { + HCode += HexString(hp.argidx); + HCode += L'@' + HexString(hp.emu_addr); + switch (hp.jittype) + { + case JITTYPE::YUZU: + HCode+=L":JIT:YUZU"; + break; + case JITTYPE::PPSSPP: + HCode+=L":JIT:PPSSPP"; + } + } + break; + } - // Attempt to make the address relative - 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))) - 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); - if (hp.type & MODULE_OFFSET) HCode += L':' + std::wstring(hp.module); - if (hp.type & FUNCTION_OFFSET) HCode += L':' + StringToWideString(hp.function); - return HCode; } } diff --git a/include/stringutils.cpp b/include/stringutils.cpp index 2ad44ab..1fdb1f9 100644 --- a/include/stringutils.cpp +++ b/include/stringutils.cpp @@ -151,6 +151,26 @@ inline unsigned int convertUTF32ToUTF16(unsigned int cUTF32, unsigned int& h, un unsigned int ret = ((h << 16) | (l & 0x0000FFFF)); return ret; } + +std::basic_string utf16_to_utf32(const wchar_t* u16str,size_t size){ + std::basic_string utf32String; + for (size_t i=0;i utf16_to_utf32(const wchar_t* u16str,size_t size); + std::string WideStringToString(const std::wstring& text,UINT cp=CP_UTF8); std::wstring StringToWideString(const std::string& text); std::optional StringToWideString(const std::string& text, UINT encoding); diff --git a/include/texthook.h b/include/texthook.h index 157778d..b3871b2 100644 --- a/include/texthook.h +++ b/include/texthook.h @@ -42,7 +42,7 @@ class TextHook public: HookParam hp; ALIGNPTR(uint64_t address,void* location); - + uint64_t savetypeforremove; bool Insert(HookParam hp); void Clear(); @@ -52,7 +52,7 @@ private: bool InsertReadCode(); bool InsertBreakPoint(); bool RemoveBreakPoint(); - void breakpointcontext(PCONTEXT); + bool breakpointcontext(PCONTEXT); void Send(uintptr_t dwDatabase); int GetLength(hook_stack* stack, uintptr_t in); // jichi 12/25/2013: Return 0 if failed int HookStrlen(BYTE* data); diff --git a/include/types.h b/include/types.h index 572afb4..04713bb 100644 --- a/include/types.h +++ b/include/types.h @@ -61,17 +61,30 @@ struct hook_stack uintptr_t retaddr; BYTE base[1]; }; - + uintptr_t get_base(){ + return (uintptr_t)this+sizeof(hook_stack)-sizeof(uintptr_t); + } }; + +inline hook_stack* get_hook_stack(uintptr_t lpDataBase){ + return (hook_stack*)(lpDataBase-sizeof(hook_stack)+sizeof(uintptr_t)); +} // jichi 3/7/2014: Add guessed comment #define ALIGNPTR(Y,X) union { \ ##Y; \ ##X; \ }; + + +enum class JITTYPE{ + PC,//not a jit + YUZU, + PPSSPP +}; struct HookParam { - uint64_t address; // absolute or relative address + ALIGNPTR(uint64_t __11,uintptr_t address); // absolute or relative address int offset, // offset of the data in the memory index, // deref_offset1 split, // offset of the split character @@ -96,6 +109,9 @@ struct HookParam HookParam(){ ZeroMemory(this,sizeof(HookParam)); } + ALIGNPTR(uint64_t __10,uintptr_t emu_addr); + int argidx; + JITTYPE jittype; }; struct ThreadParam