mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-27 07:44:02 +08:00
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
This commit is contained in:
parent
eaf7fa6e6a
commit
964868b2d2
@ -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<int index>
|
||||
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<int index,int offset=0>
|
||||
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<int index,DWORD _type=0>
|
||||
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<int index>
|
||||
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;
|
||||
|
52
LunaHook/engines/emujitarg.hpp
Normal file
52
LunaHook/engines/emujitarg.hpp
Normal file
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -1,63 +1,5 @@
|
||||
#include<queue>
|
||||
namespace ppsspp{
|
||||
inline DWORD x86_baseaddr;
|
||||
}
|
||||
namespace
|
||||
{
|
||||
|
||||
template<int index,int offset=0>
|
||||
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<int index>
|
||||
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<int index,DWORD _type=0>
|
||||
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_map<uintptr_t,emfuncinfo>emfunctionhooks= {
|
||||
{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
|
||||
};
|
||||
|
||||
}
|
@ -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<HookRecord[]> 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_map<uintptr_t,uint64_t>addresscalledtime;
|
||||
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<hook_stack>();
|
||||
context_get(stack.get(),context);
|
||||
SafeSendJitVeh(stack.get(),address,em_addr,jittype);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<uintptr_t> GetFunctions(uintptr_t module)
|
||||
{
|
||||
if (!module) return {};
|
||||
@ -190,6 +244,43 @@ void mergevector(std::vector<uintptr_t> &v1,std::vector<uintptr_t> &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<HookRecord[]>(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<HookRecord[]>(recordsAvailable = sp.maxRecords); }
|
||||
catch (std::bad_alloc) { ConsoleOutput(SearchForHooks_ERROR, sp.maxRecords /= 2); }
|
||||
while (!records && sp.maxRecords);
|
||||
initrecords();
|
||||
|
||||
std::vector<uintptr_t> 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<uintptr_t> 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;addr<sp.maxAddress;addr++){
|
||||
if(((*(DWORD*)addr)==0xCCCCCCCC)||((*(DWORD*)addr)==0x90909090)){
|
||||
if(((*(BYTE*)(addr+4))!=0xCC)&&(*(BYTE*)(addr+4))!=0x90){
|
||||
addresses1.push_back(addr+4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(sp.search_method==2){
|
||||
for(uintptr_t addr=sp.minAddress;addr<sp.maxAddress;addr++){
|
||||
if(((*(BYTE*)addr)==0xe8)){
|
||||
auto off=*(DWORD*)(addr+1);
|
||||
auto funcaddr=addr+5+off;
|
||||
if(sp.minAddress<funcaddr && sp.maxAddress>funcaddr){
|
||||
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::vector<uintptr_t>mherroridx;
|
||||
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<uintptr_t> 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;addr<sp.maxAddress;addr++){
|
||||
if(((*(DWORD*)addr)==0xCCCCCCCC)||((*(DWORD*)addr)==0x90909090)){
|
||||
if(((*(BYTE*)(addr+4))!=0xCC)&&(*(BYTE*)(addr+4))!=0x90){
|
||||
addresses1.push_back(addr+4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(sp.search_method==2){
|
||||
for(uintptr_t addr=sp.minAddress;addr<sp.maxAddress;addr++){
|
||||
if(((*(BYTE*)addr)==0xe8)){
|
||||
auto off=*(DWORD*)(addr+1);
|
||||
auto funcaddr=addr+5+off;
|
||||
if(sp.minAddress<funcaddr && sp.maxAddress>funcaddr){
|
||||
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::vector<uintptr_t>mherroridx;
|
||||
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;i<mherroridx.size();i++){
|
||||
auto reverseidx=mherroridx[mherroridx.size()-1-i];
|
||||
addresses.erase(addresses.begin()+reverseidx);
|
||||
}
|
||||
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
|
||||
for(int i=0;i<mherroridx.size();i++){
|
||||
auto reverseidx=mherroridx[mherroridx.size()-1-i];
|
||||
addresses.erase(addresses.begin()+reverseidx);
|
||||
}
|
||||
|
||||
ConsoleOutput(HOOK_SEARCH_INITIALIZED, addresses.size());
|
||||
MH_ApplyQueued();
|
||||
ConsoleOutput(HOOK_SEARCH_STARTING);
|
||||
ConsoleOutput(MAKE_GAME_PROCESS_TEXT, sp.searchTime / 1000);
|
||||
Sleep(sp.searchTime);
|
||||
for (auto addr : addresses) MH_QueueDisableHook((void*)addr);
|
||||
MH_ApplyQueued();
|
||||
Sleep(1000);
|
||||
for (auto addr : addresses) MH_RemoveHook((void*)addr);
|
||||
ConsoleOutput(HOOK_SEARCH_FINISHED, sp.maxRecords - recordsAvailable);
|
||||
for (int i = 0, results = 0; i < sp.maxRecords; ++i)
|
||||
{
|
||||
if (!records[i].address) continue;
|
||||
if (++results % 100'000 == 0) ConsoleOutput(ResultsNum, results);
|
||||
HookParam hp;
|
||||
hp.offset = records[i].offset;
|
||||
hp.type = CODEC_UTF16 | USING_STRING;
|
||||
hp.address = records[i].address;
|
||||
hp.padding = records[i].padding;
|
||||
hp.codepage = sp.codepage;
|
||||
if (sp.hookPostProcessor) sp.hookPostProcessor(hp);
|
||||
NotifyHookFound(hp, (wchar_t*)records[i].text);
|
||||
ConsoleOutput(HOOK_SEARCH_INITIALIZED, addresses.size());
|
||||
MH_ApplyQueued();
|
||||
ConsoleOutput(HOOK_SEARCH_STARTING);
|
||||
ConsoleOutput(MAKE_GAME_PROCESS_TEXT, sp.searchTime / 1000);
|
||||
Sleep(sp.searchTime);
|
||||
for (auto addr : addresses) MH_QueueDisableHook((void*)addr);
|
||||
MH_ApplyQueued();
|
||||
Sleep(1000);
|
||||
for (auto addr : addresses) MH_RemoveHook((void*)addr);
|
||||
VirtualFree(trampolines, 0, MEM_RELEASE);
|
||||
SearchForHooks_Return();
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleOutput(HOOK_SEARCH_INITIALIZED, jitaddr2emuaddr.size());
|
||||
std::vector<uint64_t>successaddr;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();
|
||||
}
|
||||
|
||||
|
114
LunaHook/main.cc
114
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -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<uintptr_t,std::pair<JITTYPE,std::set<uintptr_t> > >emuaddr2jitaddr;
|
||||
inline std::unordered_map<uintptr_t,std::pair<JITTYPE,uintptr_t>>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);
|
||||
|
@ -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<hook_stack>();
|
||||
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
|
||||
|
@ -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<class CharT>
|
||||
@ -611,6 +620,7 @@ namespace{
|
||||
void write_string_new(uintptr_t* data, size_t* len,const std::wstring& s){write_string_new_impl<wchar_t>(data,len,s);}
|
||||
void write_string_new(uintptr_t* data, size_t* len,const std::string& s){write_string_new_impl<char>(data,len,s);}
|
||||
|
||||
bool write_string_overwrite(void* data, size_t* len,const std::basic_string<uint32_t>& s){return write_string_overwrite_impl<uint32_t>(data,len,s);}
|
||||
bool write_string_overwrite(void* data, size_t* len,const std::wstring& s){return write_string_overwrite_impl<wchar_t>(data,len,s);}
|
||||
bool write_string_overwrite(void* data, size_t* len,const std::string& s){return write_string_overwrite_impl<char>(data,len,s);}
|
||||
|
||||
|
@ -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<uint32_t>& s);
|
||||
|
||||
struct WindowInfo {
|
||||
HWND handle;
|
||||
|
@ -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;
|
||||
|
@ -20,10 +20,12 @@ Version: 24-March-2008
|
||||
|
||||
//typedef void (*pfvoid)();
|
||||
//typedef void (*newFuncType)(PCONTEXT);
|
||||
using newFuncType = std::function<void(PCONTEXT)>;
|
||||
using newFuncType = std::function<bool(PCONTEXT)>;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<uint32_t> utf16_to_utf32(const wchar_t* u16str,size_t size){
|
||||
std::basic_string<uint32_t> utf32String;
|
||||
for (size_t i=0;i<size;i++)
|
||||
{
|
||||
auto u16c=u16str[i];
|
||||
if(u16c-0xd800u<2048u)
|
||||
if((u16c&0xfffffc00==0xd800)&&(i<size-1)&&(u16str[i+1]&0xfffffc00==0xdc00)){
|
||||
utf32String+=(u16c << 10) + u16str[i+1] - 0x35fdc00;
|
||||
i+=1;
|
||||
}
|
||||
else{
|
||||
//error invalid u16 char
|
||||
}
|
||||
else
|
||||
utf32String+=u16str[i];
|
||||
}
|
||||
return utf32String;
|
||||
}
|
||||
|
||||
std::wstring utf32_to_utf16(uint32_t* u32str,size_t size){
|
||||
std::wstring u16str;
|
||||
for(auto i=0;i<size;i++){
|
||||
|
@ -23,6 +23,8 @@ bool endWith(const std::string& s,const std::string& s2);
|
||||
bool endWith(const std::wstring& s,const std::wstring& s2);
|
||||
|
||||
std::wstring utf32_to_utf16(uint32_t* u32str,size_t size);
|
||||
std::basic_string<uint32_t> 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<std::wstring> StringToWideString(const std::string& text, UINT encoding);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user