mireado

starting commit

#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
# 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
// 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;
}
*/
// 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
#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
# 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
# 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
)
/* 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];
};
\ No newline at end of file
#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
#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
// 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
#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
# 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
# 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
// 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
// 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
#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
#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
#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
// 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
#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
#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
*/
#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
# mono.pri
# 12/26/2014 jichi
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/funcinfo.h \
$$PWD/types.h
# EOF
#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
#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