diff --git a/LunaHook/CMakeLists.txt b/LunaHook/CMakeLists.txt index 1a0d7fa..02a0603 100644 --- a/LunaHook/CMakeLists.txt +++ b/LunaHook/CMakeLists.txt @@ -23,6 +23,7 @@ set(texthook_src enginecontrol.cpp embed_util.cc hijackfuns.cc + veh_hook.cpp ) add_subdirectory(util) add_subdirectory(engines) diff --git a/LunaHook/engine64/yuzusuyu.cpp b/LunaHook/engine64/yuzusuyu.cpp index 363767f..1ddb831 100644 --- a/LunaHook/engine64/yuzusuyu.cpp +++ b/LunaHook/engine64/yuzusuyu.cpp @@ -100,13 +100,13 @@ bool checkiscurrentgame(const emfuncinfo& em){ 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; + 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|_type; + hp->type=USING_STRING|CODEC_UTF16|NO_CONTEXT|BREAK_POINT|_type; *data=address;*len=wcslen((wchar_t*)address)*2; } @@ -130,7 +130,7 @@ bool yuzusuyu::attach_function() HookParam hpinternal; hpinternal.address=entrypoint; - hpinternal.type=CODEC_UTF16|USING_STRING|NO_CONTEXT; + hpinternal.type=CODEC_UTF16|USING_STRING|NO_CONTEXT|BREAK_POINT; hpinternal.text_fun=(decltype(hpinternal.text_fun))op.hookfunc; hpinternal.filter_fun=(decltype(hpinternal.filter_fun))op.filterfun; NewHook(hpinternal,op.hookname); diff --git a/LunaHook/engines/mono/monocommon.hpp b/LunaHook/engines/mono/monocommon.hpp index 8c3c04b..b3a2f43 100644 --- a/LunaHook/engines/mono/monocommon.hpp +++ b/LunaHook/engines/mono/monocommon.hpp @@ -11,6 +11,9 @@ std::unordered_setinserted_addr; void NewHook_check(HookParam& hp,LPCSTR n){ std::lock_guard _(mutex); if(inserted_addr.find(hp.address)==inserted_addr.end()){ + #ifdef _WIN64 + hp.type|=BREAK_POINT; + #endif NewHook(hp,n); inserted_addr.insert(hp.address); } diff --git a/LunaHook/main.cc b/LunaHook/main.cc index 8b394c7..f4bfb24 100644 --- a/LunaHook/main.cc +++ b/LunaHook/main.cc @@ -169,8 +169,7 @@ bool NewHook(HookParam hp, LPCSTR lpname) } if (lpname && *lpname) strncpy_s(hp.name, lpname, HOOK_NAME_SIZE - 1); ConsoleOutput(INSERTING_HOOK, hp.name); - RemoveHook(hp.address, 0); - + wcscpy_s(hp.hookcode,HOOKCODE_LEN,HookCode::Generate(hp, GetCurrentProcessId()).c_str()); if (!(*hooks)[currentHook].Insert(hp)) { diff --git a/LunaHook/texthook.cc b/LunaHook/texthook.cc index e73fb85..7694dd9 100644 --- a/LunaHook/texthook.cc +++ b/LunaHook/texthook.cc @@ -8,6 +8,7 @@ #include "ithsys/ithsys.h" #include "MinHook.h" #include"Lang/Lang.h" +#include"veh_hook.h" extern WinMutex viewMutex; // - Unnamed helpers - @@ -94,15 +95,39 @@ namespace { // unnamed // - TextHook methods - +uintptr_t getasbaddr(const HookParam &hp){ + auto address=hp.address; + if (hp.type & MODULE_OFFSET) + { + 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 + { + if (HMODULE moduleBase = GetModuleHandleW(hp.module)) address += (uint64_t)moduleBase; + else return ConsoleOutput(MODULE_MISSING), false; + } + } + return address; +} bool TextHook::Insert(HookParam hp) { + + auto addr=getasbaddr(hp); + RemoveHook(addr, 0); + local_buffer=new BYTE[PIPE_BUFFER_SIZE]; { std::scoped_lock lock(viewMutex); this->hp = hp; - address = hp.address; + address = addr; } + + if (hp.type & DIRECT_READ) return InsertReadCode(); + if (hp.type & BREAK_POINT) return InsertBreakPoint(); return InsertHookCode(); } @@ -248,17 +273,61 @@ void TextHook::Send(uintptr_t lpDataBase) _InterlockedDecrement((long*) & useCount); } +void TextHook::breakpointcontext(PCONTEXT context){ +#ifdef _WIN64 + auto stack=new 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+sizeof(hook_stack)-sizeof(uintptr_t); + 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 +} +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); +} +bool TextHook::RemoveBreakPoint() +{ + return remove_veh_hook(location); +} bool TextHook::InsertHookCode() { - // jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7 - // Artikash 10/30/2018: No, I think that's impossible now that I moved to minhook - if (hp.type & MODULE_OFFSET) // Map hook offset to real address - 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 if (HMODULE moduleBase = GetModuleHandleW(hp.module)) address += (uint64_t)moduleBase; - else return ConsoleOutput(MODULE_MISSING), false; + VirtualProtect(location, 10, PAGE_EXECUTE_READWRITE, DUMMY); void* original; @@ -326,6 +395,7 @@ void TextHook::Clear() { if (address == 0) return; if (hp.type & DIRECT_READ) RemoveReadCode(); + if (hp.type & BREAK_POINT) RemoveBreakPoint(); else RemoveHookCode(); NotifyHookRemove(address, hp.name); std::scoped_lock lock(viewMutex); diff --git a/LunaHook/veh_hook.cpp b/LunaHook/veh_hook.cpp new file mode 100644 index 0000000..2bc676b --- /dev/null +++ b/LunaHook/veh_hook.cpp @@ -0,0 +1,176 @@ +/** +veh_hook Vectored Exception Handler hooking library +Version: 24-March-2008 +**/ +// #define WINVER 0x0501 +// #define _WIN32_WINNT 0x0501 +#include +#include "veh_hook.h" +#include +static veh_list_t* list = NULL; +char int3bp[] = "\xCC"; +std::mutex vehlistlock; +bool add_veh_hook(void* origFunc, newFuncType newFunc, DWORD hook_type) +{ + std::lock_guard _(vehlistlock); + //static veh_list_t* list = NULL; + DWORD oldProtect; + if (list == NULL) list = new_veh_list(); + if (list == NULL) return false; + void* handle = AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)veh_dispatch); + veh_node_t* newnode = insert_veh_node(list, origFunc, newFunc, handle, hook_type); + + // 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); + memcpy((void*)(&newnode->origBaseByte), (const void*)origFunc, sizeof (BYTE)); + memcpy((void*)origFunc, (const void*)&int3bp, sizeof (BYTE)); + VirtualProtect(origFunc, sizeof(int), newnode->OldProtect, &oldProtect); + return true; +} + +bool remove_veh_hook(void* origFunc) +{ + std::lock_guard _(vehlistlock); + if (list == NULL) return false; + veh_node_t* node = get_veh_node(list, origFunc, true); + 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); + 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; + + 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; + } + delete (searchnode); + return true; + } + lastsearchnode = searchnode; + searchnode = searchnode->next; + } + return false; +} +LONG CALLBACK veh_dispatch(PEXCEPTION_POINTERS ExceptionInfo) +{ + + DWORD oldProtect; + void* Addr = ExceptionInfo->ExceptionRecord->ExceptionAddress; + ULONG Code = ExceptionInfo->ExceptionRecord->ExceptionCode; + + 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; + veh_node_t* currnode ; + { + std::lock_guard _(vehlistlock); + currnode = get_veh_node(list, Addr, false); + } + if (currnode == NULL) return EXCEPTION_CONTINUE_SEARCH; + DWORD hooktype = currnode->hooktype; + // Pre-callback functions: + if (Code == STATUS_BREAKPOINT && hooktype == VEH_HK_INT3) + { + //(Temporarily) remove the int3 breakpoint + 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; + } + else if (Code == STATUS_SINGLE_STEP && hooktype == VEH_HK_INT3) + { + // Restore the INT3 breakpoint + VirtualProtect(Addr, sizeof(int), PAGE_EXECUTE_READWRITE, &currnode->OldProtect); + memcpy((void*)currnode->origFunc, (const void*)&int3bp, sizeof (BYTE)); + VirtualProtect(Addr, sizeof(int), currnode->OldProtect, &oldProtect); + ExceptionInfo->ContextRecord->EFlags &= ~0x00000100; // Remove TRACE from EFLAGS + return EXCEPTION_CONTINUE_EXECUTION; + } + else if (Code == STATUS_SINGLE_STEP && hooktype == VEH_HK_HW) + { + currnode->newFunc(ExceptionInfo->ContextRecord); + } + else if (Code == STATUS_SINGLE_STEP && hooktype == VEH_HK_MEM) + { + + currnode->newFunc(ExceptionInfo->ContextRecord); + } + return EXCEPTION_CONTINUE_EXECUTION; +} + +veh_list_t* new_veh_list() +{ + veh_list_t* newlist = (veh_list_t*)malloc(sizeof(veh_list_t)); + if (newlist == NULL) return NULL; + newlist->head = NULL; + newlist->tail = NULL; + return newlist; +} +veh_node_t* insert_veh_node(veh_list_t* list, 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->origFunc = origFunc; + newnode->newFunc = newFunc; + newnode->handle = handle; + newnode->OldProtect = PAGE_EXECUTE_READWRITE; + newnode->next = NULL; + newnode->hooktype=hook_type; + if (list->head == NULL) + { + list->head = newnode; + list->tail = newnode; + } + else + { + list->tail->next = newnode; + list->tail = newnode; + } + return newnode; +} + +veh_node_t* get_veh_node(veh_list_t* list, void* origFunc, bool exactmatch) +{ + veh_node_t* newnode; + veh_node_t* closestnode = NULL; + if (list == NULL) return NULL; + newnode = list->head; + MEMORY_BASIC_INFORMATION mem_info; + VirtualQuery(origFunc, &mem_info, sizeof(MEMORY_BASIC_INFORMATION)); + while (newnode != NULL) + { + + if (newnode->origFunc == origFunc) + { + return newnode; + } + if (!exactmatch) if (newnode->baseAddr == mem_info.BaseAddress) closestnode = newnode; + newnode = newnode->next; + } + + return closestnode; +} diff --git a/LunaHook/veh_hook.h b/LunaHook/veh_hook.h new file mode 100644 index 0000000..6777d7d --- /dev/null +++ b/LunaHook/veh_hook.h @@ -0,0 +1,57 @@ +/** +veh_hook Vectored Exception Handler hooking library +Version: 24-March-2008 +**/ + +#ifndef LIST_T_H_INCLUDED +#define LIST_T_H_INCLUDED +#include +#include +#include +#include +// VEH Hooking types +#define VEH_HK_INT3 0 +#define VEH_HK_MEM 1 +#define VEH_HK_HW 2 +// - + +#define OPCODE_INT3 "\xCC" + + +//typedef void (*pfvoid)(); +//typedef void (*newFuncType)(PCONTEXT); +using newFuncType = std::function; + +typedef struct veh_node +{ + void* origFunc; + newFuncType newFunc; + void* handle; + DWORD hooktype; + void* baseAddr; // Address of the page in which origFunc resides. + BYTE origBaseByte; + DWORD OldProtect; + struct veh_node* next; +} veh_node_t; + +typedef struct +{ + veh_node_t* head; + veh_node_t* tail; +} veh_list_t; + +// VEH hook interface functions for creating and removing hooks. +bool add_veh_hook(void* origFunc, newFuncType newFunc, DWORD hook_type); +bool remove_veh_hook(void* origFunc); + +// The VEH dispathing function is called by Windows every time an exception is encountered. +// the function dispatches calls to the correct inctercept function. +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); +bool remove_veh_node(veh_list_t* list, void* origFunc); +veh_node_t* get_veh_node(veh_list_t* list, void* origFunc, bool exactmatch); + +#endif // LIST_T_H_INCLUDED diff --git a/include/const.h b/include/const.h index b17d60d..45c008a 100644 --- a/include/const.h +++ b/include/const.h @@ -56,7 +56,9 @@ enum HookParamType : uint64_t EMBED_BEFORE_SIMPLE=0x200000, EMBED_AFTER_NEW=0x400000, EMBED_AFTER_OVERWRITE=0x800000, - EMBED_CODEC_UTF16=0x4000000 + EMBED_CODEC_UTF16=0x4000000, + + BREAK_POINT=0x8000000 }; diff --git a/include/hookcode.cpp b/include/hookcode.cpp index 484ca58..a409a4f 100644 --- a/include/hookcode.cpp +++ b/include/hookcode.cpp @@ -255,7 +255,10 @@ namespace } } - HCode += L"H"; + if(hp.type&BREAK_POINT) + HCode+=L"B"; + else + HCode += L"H"; if (hp.type & CODEC_UTF16||hp.type & CODEC_UTF32) { @@ -328,7 +331,14 @@ namespace HookCode code.erase(std::find(code.begin(), code.end(), L'/'), code.end()); // legacy/AGTH compatibility Trim(code); if (code[0] == L'R') return ParseRCode(code.erase(0, 1)); - else if (code[0] == L'H') return ParseHCode(code.erase(0, 1)); + else if (code[0] == L'B'||code[0] == L'H'){ + auto isbreakpoint=code[0] == L'B'; + auto hpo=ParseHCode(code.erase(0, 1)); + if(isbreakpoint && hpo.has_value()){ + hpo.value().type|=BREAK_POINT; + } + return hpo; + } else if (code[0] == L'E') return ParseECode(code.erase(0, 1)); return {}; } diff --git a/include/texthook.h b/include/texthook.h index eec49f3..8fac509 100644 --- a/include/texthook.h +++ b/include/texthook.h @@ -49,6 +49,9 @@ private: void Read(); bool InsertHookCode(); bool InsertReadCode(); + bool InsertBreakPoint(); + bool RemoveBreakPoint(); + void 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 3a4c8a3..572afb4 100644 --- a/include/types.h +++ b/include/types.h @@ -84,7 +84,7 @@ struct HookParam UINT codepage; // text encoding short length_offset; // index of the string length ALIGNPTR(uint64_t __1,uintptr_t padding); // padding before string - DWORD user_value; // 7/20/2014: jichi additional parameters for PSP games + ALIGNPTR(uint64_t __10,uintptr_t user_value); ALIGNPTR(uint64_t __2,void(*text_fun)(hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len)) ALIGNPTR(uint64_t __3,bool(*filter_fun)(void* data, size_t* len, HookParam* hp)); // jichi 10/24/2014: Add filter function. Return false to skip the text ALIGNPTR(uint64_t __6,bool (*hook_before)(hook_stack* stack,void* data, size_t* len,uintptr_t*role));