From 62a204dc85b0ec91738414802dfaf78fe57a937b Mon Sep 17 00:00:00 2001 From: mireado Date: Thu, 2 Apr 2015 23:30:22 +0900 Subject: [PATCH] starting commit --- vnr/ith/hook/hook.h | 38 ++ vnr/ith/hook/hook.pro | 56 ++ vnr/ith/hook/main.cc | 416 ++++++++++++++ vnr/ith/hook/rpc/pipe.cc | 346 +++++++++++ vnr/ith/hook/tree/avl.h | 590 +++++++++++++++++++ vnr/ith/hookxp/hookxp.pro | 59 ++ vnr/ith/host/CMakeLists.txt | 66 +++ vnr/ith/host/SettingManager.h | 46 ++ vnr/ith/host/avl_p.h | 587 +++++++++++++++++++ vnr/ith/host/config.h | 16 + vnr/ith/host/hookman.cc | 945 +++++++++++++++++++++++++++++++ vnr/ith/host/hookman.h | 144 +++++ vnr/ith/host/host.pri | 18 + vnr/ith/host/host.pro | 54 ++ vnr/ith/host/main.cc | 611 ++++++++++++++++++++ vnr/ith/host/pipe.cc | 322 +++++++++++ vnr/ith/host/settings.h | 14 + vnr/ith/host/srv.h | 36 ++ vnr/ith/host/srv_p.h | 46 ++ vnr/ith/host/textthread.cc | 787 +++++++++++++++++++++++++ vnr/ith/host/textthread.h | 136 +++++ vnr/ith/host/textthread_p.h | 155 +++++ vnr/ith/import/mono/funcinfo.h | 52 ++ vnr/ith/import/mono/mono.pri | 10 + vnr/ith/import/mono/types.h | 41 ++ vnr/ith/import/ppsspp/funcinfo.h | 106 ++++ 26 files changed, 5697 insertions(+) create mode 100644 vnr/ith/hook/hook.h create mode 100644 vnr/ith/hook/hook.pro create mode 100644 vnr/ith/hook/main.cc create mode 100644 vnr/ith/hook/rpc/pipe.cc create mode 100644 vnr/ith/hook/tree/avl.h create mode 100644 vnr/ith/hookxp/hookxp.pro create mode 100644 vnr/ith/host/CMakeLists.txt create mode 100644 vnr/ith/host/SettingManager.h create mode 100644 vnr/ith/host/avl_p.h create mode 100644 vnr/ith/host/config.h create mode 100644 vnr/ith/host/hookman.cc create mode 100644 vnr/ith/host/hookman.h create mode 100644 vnr/ith/host/host.pri create mode 100644 vnr/ith/host/host.pro create mode 100644 vnr/ith/host/main.cc create mode 100644 vnr/ith/host/pipe.cc create mode 100644 vnr/ith/host/settings.h create mode 100644 vnr/ith/host/srv.h create mode 100644 vnr/ith/host/srv_p.h create mode 100644 vnr/ith/host/textthread.cc create mode 100644 vnr/ith/host/textthread.h create mode 100644 vnr/ith/host/textthread_p.h create mode 100644 vnr/ith/import/mono/funcinfo.h create mode 100644 vnr/ith/import/mono/mono.pri create mode 100644 vnr/ith/import/mono/types.h create mode 100644 vnr/ith/import/ppsspp/funcinfo.h diff --git a/vnr/ith/hook/hook.h b/vnr/ith/hook/hook.h new file mode 100644 index 0000000..b036a81 --- /dev/null +++ b/vnr/ith/hook/hook.h @@ -0,0 +1,38 @@ +#pragma once + +// hook.h +// 8/23/2013 jichi +// Branch: ITH/IHF_DLL.h, rev 66 + +#include "ith/common/const.h" +#include "ith/common/types.h" + +//#ifdef IHF +//# define IHFAPI __declspec(dllexport) __stdcall +//#else +//# define IHFAPI __declspec(dllimport) __stdcall +//#endif // IHF +#define IHFAPI // 9/19/2014 jichi: dummy + +//extern "C" { +//DWORD IHFAPI OutputConsole(LPCWSTR text); +void IHFAPI ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text +//DWORD IHFAPI OutputDWORD(DWORD d); +//DWORD IHFAPI OutputRegister(DWORD *base); +DWORD IHFAPI NotifyHookInsert(DWORD addr); +DWORD IHFAPI NewHook(const HookParam &hp, LPCWSTR name, DWORD flag = HOOK_ENGINE); +DWORD IHFAPI RemoveHook(DWORD addr); +DWORD IHFAPI SwitchTrigger(DWORD on); +DWORD IHFAPI GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name); +//DWORD IHFAPI RegisterEngineModule(DWORD idEngine, DWORD dnHook); +//} // extern "C" + +// 10/21/2014 jichi: TODO: Get rid of this global variable +// Defined in pipe.cc +extern bool engine_registered; + + +// 10/14/2014 jichi: disable GDI hooks +void DisableGDIHooks(); + +// EOF diff --git a/vnr/ith/hook/hook.pro b/vnr/ith/hook/hook.pro new file mode 100644 index 0000000..f8611b5 --- /dev/null +++ b/vnr/ith/hook/hook.pro @@ -0,0 +1,56 @@ +# hook.pro +# 8/9/2013 jichi +# Build vnrhook.dll for Windows 7+ + +CONFIG += eh eha # exception handler to catch all exceptions +#CONFIG += noeh # msvcrt on Windows XP does not has exception handler +include(../dllconfig.pri) +include(../sys/sys.pri) +include($$LIBDIR/disasm/disasm.pri) +include($$LIBDIR/memdbg/memdbg.pri) +include($$LIBDIR/ntinspect/ntinspect.pri) +#include($$LIBDIR/winseh/winseh_safe.pri) +include($$LIBDIR/winversion/winversion.pri) + +# 9/27/2013: disable ITH this game engine, only for debugging purpose +#DEFINES += ITH_DISABLE_ENGINE + +# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile +#DEFINES += ITH_WINE +#DEFINES += ITH_SYNC_PIPE + +## Libraries + +LIBS += -lkernel32 -luser32 -lgdi32 + +## Sources + +TEMPLATE = lib +TARGET = vnrhook + +#CONFIG += staticlib + +HEADERS += \ + config.h \ + cli.h \ + hook.h \ + engine/engine.h \ + engine/hookdefs.h \ + engine/match.h \ + engine/pchooks.h \ + engine/util.h \ + tree/avl.h + +SOURCES += \ + main.cc \ + rpc/pipe.cc \ + hijack/texthook.cc \ + engine/engine.cc \ + engine/match.cc \ + engine/pchooks.cc \ + engine/util.cc + +#RC_FILE += vnrhook.rc +#OTHER_FILES += vnrhook.rc + +# EOF diff --git a/vnr/ith/hook/main.cc b/vnr/ith/hook/main.cc new file mode 100644 index 0000000..3fa35b8 --- /dev/null +++ b/vnr/ith/hook/main.cc @@ -0,0 +1,416 @@ +// 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 "cli.h" +#include "tree/avl.h" +#include "engine/match.h" +#include "ith/common/const.h" +#include "ith/common/defs.h" +#include "ith/common/except.h" +//#include "ith/common/growl.h" +#include "ith/sys/sys.h" +#include "ccutil/ccmacro.h" +//#include "ntinspect/ntinspect.h" +//#include "winseh/winseh.h" +//#include +//#include "md5.h" +//#include +//#include + +// Global variables + +// jichi 6/3/2014: memory range of the current module +DWORD processStartAddress, + processStopAddress; + +namespace { // unnamed +wchar_t processName[MAX_PATH]; + +inline void GetProcessName(wchar_t *name) +{ + //assert(name); + PLDR_DATA_TABLE_ENTRY it; + __asm + { + mov eax,fs:[0x30] + mov eax,[eax+0xc] + mov eax,[eax+0xc] + mov it,eax + } + wcscpy(name, it->BaseDllName.Buffer); +} +} // unmaed namespace + +enum { HOOK_BUFFER_SIZE = MAX_HOOK * sizeof(TextHook) }; +//#define MAX_HOOK (HOOK_BUFFER_SIZE/sizeof(TextHook)) +DWORD hook_buff_len = HOOK_BUFFER_SIZE; + +namespace { FilterRange _filter[IHF_FILTER_CAPACITY]; } +FilterRange *filter = _filter; + +WCHAR dll_mutex[0x100]; +//WCHAR dll_name[0x100]; +WCHAR hm_mutex[0x100]; +WCHAR hm_section[0x100]; +HINSTANCE hDLL; +HANDLE hSection; +bool running, + live = false; +int current_hook = 0, + user_hook_count = 0; +DWORD trigger = 0; +HANDLE + hFile, + hMutex, + hmMutex; +//DWORD current_process_id; +extern DWORD enter_count; +//extern LPWSTR current_dir; +extern DWORD engine_type; +extern DWORD module_base; +AVLTree *tree; + +namespace { // unnamed + +void AddModule(DWORD hModule, DWORD size, LPWSTR name) +{ + IMAGE_DOS_HEADER *DosHdr; + IMAGE_NT_HEADERS *NtHdr; + IMAGE_EXPORT_DIRECTORY *ExtDir; + UINT uj; + FunctionInfo info = {0, hModule, size, name}; + char *pcFuncPtr, *pcBuffer; + DWORD dwReadAddr, dwFuncName, dwExportAddr; + WORD wOrd; + DosHdr = (IMAGE_DOS_HEADER *)hModule; + if (IMAGE_DOS_SIGNATURE==DosHdr->e_magic) { + dwReadAddr = hModule + DosHdr->e_lfanew; + NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr; + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) { + dwExportAddr = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + if (dwExportAddr == 0) + return; + dwExportAddr+=hModule; + ExtDir=(IMAGE_EXPORT_DIRECTORY*)dwExportAddr; + dwExportAddr=hModule+ExtDir->AddressOfNames; + for (uj = 0; uj < ExtDir->NumberOfNames; uj++) { + dwFuncName=*(DWORD*)dwExportAddr; + pcBuffer = (char *)(hModule+dwFuncName); + pcFuncPtr=(char *)(hModule+(DWORD)ExtDir->AddressOfNameOrdinals+(uj*sizeof(WORD))); + wOrd = *(WORD *)pcFuncPtr; + pcFuncPtr = (char *)(hModule+(DWORD)ExtDir->AddressOfFunctions+(wOrd*sizeof(DWORD))); + info.addr=hModule+*(DWORD*)pcFuncPtr; + ::tree->Insert(pcBuffer,info); + dwExportAddr+=sizeof(DWORD); + } + } + } +} + +void GetFunctionNames() +{ + // jichi 9/26/2013: AVLTree is already zero + PPEB ppeb; + __asm { + mov eax, fs:[0x30] + mov ppeb, eax + } + DWORD temp = *(DWORD *)(&ppeb->Ldr->InLoadOrderModuleList); + PLDR_DATA_TABLE_ENTRY it = (PLDR_DATA_TABLE_ENTRY)temp; + while (it->SizeOfImage) { + AddModule((DWORD)it->DllBase, it->SizeOfImage, it->BaseDllName.Buffer); + it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink; + if (*(DWORD *)it == temp) + break; + } +} + +void RequestRefreshProfile() +{ + if (::live) { + BYTE buffer[0x80] = {}; // 11/14/2013: reset to zero. Shouldn't it be 0x8 instead of 0x80? + *(DWORD *)buffer = -1; + *(DWORD *)(buffer + 4) = 1; + *(DWORD *)(buffer + 8) = 0; + IO_STATUS_BLOCK ios; + CliLockPipe(); + NtWriteFile(hPipe, 0, 0, 0, &ios, buffer, HEADER_SIZE, 0, 0); + CliUnlockPipe(); + } +} + +} // unnamed namespace + +DWORD IHFAPI GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name) +{ + TreeNode *node = ::tree->Search(name); + if (node) { + if (addr) *addr = node->data.addr; + if (base) *base = node->data.module; + if (size) *size = node->data.size; + if (base_name) *base_name = node->data.name; + return TRUE; + } + else + return FALSE; +} + +BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved) +{ + + static HANDLE hSendThread, + hCmdThread, + hEngineThread; + + + CC_UNUSED(lpReserved); + + //static WCHAR dll_exist[] = L"ITH_DLL_RUNNING"; + static WCHAR dll_exist[] = ITH_CLIENT_MUTEX; + static HANDLE hDllExist; + + // jichi 9/23/2013: wine deficenciy on mapping sections + // Whe set to false, do not map sections. + //static bool ith_has_section = true; + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + { + LdrDisableThreadCalloutsForDll(hModule); + //IthBreak(); + ::module_base = (DWORD)hModule; + IthInitSystemService(); + swprintf(hm_section, ITH_SECTION_ L"%d", current_process_id); + + // jichi 9/25/2013: Interprocedural communication with vnrsrv. + hSection = IthCreateSection(hm_section, HOOK_SECTION_SIZE, PAGE_EXECUTE_READWRITE); + ::hookman = nullptr; + NtMapViewOfSection(hSection, NtCurrentProcess(), + (LPVOID *)&::hookman, 0, hook_buff_len, 0, &hook_buff_len, ViewUnmap, 0, + PAGE_EXECUTE_READWRITE); + //PAGE_EXECUTE_READWRITE); + + GetProcessName(::processName); + FillRange(::processName, &::processStartAddress, &::processStopAddress); + //NtInspect::getCurrentMemoryRange(&::processStartAddress, &::processStopAddress); + + //if (!::hookman) { + // ith_has_section = false; + // ::hookman = new TextHook[MAX_HOOK]; + // memset(::hookman, 0, MAX_HOOK * sizeof(TextHook)); + //} + + //LPCWSTR p; + //for (p = GetMainModulePath(); *p; p++); + //for (p = p; *p != L'\\'; p--); + //wcscpy(dll_name, p + 1); + //swprintf(dll_mutex,L"ITH_%.4d_%s",current_process_id,current_dir); + swprintf(dll_mutex, ITH_PROCESS_MUTEX_ L"%d", current_process_id); + swprintf(hm_mutex, ITH_HOOKMAN_MUTEX_ L"%d", current_process_id); + hmMutex = IthCreateMutex(hm_mutex, FALSE); + + DWORD s; + hMutex = IthCreateMutex(dll_mutex, TRUE, &s); // jichi 9/18/2013: own is true + if (s) + return FALSE; + + hDllExist = IthCreateMutex(dll_exist, 0); + hDLL = hModule; + ::running = true; + ::current_available = ::hookman; + ::tree = new AVLTree; + GetFunctionNames(); + InitFilterTable(); + //InitDefaultHook(); // jichi 7/17/2014: Disabled by default + hSendThread = IthCreateThread(WaitForPipe, 0); + hCmdThread = IthCreateThread(CommandPipe, 0); + hEngineThread = IthCreateThread(Engine::match, 0); + } + break; + case DLL_PROCESS_DETACH: + { + // jichi 10/2/2103: Cannot use __try in functions that require object unwinding + //ITH_TRY { + ::running = false; + ::live = false; + + const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds + + if (hEngineThread) { + NtWaitForSingleObject(hEngineThread, 0, (PLARGE_INTEGER)&timeout); + NtClose(hEngineThread); + } + + if (hSendThread) { + NtWaitForSingleObject(hSendThread, 0, (PLARGE_INTEGER)&timeout); + NtClose(hSendThread); + } + + if (hCmdThread) { + NtWaitForSingleObject(hCmdThread, 0, (PLARGE_INTEGER)&timeout); + NtClose(hCmdThread); + } + + for (TextHook *man = ::hookman; man->RemoveHook(); man++); + //LARGE_INTEGER lint = {-10000, -1}; + while (::enter_count) + IthSleep(1); // jichi 9/28/2013: sleep for 1 ms + //NtDelayExecution(0, &lint); + for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++) + man->ClearHook(); + //if (ith_has_section) + NtUnmapViewOfSection(NtCurrentProcess(), ::hookman); + //else + // delete[] ::hookman; + NtClose(hSection); + NtClose(hMutex); + + delete ::tree; + IthCloseSystemService(); + NtClose(hmMutex); + NtClose(hDllExist); + //} ITH_EXCEPT {} + } break; + } + return TRUE; +} + +//extern "C" { +DWORD IHFAPI NewHook(const HookParam &hp, LPCWSTR name, DWORD flag) +{ + WCHAR str[128]; + int current = ::current_available - ::hookman; + if (current < MAX_HOOK) { + //flag &= 0xffff; + //if ((flag & HOOK_AUXILIARY) == 0) + flag |= HOOK_ADDITIONAL; + if (name == NULL || name[0] == '\0') + { + swprintf(str, L"UserHook%d", user_hook_count++); + } + else + { + wcscpy(str, name); + } + + ConsoleOutput("vnrcli:NewHook: try inserting hook"); + + // jichi 7/13/2014: This function would raise when too many hooks added + ::hookman[current].InitHook(hp, str, flag & 0xffff); + + if (::hookman[current].InsertHook() == 0) { + ConsoleOutput("vnrcli:NewHook: hook inserted"); + //ConsoleOutputW(name); + //swprintf(str,L"Insert address 0x%.8X.", hookman[current].Address()); + RequestRefreshProfile(); + } else + ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook"); + } + return 0; +} +DWORD IHFAPI RemoveHook(DWORD addr) +{ + for (int i = 0; i < MAX_HOOK; i++) + if (::hookman[i].Address ()== addr) { + ::hookman[i].ClearHook(); + return 0; + } + return 0; +} + +DWORD IHFAPI SwitchTrigger(DWORD t) +{ + trigger = t; + return 0; +} + +//} // extern "C" + + +namespace { // unnamed + +BOOL SafeFillRange(LPCWSTR dll, DWORD *lower, DWORD *upper) +{ + BOOL ret = FALSE; + ITH_WITH_SEH(ret = FillRange(dll, lower, upper)); + return ret; +} + +} // unnamed namespace + +// jichi 12/13/2013 +// Use listdlls from SystemInternals +void InitFilterTable() +{ + LPCWSTR l[] = { IHF_FILTER_DLL_LIST }; + enum { capacity = sizeof(l)/sizeof(*l) }; + + size_t count = 0; + //for (auto p : l) + for (size_t i = 0; i < capacity; i++) + if (SafeFillRange(l[i], &::filter[count].lower, &::filter[count].upper)) + count++; +} + +// EOF +/* + +static DWORD recv_esp, recv_addr; +static CONTEXT recover_context; +static __declspec(naked) void MySEH() +{ + __asm{ + mov eax, [esp+0xC] + mov edi,eax + mov ecx,0xB3 + mov esi, offset recover_context + rep movs + mov ecx, [recv_esp] + mov [eax+0xC4],ecx + mov edx, [recv_addr] + mov [eax+0xB8],edx + xor eax,eax + retn + } +} + +EXCEPTION_DISPOSITION ExceptHandler( + EXCEPTION_RECORD *ExceptionRecord, + void * EstablisherFrame, + CONTEXT *ContextRecord, + void * DispatcherContext ) +{ + ContextRecord->Esp=recv_esp; + ContextRecord->Eip=recv_addr; + return ExceptionContinueExecution; +} +int GuardRange(LPWSTR module, DWORD *a, DWORD *b) +{ + int flag=0; + __asm + { + mov eax,seh_recover + mov recv_addr,eax + push ExceptHandler + push fs:[0] + mov recv_esp,esp + mov fs:[0],esp + } + flag = FillRange(module, a, b); + __asm + { +seh_recover: + mov eax,[esp] + mov fs:[0],eax + add esp,8 + } + return flag; +} +*/ diff --git a/vnr/ith/hook/rpc/pipe.cc b/vnr/ith/hook/rpc/pipe.cc new file mode 100644 index 0000000..fbe7302 --- /dev/null +++ b/vnr/ith/hook/rpc/pipe.cc @@ -0,0 +1,346 @@ +// 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 "cli.h" +#include "engine/match.h" +#include "ith/common/defs.h" +//#include "ith/common/growl.h" +#include "ith/sys/sys.h" +#include "ccutil/ccmacro.h" + +//#include +//#include +WCHAR mutex[] = ITH_GRANTPIPE_MUTEX; +WCHAR exist[] = ITH_PIPEEXISTS_EVENT; +WCHAR detach_mutex[0x20]; +//WCHAR write_event[0x20]; +//WCHAR engine_event[0x20]; + +//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE"; +//WCHAR command[] = L"\\??\\pipe\\ITH_COMMAND"; +wchar_t recv_pipe[] = ITH_TEXT_PIPE; +wchar_t command[] = ITH_COMMAND_PIPE; + +LARGE_INTEGER wait_time = {-100*10000, -1}; +LARGE_INTEGER sleep_time = {-20*10000, -1}; + +DWORD engine_type; +DWORD module_base; + +//DWORD engine_base; +bool engine_registered; // 10/19/2014 jichi: disable engine dll + +HANDLE hPipe, + hCommand, + hDetach; //,hLose; +//InsertHookFun InsertHook; +//IdentifyEngineFun IdentifyEngine; +//InsertDynamicHookFun InsertDynamicHook; + +bool hook_inserted = false; + +// jichi 9/28/2013: protect pipe on wine +// Put the definition in this file so that it might be inlined +void CliUnlockPipe() +{ + if (IthIsWine()) + IthReleaseMutex(::hmMutex); +} + +void CliLockPipe() +{ + if (IthIsWine()) { + const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds + NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout); + } +} + +HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction) +{ + UNICODE_STRING us; + RtlInitUnicodeString(&us,name); + SECURITY_DESCRIPTOR sd = {1}; + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0}; + HANDLE hFile; + IO_STATUS_BLOCK isb; + if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0))) + return hFile; + else + return INVALID_HANDLE_VALUE; +} + +DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter) // Dynamically detect ITH main module status. +{ + CC_UNUSED(lpThreadParameter); + int i; + TextHook *man; + struct { + DWORD pid; + TextHook *man; + DWORD module; + //DWORD engine; + } u; + HANDLE hMutex, + hPipeExist; + //swprintf(engine_event,L"ITH_ENGINE_%d",current_process_id); + swprintf(detach_mutex, ITH_DETACH_MUTEX_ L"%d", current_process_id); + //swprintf(lose_event,L"ITH_LOSEPIPE_%d",current_process_id); + //hEngine=IthCreateEvent(engine_event); + //NtWaitForSingleObject(hEngine,0,0); + //NtClose(hEngine); + while (!engine_registered) + NtDelayExecution(0, &wait_time); + //LoadEngine(L"ITH_Engine.dll"); + u.module = module_base; + u.pid = current_process_id; + u.man = hookman; + //u.engine = engine_base; // jichi 10/19/2014: disable the second dll + hPipeExist = IthOpenEvent(exist); + IO_STATUS_BLOCK ios; + //hLose=IthCreateEvent(lose_event,0,0); + if (hPipeExist != INVALID_HANDLE_VALUE) + while (running) { + hPipe = INVALID_HANDLE_VALUE; + hCommand = INVALID_HANDLE_VALUE; + while (NtWaitForSingleObject(hPipeExist,0,&wait_time) == WAIT_TIMEOUT) + if (!running) + goto _release; + hMutex = IthCreateMutex(mutex,0); + NtWaitForSingleObject(hMutex,0,0); + while (hPipe == INVALID_HANDLE_VALUE|| + hCommand == INVALID_HANDLE_VALUE) { + NtDelayExecution(0, &sleep_time); + if (hPipe == INVALID_HANDLE_VALUE) + hPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE); + if (hCommand == INVALID_HANDLE_VALUE) + hCommand = IthOpenPipe(command, GENERIC_READ); + } + //NtClearEvent(hLose); + CliLockPipe(); + NtWriteFile(hPipe, 0, 0, 0, &ios, &u, sizeof(u), 0, 0); + CliUnlockPipe(); + live = true; + for (man = hookman, i = 0; i < current_hook; man++) + if (man->RecoverHook()) // jichi 9/27/2013: This is the place where built-in hooks like TextOutA are inserted + i++; + //ConsoleOutput(dll_name); + ConsoleOutput("vnrcli:WaitForPipe: pipe connected"); + //OutputDWORD(tree->Count()); + NtReleaseMutant(hMutex,0); + NtClose(hMutex); + if (!hook_inserted && engine_registered) { + hook_inserted = true; + Engine::IdentifyEngine(); + } + hDetach = IthCreateMutex(detach_mutex,1); + while (running && NtWaitForSingleObject(hPipeExist, 0, &sleep_time) == WAIT_OBJECT_0) + NtDelayExecution(0, &sleep_time); + live = false; + for (man = hookman, i = 0; i < current_hook; man++) + if (man->RemoveHook()) + i++; + if (!running) { + IthCoolDown(); // jichi 9/28/2013: Use cooldown instead of lock pipe to prevent from hanging on exit + //CliLockPipe(); + NtWriteFile(hPipe, 0, 0, 0, &ios, man, 4, 0, 0); + //CliUnlockPipe(); + IthReleaseMutex(hDetach); + } + NtClose(hDetach); + NtClose(hPipe); + } +_release: + //NtClose(hLose); + NtClose(hPipeExist); + return 0; +} +DWORD WINAPI CommandPipe(LPVOID lpThreadParameter) +{ + CC_UNUSED(lpThreadParameter); + DWORD command; + BYTE buff[0x400] = {}; + HANDLE hPipeExist; + hPipeExist = IthOpenEvent(exist); + IO_STATUS_BLOCK ios={}; + if (hPipeExist!=INVALID_HANDLE_VALUE) + while (running) { + while (!live) { + if (!running) + goto _detach; + NtDelayExecution(0, &sleep_time); + } + // jichi 9/27/2013: Why 0x200 not 0x400? wchar_t? + switch (NtReadFile(hCommand, 0, 0, 0, &ios, buff, 0x200, 0, 0)) { + case STATUS_PIPE_BROKEN: + case STATUS_PIPE_DISCONNECTED: + NtClearEvent(hPipeExist); + continue; + case STATUS_PENDING: + NtWaitForSingleObject(hCommand, 0, 0); + switch (ios.Status) { + case STATUS_PIPE_BROKEN: + case STATUS_PIPE_DISCONNECTED: + NtClearEvent(hPipeExist); + continue; + case 0: break; + default: + if (NtWaitForSingleObject(hDetach, 0, &wait_time) == WAIT_OBJECT_0) + goto _detach; + } + } + if (ios.uInformation && live) { + command = *(DWORD *)buff; + switch(command) { + case IHF_COMMAND_NEW_HOOK: + //IthBreak(); + buff[ios.uInformation] = 0; + buff[ios.uInformation + 1] = 0; + NewHook(*(HookParam *)(buff + 4), (LPWSTR)(buff + 4 + sizeof(HookParam)), 0); + break; + case IHF_COMMAND_REMOVE_HOOK: + { + DWORD rm_addr = *(DWORD *)(buff+4); + HANDLE hRemoved = IthOpenEvent(ITH_REMOVEHOOK_EVENT); + + TextHook *in = hookman; + for (int i = 0; i < current_hook; in++) { + if (in->Address()) i++; + if (in->Address() == rm_addr) break; + } + if (in->Address()) + in->ClearHook(); + IthSetEvent(hRemoved); + NtClose(hRemoved); + } break; + case IHF_COMMAND_MODIFY_HOOK: + { + DWORD rm_addr = *(DWORD *)(buff + 4); + HANDLE hModify = IthOpenEvent(ITH_MODIFYHOOK_EVENT); + TextHook *in = hookman; + for (int i = 0; i < current_hook; in++) { + if (in->Address()) + i++; + if (in->Address() == rm_addr) + break; + } + if (in->Address()) + in->ModifyHook(*(HookParam *)(buff + 4)); + IthSetEvent(hModify); + NtClose(hModify); + } break; + case IHF_COMMAND_DETACH: + running = false; + live = false; + goto _detach; + default: ; + } + } + } +_detach: + NtClose(hPipeExist); + NtClose(hCommand); + return 0; +} +//extern "C" { +void IHFAPI ConsoleOutput(LPCSTR text) +{ // jichi 12/25/2013: Rewrite the implementation + if (!live || !text) + return; + enum { buf_size = 0x50 }; + BYTE buf[buf_size]; // buffer is needed to append the message header + size_t text_size = strlen(text) + 1; + size_t data_size = text_size + 8; + + BYTE *data = (data_size <= buf_size) ? buf : new BYTE[data_size]; + *(DWORD *)data = IHF_NOTIFICATION; //cmd + *(DWORD *)(data + 4) = IHF_NOTIFICATION_TEXT; //console + memcpy(data + 8, text, text_size); + + IO_STATUS_BLOCK ios; + NtWriteFile(hPipe, 0, 0, 0, &ios, data, data_size, 0, 0); + if (data != buf) + delete[] data; +} + //if (str) { + // int t, len, sum; + // BYTE buffer[0x80]; + // BYTE *buff; + // len = wcslen(str) << 1; + // t = swprintf((LPWSTR)(buffer + 8),L"%d: ",current_process_id) << 1; + // sum = len + t + 8; + // if (sum > 0x80) { + // buff = new BYTE[sum]; + // memset(buff, 0, sum); // jichi 9/25/2013: zero memory + // memcpy(buff + 8, buffer + 8, t); + // } + // else + // buff = buffer; + // *(DWORD *)buff = IHF_NOTIFICATION; //cmd + // *(DWORD *)(buff + 4) = IHF_NOTIFICATION_TEXT; //console + // memcpy(buff + t + 8, str, len); + // IO_STATUS_BLOCK ios; + // NtWriteFile(hPipe,0,0,0,&ios,buff,sum,0,0); + // if (buff != buffer) + // delete[] buff; + // return len; + //} + +//DWORD IHFAPI OutputDWORD(DWORD d) +//{ +// WCHAR str[0x10]; +// swprintf(str,L"%.8X",d); +// ConsoleOutput(str); +// return 0; +//} +//DWORD IHFAPI OutputRegister(DWORD *base) +//{ +// WCHAR str[0x40]; +// swprintf(str,L"EAX:%.8X",base[0]); +// ConsoleOutput(str); +// swprintf(str,L"ECX:%.8X",base[-1]); +// ConsoleOutput(str); +// swprintf(str,L"EDX:%.8X",base[-2]); +// ConsoleOutput(str); +// swprintf(str,L"EBX:%.8X",base[-3]); +// ConsoleOutput(str); +// swprintf(str,L"ESP:%.8X",base[-4]); +// ConsoleOutput(str); +// swprintf(str,L"EBP:%.8X",base[-5]); +// ConsoleOutput(str); +// swprintf(str,L"ESI:%.8X",base[-6]); +// ConsoleOutput(str); +// swprintf(str,L"EDI:%.8X",base[-7]); +// ConsoleOutput(str); +// return 0; +//} +//DWORD IHFAPI RegisterEngineModule(DWORD idEngine, DWORD dnHook) +//{ +// ::IdentifyEngine = (IdentifyEngineFun)idEngine; +// ::InsertDynamicHook = (InsertDynamicHookFun)dnHook; +// ::engine_registered = true; +// return 0; +//} +DWORD IHFAPI NotifyHookInsert(DWORD addr) +{ + if (live) { + BYTE buffer[0x10]; + *(DWORD *)buffer = IHF_NOTIFICATION; + *(DWORD *)(buffer + 4) = IHF_NOTIFICATION_NEWHOOK; + *(DWORD *)(buffer + 8) = addr; + *(DWORD *)(buffer + 0xc) = 0; + IO_STATUS_BLOCK ios; + CliLockPipe(); + NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0); + CliUnlockPipe(); + } + return 0; +} +//} // extern "C" + +// EOF diff --git a/vnr/ith/hook/tree/avl.h b/vnr/ith/hook/tree/avl.h new file mode 100644 index 0000000..27bf222 --- /dev/null +++ b/vnr/ith/hook/tree/avl.h @@ -0,0 +1,590 @@ +#pragma once + +// avl.h +// 8/23/2013 jichi +// Branch: ITH/AVL.h, rev 133 +// 8/24/2013 TODO: Clean up this file + +#include "config.h" + +enum { STACK_SIZE = 32 }; + +//#ifndef ITH_STACK +//#define ITH_STACK + +template +class MyStack +{ + int index; + T s[stack_size]; + +public: + MyStack(): index(0) + { ITH_MEMSET_HEAP(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type + + T &back() { return s[index-1]; } + int size() { return index; } + + void push_back(const T &e) + { + if (index < stack_size) + s[index++]=e; + } + + void pop_back() { index--; } + + T &operator[](int i) { return s[i]; } +}; +//#endif // ITH_STACK + +// jichi 9/22/2013: T must be a pointer type which can be deleted +template +struct TreeNode +{ + //typedef TreeNode Self; + TreeNode() : + Left(nullptr), Right(nullptr), Parent(nullptr) + , rank(1) + , factor('\0'), reserve('\0') + //, key() + //, data() + { + ITH_MEMSET_HEAP(&key, 0, sizeof(key)); // jichi 9/26/2013: zero memory + ITH_MEMSET_HEAP(&data, 0, sizeof(data)); // jichi 9/26/2013: zero memory + } + + TreeNode(const T &k, const D &d) : + Left(nullptr), Right(nullptr), Parent(nullptr) + , rank(1) + , factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve + , key(k) + , data(d) + {} + + TreeNode *Successor() + { + TreeNode *Node, + *ParentNode; + Node = Right; + if (!Node) { + Node = this; + for (;;) { + ParentNode = Node->Parent; + if (!ParentNode) + return nullptr; + if (ParentNode->Left == Node) + break; + Node = ParentNode; + } + return ParentNode; + } + else + while (Node->Left) + Node = Node->Left; + return Node; + } + TreeNode *Predecessor() + { + TreeNode *Node, + *ParentNode; + Node = Left; + if (!Node) { + Node = this; + for(;;) { + ParentNode = Node->Parent; + if (!ParentNode) + return nullptr; + if (ParentNode->Right == Node) + break; + Node = ParentNode; + } + return ParentNode; + } + else + while (Node->Right) + Node = Node->Right; + return Node; + } + int height() + { + if (!this) // jichi 9/26/2013: what?! + return 0; + int l = Left->height(), + r = Right->height(), + f = factor; + if (l - r + f != 0) + __debugbreak(); + f = l > r ? l : r; + return f + 1; + } + TreeNode *Left, + *Right, + *Parent; + unsigned short rank; + char factor, + reserve; + T key; + D data; +}; + +template +struct NodePath +{ + NodePath() { memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH + NodePath(TreeNode *n, int f): Node(n), fact(f) {} + TreeNode *Node; + union { char factor; int fact; }; +}; + +template +class AVLTree +{ + fComp fCmp; + fCopy fCpy; + fLength fLen; + +protected: + TreeNode head; + +public: + // - Construction - + AVLTree() {} + + virtual ~AVLTree() { DeleteAll(); } + + // - Properties - + + TreeNode *TreeRoot() const { return head.Left; } + + // - Actions - + + void DeleteAll() + { + while (head.Left) + DeleteRoot(); + } + + TreeNode *Insert(const T *key, const D &data) + { + if (head.Left) { + MyStack *,STACK_SIZE> path; + TreeNode *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q + ParentNode = &head; + path.push_back(ParentNode); + char factor,f; + BalanceNode = DownNode = head.Left; + for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes. + factor = fCmp(key,DownNode->key); + if (factor == 0) + return DownNode; //Duplicate key. Return and do nothing. + TryNode = _FactorLink(DownNode, factor); + if (factor == -1) + path.push_back(DownNode); + if (TryNode) { //DownNode has a child. + if (TryNode->factor != 0) { //Keep track of unbalance node and its parent. + ParentNode = DownNode; + BalanceNode = TryNode; + } + DownNode = TryNode; + } + else + break; //Finished binary tree search; + } + while (path.size()) { + path.back()->rank++; + path.pop_back(); + } + size_t sz = fLen(key) + 1; + T *new_key = new T[sz]; + ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory + fCpy(new_key, key); + TryNode = new TreeNode(new_key, data); + _FactorLink(DownNode, factor) = TryNode; + TryNode->Parent = DownNode; + NewNode = TryNode; + //Finished binary tree insert. Next to do is to modify balance factors between + //BalanceNode and the new node. + TreeNode *ModifyNode; + factor = fCmp(key, BalanceNode->key); + //factor=keykey ? factor=-1:1; //Determine the balance factor at BalanceNode. + ModifyNode = DownNode = _FactorLink(BalanceNode,factor); + //ModifyNode will be the 1st child. + //DownNode will travel from here to the recent inserted node (TryNode). + while (DownNode != TryNode) { //Check if we reach the bottom. + f = fCmp(key,DownNode->key); + //f=_FactorCompare(key,DownNode->key); + DownNode->factor = f; + DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down. + } + //Finshed modifying balance factor. + //Next to do is check the tree if it's unbalance and recover balance. + if (BalanceNode->factor == 0) { //Tree has grown higher. + BalanceNode->factor = factor; + _IncreaseHeight(); //Modify balance factor and increase the height. + return NewNode; + } + if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced. + BalanceNode->factor = 0; //Set balance factor to 0. + return NewNode; + } + //Tree has gotten out of balance. + if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation. + DownNode = _SingleRotation(BalanceNode, ModifyNode, factor); + else //A node and its child has converse factor. Double rotation. + DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor); + //Finished the balancing work. Set child field to the root of the new child tree. + if (BalanceNode == ParentNode->Left) + ParentNode->Left = DownNode; + else + ParentNode->Right = DownNode; + return NewNode; + } + else { //root null? + size_t sz = fLen(key) + 1; + T *new_key = new T[sz]; + ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory + fCpy(new_key, key); + head.Left = new TreeNode(new_key, data); + head.rank++; + _IncreaseHeight(); + return head.Left; + } + } + bool Delete(T *key) + { + NodePath PathNode; + MyStack,STACK_SIZE> path; //Use to record a path to the destination node. + path.push_back(NodePath(&head,-1)); + TreeNode *TryNode,*ChildNode,*BalanceNode,*SuccNode; + TryNode=head.Left; + char factor; + for (;;) { //Search for the + if (TryNode == 0) + return false; //Not found. + factor = fCmp(key, TryNode->key); + if (factor == 0) + break; //Key found, continue to delete. + //factor = _FactorCompare( key, TryNode->key ); + path.push_back(NodePath(TryNode,factor)); + TryNode = _FactorLink(TryNode,factor); //Move to left. + } + SuccNode = TryNode->Right; //Find a successor. + factor = 1; + if (SuccNode == 0) { + SuccNode = TryNode->Left; + factor = -1; + } + path.push_back(NodePath(TryNode,factor)); + while (SuccNode) { + path.push_back(NodePath(SuccNode, -factor)); + SuccNode = _FactorLink(SuccNode,-factor); + } + PathNode = path.back(); + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array + TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor. + PathNode.Node->key = nullptr; + TryNode->data = PathNode.Node->data; + path.pop_back(); + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor); + delete PathNode.Node; //Remove the successor from the tree and release memory. + PathNode = path.back(); + for (int i=0; irank--; + for (;;) { //Rebalance the tree along the path back to the root. + if (path.size()==1) { + _DecreaseHeight(); + break; + } + BalanceNode = PathNode.Node; + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays. + BalanceNode->factor=-PathNode.factor; + break; + } + if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve. + BalanceNode->factor = 0; + path.pop_back(); + PathNode = path.back(); + continue; + } + //Node get out of balance. Here raises 3 cases. + ChildNode = _FactorLink(BalanceNode, -PathNode.factor); + if (ChildNode->factor == 0) { // New case different to insert operation. + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor ); + path.pop_back(); + PathNode = path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + break; + } + else { + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1. + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2. + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + } + path.pop_back(); //Recurse back along the path. + PathNode = path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + } + return true; + } + + D &operator [](T *key) + { return (Insert(key,D())->data); } + + TreeNode *Search(const T *key) + { + TreeNode *Find=head.Left; + char k; + while (Find != 0) {//&&Find->key!=key) + k=fCmp(key, Find->key); + if (k==0) break; + Find = _FactorLink(Find, k); + } + return Find; + } + + TreeNode *SearchIndex(unsigned int rank) + { + unsigned int r = head.rank; + if (rank == -1) + return 0; + if (++rank>=r) + return 0; + TreeNode *n=&head; + while (r!=rank) { + if (rank>r) { + n=n->Right; + rank-=r; + r=n->rank; + } else { + n=n->Left; + r=n->rank; + } + } + return n; + } + + TreeNode *Begin() + { + TreeNode *Node = head.Left; + if (Node) + while (Node->Left) Node = Node->Left; + return Node; + } + + TreeNode *End() + { + TreeNode *Node=head.Left; + if (Node) + while (Node->Right) Node = Node->Right; + return Node; + } + unsigned int Count() const { return head.rank - 1; } + + template + Fn TraverseTree(Fn &f) + { return TraverseTreeNode(head.Left,f); } + +protected: + bool DeleteRoot() + { + NodePath PathNode; + MyStack,STACK_SIZE> path; //Use to record a path to the destination node. + path.push_back(NodePath(&head,-1)); + TreeNode *TryNode,*ChildNode,*BalanceNode,*SuccNode; + TryNode=head.Left; + char factor; + SuccNode=TryNode->Right; //Find a successor. + factor=1; + if (SuccNode==0) + { + SuccNode=TryNode->Left; + factor=-1; + } + path.push_back(NodePath(TryNode,factor)); + while (SuccNode) { + path.push_back(NodePath(SuccNode,-factor)); + SuccNode=_FactorLink(SuccNode,-factor); + } + PathNode=path.back(); + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array + TryNode->key=PathNode.Node->key; //Replace key and data field with the successor. + PathNode.Node->key = nullptr; + TryNode->data=PathNode.Node->data; + path.pop_back(); + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor); + delete PathNode.Node; //Remove the successor from the tree and release memory. + PathNode=path.back(); + for (int i=0;irank--; + for (;;) { //Rebalance the tree along the path back to the root. + if (path.size() == 1) { + _DecreaseHeight(); + break; + } + + BalanceNode = PathNode.Node; + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed. + BalanceNode->factor=-PathNode.factor; + break; + } + if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse. + BalanceNode->factor=0; + path.pop_back(); + PathNode=path.back(); + continue; + } + //Node get out of balance. Here raises 3 cases. + ChildNode = _FactorLink(BalanceNode, -PathNode.factor); + if (ChildNode->factor == 0) { // New case different to insert operation. + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor ); + path.pop_back(); + PathNode=path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + break; + } else { + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1. + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2. + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + } + path.pop_back(); // Recurve back along the path. + PathNode=path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + } + return true; + } + template + Fn TraverseTreeNode(TreeNode *Node, Fn &f) + { + if (Node) { + if (Node->Left) + TraverseTreeNode(Node->Left,f); + f(Node); + if (Node->Right) + TraverseTreeNode(Node->Right,f); + } + return f; + } + TreeNode *_SingleRotation(TreeNode *BalanceNode, TreeNode *ModifyNode, char factor) + { + TreeNode *Node = _FactorLink(ModifyNode, -factor); + _FactorLink(BalanceNode, factor) = Node; + _FactorLink(ModifyNode, -factor) = BalanceNode; + if (Node) + Node->Parent = BalanceNode; + ModifyNode->Parent = BalanceNode->Parent; + BalanceNode->Parent = ModifyNode; + BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0. + if (factor == 1) + ModifyNode->rank += BalanceNode->rank; + else + BalanceNode->rank -= ModifyNode->rank; + return ModifyNode; + } + TreeNode *_SingleRotation2(TreeNode *BalanceNode, TreeNode *ModifyNode, char factor) + { + TreeNode *Node = _FactorLink(ModifyNode, -factor); + _FactorLink(BalanceNode, factor) = Node; + _FactorLink(ModifyNode, -factor) = BalanceNode; + if (Node) Node->Parent = BalanceNode; + ModifyNode->Parent = BalanceNode->Parent; + BalanceNode->Parent = ModifyNode; + ModifyNode->factor = -factor; + if (factor == 1) + ModifyNode->rank+=BalanceNode->rank; + else + BalanceNode->rank-=ModifyNode->rank; + return ModifyNode; + } + TreeNode *_DoubleRotation(TreeNode *BalanceNode, TreeNode *ModifyNode, char factor) + { + TreeNode *DownNode = _FactorLink(ModifyNode, -factor); + TreeNode *Node1, *Node2; + Node1 = _FactorLink(DownNode, factor); + Node2 = _FactorLink(DownNode, -factor); + _FactorLink(ModifyNode, -factor) = Node1; + _FactorLink(DownNode, factor) = ModifyNode; + _FactorLink(BalanceNode, factor) = Node2; + _FactorLink(DownNode, -factor) = BalanceNode; + if (Node1) + Node1->Parent = ModifyNode; + if (Node2) + Node2->Parent = BalanceNode; + DownNode->Parent = BalanceNode->Parent; + BalanceNode->Parent = DownNode; + ModifyNode->Parent = DownNode; + //Set factor according to the result. + if (DownNode->factor == factor) { + BalanceNode->factor = -factor; + ModifyNode->factor = 0; + } else if (DownNode->factor == 0) + BalanceNode->factor = ModifyNode->factor = 0; + else { + BalanceNode->factor = 0; + ModifyNode->factor = factor; + } + DownNode->factor = 0; + if (factor==1) { + ModifyNode->rank -= DownNode->rank; + DownNode->rank += BalanceNode->rank; + } else { + DownNode->rank += ModifyNode->rank; + BalanceNode->rank -= DownNode->rank; + } + return DownNode; + } + + TreeNode* &__fastcall _FactorLink(TreeNode *Node, char factor) + //Private helper method to retrieve child according to factor. + //Return right child if factor>0 and left child otherwise. + { return factor>0? Node->Right : Node->Left; } + + void Check() + { + unsigned int k = (unsigned int)head.Right; + unsigned int t = head.Left->height(); + if (k != t) + __debugbreak(); + } + + void _IncreaseHeight() + { + unsigned int k = (unsigned int)head.Right; + head.Right = (TreeNode*)++k; + } + + void _DecreaseHeight() + { + unsigned int k = (unsigned int)head.Right; + head.Right = (TreeNode*)--k; + } +}; + +struct SCMP +{ + char operator()(const char *s1,const char *s2) + { + int t = _stricmp(s1, s2); + return t == 0 ? 0 : t > 0 ? 1 :-1; + } +}; + +struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } }; +struct SLEN { int operator()(const char *str) { return strlen(str); } }; + +struct WCMP +{ + char operator()(const wchar_t *s1,const wchar_t *s2) + { + int t =_wcsicmp(s1, s2); + return t == 0 ? 0 : t > 0 ? 1 : -1; + } +}; + +struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } }; +struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } }; + +// EOF diff --git a/vnr/ith/hookxp/hookxp.pro b/vnr/ith/hookxp/hookxp.pro new file mode 100644 index 0000000..eac2cc0 --- /dev/null +++ b/vnr/ith/hookxp/hookxp.pro @@ -0,0 +1,59 @@ +# hookxp.pro +# 8/9/2013 jichi +# Build vnrhookxp.dll for Windows XP + +CONFIG += noeh # msvcrt on Windows XP does not has exception handler +include(../dllconfig.pri) +include(../sys/sys.pri) +include($$LIBDIR/disasm/disasm.pri) +include($$LIBDIR/memdbg/memdbg.pri) +include($$LIBDIR/ntinspect/ntinspect.pri) +include($$LIBDIR/winseh/winseh_safe.pri) +include($$LIBDIR/winversion/winversion.pri) + +VPATH += ../hook +INCLUDEPATH += ../hook + +# 9/27/2013: disable ITH this game engine, only for debugging purpose +#DEFINES += ITH_DISABLE_ENGINE + + +# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile +#DEFINES += ITH_WINE +#DEFINES += ITH_SYNC_PIPE + +## Libraries + +LIBS += -lkernel32 -luser32 -lgdi32 + +## Sources + +TEMPLATE = lib +TARGET = vnrhookxp + +#CONFIG += staticlib + +HEADERS += \ + config.h \ + cli.h \ + hook.h \ + engine/engine.h \ + engine/hookdefs.h \ + engine/match.h \ + engine/pchooks.h \ + engine/util.h \ + tree/avl.h + +SOURCES += \ + main.cc \ + rpc/pipe.cc \ + hijack/texthook.cc \ + engine/engine.cc \ + engine/match.cc \ + engine/pchooks.cc \ + engine/util.cc + +#RC_FILE += vnrhook.rc +#OTHER_FILES += vnrhook.rc + +# EOF diff --git a/vnr/ith/host/CMakeLists.txt b/vnr/ith/host/CMakeLists.txt new file mode 100644 index 0000000..42dbbec --- /dev/null +++ b/vnr/ith/host/CMakeLists.txt @@ -0,0 +1,66 @@ +# host.pro +# #CONFIG += eha # 3/1/2014: catchlng all exceptions will break pytexthook on Windows XP +# CONFIG += noeh # Needed by pytexthook ONLY on windows xp orz +# include(../dllconfig.pri) +# include(../sys/sys.pri) +# include($$LIBDIR/winmaker/winmaker.pri) +# include($$LIBDIR/winmutex/winmutex.pri) + +# config.pri +# CONFIG(noeh) { # No Exception handler +# message(CONFIG noeh) +# QMAKE_CXXFLAGS += /GR- +# QMAKE_CXXFLAGS_RTTI_ON -= /GR +# QMAKE_CXXFLAGS_STL_ON -= /EHsc +# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc +# CONFIG(dll) { +# QMAKE_LFLAGS += /ENTRY:"DllMain" +# } +# } + +set(vnrhost_src + avl_p.h + config.h + hookman.h + settings.h + srv.h + srv_p.h + textthread.h + textthread_p.h + SettingManager.h + hookman.cc + main.cc + pipe.cc + textthread.cc + ${PROJECT_SOURCE_DIR}/winmaker/winmaker.h + ${PROJECT_SOURCE_DIR}/winmaker/winmaker.cc + ${PROJECT_SOURCE_DIR}/winmutex/winmutex.h + ${common_src} +) + +source_group("common" FILES ${common_src}) + +add_library(vnrhost SHARED ${vnrhost_src}) + +set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS) + +target_compile_options(vnrhost PRIVATE + /GR- + $<$:> + $<$:> +) + +STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + +target_link_libraries(vnrhost + vnrsys + ${WDK_HOME}/lib/wxp/i386/ntdll.lib +) + +target_compile_definitions(vnrhost PRIVATE +) + +install(TARGETS vnrhost RUNTIME + DESTINATION . + CONFIGURATIONS Release +) diff --git a/vnr/ith/host/SettingManager.h b/vnr/ith/host/SettingManager.h new file mode 100644 index 0000000..6c933a3 --- /dev/null +++ b/vnr/ith/host/SettingManager.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) + * This file is part of the Interactive Text Hooker. + + * Interactive Text Hooker is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "config.h" +#include +#define SETTING_SPLIT_TIME 0 +#define SETTING_CYCLIC_REMOVE 1 +#define SETTING_REPEAT_COUNT 2 +#define SETTING_CLIPFLAG 3 +#define SETTING_MAX_INDEX 4 +class IHFSERVICE SettingManager +{ +public: + SettingManager() {memset(setting_int,0,sizeof(setting_int));} + ~SettingManager(){} + unsigned int SetValue(unsigned int index, unsigned int value) + { + if (index < SETTING_MAX_INDEX) + return (unsigned int)_InterlockedExchange((long*)setting_int+index,(long)value); + else return 0; + } + unsigned int GetValue(unsigned int index) + { + if (index < SETTING_MAX_INDEX) + return setting_int[index]; + else return 0; + } +private: + unsigned int setting_int[SETTING_MAX_INDEX]; + +}; \ No newline at end of file diff --git a/vnr/ith/host/avl_p.h b/vnr/ith/host/avl_p.h new file mode 100644 index 0000000..c701282 --- /dev/null +++ b/vnr/ith/host/avl_p.h @@ -0,0 +1,587 @@ +#pragma once +// avl_p.h +// 8/23/2013 jichi +// Branch: ITH/AVL.h, rev 133 + +#include "config.h" + +enum { STACK_SIZE = 32 }; + +//#ifndef ITH_STACK +//#define ITH_STACK + +template +class MyStack +{ + int index; + T s[stack_size]; + +public: + MyStack(): index(0) + { ITH_MEMSET_HEAP(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type + + T &back() { return s[index-1]; } + int size() { return index; } + + void push_back(const T &e) + { + if (index < stack_size) + s[index++]=e; + } + + void pop_back() { index--; } + + T &operator[](int i) { return s[i]; } +}; +//#endif // ITH_STACK + +// jichi 9/22/2013: T must be a pointer type which can be deleted +template +struct IHFSERVICE TreeNode +{ + //typedef TreeNode Self; + TreeNode() : + Left(nullptr), Right(nullptr), Parent(nullptr) + , rank(1) + , factor('\0'), reserve('\0') + //, key() + //, data() + { + ITH_MEMSET_HEAP(&key, 0, sizeof(key)); // jcihi 9/26/2013: zero memory + ITH_MEMSET_HEAP(&data, 0, sizeof(data)); // jcihi 9/26/2013: zero memory + } + + TreeNode(const T &k, const D &d) : + Left(nullptr), Right(nullptr), Parent(nullptr) + , rank(1) + , factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve + , key(k) + , data(d) + {} + + TreeNode *Successor() + { + TreeNode *Node, + *ParentNode; + Node = Right; + if (!Node) { + Node = this; + for (;;) { + ParentNode = Node->Parent; + if (!ParentNode) + return nullptr; + if (ParentNode->Left == Node) + break; + Node = ParentNode; + } + return ParentNode; + } + else + while (Node->Left) + Node = Node->Left; + return Node; + } + TreeNode *Predecessor() + { + TreeNode *Node, + *ParentNode; + Node = Left; + if (!Node) { + Node = this; + for(;;) { + ParentNode = Node->Parent; + if (!ParentNode) + return nullptr; + if (ParentNode->Right == Node) + break; + Node = ParentNode; + } + return ParentNode; + } + else + while (Node->Right) + Node = Node->Right; + return Node; + } + int height() + { + if (!this) // jichi 9/26/2013: what?! + return 0; + int l = Left->height(), + r = Right->height(), + f = factor; + if (l - r + f != 0) + __debugbreak(); + f = l > r ? l : r; + return f + 1; + } + TreeNode *Left, + *Right, + *Parent; + unsigned short rank; + char factor, + reserve; + T key; + D data; +}; + +template +struct NodePath +{ + NodePath() { memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH + NodePath(TreeNode *n, int f): Node(n), fact(f) {} + TreeNode *Node; + union { char factor; int fact; }; +}; + +template +class IHFSERVICE AVLTree +{ +protected: + TreeNode head; + fComp fCmp; + fCopy fCpy; + fLength fLen; + +public: + // - Construction - + AVLTree() {} + + virtual ~AVLTree() { DeleteAll(); } + + // - Properties - + + TreeNode *TreeRoot() const { return head.Left; } + + // - Actions - + + void DeleteAll() + { + while (head.Left) + DeleteRoot(); + } + + TreeNode *Insert(const T *key, const D &data) + { + if (head.Left) { + MyStack *,STACK_SIZE> path; + TreeNode *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q + ParentNode = &head; + path.push_back(ParentNode); + char factor,f; + BalanceNode = DownNode = head.Left; + for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes. + factor = fCmp(key,DownNode->key); + if (factor == 0) + return DownNode; //Duplicate key. Return and do nothing. + TryNode = _FactorLink(DownNode, factor); + if (factor == -1) + path.push_back(DownNode); + if (TryNode) { //DownNode has a child. + if (TryNode->factor != 0) { //Keep track of unbalance node and its parent. + ParentNode = DownNode; + BalanceNode = TryNode; + } + DownNode = TryNode; + } + else + break; //Finished binary tree search; + } + while (path.size()) { + path.back()->rank++; + path.pop_back(); + } + size_t sz = fLen(key) + 1; + T *new_key = new T[sz]; + ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory + fCpy(new_key, key); + TryNode = new TreeNode(new_key, data); + _FactorLink(DownNode, factor) = TryNode; + TryNode->Parent = DownNode; + NewNode = TryNode; + //Finished binary tree insert. Next to do is to modify balance factors between + //BalanceNode and the new node. + TreeNode *ModifyNode; + factor = fCmp(key, BalanceNode->key); + //factor=keykey ? factor=-1:1; //Determine the balance factor at BalanceNode. + ModifyNode = DownNode = _FactorLink(BalanceNode,factor); + //ModifyNode will be the 1st child. + //DownNode will travel from here to the recent inserted node (TryNode). + while (DownNode != TryNode) { //Check if we reach the bottom. + f = fCmp(key,DownNode->key); + //f=_FactorCompare(key,DownNode->key); + DownNode->factor = f; + DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down. + } + //Finshed modifying balance factor. + //Next to do is check the tree if it's unbalance and recover balance. + if (BalanceNode->factor == 0) { //Tree has grown higher. + BalanceNode->factor = factor; + _IncreaseHeight(); //Modify balance factor and increase the height. + return NewNode; + } + if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced. + BalanceNode->factor = 0; //Set balance factor to 0. + return NewNode; + } + //Tree has gotten out of balance. + if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation. + DownNode = _SingleRotation(BalanceNode, ModifyNode, factor); + else //A node and its child has converse factor. Double rotation. + DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor); + //Finished the balancing work. Set child field to the root of the new child tree. + if (BalanceNode == ParentNode->Left) + ParentNode->Left = DownNode; + else + ParentNode->Right = DownNode; + return NewNode; + } + else { //root null? + size_t sz = fLen(key) + 1; + T *new_key = new T[sz]; + ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory + fCpy(new_key, key); + head.Left = new TreeNode(new_key, data); + head.rank++; + _IncreaseHeight(); + return head.Left; + } + } + bool Delete(T *key) + { + NodePath PathNode; + MyStack,STACK_SIZE> path; //Use to record a path to the destination node. + path.push_back(NodePath(&head,-1)); + TreeNode *TryNode,*ChildNode,*BalanceNode,*SuccNode; + TryNode=head.Left; + char factor; + for (;;) { //Search for the + if (TryNode == 0) + return false; //Not found. + factor = fCmp(key, TryNode->key); + if (factor == 0) + break; //Key found, continue to delete. + //factor = _FactorCompare( key, TryNode->key ); + path.push_back(NodePath(TryNode,factor)); + TryNode = _FactorLink(TryNode,factor); //Move to left. + } + SuccNode = TryNode->Right; //Find a successor. + factor = 1; + if (SuccNode == 0) { + SuccNode = TryNode->Left; + factor = -1; + } + path.push_back(NodePath(TryNode,factor)); + while (SuccNode) { + path.push_back(NodePath(SuccNode, -factor)); + SuccNode = _FactorLink(SuccNode,-factor); + } + PathNode = path.back(); + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array + TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor. + PathNode.Node->key = nullptr; + TryNode->data = PathNode.Node->data; + path.pop_back(); + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor); + delete PathNode.Node; //Remove the successor from the tree and release memory. + PathNode = path.back(); + for (int i=0; irank--; + for (;;) { //Rebalance the tree along the path back to the root. + if (path.size()==1) { + _DecreaseHeight(); + break; + } + BalanceNode = PathNode.Node; + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays. + BalanceNode->factor=-PathNode.factor; + break; + } + if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve. + BalanceNode->factor = 0; + path.pop_back(); + PathNode = path.back(); + continue; + } + //Node get out of balance. Here raises 3 cases. + ChildNode = _FactorLink(BalanceNode, -PathNode.factor); + if (ChildNode->factor == 0) { // New case different to insert operation. + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor ); + path.pop_back(); + PathNode = path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + break; + } + else { + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1. + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2. + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + } + path.pop_back(); //Recurse back along the path. + PathNode = path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + } + return true; + } + + D &operator [](T *key) + { return (Insert(key,D())->data); } + + TreeNode *Search(const T *key) + { + TreeNode *Find=head.Left; + char k; + while (Find != 0) {//&&Find->key!=key) + k = fCmp(key, Find->key); + if (k == 0) break; + Find = _FactorLink(Find, k); + } + return Find; + } + + TreeNode *SearchIndex(unsigned int rank) + { + unsigned int r = head.rank; + if (rank == -1) + return 0; + if (++rank>=r) + return 0; + TreeNode *n=&head; + while (r!=rank) { + if (rank>r) { + n=n->Right; + rank-=r; + r=n->rank; + } else { + n=n->Left; + r=n->rank; + } + } + return n; + } + + TreeNode *Begin() + { + TreeNode *Node = head.Left; + if (Node) + while (Node->Left) Node = Node->Left; + return Node; + } + + TreeNode *End() + { + TreeNode *Node=head.Left; + if (Node) + while (Node->Right) Node = Node->Right; + return Node; + } + unsigned int Count() const { return head.rank - 1; } + + template + Fn TraverseTree(Fn &f) + { return TraverseTreeNode(head.Left,f); } + +protected: + bool DeleteRoot() + { + NodePath PathNode; + MyStack,STACK_SIZE> path; //Use to record a path to the destination node. + path.push_back(NodePath(&head,-1)); + TreeNode *TryNode,*ChildNode,*BalanceNode,*SuccNode; + TryNode=head.Left; + char factor; + SuccNode=TryNode->Right; //Find a successor. + factor=1; + if (SuccNode==0) + { + SuccNode=TryNode->Left; + factor=-1; + } + path.push_back(NodePath(TryNode,factor)); + while (SuccNode) { + path.push_back(NodePath(SuccNode,-factor)); + SuccNode=_FactorLink(SuccNode,-factor); + } + PathNode=path.back(); + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array + TryNode->key=PathNode.Node->key; //Replace key and data field with the successor. + PathNode.Node->key = nullptr; + TryNode->data=PathNode.Node->data; + path.pop_back(); + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor); + delete PathNode.Node; //Remove the successor from the tree and release memory. + PathNode=path.back(); + for (int i=0;irank--; + for (;;) { //Rebalance the tree along the path back to the root. + if (path.size() == 1) { + _DecreaseHeight(); + break; + } + + BalanceNode = PathNode.Node; + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed. + BalanceNode->factor=-PathNode.factor; + break; + } + if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse. + BalanceNode->factor=0; + path.pop_back(); + PathNode=path.back(); + continue; + } + //Node get out of balance. Here raises 3 cases. + ChildNode = _FactorLink(BalanceNode, -PathNode.factor); + if (ChildNode->factor == 0) { // New case different to insert operation. + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor ); + path.pop_back(); + PathNode=path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + break; + } else { + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1. + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2. + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor ); + } + path.pop_back(); // Recurve back along the path. + PathNode=path.back(); + _FactorLink(PathNode.Node, PathNode.factor) = TryNode; + } + return true; + } + template + Fn TraverseTreeNode(TreeNode *Node, Fn &f) + { + if (Node) { + if (Node->Left) + TraverseTreeNode(Node->Left,f); + f(Node); + if (Node->Right) + TraverseTreeNode(Node->Right,f); + } + return f; + } + TreeNode *_SingleRotation(TreeNode *BalanceNode, TreeNode *ModifyNode, char factor) + { + TreeNode *Node = _FactorLink(ModifyNode, -factor); + _FactorLink(BalanceNode, factor) = Node; + _FactorLink(ModifyNode, -factor) = BalanceNode; + if (Node) + Node->Parent = BalanceNode; + ModifyNode->Parent = BalanceNode->Parent; + BalanceNode->Parent = ModifyNode; + BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0. + if (factor == 1) + ModifyNode->rank += BalanceNode->rank; + else + BalanceNode->rank -= ModifyNode->rank; + return ModifyNode; + } + TreeNode *_SingleRotation2(TreeNode *BalanceNode, TreeNode *ModifyNode, char factor) + { + TreeNode *Node = _FactorLink(ModifyNode, -factor); + _FactorLink(BalanceNode, factor) = Node; + _FactorLink(ModifyNode, -factor) = BalanceNode; + if (Node) Node->Parent = BalanceNode; + ModifyNode->Parent = BalanceNode->Parent; + BalanceNode->Parent = ModifyNode; + ModifyNode->factor = -factor; + if (factor == 1) + ModifyNode->rank+=BalanceNode->rank; + else + BalanceNode->rank-=ModifyNode->rank; + return ModifyNode; + } + TreeNode *_DoubleRotation(TreeNode *BalanceNode, TreeNode *ModifyNode, char factor) + { + TreeNode *DownNode = _FactorLink(ModifyNode, -factor); + TreeNode *Node1, *Node2; + Node1 = _FactorLink(DownNode, factor); + Node2 = _FactorLink(DownNode, -factor); + _FactorLink(ModifyNode, -factor) = Node1; + _FactorLink(DownNode, factor) = ModifyNode; + _FactorLink(BalanceNode, factor) = Node2; + _FactorLink(DownNode, -factor) = BalanceNode; + if (Node1) + Node1->Parent = ModifyNode; + if (Node2) + Node2->Parent = BalanceNode; + DownNode->Parent = BalanceNode->Parent; + BalanceNode->Parent = DownNode; + ModifyNode->Parent = DownNode; + //Set factor according to the result. + if (DownNode->factor == factor) { + BalanceNode->factor = -factor; + ModifyNode->factor = 0; + } else if (DownNode->factor == 0) + BalanceNode->factor = ModifyNode->factor = 0; + else { + BalanceNode->factor = 0; + ModifyNode->factor = factor; + } + DownNode->factor = 0; + if (factor==1) { + ModifyNode->rank -= DownNode->rank; + DownNode->rank += BalanceNode->rank; + } else { + DownNode->rank += ModifyNode->rank; + BalanceNode->rank -= DownNode->rank; + } + return DownNode; + } + + TreeNode* &__fastcall _FactorLink(TreeNode *Node, char factor) + //Private helper method to retrieve child according to factor. + //Return right child if factor>0 and left child otherwise. + { return factor>0? Node->Right : Node->Left; } + + void Check() + { + unsigned int k = (unsigned int)head.Right; + unsigned int t = head.Left->height(); + if (k != t) + __debugbreak(); + } + + void _IncreaseHeight() + { + unsigned int k = (unsigned int)head.Right; + head.Right = (TreeNode*)++k; + } + + void _DecreaseHeight() + { + unsigned int k = (unsigned int)head.Right; + head.Right = (TreeNode*)--k; + } +}; + +struct SCMP +{ + char operator()(const char *s1,const char *s2) + { + int t = _stricmp(s1, s2); + return t == 0 ? 0 : t > 0 ? 1 :-1; + } +}; + +struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } }; +struct SLEN { int operator()(const char *str) { return strlen(str); } }; + +struct WCMP +{ + char operator()(const wchar_t *s1,const wchar_t *s2) + { + int t =_wcsicmp(s1, s2); + return t == 0 ? 0 : t > 0 ? 1 : -1; + } +}; + +struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } }; +struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } }; + +// EOF diff --git a/vnr/ith/host/config.h b/vnr/ith/host/config.h new file mode 100644 index 0000000..394cba7 --- /dev/null +++ b/vnr/ith/host/config.h @@ -0,0 +1,16 @@ +#pragma once + +// config.h +// 8/23/2013 jichi +// The first header file that are included by all source files. + +#define IHF // for dll import +#include "ith/dllconfig.h" +#define IHFAPI __stdcall +#ifdef IHF +# define IHFSERVICE __declspec(dllexport) +#else +# define IHFSERVICE __declspec(dllimport) +#endif + +// EOF diff --git a/vnr/ith/host/hookman.cc b/vnr/ith/host/hookman.cc new file mode 100644 index 0000000..86c8ddd --- /dev/null +++ b/vnr/ith/host/hookman.cc @@ -0,0 +1,945 @@ +// hookman.cc +// 8/24/2013 jichi +// Branch IHF/HookManager.cpp, rev 133 +// 8/24/2013 TODO: Clean up this file + +#ifdef _MSC_VER +# pragma warning (disable:4100) // C4100: unreference formal parameter +# pragma warning (disable:4146) // C4146: unary minus operator applied to unsigned type +#endif // _MSC_VER + +#include "hookman.h" +#include "ith/common/const.h" +#include "ith/common/defs.h" +#include "ith/common/types.h" +//#include "ith/common/growl.h" +#include "ith/sys/sys.h" +//#include + +namespace { // unnamed +//enum { MAX_ENTRY = 0x40 }; + +#define HM_LOCK win_mutex_lock d_locker(hmcs) // Synchronized scope for accessing private data +// jichi 9/23/2013: wine deficenciy on mapping sections +// Whe set to false, do not map sections. +//bool ith_has_section = true; + +// jichi 9/28/2013: Remove ConsoleOutput from available hooks +//LPWSTR HookNameInitTable[]={ L"ConsoleOutput" , HOOK_FUN_NAME_LIST }; +//LPCWSTR HookNameInitTable[] = {HOOK_FUN_NAME_LIST}; +//LPVOID DefaultHookAddr[HOOK_FUN_COUNT]; + +//BYTE null_buffer[4]={0,0,0,0}; +//BYTE static_small_buffer[0x100]; +//DWORD zeros[4]={0,0,0,0}; +//WCHAR user_entry[0x40]; + +bool GetProcessPath(HANDLE hProc, __out LPWSTR path) +{ + PROCESS_BASIC_INFORMATION info; + LDR_DATA_TABLE_ENTRY entry; + PEB_LDR_DATA ldr; + PEB peb; + if (NT_SUCCESS(NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), 0))) + if (info.PebBaseAddress) + if (NT_SUCCESS(NtReadVirtualMemory(hProc, info.PebBaseAddress, &peb,sizeof(peb), 0))) + if (NT_SUCCESS(NtReadVirtualMemory(hProc, peb.Ldr, &ldr, sizeof(ldr), 0))) + if (NT_SUCCESS(NtReadVirtualMemory(hProc, (LPVOID)ldr.InLoadOrderModuleList.Flink, + &entry, sizeof(LDR_DATA_TABLE_ENTRY), 0))) + if (NT_SUCCESS(NtReadVirtualMemory(hProc, entry.FullDllName.Buffer, + path, MAX_PATH * 2, 0))) + return true; + path = L""; + return false; +} + +bool GetProcessPath(DWORD pid, __out LPWSTR path) +{ + CLIENT_ID id; + OBJECT_ATTRIBUTES oa = {}; + HANDLE hProc; + id.UniqueProcess = pid; + id.UniqueThread = 0; + oa.uLength = sizeof(oa); + if (NT_SUCCESS(NtOpenProcess(&hProc , PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, &oa, &id))) { + bool flag = GetProcessPath(hProc, path); + NtClose(hProc); + return flag; + } + path = L""; + return false; +} + +} // unnamed namespace + +HookManager *man; // jichi 9/22/2013: initialized in main +//BitMap* pid_map; +DWORD clipboard_flag, + split_time, + repeat_count, + global_filter, + cyclic_remove; + +DWORD GetHookName(LPWSTR str, DWORD pid, DWORD hook_addr, DWORD max) +{ + if (!pid) + return 0; + + DWORD len = 0; + max--; //for '\0' magic marker. + + //if (pid == 0) { + // len = wcslen(HookNameInitTable[0]); + // if (len >= max) + // len = max; + // memcpy(str, HookNameInitTable[0], len << 1); + // str[len] = 0; + // return len; + //} + + //::man->LockProcessHookman(pid); + ProcessRecord *pr = ::man->GetProcessRecord(pid); + if (!pr) + return 0; + NtWaitForSingleObject(pr->hookman_mutex, 0, 0); + Hook *hks = (Hook *)pr->hookman_map; + for (int i = 0; i < MAX_HOOK; i++) + if (hks[i].Address() == hook_addr) { + len = hks[i].NameLength(); + if (len >= max) + len = max; + NtReadVirtualMemory(pr->process_handle, hks[i].Name(), str, len << 1, &len); + if (str[len - 1] == 0) + len--; + else + str[len] = 0; + break; + } + + // jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli + //Hook *h = (Hook *)hks; + //for (int i = 0; i < MAX_HOOK; i++) + // if (!h[i].hook_name) + // break; + // else { + // const Hook &hi = h[i]; + // wchar_t buffer[1000]; + // DWORD len = hi.NameLength(); + // NtReadVirtualMemory(pr->process_handle, hi.hook_name, buffer, len << 1, &len); + // buffer[len] = 0; + // ITH_MSG(buffer); + // } + + NtReleaseMutant(pr->hookman_mutex, 0); + //::man->UnlockProcessHookman(pid); + return len; +} + +int GetHookNameByIndex(LPWSTR str, DWORD pid, DWORD index) +{ + if (!pid) + return 0; + + //if (pid == 0) { + // wcscpy(str, HookNameInitTable[0]); + // return wcslen(HookNameInitTable[0]); + //} + DWORD len = 0; + //::man->LockProcessHookman(pid); + ProcessRecord *pr = ::man->GetProcessRecord(pid); + if (!pr) + return 0; + //NtWaitForSingleObject(pr->hookman_mutex,0,0); //already locked + Hook *hks = (Hook *)pr->hookman_map; + if (hks[index].Address()) { + NtReadVirtualMemory(pr->process_handle, hks[index].Name(), str, hks[index].NameLength() << 1, &len); + len = hks[index].NameLength(); + } + //NtReleaseMutant(pr->hookman_mutex,0); + return len; +} + +//int GetHookString(LPWSTR str, DWORD pid, DWORD hook_addr, DWORD status) +//{ +// LPWSTR begin=str; +// str+=swprintf(str,L"%4d:0x%08X:",pid,hook_addr); +// str+=GetHookName(str,pid,hook_addr); +// return str-begin; +//} + + +void ThreadTable::SetThread(DWORD num, TextThread *ptr) +{ + int number = num; + if (number >= size) { + while (number >= size) + size <<= 1; + TextThread **temp; + //if (size < 0x10000) { + temp = new TextThread*[size]; + if (size > used) + ITH_MEMSET_HEAP(temp, 0, (size - used) * sizeof(TextThread *)); // jichi 9/21/2013: zero memory + memcpy(temp, storage, used * sizeof(TextThread *)); + //} + delete[] storage; + storage = temp; + } + storage[number] = ptr; + if (ptr == nullptr) { + if (number == used - 1) + while (storage[used - 1] == 0) + used--; + } else if (number >= used) + used = number + 1; +} + +TextThread *ThreadTable::FindThread(DWORD number) +{ return number <= (DWORD)used ? storage[number] : nullptr; } + +static const char sse_table_eq[0x100]={ + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + -1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + -1,-1,-1,-1, -1,-1,-1,-1, 1,1,1,1, 1,1,1,1, //7, compare 4 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + -1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1 + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 //f, equal +}; +char original_cmp(const ThreadParameter *t1, const ThreadParameter *t2) +{ + //Q_ASSERT(t1 && t2); + int t = t1->pid - t2->pid; + if (t == 0) { + t = t1->hook - t2->hook; + if (t == 0) { + t = t1->retn - t2->retn; + if (t == 0) { + t = t1->spl-t2->spl; + if (t == 0) return 0; + return t1->spl > t2->spl ? 1 : -1; + } + else return t1->retn > t2->retn ? 1 : -1; + } + else return t1->hook > t2->hook ? 1: -1; + } + else return t1->pid > t2->pid ? 1 : -1; + //return t>0?1:-1; +} +char TCmp::operator()(const ThreadParameter* t1, const ThreadParameter* t2) + //SSE speed up. Compare four integers in const time without branching. + //The AVL tree branching operation needs 2 bit of information. + //One bit for equality and one bit for "less than" or "greater than". + +{ + union{__m128 m0;__m128i i0;}; + union{__m128 m1;__m128i i1;}; + union{__m128 m2;__m128i i2;}; + int k0,k1; + i1 = _mm_loadu_si128((const __m128i*)t1); + i2 = _mm_loadu_si128((const __m128i*)t2); + i0 = _mm_cmpgt_epi32(i1,i2); + k0 = _mm_movemask_ps(m0); + i1 = _mm_cmpeq_epi32(i1,i2); + k1 = _mm_movemask_ps(m1); + return sse_table_eq[k1*16+k0]; +} +void TCpy::operator()(ThreadParameter* t1, const ThreadParameter* t2) +{ memcpy(t1,t2,sizeof(ThreadParameter)); } + +int TLen::operator()(const ThreadParameter* t) { return 0; } + +#define NAMED_PIPE_DISCONNECT 1 +//Class member of HookManger +HookManager::HookManager() : + // jichi 9/21/2013: Zero memory + //CRITICAL_SECTION hmcs; + current(nullptr) + , console(nullptr) + , wconsole(nullptr) + , create(nullptr) + , remove(nullptr) + , reset(nullptr) + , attach(nullptr) + , detach(nullptr) + , hook(nullptr) + , current_pid(0) + , thread_table(nullptr) + , destroy_event(nullptr) + , register_count(0) + , new_thread_number(0) +{ + // jichi 9/21/2013: zero memory + ITH_MEMSET_HEAP(record, 0, sizeof(record)); + ITH_MEMSET_HEAP(text_pipes, 0, sizeof(text_pipes)); + ITH_MEMSET_HEAP(cmd_pipes, 0, sizeof(cmd_pipes)); + ITH_MEMSET_HEAP(recv_threads, 0, sizeof(recv_threads)); + + head.key = new ThreadParameter; + head.key->pid = 0; + head.key->hook = -1; + head.key->retn = -1; + head.key->spl = -1; + head.data = 0; + thread_table = new ThreadTable; // jichi 9/26/2013: zero memory in ThreadTable + + TextThread *entry = new TextThread(0, -1,-1,-1, new_thread_number++); // jichi 9/26/2013: zero memory in TextThread + thread_table->SetThread(0, entry); + SetCurrent(entry); + entry->Status() |= USING_UNICODE; + //texts->SetUnicode(true); + //entry->AddToCombo(); + //entry->ComboSelectCurrent(); + + //if (background==0) entry->AddToStore((BYTE*)BackgroundMsg,wcslen(BackgroundMsg)<<1,0,1); + + //InitializeCriticalSection(&hmcs); + destroy_event = IthCreateEvent(0, 0, 0); +} + +HookManager::~HookManager() +{ + //LARGE_INTEGER timeout={-1000*1000,-1}; + //IthBreak(); + NtWaitForSingleObject(destroy_event, 0, 0); + NtClose(destroy_event); + NtClose(cmd_pipes[0]); + NtClose(recv_threads[0]); + delete thread_table; + delete head.key; + //DeleteCriticalSection(&hmcs); +} + +TextThread *HookManager::FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split) +{ + if (pid == 0) + return thread_table->FindThread(0); + ThreadParameter tp = {pid, hook, retn, split}; + TreeNode *node = Search(&tp); + return node ? thread_table->FindThread(node->data) : nullptr; +} + +TextThread *HookManager::FindSingle(DWORD number) +{ return (number & 0x80008000) ? nullptr : thread_table->FindThread(number); } + +void HookManager::DetachProcess(DWORD pid) {} + +void HookManager::SetCurrent(TextThread *it) +{ + if (current) + current->Status() &= ~CURRENT_SELECT; + current = it; + if (it) + it->Status() |= CURRENT_SELECT; +} +void HookManager::SelectCurrent(DWORD num) +{ + if (TextThread *st = FindSingle(num)) { + SetCurrent(st); + if (reset) + reset(st); + //st->ResetEditText(); + } +} +void HookManager::RemoveSingleHook(DWORD pid, DWORD addr) +{ + HM_LOCK; + //ConsoleOutput("vnrhost:RemoveSingleHook: lock"); + //EnterCriticalSection(&hmcs); + DWORD max = thread_table->Used(); + bool flag = false; + for (DWORD i = 1; i <= max; i++) + if (TextThread *it = thread_table->FindThread(i)) + if (it->PID() == pid && it->Addr() == addr) { + flag |= (it == current); + //flag|=it->RemoveFromCombo(); + thread_table->SetThread(i, 0); + if (it->Number() < new_thread_number) + new_thread_number = it->Number(); + Delete(it->GetThreadParameter()); + if (remove) + remove(it); + delete it; + } + + for (DWORD i = 0; i <= max; i++) + if (TextThread *it = thread_table->FindThread(i)) + if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) { + it->LinkNumber() = -1; + it->Link() = nullptr; + } + + if (flag) { + current = nullptr; + DWORD number = head.Left ? head.Left->data : 0; + SetCurrent(thread_table->FindThread(number)); + if (reset && current) + reset(current); + //it->ResetEditText(); + } + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:RemoveSingleHook: unlock"); +} +void HookManager::RemoveSingleThread(DWORD number) +{ + if (number == 0) + return; + HM_LOCK; + //ConsoleOutput("vnrhost:RemoveSingleThread: lock"); + //EnterCriticalSection(&hmcs); + if (TextThread *it = thread_table->FindThread(number)) { + thread_table->SetThread(number, 0); + Delete(it->GetThreadParameter()); + if (remove) + remove(it); + bool flag = (it == current); + if (it->Number() < new_thread_number) + new_thread_number = it->Number(); + delete it; + for (int i = 0; i <= thread_table->Used(); i++) + if (TextThread *t = thread_table->FindThread(i)) + if (t->LinkNumber() == number) { + t->Link() = 0; + t->LinkNumber() = -1; + } + + if (flag) { + current = nullptr; + number = head.Left ? head.Left->data : 0; + SetCurrent(thread_table->FindThread(number)); + if (reset && current) + reset(current); + //it->ResetEditText(); + } + } + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:RemoveSingleThread: unlock"); +} + +void HookManager::RemoveProcessContext(DWORD pid) +{ + HM_LOCK; + bool flag = false; + //ConsoleOutput("vnrhost:RemoveProcessContext: lock"); + //EnterCriticalSection(&hmcs); + for (int i = 1; i < thread_table->Used(); i++) + if (TextThread *it = thread_table->FindThread(i)) + if (it->PID() == pid) { + Delete(it->GetThreadParameter()); + //if (false == Delete(it->GetThreadParameter())) { + // // jichi 11/26/2013: Remove debugging instructions + // //if (debug) + // // __asm int 3 + //} + flag |= (it == current); + //flag|=it->RemoveFromCombo(); + if (it->Number() Number(); + thread_table->SetThread(i,0); + if (remove) + remove(it); + delete it; + } + + for (int i = 0; i < thread_table->Used(); i++) + if (TextThread *it=thread_table->FindThread(i)) + if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) { + it->LinkNumber()=-1; + it->Link() = nullptr; + } + + if (flag) { + current = nullptr; + DWORD number = head.Left ? head.Left->data : 0; + SetCurrent(thread_table->FindThread(number)); + if (reset && current) + reset(current); + //if (it) it->ResetEditText(); + } + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:RemoveProcessContext: unlock"); +} +void HookManager::RegisterThread(TextThread* it, DWORD num) +{ thread_table->SetThread(num, it); } + +void HookManager::RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread) +{ + text_pipes[register_count] = text; + cmd_pipes[register_count] = cmd; + recv_threads[register_count] = thread; + register_count++; + if (register_count == 1) + NtSetEvent(destroy_event, 0); + else + NtClearEvent(destroy_event); +} +void HookManager::RegisterProcess(DWORD pid, DWORD hookman, DWORD module) +{ + HM_LOCK; + wchar_t str[0x40], + path[MAX_PATH]; + //pid_map->Set(pid>>2); + //ConsoleOutput("vnrhost:RegisterProcess: lock"); + //EnterCriticalSection(&hmcs); + record[register_count - 1].pid_register = pid; + record[register_count - 1].hookman_register = hookman; + record[register_count - 1].module_register = module; + //record[register_count - 1].engine_register = engine; + swprintf(str, ITH_SECTION_ L"%d", pid); + HANDLE hSection = IthCreateSection(str, HOOK_SECTION_SIZE, PAGE_READONLY); + LPVOID map = nullptr; + //DWORD map_size = 0x1000; + DWORD map_size = HOOK_SECTION_SIZE / 2; // jichi 1/16/2015: Changed to half to hook section size + //if (::ith_has_section) + NtMapViewOfSection(hSection, NtCurrentProcess(), + &map, 0, map_size, 0, &map_size, ViewUnmap, 0, + PAGE_READONLY); + + record[register_count - 1].hookman_section = hSection; + record[register_count - 1].hookman_map = map; + + HANDLE hProc; + CLIENT_ID id; + id.UniqueProcess = pid; + id.UniqueThread = 0; + OBJECT_ATTRIBUTES oa = {}; + oa.uLength = sizeof(oa); + if (NT_SUCCESS(NtOpenProcess(&hProc, + PROCESS_QUERY_INFORMATION| + PROCESS_CREATE_THREAD| + PROCESS_VM_READ| + PROCESS_VM_WRITE| + PROCESS_VM_OPERATION, + &oa,&id))) + record[register_count - 1].process_handle = hProc; + else { + ConsoleOutput("failed to open process"); + //::man->AddConsoleOutput(ErrorOpenProcess); + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:RegisterProcess: unlock"); + return; + } + + // jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli + //Hook *h = (Hook *)map; + //for (int i = 0; i < MAX_HOOK; i++) + // if (!h[i].hook_name) + // break; + // else { + // const Hook &hi = h[i]; + // wchar_t buffer[1000]; + // DWORD len = hi.NameLength(); + // NtReadVirtualMemory(hProc, hi.hook_name, buffer, len << 1, &len); + // buffer[len] = 0; + // ITH_MSG(buffer); + // } + + swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid); + record[register_count - 1].hookman_mutex = IthOpenMutex(str); + if (!GetProcessPath(pid, path)) + path[0] = 0; + //swprintf(str,L"%.4d:%s", pid, wcsrchr(path, L'\\') + 1); // jichi 9/25/2013: this is useless? + current_pid = pid; + if (attach) + attach(pid); + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:RegisterProcess: unlock"); +} + +void HookManager::UnRegisterProcess(DWORD pid) +{ + HM_LOCK; + //ConsoleOutput("vnrhost:UnRegisterProcess: lock"); + //EnterCriticalSection(&hmcs); + + int i; + for (i = 0; i < MAX_REGISTER; i++) + if(record[i].pid_register == pid) + break; + + if (i < MAX_REGISTER) { + NtClose(text_pipes[i]); + NtClose(cmd_pipes[i]); + NtClose(recv_threads[i]); + NtClose(record[i].hookman_mutex); + + //if (::ith_has_section) + NtUnmapViewOfSection(NtCurrentProcess(), record[i].hookman_map); + //else + // delete[] record[i].hookman_map; + + NtClose(record[i].process_handle); + NtClose(record[i].hookman_section); + + for (; i < MAX_REGISTER; i++) { + record[i] = record[i+1]; + text_pipes[i] = text_pipes[i+1]; + cmd_pipes[i] = cmd_pipes[i+1]; + recv_threads[i] = recv_threads[i+1]; + if (text_pipes[i] == 0) + break; + } + register_count--; + if (current_pid == pid) + current_pid = register_count ? record[0].pid_register : 0; + RemoveProcessContext(pid); + } + //pid_map->Clear(pid>>2); + + if (register_count == 1) + NtSetEvent(destroy_event, 0); + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:UnRegisterProcess: unlock"); + if (detach) + detach(pid); +} + +// jichi 9/28/2013: I do not need this +//void HookManager::SetName(DWORD type) +//{ +// WCHAR c; +// if (type & PRINT_DWORD) +// c = L'H'; +// else if (type & USING_UNICODE) { +// if (type & STRING_LAST_CHAR) +// c = L'L'; +// else if (type & USING_STRING) +// c = L'Q'; +// else +// c = L'W'; +// } else { +// if (type & USING_STRING) +// c = L'S'; +// else if (type & BIG_ENDIAN) +// c = L'A'; +// else +// c = L'B'; +// } +// //swprintf(user_entry,L"UserHook%c",c); +//} + +void HookManager::AddLink(WORD from, WORD to) +{ + HM_LOCK; + //bool flag=false; + //ConsoleOutput("vnrhost:AddLink: lock"); + //EnterCriticalSection(&hmcs); + TextThread *from_thread = thread_table->FindThread(from), + *to_thread = thread_table->FindThread(to); + if (to_thread && from_thread) { + if (from_thread->GetThreadParameter()->pid != to_thread->GetThreadParameter()->pid) + ConsoleOutput("vnrhost:AddLink: link to different process"); + else if (from_thread->Link()==to_thread) + ConsoleOutput("vnrhost:AddLink: link already exists"); + else if (to_thread->CheckCycle(from_thread)) + ConsoleOutput("vnrhost:AddLink: cyclic link"); + else { + from_thread->Link()=to_thread; + from_thread->LinkNumber()=to; + ConsoleOutput("vnrhost:AddLink: thread linked"); + if (addRemoveLink) + addRemoveLink(from_thread); + //WCHAR str[0x40]; + //swprintf(str,FormatLink,from,to); + //AddConsoleOutput(str); + } + } else + ConsoleOutput("vnrhost:AddLink: error link"); + //else + // AddConsoleOutput(ErrorLink); + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:AddLink: unlock"); +} +void HookManager::UnLink(WORD from) +{ + HM_LOCK; + //bool flag=false; + //ConsoleOutput("vnrhost:UnLink: lock"); + //EnterCriticalSection(&hmcs); + if (TextThread *from_thread = thread_table->FindThread(from)) { + from_thread->Link() = nullptr; + from_thread->LinkNumber() = 0xffff; + ConsoleOutput("vnrhost:UnLink: link deleted"); + if (addRemoveLink) + addRemoveLink(from_thread); + } + //else // jichi 12/25/2013: This could happen when the game exist + // ConsoleOutput("vnrhost:UnLink: thread does not exist"); + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:UnLink: unlock"); +} +void HookManager::UnLinkAll(WORD from) +{ + HM_LOCK; + //bool flag=false; + //ConsoleOutput("vnrhost:UnLinkAll: lock"); + //EnterCriticalSection(&hmcs); + if (TextThread *from_thread = thread_table->FindThread(from)) { + from_thread->UnLinkAll(); + ConsoleOutput("vnrhost:UnLinkAll: link deleted"); + } + //else // jichi 12/25/2013: This could happen after the process exists + // ConsoleOutput("vnrhost:UnLinkAll: thread not exist"); + //AddConsoleOutput(L"Link deleted."); + //} else + // AddConsoleOutput(L"Thread not exist."); + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:UnLinkAll: unlock"); +} + +void HookManager::DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD spl, int len, bool space) +{ + // jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need + if (!text || !pid || (len <= 0 && !space)) + return; + HM_LOCK; + //bool flag=false; + ThreadParameter tp = {pid, hook, retn, spl}; + //ConsoleOutput("vnrhost:DispatchText: lock"); + //EnterCriticalSection(&hmcs); + TextThread *it; + //`try { + if (TreeNode *in = Search(&tp)) { + DWORD number = in->data; + it = thread_table->FindThread(number); + } else if (IsFull()) { // jichi 1/16/2015: Skip adding threads when full + static bool once = true; // output only once + if (once) { + once = false; + ConsoleOutput("vnrhost:DispatchText: so many new threads, skip"); + } + return; + } else { // New thread + Insert(&tp, new_thread_number); + it = new TextThread(pid, hook, retn, spl, new_thread_number); + RegisterThread(it, new_thread_number); + ConsoleOutput("vnrhost:DispatchText: found new thread"); + WCHAR entstr[0x200]; + it->GetEntryString(entstr); + ConsoleOutputW(entstr); + while (thread_table->FindThread(++new_thread_number)); + if (create) + create(it); + } + if (it) + it->AddText(text, len, false, space); // jichi 10/27/2013: new line is false + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:DispatchText: unlock"); + //} catch (...) { + // // ignored + //} +} + +void HookManager::AddConsoleOutput(LPCWSTR text) +{ + if (text) { + int len = wcslen(text) << 1; + TextThread *console = thread_table->FindThread(0); + //EnterCriticalSection(&hmcs); + console->AddText((BYTE*)text,len,false,true); + console->AddText((BYTE*)L"\r\n",4,false,true); + //LeaveCriticalSection(&hmcs); + } +} + +void HookManager::ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD spl) +{ + HM_LOCK; + //bool flag=false; + //ConsoleOutput("vnrhost:ClearText: lock"); + //EnterCriticalSection(&hmcs); + ThreadParameter tp = {pid, hook, retn, spl}; + if (TreeNode *in = Search(&tp)) + if (TextThread *it = thread_table->FindThread(in->data)) { + it->Reset(); + //SetCurrent(it); + if (reset) + reset(it); + //it->ResetEditText(); + } + + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:ClearText: unlock"); +} +void HookManager::ClearCurrent() +{ + HM_LOCK; + //ConsoleOutput("vnrhost:ClearCurrent: lock"); + //EnterCriticalSection(&hmcs); + if (current) { + current->Reset(); + if (reset) + reset(current); + } + //current->ResetEditText(); + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:ClearCurrent: unlock"); +} +void HookManager::ResetRepeatStatus() +{ + HM_LOCK; + //ConsoleOutput("vnrhost:ResetRepeatStatus: lock"); + //EnterCriticalSection(&hmcs); + for (int i = 1; i < thread_table->Used(); i++) + if (TextThread *it = thread_table->FindThread(i)) + it->ResetRepeatStatus(); + + //LeaveCriticalSection(&hmcs); + //ConsoleOutput("vnrhost:ResetRepeatStatus: unlock"); +} +//void HookManager::LockHookman(){ EnterCriticalSection(&hmcs); } +//void HookManager::UnlockHookman(){ LeaveCriticalSection(&hmcs); } +void HookManager::LockHookman(){ hmcs.lock(); } +void HookManager::UnlockHookman(){ hmcs.unlock(); } + +/*void HookManager::SetProcessEngineType(DWORD pid, DWORD type) +{ + int i; + for (i=0;i>7)|(hash<<25)) + *module; +// return hash; +//} + +DWORD GetCurrentPID() { return ::man->GetCurrentPID(); } + +HANDLE GetCmdHandleByPID(DWORD pid) { return ::man->GetCmdHandleByPID(pid); } + +void AddLink(WORD from, WORD to) { ::man->AddLink(from, to); } + +// jichi 9/27/2013: Unparse to hook parameters /H code +void GetCode(const HookParam &hp, LPWSTR buffer, DWORD pid) +{ + WCHAR c; + LPWSTR ptr = buffer; + // jichi 12/7/2014: disabled + //if (hp.type&PRINT_DWORD) + // c = L'H'; + if (hp.type&USING_UNICODE) { + if (hp.type&USING_STRING) + c = L'Q'; + else if (hp.type&STRING_LAST_CHAR) + c = L'L'; + else + c = L'W'; + } else { + if (hp.type&USING_STRING) + c = L'S'; + else if (hp.type&BIG_ENDIAN) + c = L'A'; + else if (hp.type&STRING_LAST_CHAR) + c = L'E'; + else + c = L'B'; + } + ptr += swprintf(ptr, L"/H%c",c); + if (hp.type & NO_CONTEXT) + *ptr++ = L'N'; + if (hp.off>>31) + ptr += swprintf(ptr,L"-%X",-(hp.off+4)); + else + ptr += swprintf(ptr,L"%X",hp.off); + if (hp.type & DATA_INDIRECT) { + if (hp.ind>>31) + ptr += swprintf(ptr,L"*-%X",-hp.ind); + else + ptr += swprintf(ptr,L"*%X",hp.ind); + } + if (hp.type & USING_SPLIT) { + if (hp.split >> 31) + ptr += swprintf(ptr,L":-%X", -(4 + hp.split)); + else + ptr += swprintf(ptr,L":%X", hp.split); + } + if (hp.type & SPLIT_INDIRECT) { + if (hp.split_ind >> 31) + ptr += swprintf(ptr, L"*-%X", -hp.split_ind); + else + ptr += swprintf(ptr, L"*%X", hp.split_ind); + } + if (hp.module) { + if (pid) { + WCHAR path[MAX_PATH]; + MEMORY_BASIC_INFORMATION info; + ProcessRecord* pr = ::man->GetProcessRecord(pid); + if (pr) { + HANDLE hProc = pr->process_handle; + if (NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.addr, MemorySectionName, path, MAX_PATH*2, 0)) && + NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.addr, MemoryBasicInformation, &info, sizeof(info), 0))) + ptr += swprintf(ptr, L"@%X:%s", hp.addr - (DWORD)info. AllocationBase, wcsrchr(path,L'\\') + 1); + } + } else { + ptr += swprintf(ptr, L"@%X!%X", hp.addr, hp.module); + if (hp.function) + ptr += swprintf(ptr, L"!%X", hp.function); + } + } + else + ptr += swprintf(ptr, L"@%X", hp.addr); +} + +// jichi 1/16/2015 +bool HookManager::IsFull() const { return new_thread_number >= MAX_HOOK; } + +// EOF diff --git a/vnr/ith/host/hookman.h b/vnr/ith/host/hookman.h new file mode 100644 index 0000000..48ec2b0 --- /dev/null +++ b/vnr/ith/host/hookman.h @@ -0,0 +1,144 @@ +#pragma once + +// hookman.h +// 8/23/2013 jichi +// Branch: ITH/HookManager.h, rev 133 + +#include "ith/host/avl_p.h" +#include "ith/host/textthread.h" +#include "winmutex/winmutex.h" + +enum { MAX_REGISTER = 0xf }; +enum { MAX_PREV_REPEAT_LENGTH = 0x20 }; + +struct ProcessRecord { + DWORD pid_register; + DWORD hookman_register; + DWORD module_register; + //DWORD engine_register; // jichi 10/19/2014: removed + HANDLE process_handle; + HANDLE hookman_mutex; + HANDLE hookman_section; + LPVOID hookman_map; +}; + +class ThreadTable : public MyVector +{ +public: + virtual void SetThread(DWORD number, TextThread *ptr); + virtual TextThread *FindThread(DWORD number); +}; + +struct TCmp { char operator()(const ThreadParameter *t1,const ThreadParameter *t2); }; +struct TCpy { void operator()(ThreadParameter *t1,const ThreadParameter *t2); }; +struct TLen { int operator()(const ThreadParameter *t); }; + +typedef DWORD (*ProcessEventCallback)(DWORD pid); + +class IHFSERVICE HookManager : public AVLTree +{ +public: + HookManager(); + ~HookManager(); + // jichi 12/26/2013: remove virtual modifiers + TextThread *FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split); + TextThread *FindSingle(DWORD number); + ProcessRecord *GetProcessRecord(DWORD pid); + DWORD GetProcessIDByPath(LPCWSTR str); + void RemoveSingleThread(DWORD number); + void LockHookman(); + void UnlockHookman(); + void ResetRepeatStatus(); + void ClearCurrent(); + void AddLink(WORD from, WORD to); + void UnLink(WORD from); + void UnLinkAll(WORD from); + void SelectCurrent(DWORD num); + void DetachProcess(DWORD pid); + void SetCurrent(TextThread *it); + void AddConsoleOutput(LPCWSTR text); + + // jichi 10/27/2013: Add const; add space. + void DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD split, int len, bool space); + + void ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD split); + void RemoveProcessContext(DWORD pid); + void RemoveSingleHook(DWORD pid, DWORD addr); + void RegisterThread(TextThread*, DWORD); + void RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread); + void RegisterProcess(DWORD pid, DWORD hookman, DWORD module); + void UnRegisterProcess(DWORD pid); + //void SetName(DWORD); + + DWORD GetCurrentPID(); + HANDLE GetCmdHandleByPID(DWORD pid); + + ConsoleCallback RegisterConsoleCallback(ConsoleCallback cf) + { return (ConsoleCallback)_InterlockedExchange((long*)&console,(long)cf); } + + ConsoleWCallback RegisterConsoleWCallback(ConsoleWCallback cf) + { return (ConsoleWCallback)_InterlockedExchange((long*)&wconsole,(long)cf); } + + ThreadEventCallback RegisterThreadCreateCallback(ThreadEventCallback cf) + { return (ThreadEventCallback)_InterlockedExchange((long*)&create,(long)cf); } + + ThreadEventCallback RegisterThreadRemoveCallback(ThreadEventCallback cf) + { return (ThreadEventCallback)_InterlockedExchange((long*)&remove,(long)cf); } + + ThreadEventCallback RegisterThreadResetCallback(ThreadEventCallback cf) + { return (ThreadEventCallback)_InterlockedExchange((long*)&reset,(long)cf); } + + ThreadEventCallback RegisterAddRemoveLinkCallback(ThreadEventCallback cf) + { return (ThreadEventCallback)_InterlockedExchange((long*)&addRemoveLink, (long)cf); } + + ProcessEventCallback RegisterProcessAttachCallback(ProcessEventCallback cf) + { return (ProcessEventCallback)_InterlockedExchange((long*)&attach,(long)cf); } + + ProcessEventCallback RegisterProcessDetachCallback(ProcessEventCallback cf) + { return (ProcessEventCallback)_InterlockedExchange((long*)&detach,(long)cf); } + + ProcessEventCallback RegisterProcessNewHookCallback(ProcessEventCallback cf) + { return (ProcessEventCallback)_InterlockedExchange((long*)&hook,(long)cf); } + + ProcessEventCallback ProcessNewHook() { return hook; } + TextThread *GetCurrentThread() { return current; } + ProcessRecord *Records() { return record; } + ThreadTable *Table() { return thread_table; } + + //DWORD& SplitTime() { return split_time; } + //DWORD& RepeatCount() { return repeat_count; } + //DWORD& CyclicRemove() { return cyclic_remove; } + //DWORD& GlobalFilter() { return global_filter; } + void ConsoleOutput(LPCSTR text) { if (console) console(text); } // not thread safe + void ConsoleOutputW(LPCWSTR text) { if (wconsole) wconsole(text); } // not thread safe + +private: + typedef win_mutex mutex_type; + mutex_type hmcs; + + TextThread *current; + ConsoleCallback console; // jichi 12/25/2013: add console output callback + ConsoleWCallback wconsole; + ThreadEventCallback create, + remove, + reset, + addRemoveLink; + ProcessEventCallback attach, + detach, + hook; + DWORD current_pid; + ThreadTable *thread_table; + HANDLE destroy_event; + ProcessRecord record[MAX_REGISTER + 1]; + HANDLE text_pipes[MAX_REGISTER + 1], + cmd_pipes[MAX_REGISTER + 1], + recv_threads[MAX_REGISTER + 1]; + WORD register_count, + new_thread_number; + + // jichi 1/16/2014: Stop adding new threads when full + bool IsFull() const; // { return new_thread_number >= MAX_HOOK; } + bool IsEmpty() const { return !new_thread_number; } +}; + +// EOF diff --git a/vnr/ith/host/host.pri b/vnr/ith/host/host.pri new file mode 100644 index 0000000..5328551 --- /dev/null +++ b/vnr/ith/host/host.pri @@ -0,0 +1,18 @@ +# host.pri +# 8/9/2011 jichi + +DEFINES += WITH_LIB_ITH_HOST + +DEPENDPATH += $$PWD + +LIBS += -lvnrhost + +HEADERS += \ + $$PWD/avl_p.h \ + $$PWD/hookman.h \ + $$PWD/settings.h \ + $$PWD/srv.h \ + $$PWD/textthread.h \ + $$PWD/textthread_p.h + +# EOF diff --git a/vnr/ith/host/host.pro b/vnr/ith/host/host.pro new file mode 100644 index 0000000..f34eedf --- /dev/null +++ b/vnr/ith/host/host.pro @@ -0,0 +1,54 @@ +# host.pro +# 8/9/2013 jichi +# Build vnrhost + +#CONFIG += eha # 3/1/2014: catchlng all exceptions will break pytexthook on Windows XP +CONFIG += noeh # Needed by pytexthook ONLY on windows xp orz +include(../dllconfig.pri) +include(../sys/sys.pri) +include($$LIBDIR/winmaker/winmaker.pri) +include($$LIBDIR/winmutex/winmutex.pri) + +# 9/22/2013: When ITH is on wine, certain NT functions are replaced +#DEFINES += ITH_WINE + +# 9/27/2013: Only for debugging purpose +#DEFINES += ITH_DISABLE_REPEAT # disable repetition elimination +#DEFINES += ITH_DISABLE_FILTER # disable space filter in pipe + +## Libraries + +LIBS += -lkernel32 -luser32 #-lcomctl32 + +## Sources + +TEMPLATE = lib +#TARGET = IHF # compatible with ITHv3 +TARGET = vnrhost + +#CONFIG += staticlib + +HEADERS += \ + avl_p.h \ + config.h \ + hookman.h \ + settings.h \ + srv.h \ + srv_p.h \ + textthread.h \ + textthread_p.h + #util.h + +SOURCES += \ + hookman.cc \ + main.cc \ + pipe.cc \ + textthread.cc + #util.cc + +#RC_FILE += engine.rc +#OTHER_FILES += engine.rc + +OTHER_FILES += host.pri + +# EOF diff --git a/vnr/ith/host/main.cc b/vnr/ith/host/main.cc new file mode 100644 index 0000000..2727c34 --- /dev/null +++ b/vnr/ith/host/main.cc @@ -0,0 +1,611 @@ +// main.cc +// 8/24/2013 jichi +// Branch IHF/main.cpp, rev 111 +// 8/24/2013 TODO: Clean up this file + +//#ifdef _MSC_VER +//# pragma warning(disable:4800) // C4800: forcing value to bool (performance warning) +//#endif // _MSC_VER + +#include "config.h" +//#include "customfilter.h" +#include "srv.h" +#include "srv_p.h" +#include "settings.h" +#include "ith/common/const.h" +#include "ith/common/defs.h" +#include "ith/common/growl.h" +#include "ith/common/types.h" +#include "ith/sys/sys.h" +#include "winmaker/winmaker.h" +#include "ccutil/ccmacro.h" +#include + +//#define ITH_WINE +//#define ITH_USE_UX_DLLS IthIsWine() +#define ITH_USE_XP_DLLS (IthIsWindowsXp() && !IthIsWine()) + +namespace { // unnamed + +//enum { HOOK_TIMEOUT = -50000000 }; // in nanoseconds = 5 seconds + +CRITICAL_SECTION cs; +//WCHAR exist[] = ITH_PIPEEXISTS_EVENT; +//WCHAR mutex[] = L"ITH_RUNNING"; +//WCHAR EngineName[] = ITH_ENGINE_DLL; +//WCHAR EngineNameXp[] = ITH_ENGINE_XP_DLL; +//WCHAR DllName[] = ITH_CLIENT_DLL; +//WCHAR DllNameXp[] = ITH_CLIENT_XP_DLL; +HANDLE hServerMutex; // jichi 9/28/2013: used to guard pipe +HANDLE hHookMutex; // jichi 9/28/2013: used to guard hook modification +DWORD admin; +} // unnamed namespace + +//extern LPWSTR current_dir; +extern CRITICAL_SECTION detach_cs; + +SettingManager* setman; +Settings *settings; +HWND hMainWnd; +HANDLE hPipeExist; +BOOL running; + +#define ITH_SYNC_HOOK IthMutexLocker locker(::hHookMutex) + +namespace { // unnamed + +void GetDebugPriv() +{ + HANDLE hToken; + DWORD dwRet; + NTSTATUS status; + + TOKEN_PRIVILEGES Privileges = {1,{0x14,0,SE_PRIVILEGE_ENABLED}}; + + NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); + + status = NtAdjustPrivilegesToken(hToken, 0, &Privileges, sizeof(Privileges), 0, &dwRet); + admin = 1; // jichi 8/24/2013: ITH do not need admin + //if (STATUS_SUCCESS == status) + //{ + // admin = 1; + //} + //else + //{ + // WCHAR buffer[0x10]; + // swprintf(buffer, L"%.8X",status); + // MessageBox(0, NotAdmin, buffer, 0); + //} + NtClose(hToken); +} + +// jichi 9/22/2013: Change current directory to the same as main module path +// Otherwise NtFile* would not work for files with relative paths. +//BOOL ChangeCurrentDirectory() +//{ +// if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe +// if (const wchar_t *base = wcsrchr(path, L'\\')) { +// size_t len = base - path; +// if (len < MAX_PATH) { +// wchar_t buf[MAX_PATH]; +// wcsncpy(buf, path, len); +// buf[len] = 0; +// return SetCurrentDirectoryW(buf); +// } +// } +// return FALSE; +//} + +DWORD Inject(HANDLE hProc) +{ + enum : DWORD { error = (DWORD)-1 }; + LPVOID lpvAllocAddr = 0; + DWORD dwWrite = 0x1000; //, len = 0; + HANDLE hTH; + //LPWSTR dllname = (IthIsWindowsXp() && !IthIsWine()) ? DllNameXp : DllName; + LPCWSTR dllname = ITH_USE_XP_DLLS ? ITH_DLL_XP : ITH_DLL; + //if (!IthCheckFile(dllname)) + // return error; + wchar_t path[MAX_PATH]; + size_t len = IthGetCurrentModulePath(path, MAX_PATH); + if (!len) + return error; + + wchar_t *p; + for (p = path + len; *p != L'\\'; p--); + p++; // ending with L"\\" + + //LPCWSTR mp = GetMainModulePath(); + //len = wcslen(mp); + //memcpy(path, mp, len << 1); + //memset(path + len, 0, (MAX_PATH - len) << 1); + //LPWSTR p; + //for (p = path + len; *p != L'\\'; p--); // Always a \ after drive letter. + //p++; + wcscpy(p, dllname); + //if (IthIsWine()) + // lpvAllocAddr = VirtualAllocEx(hProc, nullptr, dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + //else + NtAllocateVirtualMemory(hProc, &lpvAllocAddr, 0, &dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + if (!lpvAllocAddr) + return error; + + CheckThreadStart(); + + //Copy module path into address space of target process. + //if (IthIsWine()) + // WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); + //else + NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); + hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc); + if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) { + ConsoleOutput("vnrhost:inject: ERROR: failed to create remote cli thread"); + //ConsoleOutput(ErrorRemoteThread); + return -1; + } + // jichi 9/28/2013: no wait as it will not blocked + NtWaitForSingleObject(hTH, 0, nullptr); + THREAD_BASIC_INFORMATION info; + NtQueryInformationThread(hTH, ThreadBasicInformation, &info, sizeof(info), &dwWrite); + NtClose(hTH); + + // jichi 10/19/2014: Disable inject the second dll + //if (info.ExitStatus) { + // //IthCoolDown(); + // wcscpy(p, engine); + // //if (IthIsWine()) + // // WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); + // //else + // NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite); + // hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc); + // if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) { + // //ConsoleOutput(ErrorRemoteThread); + // ConsoleOutput("vnrhost:inject: ERROR: failed to create remote eng thread"); + // return error; + // } + // + // // jichi 9/28/2013: no wait as it will not blocked + // NtWaitForSingleObject(hTH, 0, nullptr); + // NtClose(hTH); + //} + + dwWrite = 0; + //if (IthIsWine()) + // VirtualFreeEx(hProc, lpvAllocAddr, dwWrite, MEM_RELEASE); + //else + NtFreeVirtualMemory(hProc, &lpvAllocAddr, &dwWrite, MEM_RELEASE); + return info.ExitStatus; +} + + +} // unnamed namespace + +void CreateNewPipe(); + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + CC_UNUSED(lpvReserved); + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + LdrDisableThreadCalloutsForDll(hinstDLL); + InitializeCriticalSection(&cs); + IthInitSystemService(); + GetDebugPriv(); + // jichi 12/20/2013: Since I already have a GUI, I don't have to InitCommonControls() + //Used by timers. + InitCommonControls(); + // jichi 8/24/2013: Create hidden window so that ITH can access timer and events + //hMainWnd = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0); + //wm_register_hidden_class("vnrsrv.class"); + hMainWnd = (HWND)wm_create_hidden_window(L"vnrsrv", L"Button", hinstDLL); + //ChangeCurrentDirectory(); + break; + case DLL_PROCESS_DETACH: + if (::running) + IHF_Cleanup(); + DeleteCriticalSection(&cs); + IthCloseSystemService(); + wm_destroy_window(hMainWnd); + break; + default: + break; + } + return true; +} + +HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction) +{ + UNICODE_STRING us; + RtlInitUnicodeString(&us, name); + SECURITY_DESCRIPTOR sd = {1}; + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0}; + HANDLE hFile; + IO_STATUS_BLOCK isb; + if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0))) + return hFile; + else + return INVALID_HANDLE_VALUE; +} + +void ConsoleOutput(LPCSTR text) { man->ConsoleOutput(text); } +void ConsoleOutputW(LPCWSTR text) { man->ConsoleOutputW(text); } + +enum { IHS_SIZE = 0x80 }; +enum { IHS_BUFF_SIZE = IHS_SIZE - sizeof(HookParam) }; + +struct InsertHookStruct +{ + SendParam sp; + BYTE name_buffer[IHS_SIZE]; +}; + +IHFSERVICE DWORD IHFAPI IHF_Init() +{ + BOOL result = false; + DWORD present; + EnterCriticalSection(&cs); + hServerMutex = IthCreateMutex(ITH_SERVER_MUTEX, 1, &present); + if (present) + //MessageBox(0,L"Already running.",0,0); + // jichi 8/24/2013 + ITH_WARN(L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!"); + else if (!::running) { + ::running = true; + ::settings = new Settings; + setman = new SettingManager; + setman->SetValue(SETTING_SPLIT_TIME, 200); + ::man = new HookManager; + //cmdq = new CommandQueue; + InitializeCriticalSection(&detach_cs); + + ::hHookMutex = IthCreateMutex(ITH_SERVER_HOOK_MUTEX, FALSE); + result = true; + + } + LeaveCriticalSection(&cs); + return result; +} + +IHFSERVICE DWORD IHFAPI IHF_Start() +{ + //IthBreak(); + CreateNewPipe(); + hPipeExist = IthCreateEvent(ITH_PIPEEXISTS_EVENT); + NtSetEvent(hPipeExist, nullptr); + return 0; +} + +IHFSERVICE DWORD IHFAPI IHF_Cleanup() +{ + BOOL result = FALSE; + EnterCriticalSection(&cs); + if (::running) { + ::running = FALSE; + HANDLE hRecvPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE); + NtClose(hRecvPipe); + NtClearEvent(hPipeExist); + //delete cmdq; + delete man; + delete settings; + NtClose(::hHookMutex); + NtClose(hServerMutex); + NtClose(hPipeExist); + DeleteCriticalSection(&detach_cs); + result = TRUE; + } + LeaveCriticalSection(&cs); + return result; +} + +IHFSERVICE DWORD IHFAPI IHF_GetPIDByName(LPCWSTR pwcTarget) +{ + DWORD dwSize = 0x20000, + dwExpectSize = 0; + LPVOID pBuffer = 0; + SYSTEM_PROCESS_INFORMATION *spiProcessInfo; + DWORD dwPid = 0; + DWORD dwStatus; + + NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize); + if (!NT_SUCCESS(dwStatus)) { + NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE); + if (dwStatus != STATUS_INFO_LENGTH_MISMATCH || dwExpectSize < dwSize) + return 0; + dwSize = (dwExpectSize | 0xFFF) + 0x4001; // + pBuffer = 0; + NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize); + if (!NT_SUCCESS(dwStatus)) goto _end; + } + + for (spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pBuffer; spiProcessInfo->dNext;) { + spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *) + ((DWORD)spiProcessInfo + spiProcessInfo -> dNext); + if (_wcsicmp(pwcTarget, spiProcessInfo -> usName.Buffer) == 0) { + dwPid = spiProcessInfo->dUniqueProcessId; + break; + } + } + if (!dwPid) + ConsoleOutput("vnrhost:IHF_GetPIDByName: pid not found"); + //if (dwPid == 0) ConsoleOutput(ErrorNoProcess); +_end: + NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE); + return dwPid; +} + +IHFSERVICE DWORD IHFAPI IHF_InjectByPID(DWORD pid) +{ + WCHAR str[0x80]; + if (!::running) + return 0; + if (pid == current_process_id) { + //ConsoleOutput(SelfAttach); + ConsoleOutput("vnrhost:IHF_InjectByPID: refuse to inject myself"); + return -1; + } + if (man->GetProcessRecord(pid)) { + //ConsoleOutput(AlreadyAttach); + ConsoleOutput("vnrhost:IHF_InjectByPID: already attached"); + return -1; + } + swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid); + DWORD s; + NtClose(IthCreateMutex(str, 0, &s)); + if (s) + return -1; + CLIENT_ID id; + OBJECT_ATTRIBUTES oa = {}; + HANDLE hProc; + id.UniqueProcess = pid; + id.UniqueThread = 0; + oa.uLength = sizeof(oa); + if (!NT_SUCCESS(NtOpenProcess(&hProc, + PROCESS_QUERY_INFORMATION| + PROCESS_CREATE_THREAD| + PROCESS_VM_OPERATION| + PROCESS_VM_READ| + PROCESS_VM_WRITE, + &oa, &id))) { + //ConsoleOutput(ErrorOpenProcess); + ConsoleOutput("vnrhost:IHF_InjectByPID: failed to open process"); + return -1; + } + + //if (!engine) + // engine = ITH_USE_XP_DLLS ? ITH_ENGINE_XP_DLL : ITH_ENGINE_DLL; + DWORD module = Inject(hProc); + NtClose(hProc); + if (module == -1) + return -1; + //swprintf(str, FormatInject, pid, module); + //ConsoleOutput(str); + ConsoleOutput("vnrhost:IHF_InjectByPID: inject succeed"); + return module; +} + +// jichi 7/16/2014: Test if process is valid before creating remote threads +// See: http://msdn.microsoft.com/en-us/library/ms687032.aspx +static bool isProcessTerminated(HANDLE hProc) +{ return WAIT_OBJECT_0 == ::WaitForSingleObject(hProc, 0); } +//static bool isProcessRunning(HANDLE hProc) +//{ return WAIT_TIMEOUT == ::WaitForSingleObject(hProc, 0); } + +// jichi 7/16/2014: Test if process is valid before creating remote threads +//static bool isProcessRunning(DWORD pid) +//{ +// bool ret = false; +// HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); +// if (hProc) { +// DWORD status; +// if (::GetExitCodeProcess(hProc, &status)) { +// ret = status == STILL_ACTIVE; +// ::CloseHandle(hProc); +// } else +// ret = true; +// } +// return ret; +//} + +IHFSERVICE DWORD IHFAPI IHF_ActiveDetachProcess(DWORD pid) +{ + ITH_SYNC_HOOK; + + DWORD module; + HANDLE hProc, hThread; + IO_STATUS_BLOCK ios; + //man->LockHookman(); + ProcessRecord *pr = man->GetProcessRecord(pid); + HANDLE hCmd = man->GetCmdHandleByPID(pid); + if (pr == 0 || hCmd == 0) + return FALSE; + //hProc = pr->process_handle; //This handle may be closed(thus invalid) during the detach process. + NtDuplicateObject(NtCurrentProcess(), pr->process_handle, + NtCurrentProcess(), &hProc, 0, 0, DUPLICATE_SAME_ACCESS); // Make a copy of the process handle. + module = pr->module_register; + if (module == 0) + return FALSE; + + // jichi 7/15/2014: Process already closed + if (isProcessTerminated(hProc)) { + ConsoleOutput("vnrhost::activeDetach: process has terminated"); + return FALSE; + } + + // jichi 10/19/2014: Disable the second dll + //engine = pr->engine_register; + //engine &= ~0xff; + + SendParam sp = {}; + sp.type = 4; + + ConsoleOutput("vnrhost:IHF_ActiveDetachProcess: sending cmd"); + NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0,0); + ConsoleOutput("vnrhost:IHF_ActiveDetachProcess: cmd sent"); + + //cmdq->AddRequest(sp, pid); +////#ifdef ITH_WINE // Nt series crash on wine +//// hThread = IthCreateThread(FreeLibrary, engine, hProc); +////#else +// hThread = IthCreateThread(LdrUnloadDll, engine, hProc); +////#endif // ITH_WINE +// if (hThread == 0 || hThread == INVALID_HANDLE_VALUE) +// return FALSE; +// // jichi 10/22/2013: Timeout might crash vnrsrv +// //const LONGLONG timeout = HOOK_TIMEOUT; +// //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout); +// NtWaitForSingleObject(hThread, 0, nullptr); +// NtClose(hThread); + + // jichi 7/15/2014: Process already closed + if (isProcessTerminated(hProc)) { + ConsoleOutput("vnrhost:activeDetach: process has terminated"); + return FALSE; + } + + //IthCoolDown(); +//#ifdef ITH_WINE // Nt series crash on wine +// hThread = IthCreateThread(FreeLibrary, engine, hProc); +//#else + hThread = IthCreateThread(LdrUnloadDll, module, hProc); +//#endif // ITH_WINE + if (hThread == 0 || hThread == INVALID_HANDLE_VALUE) + return FALSE; + // jichi 10/22/2013: Timeout might crash vnrsrv + //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout); + NtWaitForSingleObject(hThread, 0, nullptr); + //man->UnlockHookman(); + THREAD_BASIC_INFORMATION info; + NtQueryInformationThread(hThread, ThreadBasicInformation, &info, sizeof(info), 0); + NtClose(hThread); + NtSetEvent(hPipeExist, 0); + FreeThreadStart(hProc); + NtClose(hProc); + return info.ExitStatus; +} + +IHFSERVICE DWORD IHFAPI IHF_GetHookManager(HookManager** hookman) +{ + if (::running) { + *hookman = man; + return 0; + } + else + return 1; +} + +IHFSERVICE DWORD IHFAPI IHF_GetSettingManager(SettingManager** set_man) +{ + if (running) + { + *set_man = setman; + return 0; + } + else return 1; +} + +IHFSERVICE DWORD IHFAPI IHF_GetSettings(Settings **p) +{ + if (::running) { + *p = settings; + return 0; + } + else + return 1; +} + +IHFSERVICE DWORD IHFAPI IHF_InsertHook(DWORD pid, HookParam *hp, LPCWSTR name) +{ + ITH_SYNC_HOOK; + + HANDLE hCmd = man->GetCmdHandleByPID(pid); + if (hCmd == 0) + return -1; + + InsertHookStruct s; + s.sp.type = IHF_COMMAND_NEW_HOOK; + s.sp.hp = *hp; + DWORD len; + if (name) len = wcslen(name) << 1; + else len = 0; + if (len >= IHS_BUFF_SIZE - 2) len = IHS_BUFF_SIZE - 2; + memcpy(s.name_buffer, name, len); + s.name_buffer[len] = 0; + s.name_buffer[len + 1] = 0; + IO_STATUS_BLOCK ios; + NtWriteFile(hCmd, 0,0,0, &ios, &s, IHS_SIZE, 0, 0); + + //memcpy(&sp.hp,hp,sizeof(HookParam)); + //cmdq->AddRequest(sp, pid); + return 0; +} + +IHFSERVICE DWORD IHFAPI IHF_ModifyHook(DWORD pid, HookParam *hp) +{ + ITH_SYNC_HOOK; + + SendParam sp; + HANDLE hModify,hCmd; + hCmd = GetCmdHandleByPID(pid); + if (hCmd == 0) + return -1; + hModify = IthCreateEvent(ITH_MODIFYHOOK_EVENT); + sp.type = IHF_COMMAND_MODIFY_HOOK; + sp.hp = *hp; + IO_STATUS_BLOCK ios; + if (NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0, 0))) + // jichi 9/28/2013: no wait timeout + //const LONGLONG timeout = HOOK_TIMEOUT; + NtWaitForSingleObject(hModify, 0, nullptr); + NtClose(hModify); + man->RemoveSingleHook(pid, sp.hp.addr); + return 0; +} + +IHFSERVICE DWORD IHFAPI IHF_RemoveHook(DWORD pid, DWORD addr) +{ + ITH_SYNC_HOOK; + + HANDLE hRemoved,hCmd; + hCmd = GetCmdHandleByPID(pid); + if (hCmd == 0) + return -1; + hRemoved = IthCreateEvent(ITH_REMOVEHOOK_EVENT); + SendParam sp = {}; + IO_STATUS_BLOCK ios; + sp.type = IHF_COMMAND_REMOVE_HOOK; + sp.hp.addr = addr; + //cmdq -> AddRequest(sp, pid); + NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam),0,0); + // jichi 10/22/2013: Timeout might crash vnrsrv + //const LONGLONG timeout = HOOK_TIMEOUT; + //NtWaitForSingleObject(hRemoved, 0, (PLARGE_INTEGER)&timeout); + NtWaitForSingleObject(hRemoved, 0, nullptr); + NtClose(hRemoved); + man -> RemoveSingleHook(pid, sp.hp.addr); + return 0; +} + +IHFSERVICE DWORD IHFAPI IHF_IsAdmin() { return admin; } + +IHFSERVICE DWORD IHFAPI IHF_AddLink(DWORD from, DWORD to) +{ + man->AddLink(from & 0xffff, to & 0xffff); + return 0; +} + +IHFSERVICE DWORD IHFAPI IHF_UnLink(DWORD from) +{ + man->UnLink(from & 0xffff); + return 0; +} + +IHFSERVICE DWORD IHFAPI IHF_UnLinkAll(DWORD from) +{ + man->UnLinkAll(from & 0xffff); + return 0; +} + +// EOF diff --git a/vnr/ith/host/pipe.cc b/vnr/ith/host/pipe.cc new file mode 100644 index 0000000..c389afb --- /dev/null +++ b/vnr/ith/host/pipe.cc @@ -0,0 +1,322 @@ +// pipe.cc +// 8/24/2013 jichi +// Branch IHF/pipe.cpp, rev 93 +// 8/24/2013 TODO: Clean up this file + +#include "srv_p.h" +#include "hookman.h" +#include "ith/common/defs.h" +#include "ith/common/const.h" +//#include "ith/common/growl.h" +#include "ith/sys/sys.h" +//#include "CommandQueue.h" + +//DWORD WINAPI UpdateWindows(LPVOID lpThreadParameter); + +namespace { // unnamed +enum NamedPipeCommand { + NAMED_PIPE_DISCONNECT = 1 + , NAMED_PIPE_CONNECT = 2 +}; + +bool newline = false; +bool detach = false; + +// jichi 10/27/2013 +// Check if text has leading space +enum { _filter_limit = 0x20 }; // The same as the orignal ITH filter. So, I don't have to check \u3000 +//enum { _filter_limit = 0x19 }; +inline bool has_leading_space(const BYTE *text, int len) +{ + return len == 1 ? *text <= _filter_limit : // 1 byte + *reinterpret_cast(text) <= _filter_limit; // 2 bytes +} + +// jichi 9/28/2013: Skip leading garbage +// Note: +// - Modifying limit will break manual translation. The orignal one is 0x20 +// - Eliminating 0x20 will break English-translated games +const BYTE *Filter(const BYTE *str, int len) +{ +#ifdef ITH_DISABLE_FILTER // jichi 9/28/2013: only for debugging purpose + return str; +#endif // ITH_DISABLE_FILTER +// if (len && *str == 0x10) // jichi 9/28/2013: garbage on wine, data link escape, or ^P +// return nullptr; + //enum { limit = 0x19 }; + while (true) + if (len >= 2) { + if (*(const WORD *)str <= _filter_limit) { // jichi 10/27/2013: two bytes + str += 2; + len -= 2; + } else + break; + } else if (*str <= _filter_limit) { // jichi 10/27/2013: 1 byte + str++; + len--; + } else + break; + return str; +} +} // unnamed namespace + +//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE"; +//WCHAR command_pipe[] = L"\\??\\pipe\\ITH_COMMAND"; +wchar_t recv_pipe[] = ITH_TEXT_PIPE; +wchar_t command_pipe[] = ITH_COMMAND_PIPE; + +CRITICAL_SECTION detach_cs; // jichi 9/27/2013: also used in main +//HANDLE hDetachEvent; +extern HANDLE hPipeExist; + +void CreateNewPipe() +{ + static DWORD acl[7] = { + 0x1C0002, + 1, + 0x140000, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + 0x101, + 0x1000000, + 0}; + static SECURITY_DESCRIPTOR sd = {1, 0, 4, 0, 0, 0, (PACL)acl}; + + HANDLE hTextPipe, hCmdPipe, hThread; + IO_STATUS_BLOCK ios; + UNICODE_STRING us; + + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0}; + LARGE_INTEGER time = {-500000, -1}; + + RtlInitUnicodeString(&us, recv_pipe); + if (!NT_SUCCESS(NtCreateNamedPipeFile( + &hTextPipe, + GENERIC_READ | SYNCHRONIZE, + &oa, + &ios, + FILE_SHARE_WRITE, + FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT, + 1, 1, 0, -1, + 0x1000, + 0x1000, + &time))) { + //ConsoleOutput(ErrorCreatePipe); + ConsoleOutput("vnrhost:CreateNewPipe: failed to create recv pipe"); + return; + } + + RtlInitUnicodeString(&us, command_pipe); + if (!NT_SUCCESS(NtCreateNamedPipeFile( + &hCmdPipe, + GENERIC_WRITE | SYNCHRONIZE, + &oa, + &ios, + FILE_SHARE_READ, + FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT, + 1, 1, 0, -1, + 0x1000, + 0x1000, + &time))) { + //ConsoleOutput(ErrorCreatePipe); + ConsoleOutput("vnrhost:CreateNewPipe: failed to create cmd pipe"); + return; + } + + hThread = IthCreateThread(RecvThread, (DWORD)hTextPipe); + man->RegisterPipe(hTextPipe, hCmdPipe, hThread); +} + +void DetachFromProcess(DWORD pid) +{ + HANDLE hMutex = INVALID_HANDLE_VALUE, + hEvent = INVALID_HANDLE_VALUE; + //try { + IO_STATUS_BLOCK ios; + ProcessRecord *pr = man->GetProcessRecord(pid); + if (!pr) + return; + //IthBreak(); + hEvent = IthCreateEvent(nullptr); + if (STATUS_PENDING == NtFsControlFile( + man->GetCmdHandleByPID(pid), + hEvent, + 0,0, + &ios, + CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0), + 0,0,0,0)) + NtWaitForSingleObject(hEvent, 0, 0); + NtClose(hEvent); + //hEvent = INVALID_HANDLE_VALUE; + + WCHAR mutex[0x20]; + swprintf(mutex, ITH_DETACH_MUTEX_ L"%d", pid); + hMutex = IthOpenMutex(mutex); + if (hMutex != INVALID_HANDLE_VALUE) { + NtWaitForSingleObject(hMutex, 0, 0); + NtReleaseMutant(hMutex, 0); + NtClose(hMutex); + //hMutex = INVALID_HANDLE_VALUE; + } + + //} catch (...) { + // if (hEvent != INVALID_HANDLE_VALUE) + // NtClose(hEvent); + // else if (hMutex != INVALID_HANDLE_VALUE) { + // NtWaitForSingleObject(hMutex, 0, 0); + // NtReleaseMutant(hMutex, 0); + // NtClose(hMutex); + // } + //} + + //NtSetEvent(hDetachEvent, 0); + if (::running) + NtSetEvent(hPipeExist, 0); +} + +// jichi 9/27/2013: I don't need this +//void OutputDWORD(DWORD d) +//{ +// WCHAR str[0x20]; +// swprintf(str, L"%.8X", d); +// ConsoleOutput(str); +//} + +DWORD WINAPI RecvThread(LPVOID lpThreadParameter) +{ + HANDLE hTextPipe = (HANDLE)lpThreadParameter; + + IO_STATUS_BLOCK ios; + NtFsControlFile(hTextPipe, + 0, 0, 0, + &ios, + CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_CONNECT, 0, 0), + 0, 0, 0, 0); + if (!::running) { + NtClose(hTextPipe); + return 0; + } + + BYTE *buff; + + enum { PipeBufferSize = 0x1000 }; + buff = new BYTE[PipeBufferSize]; + ITH_MEMSET_HEAP(buff, 0, PipeBufferSize); // jichi 8/27/2013: zero memory, or it will crash wine on start up + + // 10/19/2014 jichi: there are totally three words received + // See: hook/rpc/pipe.cc + // struct { + // DWORD pid; + // TextHook *man; + // DWORD module; + // //DWORD engine; + // } u; + enum { module_struct_size = 12 }; + NtReadFile(hTextPipe, 0, 0, 0, &ios, buff, module_struct_size, 0, 0); + + DWORD pid = *(DWORD *)buff, + hookman = *(DWORD *)(buff + 0x4), + module = *(DWORD *)(buff + 0x8); + //engine = *(DWORD *)(buff + 0xc); + man->RegisterProcess(pid, hookman, module); + + // jichi 9/27/2013: why recursion? + CreateNewPipe(); + + //NtClose(IthCreateThread(UpdateWindows,0)); + while (::running) { + if (!NT_SUCCESS(NtReadFile(hTextPipe, + 0, 0, 0, + &ios, + buff, + 0xf80, + 0, 0))) + break; + + enum { data_offset = 0xc }; // jichi 10/27/2013: Seem to be the data offset in the pipe + + DWORD RecvLen = ios.uInformation; + if (RecvLen < data_offset) + break; + DWORD hook = *(DWORD *)buff; + + union { DWORD retn; DWORD cmd_type; }; + union { DWORD split; DWORD new_engine_type; }; + + retn = *(DWORD *)(buff + 4); + split = *(DWORD *)(buff + 8); + + buff[RecvLen] = 0; + buff[RecvLen + 1] = 0; + + if (hook == IHF_NOTIFICATION) { + switch (cmd_type) { + case IHF_NOTIFICATION_NEWHOOK: + { + static long lock; + while (InterlockedExchange(&lock, 1) == 1); + ProcessEventCallback new_hook = man->ProcessNewHook(); + if (new_hook) + new_hook(pid); + lock = 0; + } break; + case IHF_NOTIFICATION_TEXT: + ConsoleOutput((LPCSTR)(buff + 8)); + break; + } + } else { + // jichi 9/28/2013: Debug raw data + //ITH_DEBUG_DWORD9(RecvLen - 0xc, + // buff[0xc], buff[0xd], buff[0xe], buff[0xf], + // buff[0x10], buff[0x11], buff[0x12], buff[0x13]); + + const BYTE *data = buff + data_offset; // th + int len = RecvLen - data_offset; + bool space = ::has_leading_space(data, len); + if (space) { + const BYTE *it = ::Filter(data, len); + len -= it - data; + data = it; + } + if (len >> 31) // jichi 10/27/2013: len is too large, which seldom happens + len = 0; + //man->DispatchText(pid, len ? data : nullptr, hook, retn, split, len, space); + man->DispatchText(pid, data, hook, retn, split, len, space); + } + } + + EnterCriticalSection(&detach_cs); + + HANDLE hDisconnect = IthCreateEvent(nullptr); + + if (STATUS_PENDING == NtFsControlFile( + hTextPipe, + hDisconnect, + 0, 0, + &ios, + CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0), + 0, 0, 0, 0)) + NtWaitForSingleObject(hDisconnect, 0, 0); + + NtClose(hDisconnect); + DetachFromProcess(pid); + man->UnRegisterProcess(pid); + + //NtClearEvent(hDetachEvent); + + LeaveCriticalSection(&detach_cs); + delete[] buff; + + if (::running) + ConsoleOutput("vnrhost:DetachFromProcess: detached"); + + //if (::running) { + // swprintf((LPWSTR)buff, FormatDetach, pid); + // ConsoleOutput((LPWSTR)buff); + // NtClose(IthCreateThread(UpdateWindows, 0)); + //} + return 0; +} + +// EOF diff --git a/vnr/ith/host/settings.h b/vnr/ith/host/settings.h new file mode 100644 index 0000000..9eee77e --- /dev/null +++ b/vnr/ith/host/settings.h @@ -0,0 +1,14 @@ +#pragma once + +// settings.h +// 8/24/2013 jichi + +struct Settings { + //bool debug; // whether output debug messages using pipes + int splittingInterval;// time to split text into sentences + + Settings() : splittingInterval(200) {} + +}; + +// EOF diff --git a/vnr/ith/host/srv.h b/vnr/ith/host/srv.h new file mode 100644 index 0000000..1c494de --- /dev/null +++ b/vnr/ith/host/srv.h @@ -0,0 +1,36 @@ +#pragma once + +// srv.h +// 8/23/2013 jichi +// Branch: ITH/IHF.h, rev 105 + +#include "config.h" +//#include "ith/host/settings.h" +#include "ith/host/hookman.h" +#include "ith/host/SettingManager.h" + +struct Settings; +struct HookParam; + +// jichi 8/24/2013: Why extern "C"? Any specific reason to use C instead of C++ naming? +extern "C" { +IHFSERVICE DWORD IHFAPI IHF_Init(); +IHFSERVICE DWORD IHFAPI IHF_Start(); +IHFSERVICE DWORD IHFAPI IHF_Cleanup(); +IHFSERVICE DWORD IHFAPI IHF_GetPIDByName(LPCWSTR pwcTarget); +IHFSERVICE DWORD IHFAPI IHF_InjectByPID(DWORD pid); +IHFSERVICE DWORD IHFAPI IHF_ActiveDetachProcess(DWORD pid); +IHFSERVICE DWORD IHFAPI IHF_GetHookManager(HookManager **hookman); +IHFSERVICE DWORD IHFAPI IHF_GetSettingManager(SettingManager** set_man); +IHFSERVICE DWORD IHFAPI IHF_GetSettings(Settings **settings); +IHFSERVICE DWORD IHFAPI IHF_InsertHook(DWORD pid, HookParam *hp, LPCWSTR name = 0); +IHFSERVICE DWORD IHFAPI IHF_ModifyHook(DWORD pid, HookParam *hp); +IHFSERVICE DWORD IHFAPI IHF_RemoveHook(DWORD pid, DWORD addr); +IHFSERVICE DWORD IHFAPI IHF_IsAdmin(); +//IHFSERVICE DWORD IHFAPI IHF_GetFilters(PVOID *mb_filter, PVOID *uni_filter); +IHFSERVICE DWORD IHFAPI IHF_AddLink(DWORD from, DWORD to); +IHFSERVICE DWORD IHFAPI IHF_UnLink(DWORD from); +IHFSERVICE DWORD IHFAPI IHF_UnLinkAll(DWORD from); +} // extern "C" + +// EOF diff --git a/vnr/ith/host/srv_p.h b/vnr/ith/host/srv_p.h new file mode 100644 index 0000000..4e0ac12 --- /dev/null +++ b/vnr/ith/host/srv_p.h @@ -0,0 +1,46 @@ +#pragma once +// srv_p.h +// 8/24/2013 jichi +// Branch IHF/main.h, rev 111 +#include "config.h" + +#define GLOBAL extern +#define SHIFT_JIS 0x3A4 +class HookManager; +//class CommandQueue; +class SettingManager; +class TextHook; +//class BitMap; +//class CustomFilterMultiByte; +//class CustomFilterUnicode; +//#define TextHook Hook +GLOBAL BOOL running; +//GLOBAL BitMap *pid_map; +//GLOBAL CustomFilterMultiByte *mb_filter; +//GLOBAL CustomFilterUnicode *uni_filter; +GLOBAL HookManager *man; +//GLOBAL CommandQueue *cmdq; +GLOBAL SettingManager *setman; +GLOBAL WCHAR recv_pipe[]; +GLOBAL WCHAR command[]; +GLOBAL HANDLE hPipeExist; +GLOBAL DWORD split_time, + cyclic_remove, + clipboard_flag, + global_filter; +GLOBAL CRITICAL_SECTION detach_cs; + +DWORD WINAPI RecvThread(LPVOID lpThreadParameter); +DWORD WINAPI CmdThread(LPVOID lpThreadParameter); + +void ConsoleOutput(LPCSTR text); +void ConsoleOutputW(LPCWSTR text); +DWORD GetCurrentPID(); +//DWORD GetProcessIDByPath(LPWSTR str); +HANDLE GetCmdHandleByPID(DWORD pid); +//DWORD Inject(HANDLE hProc); +//DWORD InjectByPID(DWORD pid); +//DWORD PIDByName(LPWSTR target); +//DWORD Hash(LPCWSTR module, int length=-1); + +// EOF diff --git a/vnr/ith/host/textthread.cc b/vnr/ith/host/textthread.cc new file mode 100644 index 0000000..1fc1739 --- /dev/null +++ b/vnr/ith/host/textthread.cc @@ -0,0 +1,787 @@ +// textthread.cc +// 8/24/2013 jichi +// Branch IHF/TextThread.cpp, rev 133 +// 8/24/2013 TODO: Clean up this file + +#ifdef _MSC_VER +# pragma warning (disable:4100) // C4100: unreference formal parameter +#endif // _MSC_VER + +#include "config.h" +#include "settings.h" +#include "textthread.h" +#include "ith/common/const.h" +#include "ith/sys/sys.h" +#include "SettingManager.h" + +MK_BASIC_TYPE(BYTE) +MK_BASIC_TYPE(ThreadParameter) + +static DWORD MIN_DETECT = 0x20; +static DWORD MIN_REDETECT = 0x80; +//#define MIN_DETECT 0x20 +//#define MIN_REDETECT 0x80 +#ifndef CURRENT_SELECT +# define CURRENT_SELECT 0x1000 +#endif +#ifndef REPEAT_NUMBER_DECIDED +# define REPEAT_NUMBER_DECIDED 0x2000 +#endif + +DWORD GetHookName(LPWSTR str, DWORD pid, DWORD hook_addr,DWORD max); + +extern SettingManager* setman; +extern Settings *settings; +extern HWND hMainWnd; +void CALLBACK NewLineBuff(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hwnd,idEvent); + TextThread *id=(TextThread*)idEvent; + + if (id->Status()&CURRENT_SELECT) + //texts->SetLine(); + id->CopyLastToClipboard(); + id->SetNewLineFlag(); +} + +// jichi 10/27/2013: removed +//void CALLBACK NewLineConsole(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +//{ +// KillTimer(hwnd,idEvent); +// TextThread *id=(TextThread*)idEvent; +// if (id->Status()&USING_UNICODE) +// id->AddText((BYTE*)L"\r\n",4,true,true); +// if (id->Status()&CURRENT_SELECT) +// { +// //texts->SetLine(); +// } +//} + +// jichi 10/27/2013: removed +//void ReplaceSentence(BYTE* text, int len) +//{ +// __asm int 3 +//} + +TextThread::TextThread(DWORD id, DWORD hook, DWORD retn, DWORD spl, WORD num) : + //,tp + thread_number(num) + // jichi 9/21/2013: zero all fields + , link_number(-1) + , last (0) + , align_space(0) + , repeat_single(0) + , repeat_single_current(0) + , repeat_single_count(0) + , repeat_detect_count(0) + , head(new RepeatCountNode()) + , link(nullptr) + //, filter(nullptr) + , output(nullptr) + , app_data(nullptr) + //, comment(nullptr) + , thread_string(nullptr) + , timer(0) + , status (0) + , repeat_detect_limit(0x80) + , last_sentence(0) + , prev_sentence(0) + , sentence_length(0) + , repeat_index(0) + , last_time(0) +// , tp({id, hook, retn, spl}) +{ + tp.pid = id; + tp.hook = hook; + tp.retn = retn; + tp.spl = spl; + //head = new RepeatCountNode; + //ITH_MEMSET_HEAP(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory + //link_number = -1; + //repeat_detect_limit = 0x80; + //filter = nullptr; + //output = nullptr; +} +TextThread::~TextThread() +{ + //KillTimer(hMainWnd,timer); + RepeatCountNode *t = head, + *tt; + while (t) { + tt = t; + t = tt->next; + delete tt; + } + head = nullptr; + //if (comment) { + // delete[] comment; + // comment = nullptr; + //} + if (thread_string) + delete[] thread_string; +} +void TextThread::Reset() +{ + //timer=0; + last_sentence = 0; + //if (comment) { + // delete[] comment; + // comment = nullptr; + //} + MyVector::Reset(); +} +void TextThread::RemoveSingleRepeatAuto(const BYTE *con, int &len) +{ +#ifdef ITH_DISABLE_REPEAT // jichi 9/28/2013: only for debugging purpose + return; +#endif // ITH_DISABLE_REPEAT + WORD *text = (WORD *)con; + if (len <= 2) { + if (repeat_single) { + if (repeat_single_countMIN_REDETECT) { + repeat_detect_count = 0; + status ^= REPEAT_NUMBER_DECIDED; + last = 0; + RepeatCountNode *t = head, + *tt; + while (t) { + tt = t; + t = tt->next; + delete tt; + } + head = new RepeatCountNode; + ITH_MEMSET_HEAP(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory + } + } else { + repeat_detect_count++; + if (last == *text) + repeat_single_current++; + else { + if (last == 0) { + last = *text; + return; + } + if (repeat_single_current == 0) { + status |= REPEAT_NUMBER_DECIDED; + repeat_single = 0; + return; + } + last = *text; + RepeatCountNode *it = head; + if (repeat_detect_count > MIN_DETECT) { + while (it = it->next) + if (it->count>head->count) { + head->count=it->count; + head->repeat=it->repeat; + } + repeat_single = head->repeat; + repeat_single_current = 0; + repeat_detect_count = 0; + status |= REPEAT_NUMBER_DECIDED; + DWORD repeat_sc = repeat_single*4; + if (repeat_sc > MIN_DETECT) { + MIN_DETECT <<= 1; + MIN_REDETECT <<= 1; + } + } else { + bool flag=true; + while (it) { + if (it->repeat == repeat_single_current) { + it->count++; + flag = false; + break; + } + it=it->next; + } + if (flag) { + RepeatCountNode *n = new RepeatCountNode; + n->count = 1; + n->repeat = repeat_single_current; + n->next = head->next; + head->next = n; + } + repeat_single_current = 0; + } //Decide repeat_single + } //Check Repeat + } //repeat_single decided? + } //len + else { + status |= REPEAT_NUMBER_DECIDED; + repeat_single = 0; + } +} + +void TextThread::RemoveSingleRepeatForce(BYTE *con,int &len) +{ + // jichi 9/1/2013: manual repetition count removed + WORD *text = (WORD *)con; + //if (repeat_single_countGetValue(SETTING_REPEAT_COUNT)&&last==*text) { + // len=0; + // repeat_single_count++; + //} + //else + { + last = *text; + repeat_single_count=0; + } +} +void TextThread::RemoveCyclicRepeat(BYTE* &con, int &len) +{ + DWORD currnet_time = GetTickCount(); + if (status & REPEAT_SUPPRESS) { + if (currnet_time - last_time < (unsigned)settings->splittingInterval && + ::memcmp(storage + last_sentence + repeat_index, con, len) == 0) { + repeat_index += len; + if (repeat_index>=sentence_length) + repeat_index -= sentence_length; + len = 0; + } else { + repeat_index = 0; + status &= ~REPEAT_SUPPRESS; + } + } else if (status & REPEAT_DETECT) { + if (::memcmp(storage + last_sentence + repeat_index, con, len) == 0) { + int half_length=repeat_index+len; + if (::memcmp(storage + last_sentence, storage + last_sentence + half_length, repeat_index) == 0) { + len=0; + sentence_length=half_length; + status&=~REPEAT_DETECT; + status|=REPEAT_SUPPRESS; + + // jichi 10/27/2013: Not used + //if (status&CURRENT_SELECT) + // ReplaceSentence(storage+last_sentence+half_length,repeat_index); + ClearMemory(last_sentence+half_length,repeat_index); + used-=repeat_index; + repeat_index=0; + } + else + repeat_index += len; + } + else { + repeat_index=0; + status &= ~REPEAT_DETECT; + } + } else { + if (sentence_length == 0) + return; + else if (len <= (int)sentence_length) { + if (memcmp(storage + last_sentence, con, len) == 0) { + status |= REPEAT_DETECT; + repeat_index = len; + if (repeat_index == sentence_length) { + repeat_index = 0; + len = 0; + } + } else if (sentence_length > repeat_detect_limit) { + if (len > 2) { + DWORD u = used; + while (memcmp(storage + u - len, con, len) == 0) + u -= len; + ClearMemory(u, used - u); + used = u; + repeat_index = 0; + // jichi 10/27/2013: Not used + //if (status & CURRENT_SELECT) + // ReplaceSentence(storage + last_sentence, used - u); + status |= REPEAT_SUPPRESS; + len = 0; + } else if (len <= 2) + { + WORD tmp = *(WORD *)(storage + last_sentence); + DWORD index, last_index, tmp_len; + index = used-len; + if (index < last_sentence) + index = last_sentence; + //Locate position of current input. +_again: + *(WORD *)(storage+last_sentence) = *(WORD *)con; + while (*(WORD *)(storage + index) != *(WORD *)con) + index--; + *(WORD *)(storage + last_sentence) = tmp; + if (index > last_sentence) { + tmp_len = used - index; + if (tmp_len <= 2) { + repeat_detect_limit += 0x40; + last_time = currnet_time; + return; + } + if (index - last_sentence >= tmp_len && + memcmp(storage + index - tmp_len, storage + index, tmp_len) == 0) { + repeat_detect_limit = 0x80; + sentence_length =tmp_len; + index -= tmp_len; + while (memcmp(storage + index - sentence_length, storage + index, sentence_length) == 0) + index -= sentence_length; + repeat_index = 2; + len = 0; + last_index = index; + if (status&USING_UNICODE) { + while (storage[index] == storage[index + sentence_length]) + index -= 2; + index += 2; + while (true) { + tmp = *(WORD *)(storage + index); + if (tmp >= 0x3000 && tmp < 0x3020) + index += 2; + else + break; + } + } else { + DWORD last_char_len; + while (storage[index] == storage[index + sentence_length]) { + last_char_len = LeadByteTable[storage[index]]; + index -= last_char_len; + } + index += last_char_len; + while (storage[index] == 0x81) { + if ((storage[index+1]>>4) == 4) + index += 2; + else + break; + } + } + repeat_index += last_index - index; + status |= REPEAT_SUPPRESS; + last_sentence = index; + + index += sentence_length; + // jichi 10/27/2013: Not used + //if (status&CURRENT_SELECT) + // ReplaceSentence(storage + index, used - index); + + ClearMemory(index, used - index); + //memset(storage + index, 0, used - index); + used = index; + } else { + index--; + goto _again; + } + } + else + repeat_detect_limit += 0x40; + } + } + } + } + last_time = currnet_time; +} + +void TextThread::ResetRepeatStatus() +{ + last=0; + repeat_single=0; + repeat_single_current=0; + repeat_single_count=0; + repeat_detect_count=0; + RepeatCountNode *t = head->next, + *tt; + while (t) { + tt = t; + t = tt->next; + delete tt; + } + //head=new RepeatCountNode; + head->count = head->repeat = 0; + status &= ~REPEAT_NUMBER_DECIDED; +} +void TextThread::AddLineBreak() +{ + if (sentence_length == 0) return; + if (status&BUFF_NEWLINE) + { + prev_sentence=last_sentence; + sentence_length=0; + if (status & USING_UNICODE) + AddToStore((BYTE *)L"\r\n\r\n", 8); + else + AddToStore((BYTE *)"\r\n\r\n", 4); + if (output) + output(this, 0, 8, TRUE, app_data, false); // jichi 10/27/2013: space is false + last_sentence = used; + status &= ~BUFF_NEWLINE; + } +} +void TextThread::AddText(const BYTE *con, int len, bool new_line, bool space) +{ + if (!con || (len <= 0 && !space)) + return; + if (len && !new_line) { + // jichi 9/1/2013: manual repetition count removed + //if (setman->GetValue(SETTING_REPEAT_COUNT)) { + // status|=REPEAT_NUMBER_DECIDED; + // RemoveSingleRepeatForce(con,len); + //} + //else + RemoveSingleRepeatAuto(con, len); + if (len <= 0 && !space) + return; + } + + // jichi 9/1/2013: manual repetition count removed + //if(setman->GetValue(SETTING_CYCLIC_REMOVE)) { + // //if (status & REPEAT_NUMBER_DECIDED) + // RemoveCyclicRepeat(con,len); + //} + //if (len <= 0) + // return; + + // jichi 10/27/2013: User-defined filter callback is disabled + //if (filter) + // len = filter(this, con,len, new_line, app_data); + //if (len <= 0) + // return; + + if (len && sentence_length == 0) { + if (status & USING_UNICODE) { + if (*(WORD *)con == 0x3000) { // jichi 10/27/2013: why skip unicode space?! + con += 2; + len -= 2; + } + } else if (*(WORD *)con == 0x4081) { + con += 2; + len -= 2; + } + + if (len <= 0 && !space) + return; + } + + if (status & BUFF_NEWLINE) + AddLineBreak(); + + if (len) + if (new_line) { + prev_sentence = last_sentence; + last_sentence = used + 4; + if (status & USING_UNICODE) + last_sentence += 4; + sentence_length = 0; + } else { + SetNewLineTimer(); + if (link) { + const BYTE *send = con; + int l = len; + if (status & USING_UNICODE) { // Although unlikely, a thread and its link may have different encoding. + if ((link->Status() & USING_UNICODE) == 0) { + send = new BYTE[l]; + //ITH_MEMSET_HEAP(send, 0, l); // jichi 9/26/2013: zero memory + l = WC_MB((LPWSTR)con, (char *)send); + } + link->AddTextDirect(send, l, space); + } else { + if (link->Status() & USING_UNICODE) { + size_t sz = len * 2 + 2; + send = new BYTE[sz]; + //ITH_MEMSET_HEAP(send, 0, sz); // jichi 9/26/2013: zero memory + l = MB_WC((char *)con, (LPWSTR)send) << 1; + } + link->AddTextDirect(send, l, space); + } + link->SetNewLineTimer(); + if (send != con) + delete[] send; + } + sentence_length += len; + } + + BYTE *data = const_cast(con); // jichi 10/27/2013: TODO: Figure out where con is modified + if (output) + len = output(this, data, len, new_line, app_data, space); + if (AddToStore(data, len)) { + //sentence_length += len; + /*ResetRepeatStatus(); + last_sentence=0; + prev_sentence=0; + sentence_length=len; + repeat_index=0; + status&=~REPEAT_DETECT|REPEAT_SUPPRESS; */ + } +} + +void TextThread::AddTextDirect(const BYTE* con, int len, bool space) // Add to store directly, penetrating repetition filters. +{ + // jichi 10/27/2013: Accordig to the logic, both len and con must be > 0 + if (status & BUFF_NEWLINE) + AddLineBreak(); + SetNewLineTimer(); + if (link) { + const BYTE *send = con; + int l = len; + if (status & USING_UNICODE) { + if ((link->Status()&USING_UNICODE) == 0) { + send = new BYTE[l]; + //ITH_MEMSET_HEAP(send, 0, l); // jichi 9/26/2013: zero memory + l = WC_MB((LPWSTR)con,(char*)send); + } + link->AddText(send, l, false, space); // new_line is false + } else { + if (link->Status()&USING_UNICODE) { + size_t sz = len * 2 + 2; + send = new BYTE[sz]; + //ITH_MEMSET_HEAP(send, 0, sz); // jichi 9/26/2013: zero memory + l = MB_WC((char *)con, (LPWSTR)send) << 1; + } + link->AddText(send, l, false, space); // new_line is false + } + link->SetNewLineTimer(); + if (send != con) + delete[] send; + } + sentence_length += len; + + BYTE *data = const_cast(con); // jichi 10/27/2013: TODO: Figure out where con is modified + if (output) + len = output(this, data, len, false, app_data, space); + AddToStore(data, len); +} + +DWORD TextThread::GetEntryString(LPWSTR str, DWORD max) +{ + DWORD len = 0; + if (str && max > 0x40) { + max--; + if (thread_string) { + len = wcslen(thread_string); + len = len < max ? len : max; + memcpy(str, thread_string, len << 1); + str[len] = 0; + + } else { + len = swprintf(str, L"%.4X:%.4d:0x%08X:0x%08X:0x%08X:", + thread_number, tp. pid, tp.hook, tp.retn, tp.spl); + + len += GetHookName(str + len, tp.pid, tp.hook, max - len); + thread_string = new wchar_t[len + 1]; + //ITH_MEMSET_HEAP(thread_string, 0, (len+1) * sizeof(wchar_t)); // jichi 9/26/2013: zero memory + thread_string[len] = 0; + memcpy(thread_string, str, len * sizeof(wchar_t)); + } + //if (comment) { + // str += len; + // max--; + // DWORD cl = wcslen(comment); + // if (len + cl >= max) + // cl = max - len; + // *str++ = L'-'; + // memcpy(str, comment, cl << 1); + // str[cl] = 0; + // len += cl; + //} + } + return len; +} +// jichi 9/28/2013: removed +//void TextThread::CopyLastSentence(LPWSTR str) +//{ +// int i,j,l; +// if (status&USING_UNICODE) +// { +// if (used>8) +// { +// j=used>0xF0?(used-0xF0):0; +// for (i=used-0xA;i>=j;i-=2) +// { +// if (*(DWORD*)(storage+i)==0xA000D) break; +// } +// if (i>=j) +// { +// l=used-i; +// if (i>j) l-=4; +// j=4; +// } +// else +// { +// i+=2; +// l=used-i; +// j=0; +// } +// memcpy(str,storage+i+j,l); +// str[l>>1]=0; +// } +// else +// { +// memcpy(str,storage,used); +// str[used>>1]=0; +// } +// } +// else +// { +// if (used>4) +// { +// j=used>0x80?(used-0x80):0; +// for (i=used-5;i>=j;i--) +// { +// if (*(DWORD*)(storage+i)==0xA0D0A0D) break; +// } +// if (i>=j) +// { +// l=used-i; +// if (i>j) l-=4; +// j=4; +// } +// else +// { +// i++; +// l=used-i; +// j=0; +// } +// size_t sz = (l|0xF) + 1; +// char *buff = new char[sz]; +// //memset(buff, 0, sz); // jichi 9/26/2013: zero memory +// memcpy(buff, storage + i + j, l); +// buff[l] = 0; +// str[MB_WC(buff, str)] = 0; +// delete[] buff; +// } else { +// storage[used] = 0; +// str[MB_WC((char *)storage, str)] = 0; +// } +// } +//} + +static char clipboard_buffer[0x400]; +// jichi 8/25/2013: clipboard removed +void CopyToClipboard(void* str,bool unicode, int len) +{ + if (setman->GetValue(SETTING_CLIPFLAG) && str && len > 0) + { + int size=(len*2|0xF)+1; + if (len>=1022) return; + memcpy(clipboard_buffer,str,len); + *(WORD*)(clipboard_buffer+len)=0; + HGLOBAL hCopy; + LPWSTR copy; + if (OpenClipboard(0)) + { + if (hCopy=GlobalAlloc(GMEM_MOVEABLE,size)) + { + if (copy=(LPWSTR)GlobalLock(hCopy)) + { + if (unicode) + { + memcpy(copy,clipboard_buffer,len+2); + } + else + copy[MB_WC(clipboard_buffer,copy)]=0; + GlobalUnlock(hCopy); + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT,hCopy); + } + } + CloseClipboard(); + } + } +} +void TextThread::CopyLastToClipboard() +{ + // jichi 8/25/2013: clipboard removed + CopyToClipboard(storage+last_sentence,(status&USING_UNICODE)>0,used-last_sentence); +} + +//void TextThread::ResetEditText() +//{ +// //__asm int 3; +// WCHAR str[0x20]; +// swprintf(str,L"%.8X",_ReturnAddress()); +//} + +// jichi 9/25/2013: Removed +//void TextThread::ExportTextToFile(LPWSTR) //filename) +//{ +// HANDLE hFile=IthCreateFile(filename,FILE_WRITE_DATA,0,FILE_OPEN_IF); +// if (hFile==INVALID_HANDLE_VALUE) return; +// EnterCriticalSection(&cs_store); +// IO_STATUS_BLOCK ios; +// LPVOID buffer=storage; +// DWORD len=used; +// BYTE bom[4]={0xFF,0xFE,0,0}; +// LARGE_INTEGER offset={2,0}; +// if ((status&USING_UNICODE)==0) +// { +// len=MB_WC_count((char*)storage,used); +// buffer = new wchar_t[len+1]; +// MB_WC((char*)storage,(wchar_t*)buffer); +// len<<=1; +// } +// NtWriteFile(hFile,0,0,0,&ios,bom,2,0,0); +// NtWriteFile(hFile,0,0,0,&ios,buffer,len,&offset,0); +// NtFlushBuffersFile(hFile,&ios); +// if (buffer !=storage) +// delete[] buffer; +// NtClose(hFile); +// LeaveCriticalSection(&cs_store); +//} + +//void TextThread::SetComment(LPWSTR str) +//{ +// if (comment) +// delete[] comment; +// size_t sz = wcslen(str); +// comment = new wchar_t[sz + 1]; +// comment[sz] = 0; +// wcscpy(comment, str); +//} + +void TextThread::SetNewLineFlag() { status |= BUFF_NEWLINE; } + +bool TextThread::CheckCycle(TextThread* start) +{ + if (link==start||this==start) return true; + if (link==0) return false; + return link->CheckCycle(start); +} +void TextThread::SetNewLineTimer() +{ + if (thread_number == 0) + // jichi 10/27/2013: Not used + timer = 0; //SetTimer(hMainWnd,(UINT_PTR)this, settings->splittingInterval, NewLineConsole); + else + timer = SetTimer(hMainWnd,(UINT_PTR)this, settings->splittingInterval, NewLineBuff); +} + +DWORD TextThread::GetThreadString(LPWSTR str, DWORD max) +{ + DWORD len = 0; + if (max) { + wchar_t buffer[0x200]; + wchar_t c; + if (thread_string == nullptr) + GetEntryString(buffer, 0x200); //This will allocate thread_string. + LPWSTR p1, + end; + for (end = thread_string; *end; end++); + c = thread_string[0]; + thread_string[0] = L':'; + for (p1 = end; *p1 != L':'; p1--); + thread_string[0] = c; + if (p1 == thread_string) + return 0; + p1++; + len = end - p1; + if (len >= max) + len = max; + memcpy(str, p1, len << 1); + str[len] = 0; + } + + return len; +} +void TextThread::UnLinkAll() +{ + if (link) link->UnLinkAll(); + link = 0; + link_number = -1; +} + +// EOF diff --git a/vnr/ith/host/textthread.h b/vnr/ith/host/textthread.h new file mode 100644 index 0000000..44f6013 --- /dev/null +++ b/vnr/ith/host/textthread.h @@ -0,0 +1,136 @@ +#pragma once + +// textthread.h +// 8/23/2013 jichi +// Branch: ITH/TextThread.h, rev 120 + +#include "ith/host/textthread_p.h" +#include // require _InterlockedExchange + +struct RepeatCountNode { + short repeat; + short count; + RepeatCountNode *next; + + //RepeatCountNode() : repeat(0), count(0), next(nullptr) {} +}; + +struct ThreadParameter { + DWORD pid; // jichi: 5/11/2014: The process ID + DWORD hook; + DWORD retn; // jichi 5/11/2014: The return address of the hook + DWORD spl; // jichi 5/11/2014: the processed split value of the hook parameter +}; + +#define CURRENT_SELECT 0x1000 +#define REPEAT_NUMBER_DECIDED 0x2000 +#define BUFF_NEWLINE 0x4000 +#define CYCLIC_REPEAT 0x8000 +#define COUNT_PER_FOWARD 0x200 +#define REPEAT_DETECT 0x10000 +#define REPEAT_SUPPRESS 0x20000 +#define REPEAT_NEWLINE 0x40000 + +class TextThread; +typedef void (* ConsoleCallback)(LPCSTR text); +typedef void (* ConsoleWCallback)(LPCWSTR text); +typedef DWORD (* ThreadOutputFilterCallback)(TextThread *, BYTE *, DWORD, DWORD, PVOID, bool space); // jichi 10/27/2013: Add space +typedef DWORD (* ThreadEventCallback)(TextThread *); + +//extern DWORD split_time,repeat_count,global_filter,cyclic_remove; + +class TextThread : public MyVector +{ +public: + TextThread(DWORD pid, DWORD hook, DWORD retn, DWORD spl, WORD num); + ~TextThread(); + //virtual void CopyLastSentence(LPWSTR str); + //virtual void SetComment(LPWSTR); + //virtual void ExportTextToFile(LPWSTR filename); + + virtual bool CheckCycle(TextThread *start); + virtual DWORD GetThreadString(LPWSTR str, DWORD max); + virtual DWORD GetEntryString(LPWSTR str, DWORD max = 0x200); + + void Reset(); + void AddText(const BYTE *con,int len, bool new_line, bool space); // jichi 10/27/2013: add const; remove console; add space + void RemoveSingleRepeatAuto(const BYTE *con, int &len); // jichi 10/27/2013: add const + void RemoveSingleRepeatForce(BYTE *con, int &len); + void RemoveCyclicRepeat(BYTE *&con, int &len); + void ResetRepeatStatus(); + void AddLineBreak(); + //void ResetEditText(); + void ComboSelectCurrent(); + void UnLinkAll(); + void CopyLastToClipboard(); + + //void AdjustPrevRepeat(DWORD len); + //void PrevRepeatLength(DWORD &len); + + //bool AddToCombo(); + bool RemoveFromCombo(); + + void SetNewLineFlag(); + void SetNewLineTimer(); + + BYTE *GetStore(DWORD *len) { if (len) *len = used; return storage; } + DWORD LastSentenceLen() { return used - last_sentence; } + DWORD PID() const { return tp.pid; } + DWORD Addr() const {return tp.hook; } + DWORD &Status() { return status; } + WORD Number() const { return thread_number; } + WORD &Last() { return last; } + WORD &LinkNumber() { return link_number; } + UINT_PTR &Timer() { return timer; } + ThreadParameter *GetThreadParameter() { return &tp; } + TextThread *&Link() { return link; } + //LPCWSTR GetComment() { return comment; } + + ThreadOutputFilterCallback RegisterOutputCallBack(ThreadOutputFilterCallback cb, PVOID data) + { + app_data = data; + return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&output,(long)cb); + } + + ThreadOutputFilterCallback RegisterFilterCallBack(ThreadOutputFilterCallback cb, PVOID data) + { + app_data = data; + return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&filter,(long)cb); + } + + void SetRepeatFlag() { status |= CYCLIC_REPEAT; } + void ClearNewLineFlag() { status &= ~BUFF_NEWLINE; } + void ClearRepeatFlag() { status &= ~CYCLIC_REPEAT; } + +protected: + void AddTextDirect(const BYTE *con, int len, bool space); // jichi 10/27/2013: add const; add space; change to protected + +private: + ThreadParameter tp; + + WORD thread_number, + link_number; + WORD last, + align_space; + WORD repeat_single; + WORD repeat_single_current; + WORD repeat_single_count; + WORD repeat_detect_count; + RepeatCountNode *head; + + TextThread *link; + ThreadOutputFilterCallback filter; // jichi 10/27/2013: Remove filter + ThreadOutputFilterCallback output; + PVOID app_data; + //LPWSTR comment, + LPWSTR thread_string; + UINT_PTR timer; + DWORD status,repeat_detect_limit; + DWORD last_sentence, + prev_sentence, + sentence_length, + repeat_index, + last_time; +}; + +// EOF diff --git a/vnr/ith/host/textthread_p.h b/vnr/ith/host/textthread_p.h new file mode 100644 index 0000000..40a5d58 --- /dev/null +++ b/vnr/ith/host/textthread_p.h @@ -0,0 +1,155 @@ +#pragma once +// textthread_p.h +// 8/14/2013 jichi +// Branch: ITH/main_template.h, rev 66 + +#include "config.h" + +template +void Release(const T &p) { delete p; } + +// Prevent memory release. +// Used when T is basic types and will be automatically released (on stack). +#define MK_BASIC_TYPE(T) \ + template<> \ + void Release(const T &p) {} + +template +struct BinaryEqual { + bool operator ()(const T &a, const T &b, DWORD) { return a == b; } +}; + +template > +class MyVector +{ +public: + MyVector() : size(default_size), used(0) + { + InitializeCriticalSection(&cs_store); + storage = new T[size]; + // jichi 9/21/2013: zero memory + // This would cause trouble if T is not an atomic type + ITH_MEMSET_HEAP(storage, 0, sizeof(T) * size); + } + + virtual ~MyVector() + { + if (storage) + delete[] storage; + DeleteCriticalSection(&cs_store); + storage = 0; + } + + void Reset() + { + EnterCriticalSection(&cs_store); + for (int i = 0; i < used; i++) { + Release(storage[i]); + storage[i] = T(); + } + used = 0; + LeaveCriticalSection(&cs_store); + } + void Remove(int index) + { + if (index>=used) + return; + Release(storage[index]); + for (int i = index; i < used; i++) + storage[i] = storage[i+1]; + used--; + } + void ClearMemory(int offset, int clear_size) + { + if (clear_size < 0) + return; + EnterCriticalSection(&cs_store); + if (offset+clear_size <= size) + memset(storage+offset, 0, clear_size * sizeof(T)); // jichi 11/30/2013: This is the original code of ITH + LeaveCriticalSection(&cs_store); + //else __asm int 3 + } + int AddToStore(T *con,int amount) + { + if (amount <= 0 || con == 0) + return 0; + int status = 0; + EnterCriticalSection(&cs_store); + if (amount + used + 2 >= size) { + while (amount + used + 2 >= size) + size<<=1; + T *temp; + if (size * sizeof(T) < 0x1000000) { + temp = new T[size]; + if (size > used) + ITH_MEMSET_HEAP(temp, 0, (size - used) * sizeof(T)); // jichi 9/25/2013: zero memory + memcpy(temp, storage, used * sizeof(T)); + } else { + size = default_size; + temp = new T[size]; + ITH_MEMSET_HEAP(temp, 0, sizeof(T) * size); // jichi 9/25/2013: zero memory + used = 0; + status = 1; + } + delete[] storage; + storage = temp; + } + memcpy(storage+used, con, amount * sizeof(T)); + used += amount; + LeaveCriticalSection(&cs_store); + return status; + } + int Find(const T &item, int start = 0, DWORD control = 0) + { + int c = -1; + for (int i=start; i < used; i++) + if (fCmp(storage[i],item,control)) { + c=i; + break; + } + //if (storage[i]==item) {c=i;break;} + return c; + } + int Used() const { return used; } + T *Storage() const { return storage; } + void LockVector() { EnterCriticalSection(&cs_store); } + void UnlockVector() { LeaveCriticalSection(&cs_store); } +protected: + CRITICAL_SECTION cs_store; + int size, + used; + T *storage; + fComp fCmp; +}; + +// EOF + +/* +#ifndef ITH_STACK +#define ITH_STACK +template +class MyStack +{ +public: + MyStack(): index(0) {} + void push_back(const T& e) + { + if (index + +// mono/io-layer/uglify.h +typedef int8_t gint8; +typedef int32_t gint32; +typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled + +typedef gint8 mono_byte; +typedef gunichar2 mono_unichar2; + +// mono/metadata/object.h + +typedef mono_byte MonoBoolean; + +struct MonoArray; +struct MonoDelegate; +struct MonoException; +struct MonoString; +struct MonoThreadsSync; +struct MonoThread; +struct MonoVTable; + +struct MonoObject { + MonoVTable *vtable; + MonoThreadsSync *synchronisation; +}; + +struct MonoString { + MonoObject object; + gint32 length; + gunichar2 chars[0]; +}; + +// EOF diff --git a/vnr/ith/import/ppsspp/funcinfo.h b/vnr/ith/import/ppsspp/funcinfo.h new file mode 100644 index 0000000..ba134fc --- /dev/null +++ b/vnr/ith/import/ppsspp/funcinfo.h @@ -0,0 +1,106 @@ +#pragma once +//#include "ith/common/const.h" + +// ppsspp/funcinfo.h +// 12/26/2014 +// See: https://github.com/hrydgard/ppsspp + +// Core/HLE (High Level Emulator) +// - sceCcc +// #void sceCccSetTable(u32 jis2ucs, u32 ucs2jis) +// int sceCccUTF8toUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr) +// int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr) +// int sceCccUTF16toUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr) +// int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr) +// int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr) +// int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr) +// int sceCccStrlenUTF8(u32 strAddr) +// int sceCccStrlenUTF16(u32 strAddr) +// int sceCccStrlenSJIS(u32 strAddr) +// u32 sceCccEncodeUTF8(u32 dstAddrAddr, u32 ucs) +// void sceCccEncodeUTF16(u32 dstAddrAddr, u32 ucs) +// u32 sceCccEncodeSJIS(u32 dstAddrAddr, u32 jis) +// u32 sceCccDecodeUTF8(u32 dstAddrAddr) +// u32 sceCccDecodeUTF16(u32 dstAddrAddr) +// u32 sceCccDecodeSJIS(u32 dstAddrAddr) +// int sceCccIsValidUTF8(u32 c) +// int sceCccIsValidUTF16(u32 c) +// int sceCccIsValidSJIS(u32 c) +// int sceCccIsValidUCS2(u32 c) +// int sceCccIsValidUCS4(u32 c) +// int sceCccIsValidJIS(u32 c) +// int sceCccIsValidUnicode(u32 c) +// #u32 sceCccSetErrorCharUTF8(u32 c) +// #u32 sceCccSetErrorCharUTF16(u32 c) +// #u32 sceCccSetErrorCharSJIS(u32 c) +// u32 sceCccUCStoJIS(u32 c, u32 alt) +// u32 sceCccJIStoUCS(u32 c, u32 alt) +// - sceFont: search charCode +// int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) +// int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) +// int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) +// int sceFontGetShadowImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) +// int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) +// int sceFontGetCharGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) +// #int sceFontSetAltCharacterCode(u32 fontLibHandle, u32 charCode) +// int sceFontGetShadowGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) +// int sceFontGetShadowGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) +// - sceKernelInterrupt +// u32 sysclib_strcat(u32 dst, u32 src) +// int sysclib_strcmp(u32 dst, u32 src) +// u32 sysclib_strcpy(u32 dst, u32 src) +// u32 sysclib_strlen(u32 src) +// +// Sample debug string: +// 006EFD8E PUSH PPSSPPWi.00832188 ASCII "sceCccEncodeSJIS(%08x, U+%04x)" +// Corresponding source code in sceCcc: +// ERROR_LOG(HLE, "sceCccEncodeSJIS(%08x, U+%04x): invalid pointer", dstAddrAddr, jis); + +struct PPSSPPFunction +{ + const wchar_t *hookName; // hook name + size_t argIndex; // argument index + unsigned long hookType; // hook parameter type + unsigned long hookSplit; // hook parameter split, positive: stack, negative: registers + const char *pattern; // debug string used within the function +}; + +// jichi 7/14/2014: UTF-8 is treated as STRING +// http://867258173.diandian.com/post/2014-06-26/40062099618 +// sceFontGetCharGlyphImage_Clip +// Sample game: [KID] Monochrome: sceFontGetCharInfo, sceFontGetCharGlyphImage_Clip +// +// Example: { L"sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" } +// Text is at arg2, using arg1 as split +#define PPSSPP_FUNCTIONS_INITIALIZER \ + { L"sceCccStrlenSJIS", 1, USING_STRING, 0, "sceCccStrlenSJIS(" } \ + , { L"sceCccStrlenUTF8", 1, USING_UTF8, 0, "sceCccStrlenUTF8(" } \ + , { L"sceCccStrlenUTF16", 1, USING_UNICODE, 0, "sceCccStrlenUTF16(" } \ +\ + , { L"sceCccSJIStoUTF8", 3, USING_UTF8, 0, "sceCccSJIStoUTF8(" } \ + , { L"sceCccSJIStoUTF16", 3, USING_STRING, 0, "sceCccSJIStoUTF16(" } \ + , { L"sceCccUTF8toSJIS", 3, USING_UTF8, 0, "sceCccUTF8toSJIS(" } \ + , { L"sceCccUTF8toUTF16", 3, USING_UTF8, 0, "sceCccUTF8toUTF16(" } \ + , { L"sceCccUTF16toSJIS", 3, USING_UNICODE, 0, "sceCccUTF16toSJIS(" } \ + , { L"sceCccUTF16toUTF8", 3, USING_UNICODE, 0, "sceCccUTF16toUTF8(" } \ +\ + , { L"sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" } \ + , { L"sceFontGetShadowInfo", 2, USING_UNICODE, 4, "sceFontGetShadowInfo("} \ + , { L"sceFontGetCharImageRect", 2, USING_UNICODE, 4, "sceFontGetCharImageRect(" } \ + , { L"sceFontGetShadowImageRect", 2, USING_UNICODE, 4, "sceFontGetShadowImageRect(" } \ + , { L"sceFontGetCharGlyphImage", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage(" } \ + , { L"sceFontGetCharGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage_Clip(" } \ + , { L"sceFontGetShadowGlyphImage", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage(" } \ + , { L"sceFontGetShadowGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage_Clip(" } \ +\ + , { L"sysclib_strcat", 2, USING_STRING, 0, "Untested sysclib_strcat(" } \ + , { L"sysclib_strcpy", 2, USING_STRING, 0, "Untested sysclib_strcpy(" } \ + , { L"sysclib_strlen", 1, USING_STRING, 0, "Untested sysclib_strlen(" } + + // Disabled as I am not sure how to deal with the source string + //, { L"sceCccEncodeSJIS", 2, USING_STRING, 0, "sceCccEncodeSJIS(" } + //, { L"sceCccEncodeUTF8", 2, USING_UTF8, 0, "sceCccEncodeUTF8(" } + //, { L"sceCccEncodeUTF16", 2, USING_UNICODE, 0, "sceCccEncodeUTF16(" } + //, { L"sysclib_strcmp", 2, USING_STRING, 0, "Untested sysclib_strcmp(" } + +// EOF