diff --git a/CMakeLists.txt b/CMakeLists.txt index 838c872..1aa691e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,5 +29,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Build) set(CMAKE_CONFIGURATION_TYPES Debug Release) add_subdirectory(host) -add_subdirectory(vnrhook) +if (${x64}) + add_subdirectory(vnrhook64) +else(${x64}) + add_subdirectory(vnrhook) +endif(${x64}) add_subdirectory(GUI) diff --git a/CMakeSettings.json b/CMakeSettings.json index c91fb67..c275a98 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -27,10 +27,10 @@ "name": "x64-Debug", "generator": "Ninja", "configurationType": "Debug", - "inheritEnvironments": [ "msvc_x86_x64" ], + "inheritEnvironments": [ "msvc_x64" ], "buildRoot": "${workspaceRoot}\\Builds\\${name}", "installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", - "cmakeCommandArgs": "", + "cmakeCommandArgs": "-Dx64=1", "buildCommandArgs": "-v", "ctestCommandArgs": "" }, @@ -38,10 +38,10 @@ "name": "x64-Release", "generator": "Ninja", "configurationType": "Release", - "inheritEnvironments": [ "msvc_x86_x64" ], + "inheritEnvironments": [ "msvc_x64" ], "buildRoot": "${workspaceRoot}\\Builds\\${name}", "installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", - "cmakeCommandArgs": "", + "cmakeCommandArgs": "-Dx64=1", "buildCommandArgs": "-v", "ctestCommandArgs": "" } diff --git a/vnrhook/include/const.h b/vnrhook/include/const.h index 6a3d77a..38d9270 100644 --- a/vnrhook/include/const.h +++ b/vnrhook/include/const.h @@ -1,4 +1,5 @@ #pragma once +#include "types.h" // vnrhook/const.h // 8/23/2013 jichi @@ -50,7 +51,7 @@ enum { MAX_HOOK = 64 }; // must be larger than HOOK_FUN_COUNT //enum { HOOK_SECTION_SIZE = 0x2000 }; // default ITH value // jichi 1/16/2015: Change to a very large number to prevent crash //enum { MAX_HOOK = 0x100 }; // must be larger than HookFunCount -enum { HOOK_SECTION_SIZE = MAX_HOOK * 0x100 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook) +enum { HOOK_SECTION_SIZE = MAX_HOOK * sizeof(Hook) * 2 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook) // jichi 375/2014: Add offset of pusha/pushad // http://faydoc.tripod.com/cpu/pushad.htm diff --git a/vnrhook/src/hijack/texthook.cc b/vnrhook/src/hijack/texthook.cc index 17e21af..bf50a62 100644 --- a/vnrhook/src/hijack/texthook.cc +++ b/vnrhook/src/hijack/texthook.cc @@ -340,7 +340,7 @@ DWORD WINAPI ReaderThread(LPVOID threadParam) { TextHook* hook = (TextHook*)threadParam; BYTE buffer[PIPE_BUFFER_SIZE] = {}; - char testChar; + char testChar = 0; unsigned int changeCount = 0; const char* currentAddress = (char*)hook->hp.address; while (true) diff --git a/vnrhook64/CMakeLists.txt b/vnrhook64/CMakeLists.txt new file mode 100644 index 0000000..a3bdcfa --- /dev/null +++ b/vnrhook64/CMakeLists.txt @@ -0,0 +1,46 @@ +project(vnrhook) + +include_directories(../vnrhook) + +set(vnrhook_src + ../vnrhook/include/const.h + ../vnrhook/include/defs.h + ../vnrhook/include/types.h + src/main.cc + src/main.h + src/pipe.cc + src/hijack/texthook.cc + src/hijack/texthook.h +) + +add_library(vnrhook SHARED ${vnrhook_src}) + +set_source_files_properties( + ${PROJECT_SOURCE_DIR}/winseh/safeseh.asm + PROPERTIES + # CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711 + COMPILE_FLAGS /safeseh +) + +set_target_properties(vnrhook PROPERTIES + LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO" +) + +target_compile_options(vnrhook PRIVATE + /EHa + $<$:> + $<$:> +) + +set(vnrhook_libs + Version.lib +) + +target_link_libraries(vnrhook ${vnrhook_libs}) + +target_compile_definitions(vnrhook + PRIVATE + ITH_HAS_CRT + ITH_HAS_SEH + _CRT_NON_CONFORMING_SWPRINTFS +) \ No newline at end of file diff --git a/vnrhook64/src/hijack/texthook.cc b/vnrhook64/src/hijack/texthook.cc new file mode 100644 index 0000000..f6ba32e --- /dev/null +++ b/vnrhook64/src/hijack/texthook.cc @@ -0,0 +1,187 @@ +// texthook.cc +// 8/24/2013 jichi +// Branch: ITH_DLL/texthook.cpp, rev 128 +// 8/24/2013 TODO: Clean up this file + +#ifdef _MSC_VER +# pragma warning (disable:4100) // C4100: unreference formal parameter +# pragma warning (disable:4018) // C4018: sign/unsigned mismatch +//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler +#endif // _MSC_VER + +#include "../main.h" +#include "texthook.h" +#include "include/const.h" +//#include "winseh/winseh.h" + +//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this >< + +// - Global variables - + +// 10/14/2014 jichi: disable GDI hooks +static bool gdi_hook_enabled_ = true; // enable GDI by default +static bool gdiplus_hook_enabled_ = false; // disable GDIPlus by default +bool GDIHooksEnabled() { return ::gdi_hook_enabled_; } +bool GDIPlusHooksEnabled() { return ::gdiplus_hook_enabled_; } +void EnableGDIHooks() { ::gdi_hook_enabled_ = true; } +void EnableGDIPlusHooks() { ::gdiplus_hook_enabled_ = true; } +void DisableGDIHooks() { ::gdi_hook_enabled_ = false; } +void DisableGDIPlusHooks() { ::gdiplus_hook_enabled_ = false; } + +//FilterRange filter[8]; + +DWORD flag, + enter_count; + +TextHook *hookman, + *current_available; + +// - TextHook methods - + +// jichi 12/2/2013: This function mostly return 0. +// It return the hook address only for auxiliary case. +// However, because no known hooks are auxiliary, this function always return 0. +// +// jichi 5/11/2014: +// - dwDataBase: the stack address +// - dwRetn: the return address of the hook + + +int TextHook::InsertHook() +{ + int ok = 1; + //ConsoleOutput("vnrcli:InsertHook: enter"); + WaitForSingleObject(hmMutex, 0); + if (hp.type & DIRECT_READ) ok = InsertReadCode(); + else + { + ConsoleOutput("only /R (read) codes supported in 64 bit"); + } + ReleaseMutex(hmMutex); + //ConsoleOutput("vnrcli:InsertHook: leave"); + return ok; +} + +DWORD WINAPI ReaderThread(LPVOID threadParam) +{ + TextHook* hook = (TextHook*)threadParam; + BYTE buffer[PIPE_BUFFER_SIZE] = {}; + char testChar = 0; + unsigned int changeCount = 0; + const char* currentAddress = (char*)hook->hp.address; + while (true) + { + Sleep(50); + if (testChar == *currentAddress) + { + changeCount = 0; + continue; + } + testChar = *currentAddress; + if (++changeCount > 10) + { + ConsoleOutput("NextHooker: memory constantly changing, useless to read"); + ConsoleOutput("NextHooker: remove read code"); + break; + } + + int dataLen; + if (hook->hp.type & USING_UNICODE) + dataLen = wcslen((const wchar_t*)currentAddress) * 2; + else + dataLen = strlen(currentAddress); + + *(DWORD*)buffer = hook->hp.address; + *(DWORD*)(buffer + 4) = 0; + *(DWORD*)(buffer + 8) = 0; + memcpy(buffer + HEADER_SIZE, currentAddress, dataLen); + DWORD unused; + WriteFile(::hookPipe, buffer, dataLen + HEADER_SIZE, &unused, nullptr); + + if (hook->hp.offset == 0) continue; + currentAddress += dataLen + hook->hp.offset; + testChar = *currentAddress; + } + hook->ClearHook(); + return 0; +} + +int TextHook::InsertReadCode() +{ + hp.hook_len = 0x40; + //Check if the new hook range conflict with existing ones. Clear older if conflict. + TextHook *it = hookman; + for (int i = 0; i < currentHook; it++) { + if (it->Address()) + i++; + if (it == this) + continue; + if ((it->Address() >= hp.address && it->Address() < hp.hook_len + hp.address) || (it->Address() <= hp.address && it->Address() + it->Length() > hp.address)) + it->ClearHook(); + } + //if (!IthGetMemoryRange((LPCVOID)hp.address, 0, 0)) + //{ + // ConsoleOutput("cannot access read address"); + // return no; + //} + hp.readerHandle = CreateThread(nullptr, 0, ReaderThread, this, 0, nullptr); + return yes; + +} + +int TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag) +{ + WaitForSingleObject(hmMutex, 0); + hp = h; + hp.type |= set_flag; + if (name && name != hook_name) { + SetHookName(name); + } + currentHook++; + current_available = this+1; + while (current_available->Address()) + current_available++; + ReleaseMutex(hmMutex); + return 1; +} + +int TextHook::RemoveReadCode() +{ + if (!hp.address) return no; + TerminateThread(hp.readerHandle, 0); + CloseHandle(hp.readerHandle); + return yes; +} + +int TextHook::ClearHook() +{ + int err; + WaitForSingleObject(hmMutex, 0); + ConsoleOutput("vnrcli:RemoveHook: enter"); + err = RemoveReadCode(); + NotifyHookRemove(hp.address); + if (hook_name) { + delete[] hook_name; + hook_name = nullptr; + } + memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH + //if (current_available>this) + // current_available = this; + currentHook--; + ConsoleOutput("vnrcli:RemoveHook: leave"); + ReleaseMutex(hmMutex); + return err; +} + +int TextHook::SetHookName(LPCSTR name) +{ + name_length = strlen(name) + 1; + if (hook_name) + delete[] hook_name; + hook_name = new char[name_length]; + //ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory + strcpy(hook_name, name); + return 0; +} + +// EOF diff --git a/vnrhook64/src/hijack/texthook.h b/vnrhook64/src/hijack/texthook.h new file mode 100644 index 0000000..4bb319e --- /dev/null +++ b/vnrhook64/src/hijack/texthook.h @@ -0,0 +1,78 @@ +#pragma once + +// texthook.h +// 8/24/2013 jichi +// Branch: IHF_DLL/IHF_CLIENT.h, rev 133 +// +// 8/24/2013 TODO: +// - Clean up this file +// - Reduce global variables. Use namespaces or singleton classes instead. +#include +#include +#include "include/types.h" +#include + +extern int currentHook; +extern WCHAR dll_mutex[]; +//extern WCHAR dll_name[]; +extern DWORD trigger; +//extern DWORD current_process_id; + +// jichi 6/3/2014: Get memory range of the current module +extern DWORD processStartAddress, + processStopAddress; + +void InitFilterTable(); + +// jichi 9/25/2013: This class will be used by NtMapViewOfSectionfor +// interprocedure communication, where constructor/destructor will NOT work. +class TextHook : public Hook +{ + int InsertHookCode(); + int InsertReadCode(); + int UnsafeInsertHookCode(); + DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn); + int RemoveHookCode(); + int RemoveReadCode(); + int SetHookName(LPCSTR name); +public: + int InsertHook(); + int InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0); + DWORD Send(DWORD dwDataBase, DWORD dwRetn); + int ClearHook(); + int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed +}; + +extern TextHook *hookman, + *current_available; + +//void InitDefaultHook(); + +struct FilterRange { DWORD lower, upper; }; +extern FilterRange *filter; + +extern bool running, + live; + +extern HANDLE hookPipe, + hmMutex; + +DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter); +DWORD WINAPI CommandPipe(LPVOID lpThreadParameter); +DWORD WINAPI PipeManager(LPVOID unused); + +//void RequestRefreshProfile(); + +//typedef DWORD (*InsertHookFun)(DWORD); +//typedef DWORD (*IdentifyEngineFun)(); +//typedef DWORD (*InsertDynamicHookFun)(LPVOID addr, DWORD frame, DWORD stack); +//extern IdentifyEngineFun IdentifyEngine; +//extern InsertDynamicHookFun InsertDynamicHook; + +// jichi 9/28/2013: Protect pipeline in wine +void CliLockPipe(); +void CliUnlockPipe(); + +enum : int { yes = 0, no = 1 }; + +// EOF diff --git a/vnrhook64/src/main.cc b/vnrhook64/src/main.cc new file mode 100644 index 0000000..9b69002 --- /dev/null +++ b/vnrhook64/src/main.cc @@ -0,0 +1,150 @@ +// main.cc +// 8/24/2013 jichi +// Branch: ITH_DLL/main.cpp, rev 128 +// 8/24/2013 TODO: Clean up this file + +#ifdef _MSC_VER +# pragma warning (disable:4100) // C4100: unreference formal parameter +//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler +#endif // _MSC_VER + +#include "main.h" +#include "hijack/texthook.h" +#include "include/defs.h" + +// Global variables + +// jichi 6/3/2014: memory range of the current module +DWORD processStartAddress, + processStopAddress; + +enum { HOOK_BUFFER_SIZE = MAX_HOOK * sizeof(TextHook) }; +//#define MAX_HOOK (HOOK_BUFFER_SIZE/sizeof(TextHook)) +DWORD hook_buff_len = HOOK_BUFFER_SIZE; + +WCHAR hm_section[0x100]; +HANDLE hSection; +bool running; +int currentHook = 0, + user_hook_count = 0; +HANDLE + hFile, + hMutex, + hmMutex; +HMODULE currentModule; + +BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID unused) +{ + static HANDLE pipeThread; + + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + { + static bool attached_ = false; + if (attached_) // already attached + { + return TRUE; + } + attached_ = true; + + DisableThreadLibraryCalls(hModule); + + swprintf(hm_section, ITH_SECTION_ L"%d", GetCurrentProcessId()); + + // jichi 9/25/2013: Interprocedural communication with vnrsrv. + hSection = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_EXECUTE_READWRITE, 0, HOOK_SECTION_SIZE, hm_section); + ::hookman = nullptr; + // Artikash 6/20/2018: This crashes certain games (https://vndb.org/v7738). No idea why. + ::hookman = (TextHook*)MapViewOfFile(hSection, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, HOOK_SECTION_SIZE / 2); + + ::processStartAddress = (DWORD)GetModuleHandleW(nullptr); + + { + wchar_t hm_mutex[0x100]; + swprintf(hm_mutex, ITH_HOOKMAN_MUTEX_ L"%d", GetCurrentProcessId()); + ::hmMutex = CreateMutexW(nullptr, FALSE, hm_mutex); + } + { + wchar_t dll_mutex[0x100]; + swprintf(dll_mutex, ITH_PROCESS_MUTEX_ L"%d", GetCurrentProcessId()); + DWORD exists; + ::hMutex = CreateMutexW(nullptr, TRUE, dll_mutex); // jichi 9/18/2013: own is true, make sure the injected dll is singleton + if (GetLastError() == ERROR_ALREADY_EXISTS) + return FALSE; + } + + ::running = true; + ::current_available = ::hookman; + ::currentModule = hModule; + + pipeThread = CreateThread(nullptr, 0, PipeManager, 0, 0, nullptr); + } break; + case DLL_PROCESS_DETACH: + { + static bool detached_ = false; + if (detached_) // already detached + return TRUE; + detached_ = true; + + // jichi 10/2/2103: Cannot use __try in functions that require object unwinding + //ITH_TRY { + ::running = false; + + if (pipeThread) { + WaitForSingleObject(pipeThread, TIMEOUT); + CloseHandle(pipeThread); + } + + for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++) + man->ClearHook(); + //if (ith_has_section) + UnmapViewOfFile(::hookman); + + CloseHandle(hSection); + CloseHandle(hMutex); + CloseHandle(hmMutex); + //} ITH_EXCEPT {} + } break; + } + return TRUE; +} + +//extern "C" { +DWORD NewHook(const HookParam &hp, LPCSTR lpname, DWORD flag) +{ + std::string name = lpname; + int current = ::current_available - ::hookman; + if (current < MAX_HOOK) { + //flag &= 0xffff; + //if ((flag & HOOK_AUXILIARY) == 0) + flag |= HOOK_ADDITIONAL; + if (name[0] == '\0') + { + name = "UserHook" + std::to_string(user_hook_count++); + } + + ConsoleOutput(("vnrcli:NewHook: try inserting hook: " + name).c_str()); + + // jichi 7/13/2014: This function would raise when too many hooks added + ::hookman[current].InitHook(hp, name.c_str(), flag & 0xffff); + + if (::hookman[current].InsertHook() == 0) { + ConsoleOutput(("vnrcli:NewHook: inserted hook: " + name).c_str()); + NotifyHookInsert(hp, name.c_str()); + } else + ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook"); + } + return 0; +} +DWORD RemoveHook(DWORD addr) +{ + for (int i = 0; i < MAX_HOOK; i++) + if (::hookman[i].Address ()== addr) { + ::hookman[i].ClearHook(); + return 0; + } + return 0; +} + +// EOF \ No newline at end of file diff --git a/vnrhook64/src/main.h b/vnrhook64/src/main.h new file mode 100644 index 0000000..c52720e --- /dev/null +++ b/vnrhook64/src/main.h @@ -0,0 +1,27 @@ +#pragma once + +// main.h +// 8/23/2013 jichi +// Branch: ITH/IHF_DLL.h, rev 66 + +#include +#include "include/const.h" +#include "include/types.h" + +void ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text +void NotifyHookInsert(HookParam hp, LPCSTR name); +void NotifyHookRemove(DWORD addr); +DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag = HOOK_ENGINE); +DWORD RemoveHook(DWORD addr); +DWORD SwitchTrigger(DWORD on); +DWORD GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name); + +// 10/14/2014 jichi: disable GDI hooks +void EnableGDIHooks(); +void EnableGDIPlusHooks(); +void DisableGDIHooks(); +void DisableGDIPlusHooks(); +bool GDIHooksEnabled(); +bool GDIPlusHooksEnabled(); + +// EOF diff --git a/vnrhook64/src/pipe.cc b/vnrhook64/src/pipe.cc new file mode 100644 index 0000000..aa7bd05 --- /dev/null +++ b/vnrhook64/src/pipe.cc @@ -0,0 +1,127 @@ +// pipe.cc +// 8/24/2013 jichi +// Branch: ITH_DLL/pipe.cpp, rev 66 +// 8/24/2013 TODO: Clean up this file + +#ifdef _MSC_VER +# pragma warning (disable:4100) // C4100: unreference formal parameter +#endif // _MSC_VER + +#include "main.h" +#include "include/defs.h" +#include "hijack/texthook.h" + +HANDLE hookPipe; +extern HMODULE currentModule; + +DWORD WINAPI PipeManager(LPVOID unused) +{ + enum { STANDARD_WAIT = 50 }; + while (::running) + { + DWORD count; + BYTE buffer[PIPE_BUFFER_SIZE]; + HANDLE hostPipe = ::hookPipe = INVALID_HANDLE_VALUE, + pipeAcquisitionMutex = CreateMutexW(nullptr, TRUE, ITH_GRANTPIPE_MUTEX); + + while (::hookPipe == INVALID_HANDLE_VALUE || hostPipe == INVALID_HANDLE_VALUE) + { + Sleep(STANDARD_WAIT); + if (::hookPipe == INVALID_HANDLE_VALUE) + { + ::hookPipe = CreateFileW(ITH_TEXT_PIPE, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + } + if (hostPipe == INVALID_HANDLE_VALUE) + { + hostPipe = CreateFileW(ITH_COMMAND_PIPE, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + } + } + + *(DWORD*)buffer = GetCurrentProcessId(); + WriteFile(::hookPipe, buffer, sizeof(DWORD), &count, nullptr); + + ReleaseMutex(pipeAcquisitionMutex); + CloseHandle(pipeAcquisitionMutex); + + ConsoleOutput("vnrcli:WaitForPipe: pipe connected"); + ConsoleOutput("only read codes are supported on x64: engine disabled"); + + while (::running) + { + if (!ReadFile(hostPipe, buffer, PIPE_BUFFER_SIZE / 2, &count, nullptr)) // Artikash 5/21/2018: why / 2? wchar_t? + { + break; + } + DWORD command = *(DWORD*)buffer; + switch (command) + { + case HOST_COMMAND_NEW_HOOK: + buffer[count] = 0; + NewHook(*(HookParam *)(buffer + sizeof(DWORD)), // Hook parameter + (LPSTR)(buffer + 4 + sizeof(HookParam)), // Hook name + 0 + ); + break; + case HOST_COMMAND_REMOVE_HOOK: + { + TextHook *in = hookman; + for (int i = 0; i < currentHook; in++) + { + if (in->Address()) i++; + if (in->Address() == *(DWORD *)(buffer + sizeof(DWORD))) // Hook address + { + break; + } + } + if (in->Address()) + { + in->ClearHook(); + } + } + break; + case HOST_COMMAND_DETACH: + ::running = false; + break; + } + } + CloseHandle(::hookPipe); + CloseHandle(hostPipe); + } + FreeLibraryAndExitThread(::currentModule, 0); + return 0; +} + +void ConsoleOutput(LPCSTR text) +{ + BYTE buffer[PIPE_BUFFER_SIZE]; + *(DWORD*)buffer = HOST_NOTIFICATION; + *(DWORD*)(buffer + sizeof(DWORD)) = HOST_NOTIFICATION_TEXT; + strcpy((char*)buffer + sizeof(DWORD) * 2, text); + DWORD unused; + WriteFile(::hookPipe, buffer, strlen(text) + sizeof(DWORD) * 2, &unused, nullptr); +} + +void NotifyHookInsert(HookParam hp, LPCSTR name) +{ + BYTE buffer[PIPE_BUFFER_SIZE]; + *(DWORD*)buffer = HOST_NOTIFICATION; + *(DWORD*)(buffer + sizeof(DWORD)) = HOST_NOTIFICATION_NEWHOOK; + *(HookParam*)(buffer + sizeof(DWORD) * 2) = hp; + strcpy((char*)buffer + sizeof(DWORD) * 2 + sizeof(HookParam), name); + DWORD unused; + WriteFile(::hookPipe, buffer, strlen(name) + sizeof(DWORD) * 2 + sizeof(HookParam), &unused, nullptr); + return; +} + +void NotifyHookRemove(DWORD addr) +{ + BYTE buffer[sizeof(DWORD) * 3]; + *(DWORD*)buffer = HOST_NOTIFICATION; + *(DWORD*)(buffer + sizeof(DWORD)) = HOST_NOTIFICATION_RMVHOOK; + *(DWORD*)(buffer + sizeof(DWORD) * 2) = addr; + DWORD unused; + WriteFile(::hookPipe, buffer, sizeof(DWORD) * 3, &unused, nullptr); + return; +} + +// EOF