starting commit
This commit is contained in:
parent
c92c840bdb
commit
62a204dc85
38
vnr/ith/hook/hook.h
Normal file
38
vnr/ith/hook/hook.h
Normal file
@ -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
|
56
vnr/ith/hook/hook.pro
Normal file
56
vnr/ith/hook/hook.pro
Normal file
@ -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
|
416
vnr/ith/hook/main.cc
Normal file
416
vnr/ith/hook/main.cc
Normal file
@ -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 <boost/foreach.hpp>
|
||||||
|
//#include "md5.h"
|
||||||
|
//#include <ITH\AVL.h>
|
||||||
|
//#include <ITH\ntdll.h>
|
||||||
|
|
||||||
|
// 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<char, FunctionInfo, SCMP, SCPY, SLEN> *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<char *,FunctionInfo> *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<char, FunctionInfo, SCMP, SCPY, SLEN>;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
*/
|
346
vnr/ith/hook/rpc/pipe.cc
Normal file
346
vnr/ith/hook/rpc/pipe.cc
Normal file
@ -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 <ITH\AVL.h>
|
||||||
|
//#include <ITH\ntdll.h>
|
||||||
|
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
|
590
vnr/ith/hook/tree/avl.h
Normal file
590
vnr/ith/hook/tree/avl.h
Normal file
@ -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 T, int stack_size>
|
||||||
|
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 <class T, class D>
|
||||||
|
struct TreeNode
|
||||||
|
{
|
||||||
|
//typedef TreeNode<T, D> 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<class T,class D>
|
||||||
|
struct NodePath
|
||||||
|
{
|
||||||
|
NodePath() { memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
|
||||||
|
NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
|
||||||
|
TreeNode<T,D> *Node;
|
||||||
|
union { char factor; int fact; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class D, class fComp, class fCopy, class fLength>
|
||||||
|
class AVLTree
|
||||||
|
{
|
||||||
|
fComp fCmp;
|
||||||
|
fCopy fCpy;
|
||||||
|
fLength fLen;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TreeNode<T*, D> head;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// - Construction -
|
||||||
|
AVLTree() {}
|
||||||
|
|
||||||
|
virtual ~AVLTree() { DeleteAll(); }
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
|
||||||
|
TreeNode<T*, D> *TreeRoot() const { return head.Left; }
|
||||||
|
|
||||||
|
// - Actions -
|
||||||
|
|
||||||
|
void DeleteAll()
|
||||||
|
{
|
||||||
|
while (head.Left)
|
||||||
|
DeleteRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*, D> *Insert(const T *key, const D &data)
|
||||||
|
{
|
||||||
|
if (head.Left) {
|
||||||
|
MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
|
||||||
|
TreeNode<T*,D> *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<T*, D>(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<T*, D> *ModifyNode;
|
||||||
|
factor = fCmp(key, BalanceNode->key);
|
||||||
|
//factor=key<BalanceNode->key ? 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<T *, D>(new_key, data);
|
||||||
|
head.rank++;
|
||||||
|
_IncreaseHeight();
|
||||||
|
return head.Left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Delete(T *key)
|
||||||
|
{
|
||||||
|
NodePath<T*,D> PathNode;
|
||||||
|
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
|
||||||
|
path.push_back(NodePath<T*,D>(&head,-1));
|
||||||
|
TreeNode<T*,D> *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<T*,D>(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<T*,D>(TryNode,factor));
|
||||||
|
while (SuccNode) {
|
||||||
|
path.push_back(NodePath<T*,D>(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; i<path.size(); i++)
|
||||||
|
if (path[i].factor==-1)
|
||||||
|
path[i].Node->rank--;
|
||||||
|
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<T*,D> *Search(const T *key)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *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<T*,D> *SearchIndex(unsigned int rank)
|
||||||
|
{
|
||||||
|
unsigned int r = head.rank;
|
||||||
|
if (rank == -1)
|
||||||
|
return 0;
|
||||||
|
if (++rank>=r)
|
||||||
|
return 0;
|
||||||
|
TreeNode<T*,D> *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<T*,D> *Begin()
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node = head.Left;
|
||||||
|
if (Node)
|
||||||
|
while (Node->Left) Node = Node->Left;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*,D> *End()
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node=head.Left;
|
||||||
|
if (Node)
|
||||||
|
while (Node->Right) Node = Node->Right;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
unsigned int Count() const { return head.rank - 1; }
|
||||||
|
|
||||||
|
template <class Fn>
|
||||||
|
Fn TraverseTree(Fn &f)
|
||||||
|
{ return TraverseTreeNode(head.Left,f); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool DeleteRoot()
|
||||||
|
{
|
||||||
|
NodePath<T*,D> PathNode;
|
||||||
|
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
|
||||||
|
path.push_back(NodePath<T*,D>(&head,-1));
|
||||||
|
TreeNode<T*,D> *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<T*,D>(TryNode,factor));
|
||||||
|
while (SuccNode) {
|
||||||
|
path.push_back(NodePath<T*,D>(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;i<path.size();i++)
|
||||||
|
if (path[i].factor==-1)
|
||||||
|
path[i].Node->rank--;
|
||||||
|
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 <class Fn>
|
||||||
|
Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
|
||||||
|
{
|
||||||
|
if (Node) {
|
||||||
|
if (Node->Left)
|
||||||
|
TraverseTreeNode(Node->Left,f);
|
||||||
|
f(Node);
|
||||||
|
if (Node->Right)
|
||||||
|
TraverseTreeNode(Node->Right,f);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *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<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *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<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
|
||||||
|
TreeNode<T*,D> *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<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *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<T*,D>*)++k;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _DecreaseHeight()
|
||||||
|
{
|
||||||
|
unsigned int k = (unsigned int)head.Right;
|
||||||
|
head.Right = (TreeNode<T*,D>*)--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
|
59
vnr/ith/hookxp/hookxp.pro
Normal file
59
vnr/ith/hookxp/hookxp.pro
Normal file
@ -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
|
66
vnr/ith/host/CMakeLists.txt
Normal file
66
vnr/ith/host/CMakeLists.txt
Normal file
@ -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-
|
||||||
|
$<$<CONFIG:Release>:>
|
||||||
|
$<$<CONFIG:Debug>:>
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
46
vnr/ith/host/SettingManager.h
Normal file
46
vnr/ith/host/SettingManager.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "config.h"
|
||||||
|
#include <intrin.h>
|
||||||
|
#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];
|
||||||
|
|
||||||
|
};
|
587
vnr/ith/host/avl_p.h
Normal file
587
vnr/ith/host/avl_p.h
Normal file
@ -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 T, int stack_size>
|
||||||
|
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 <class T, class D>
|
||||||
|
struct IHFSERVICE TreeNode
|
||||||
|
{
|
||||||
|
//typedef TreeNode<T, D> 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<class T,class D>
|
||||||
|
struct NodePath
|
||||||
|
{
|
||||||
|
NodePath() { memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
|
||||||
|
NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
|
||||||
|
TreeNode<T,D> *Node;
|
||||||
|
union { char factor; int fact; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class D, class fComp, class fCopy, class fLength>
|
||||||
|
class IHFSERVICE AVLTree
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
TreeNode<T*, D> head;
|
||||||
|
fComp fCmp;
|
||||||
|
fCopy fCpy;
|
||||||
|
fLength fLen;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// - Construction -
|
||||||
|
AVLTree() {}
|
||||||
|
|
||||||
|
virtual ~AVLTree() { DeleteAll(); }
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
|
||||||
|
TreeNode<T*, D> *TreeRoot() const { return head.Left; }
|
||||||
|
|
||||||
|
// - Actions -
|
||||||
|
|
||||||
|
void DeleteAll()
|
||||||
|
{
|
||||||
|
while (head.Left)
|
||||||
|
DeleteRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*, D> *Insert(const T *key, const D &data)
|
||||||
|
{
|
||||||
|
if (head.Left) {
|
||||||
|
MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
|
||||||
|
TreeNode<T*,D> *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<T*, D>(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<T*, D> *ModifyNode;
|
||||||
|
factor = fCmp(key, BalanceNode->key);
|
||||||
|
//factor=key<BalanceNode->key ? 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<T *, D>(new_key, data);
|
||||||
|
head.rank++;
|
||||||
|
_IncreaseHeight();
|
||||||
|
return head.Left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Delete(T *key)
|
||||||
|
{
|
||||||
|
NodePath<T*,D> PathNode;
|
||||||
|
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
|
||||||
|
path.push_back(NodePath<T*,D>(&head,-1));
|
||||||
|
TreeNode<T*,D> *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<T*,D>(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<T*,D>(TryNode,factor));
|
||||||
|
while (SuccNode) {
|
||||||
|
path.push_back(NodePath<T*,D>(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; i<path.size(); i++)
|
||||||
|
if (path[i].factor==-1)
|
||||||
|
path[i].Node->rank--;
|
||||||
|
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<T*,D> *Search(const T *key)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *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<T*,D> *SearchIndex(unsigned int rank)
|
||||||
|
{
|
||||||
|
unsigned int r = head.rank;
|
||||||
|
if (rank == -1)
|
||||||
|
return 0;
|
||||||
|
if (++rank>=r)
|
||||||
|
return 0;
|
||||||
|
TreeNode<T*,D> *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<T*,D> *Begin()
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node = head.Left;
|
||||||
|
if (Node)
|
||||||
|
while (Node->Left) Node = Node->Left;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*,D> *End()
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node=head.Left;
|
||||||
|
if (Node)
|
||||||
|
while (Node->Right) Node = Node->Right;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
unsigned int Count() const { return head.rank - 1; }
|
||||||
|
|
||||||
|
template <class Fn>
|
||||||
|
Fn TraverseTree(Fn &f)
|
||||||
|
{ return TraverseTreeNode(head.Left,f); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool DeleteRoot()
|
||||||
|
{
|
||||||
|
NodePath<T*,D> PathNode;
|
||||||
|
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
|
||||||
|
path.push_back(NodePath<T*,D>(&head,-1));
|
||||||
|
TreeNode<T*,D> *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<T*,D>(TryNode,factor));
|
||||||
|
while (SuccNode) {
|
||||||
|
path.push_back(NodePath<T*,D>(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;i<path.size();i++)
|
||||||
|
if (path[i].factor==-1)
|
||||||
|
path[i].Node->rank--;
|
||||||
|
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 <class Fn>
|
||||||
|
Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
|
||||||
|
{
|
||||||
|
if (Node) {
|
||||||
|
if (Node->Left)
|
||||||
|
TraverseTreeNode(Node->Left,f);
|
||||||
|
f(Node);
|
||||||
|
if (Node->Right)
|
||||||
|
TraverseTreeNode(Node->Right,f);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *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<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *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<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
|
||||||
|
TreeNode<T*,D> *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<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *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<T*,D>*)++k;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _DecreaseHeight()
|
||||||
|
{
|
||||||
|
unsigned int k = (unsigned int)head.Right;
|
||||||
|
head.Right = (TreeNode<T*,D>*)--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
|
16
vnr/ith/host/config.h
Normal file
16
vnr/ith/host/config.h
Normal file
@ -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
|
945
vnr/ith/host/hookman.cc
Normal file
945
vnr/ith/host/hookman.cc
Normal file
@ -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 <emmintrin.h>
|
||||||
|
|
||||||
|
namespace { // unnamed
|
||||||
|
//enum { MAX_ENTRY = 0x40 };
|
||||||
|
|
||||||
|
#define HM_LOCK win_mutex_lock<HookManager::mutex_type> 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<ThreadParameter *,DWORD> *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() <new_thread_number)
|
||||||
|
new_thread_number = it->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<ThreadParameter *,DWORD> *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<ThreadParameter *, DWORD> *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<MAX_REGISTER;i++)
|
||||||
|
if (record[i].pid_register==pid) break;
|
||||||
|
if (i<MAX_REGISTER)
|
||||||
|
{
|
||||||
|
record[i].engine_register|=type;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
ProcessRecord *HookManager::GetProcessRecord(DWORD pid)
|
||||||
|
{
|
||||||
|
HM_LOCK;
|
||||||
|
//EnterCriticalSection(&hmcs);
|
||||||
|
for (int i = 0; i < MAX_REGISTER; i++)
|
||||||
|
if (record[i].pid_register == pid)
|
||||||
|
return record + i;
|
||||||
|
return nullptr;
|
||||||
|
//ProcessRecord *pr = i < MAX_REGISTER ? record + i : nullptr;
|
||||||
|
//LeaveCriticalSection(&hmcs);
|
||||||
|
//return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD HookManager::GetProcessIDByPath(LPCWSTR str)
|
||||||
|
{
|
||||||
|
WCHAR path[MAX_PATH];
|
||||||
|
for (int i = 0; i < 8 && record[i].process_handle; i++) {
|
||||||
|
::GetProcessPath(record[i].process_handle, path);
|
||||||
|
if (_wcsicmp(path,str) == 0)
|
||||||
|
return record[i].pid_register;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD HookManager::GetCurrentPID() { return current_pid; }
|
||||||
|
|
||||||
|
HANDLE HookManager::GetCmdHandleByPID(DWORD pid)
|
||||||
|
{
|
||||||
|
HM_LOCK;
|
||||||
|
//EnterCriticalSection(&hmcs);
|
||||||
|
for (int i = 0; i < MAX_REGISTER; i++)
|
||||||
|
if (record[i].pid_register == pid)
|
||||||
|
return cmd_pipes[i];
|
||||||
|
return nullptr;
|
||||||
|
//HANDLE h = i < MAX_REGISTER ? cmd_pipes[i] : 0;
|
||||||
|
//LeaveCriticalSection(&hmcs);
|
||||||
|
//return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
MK_BASIC_TYPE(DWORD)
|
||||||
|
MK_BASIC_TYPE(LPVOID)
|
||||||
|
|
||||||
|
//DWORD Hash(LPCWSTR module, int length)
|
||||||
|
//{
|
||||||
|
// bool flag = (length==-1);
|
||||||
|
// DWORD hash = 0;
|
||||||
|
// for (;*module && (flag || length--); module++)
|
||||||
|
// hash = ((hash>>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
|
144
vnr/ith/host/hookman.h
Normal file
144
vnr/ith/host/hookman.h
Normal file
@ -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<TextThread *, 0x40>
|
||||||
|
{
|
||||||
|
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<ThreadParameter, DWORD, TCmp, TCpy, TLen>
|
||||||
|
{
|
||||||
|
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<CRITICAL_SECTION> 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
|
18
vnr/ith/host/host.pri
Normal file
18
vnr/ith/host/host.pri
Normal file
@ -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
|
54
vnr/ith/host/host.pro
Normal file
54
vnr/ith/host/host.pro
Normal file
@ -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
|
611
vnr/ith/host/main.cc
Normal file
611
vnr/ith/host/main.cc
Normal file
@ -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 <commctrl.h>
|
||||||
|
|
||||||
|
//#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
|
322
vnr/ith/host/pipe.cc
Normal file
322
vnr/ith/host/pipe.cc
Normal file
@ -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<const WORD *>(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
|
14
vnr/ith/host/settings.h
Normal file
14
vnr/ith/host/settings.h
Normal file
@ -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
|
36
vnr/ith/host/srv.h
Normal file
36
vnr/ith/host/srv.h
Normal file
@ -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
|
46
vnr/ith/host/srv_p.h
Normal file
46
vnr/ith/host/srv_p.h
Normal file
@ -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
|
787
vnr/ith/host/textthread.cc
Normal file
787
vnr/ith/host/textthread.cc
Normal file
@ -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_count<repeat_single&&
|
||||||
|
last == *text) {
|
||||||
|
len = 0;
|
||||||
|
repeat_single_count++;
|
||||||
|
} else {
|
||||||
|
last = *text;
|
||||||
|
repeat_single_count=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (status & REPEAT_NUMBER_DECIDED) {
|
||||||
|
if (++repeat_detect_count>MIN_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_count<setman->GetValue(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<BYTE *>(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<BYTE *>(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
|
136
vnr/ith/host/textthread.h
Normal file
136
vnr/ith/host/textthread.h
Normal file
@ -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 <intrin.h> // 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<BYTE, 0x200>
|
||||||
|
{
|
||||||
|
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
|
155
vnr/ith/host/textthread_p.h
Normal file
155
vnr/ith/host/textthread_p.h
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#pragma once
|
||||||
|
// textthread_p.h
|
||||||
|
// 8/14/2013 jichi
|
||||||
|
// Branch: ITH/main_template.h, rev 66
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
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<T>(const T &p) {}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct BinaryEqual {
|
||||||
|
bool operator ()(const T &a, const T &b, DWORD) { return a == b; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, int default_size, class fComp=BinaryEqual<T> >
|
||||||
|
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<T>(storage[i]);
|
||||||
|
storage[i] = T();
|
||||||
|
}
|
||||||
|
used = 0;
|
||||||
|
LeaveCriticalSection(&cs_store);
|
||||||
|
}
|
||||||
|
void Remove(int index)
|
||||||
|
{
|
||||||
|
if (index>=used)
|
||||||
|
return;
|
||||||
|
Release<T>(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 T, int default_size>
|
||||||
|
class MyStack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyStack(): index(0) {}
|
||||||
|
void push_back(const T& e)
|
||||||
|
{
|
||||||
|
if (index<default_size)
|
||||||
|
s[index++]=e;
|
||||||
|
}
|
||||||
|
void pop_back()
|
||||||
|
{
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
T& back()
|
||||||
|
{
|
||||||
|
return s[index-1];
|
||||||
|
}
|
||||||
|
T& operator[](int i) {return s[i];}
|
||||||
|
int size() {return index;}
|
||||||
|
private:
|
||||||
|
int index;
|
||||||
|
T s[default_size];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
*/
|
52
vnr/ith/import/mono/funcinfo.h
Normal file
52
vnr/ith/import/mono/funcinfo.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// mono/funcinfo.h
|
||||||
|
// 12/26/2014
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
|
||||||
|
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
|
||||||
|
|
||||||
|
//#include "ith/import/mono/types.h"
|
||||||
|
|
||||||
|
// MonoString* mono_string_new (MonoDomain *domain,
|
||||||
|
// const char *text);
|
||||||
|
// MonoString* mono_string_new_len (MonoDomain *domain,
|
||||||
|
// const char *text,
|
||||||
|
// guint length);
|
||||||
|
// MonoString* mono_string_new_size (MonoDomain *domain,
|
||||||
|
// gint32 len);
|
||||||
|
// MonoString* mono_string_new_utf16 (MonoDomain *domain,
|
||||||
|
// const guint16 *text,
|
||||||
|
// gint32 len);
|
||||||
|
// MonoString* mono_string_from_utf16 (gunichar2 *data);
|
||||||
|
// mono_unichar2* mono_string_to_utf16 (MonoString *s);
|
||||||
|
// char* mono_string_to_utf8 (MonoString *s);
|
||||||
|
// gboolean mono_string_equal (MonoString *s1,
|
||||||
|
// MonoString *s2);
|
||||||
|
// guint mono_string_hash (MonoString *s);
|
||||||
|
// MonoString* mono_string_intern (MonoString *str);
|
||||||
|
// MonoString* mono_string_is_interned (MonoString *o);
|
||||||
|
// MonoString* mono_string_new_wrapper (const char *text);
|
||||||
|
// gunichar2* mono_string_chars (MonoString *s);
|
||||||
|
// int mono_string_length (MonoString *s);
|
||||||
|
// gunichar2* mono_unicode_from_external (const gchar *in, gsize *bytes);
|
||||||
|
// gchar* mono_unicode_to_external (const gunichar2 *uni);
|
||||||
|
// gchar* mono_utf8_from_external (const gchar *in);
|
||||||
|
|
||||||
|
struct MonoFunction {
|
||||||
|
const wchar_t *hookName;
|
||||||
|
const char *functionName;
|
||||||
|
size_t textIndex; // argument index, starting from 0
|
||||||
|
size_t lengthIndex; // argument index, start from 0
|
||||||
|
unsigned long hookType; // HookParam type
|
||||||
|
void *text_fun; // HookParam::text_fun_t
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MONO_FUNCTIONS_INITIALIZER \
|
||||||
|
{ L"mono_string_to_utf8", "mono_string_to_utf8", 0, 0, USING_UNICODE, SpecialHookMonoString } \
|
||||||
|
, { L"mono_string_to_utf16", "mono_string_to_utf16", 0, 0, USING_UNICODE, SpecialHookMonoString } \
|
||||||
|
, { L"mono_utf8_from_external", "mono_utf8_from_external", 1, 0, USING_STRING|USING_UTF8, nullptr } \
|
||||||
|
, { L"mono_string_from_utf16", "mono_string_from_utf16", 1, 0, USING_UNICODE, nullptr } \
|
||||||
|
, { L"mono_unicode_from_external", "mono_unicode_from_external", 1, 2, USING_UNICODE, nullptr } \
|
||||||
|
, { L"mono_unicode_to_external", "mono_unicode_to_external", 1, 0, USING_UNICODE, nullptr }
|
||||||
|
|
||||||
|
// EOF
|
10
vnr/ith/import/mono/mono.pri
Normal file
10
vnr/ith/import/mono/mono.pri
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# mono.pri
|
||||||
|
# 12/26/2014 jichi
|
||||||
|
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/funcinfo.h \
|
||||||
|
$$PWD/types.h
|
||||||
|
|
||||||
|
# EOF
|
41
vnr/ith/import/mono/types.h
Normal file
41
vnr/ith/import/mono/types.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// mono/types.h
|
||||||
|
// 12/26/2014
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
|
||||||
|
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// 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
|
106
vnr/ith/import/ppsspp/funcinfo.h
Normal file
106
vnr/ith/import/ppsspp/funcinfo.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user