forked from Public-Mirror/Textractor
delete more unused stuff
This commit is contained in:
parent
011fec97cf
commit
6b63b91326
@ -1,20 +0,0 @@
|
|||||||
# 6/6/2012
|
|
||||||
# IHF.dll
|
|
||||||
# Skip swprintf and MessageBox statements in GetDebugPriv
|
|
||||||
#
|
|
||||||
# SVN checkout: 2012/6/6
|
|
||||||
# - IHF/main.cpp: 2012/4/8
|
|
||||||
# - IHF.{dll,lib}: 2012/6/5
|
|
||||||
|
|
||||||
6F753055 CALL DWORD PTR DS:ntdll.ZwAdjustPrivilegesTOken
|
|
||||||
6F75305B TEST EAX,EAX
|
|
||||||
6F75305B JNE SHORT 6F753077 => JNE SHORT 6F75309F
|
|
||||||
...
|
|
||||||
6F753077 PUSH EAX
|
|
||||||
...
|
|
||||||
6F75309F MOVE EAX,DWORD PTR SS:[ESP]
|
|
||||||
6F7530A2 PUSH EAX
|
|
||||||
6F7530A3 CALL DWORD PTR DS:ntdll.NtClose
|
|
||||||
...
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,583 +0,0 @@
|
|||||||
// Host_p.cc
|
|
||||||
// 10/15/2011 jichi
|
|
||||||
|
|
||||||
#include "texthook/ihf_p.h"
|
|
||||||
#include "texthook/ith_p.h"
|
|
||||||
#include "texthook/textthread_p.h"
|
|
||||||
#include "host/host.h"
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
#include "ithsys/ithsys.h"
|
|
||||||
#include "wintimer/wintimer.h"
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
|
|
||||||
#ifdef WITH_LIB_WINMAKER
|
|
||||||
# include "winmaker/winmaker.h"
|
|
||||||
#endif // WITH_LIB_WINMAKER
|
|
||||||
|
|
||||||
//#define ITH_RUNNING_EVENT L"ITH_PIPE_EXIST"
|
|
||||||
//#define ITH_RUNNING_MUTEX L"ITH_RUNNING"
|
|
||||||
//#define ITH_MUTEX_NAME L"ITH_MAIN_RUNNING"
|
|
||||||
|
|
||||||
//#define DEBUG "ihf_p.cc"
|
|
||||||
#include "sakurakit/skdebug.h"
|
|
||||||
|
|
||||||
//#define ITH_WITH_LINK
|
|
||||||
|
|
||||||
// - Construction -
|
|
||||||
|
|
||||||
//bool Ihf::debug_ = true;
|
|
||||||
bool Ihf::enabled_ = true;
|
|
||||||
|
|
||||||
//Settings *Ihf::settings_;
|
|
||||||
HookManager *Ihf::hookManager_;
|
|
||||||
qint64 Ihf::messageInterval_ = 250; // 0.25 secs by default, larger than the split_time (0.2sec) in ITH::setman
|
|
||||||
WId Ihf::parentWindow_;
|
|
||||||
|
|
||||||
QHash<TextThread *, TextThreadDelegate *> Ihf::threadDelegates_;
|
|
||||||
//QHash<TextThreadDelegate *, TextThreadDelegate *> Ihf::linkedDelegates_;
|
|
||||||
QHash<QString, ulong> Ihf::hookAddresses_;
|
|
||||||
|
|
||||||
char Ihf::keptThreadName_[ITH_THREAD_NAME_CAPACITY];
|
|
||||||
|
|
||||||
bool Ihf::whitelistEnabled_;
|
|
||||||
qint32 Ihf::whitelist_[Ihf::WhitelistSize];
|
|
||||||
|
|
||||||
// Debugging output
|
|
||||||
//void Ihf::consoleOutput(const char *text)
|
|
||||||
//{ if (debug_) qDebug() << "texthook:console:" << text; }
|
|
||||||
|
|
||||||
//void Ihf::consoleOutputW(const wchar_t *text)
|
|
||||||
//{ if (debug_) qDebug() << "texthook:console:" << QString::fromWCharArray(text); }
|
|
||||||
|
|
||||||
void Ihf::init()
|
|
||||||
{
|
|
||||||
IthInitSystemService();
|
|
||||||
Host_Init();
|
|
||||||
}
|
|
||||||
void Ihf::destroy()
|
|
||||||
{
|
|
||||||
Host_Destroy();
|
|
||||||
IthCloseSystemService();
|
|
||||||
}
|
|
||||||
|
|
||||||
// See also: HelloITH/main.cpp
|
|
||||||
bool Ihf::load()
|
|
||||||
{
|
|
||||||
// 12/20/2013: This would crash the error of failure to create QTimer
|
|
||||||
//if (!parentWindow_)
|
|
||||||
|
|
||||||
//::wm_register_hidden_class("vnrtexthook.class");
|
|
||||||
//parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook.class", "vnrtexthook");
|
|
||||||
|
|
||||||
DOUT("enter");
|
|
||||||
if (hookManager_) {
|
|
||||||
DOUT("leave: already loaded");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single instance protection
|
|
||||||
//HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, ITH_MUTEX_NAME); // in kernel32.dll
|
|
||||||
//if (hMutex != 0 || ::GetLastError() != ERROR_FILE_NOT_FOUND) {
|
|
||||||
// ::CloseHandle(hMutex);
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// See: ITH/main.cpp
|
|
||||||
//if (!IthInitSystemService()) {
|
|
||||||
// DOUT("leave: error: failed to init system service");
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (::Host_Open()) {
|
|
||||||
#ifdef WITH_LIB_WINMAKER
|
|
||||||
if (!parentWindow_)
|
|
||||||
parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook");
|
|
||||||
#endif // WITH_LIB_WINMAKER
|
|
||||||
WinTimer::setGlobalWindow(parentWindow_);
|
|
||||||
::Host_GetHookManager(&hookManager_);
|
|
||||||
if (hookManager_) {
|
|
||||||
//::Host_GetSettings(&settings_);
|
|
||||||
//settings_->debug = debug_;
|
|
||||||
|
|
||||||
//hookManager_->RegisterConsoleCallback(consoleOutput);
|
|
||||||
//hookManager_->RegisterConsoleWCallback(consoleOutputW);
|
|
||||||
//hookManager_->RegisterProcessAttachCallback(processAttach);
|
|
||||||
//hookManager_->RegisterProcessDetachCallback(processDetach);
|
|
||||||
//hookManager_->RegisterProcessNewHookCallback(processNewHook);
|
|
||||||
//hookManager_->RegisterThreadResetCallback(threadReset);
|
|
||||||
hookManager_->RegisterThreadCreateCallback(threadCreate);
|
|
||||||
hookManager_->RegisterThreadRemoveCallback(threadRemove);
|
|
||||||
|
|
||||||
::Host_Start();
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
::Host_Close();
|
|
||||||
DOUT("leave: hook manager =" << hookManager_);
|
|
||||||
return hookManager_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Ihf::unload()
|
|
||||||
{
|
|
||||||
DOUT("enter: hook manager =" << hookManager_);
|
|
||||||
if (hookManager_) {
|
|
||||||
//hookManager_->RegisterProcessAttachCallback(nullptr);
|
|
||||||
//hookManager_->RegisterProcessDetachCallback(nullptr);
|
|
||||||
//hookManager_->RegisterProcessNewHookCallback(nullptr);
|
|
||||||
//hookManager_->RegisterThreadResetCallback(nullptr);
|
|
||||||
hookManager_->RegisterThreadCreateCallback(nullptr);
|
|
||||||
hookManager_->RegisterThreadRemoveCallback(nullptr);
|
|
||||||
// Console output is not unregisterd to avoid segmentation fault
|
|
||||||
//hookManager_->RegisterConsoleCallback(nullptr);
|
|
||||||
|
|
||||||
::Host_Close();
|
|
||||||
hookManager_ = nullptr;
|
|
||||||
//settings_ = nullptr;
|
|
||||||
|
|
||||||
#ifdef WITH_LIB_WINMAKER
|
|
||||||
if (parentWindow_) {
|
|
||||||
wm_destroy_window(parentWindow_);
|
|
||||||
parentWindow_ = nullptr;
|
|
||||||
}
|
|
||||||
#endif // WITH_LIB_WINMAKER
|
|
||||||
}
|
|
||||||
//if (parentWindow_) {
|
|
||||||
// wm_destroy_window(parentWindow_);
|
|
||||||
// parentWindow_ = nullptr;
|
|
||||||
//}
|
|
||||||
DOUT("leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Callbacks -
|
|
||||||
|
|
||||||
//DWORD Ihf::processAttach(DWORD pid)
|
|
||||||
//{
|
|
||||||
// DOUT("enter");
|
|
||||||
// Q_UNUSED(pid);
|
|
||||||
// DOUT("leave");
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//DWORD Ihf::processDetach(DWORD pid)
|
|
||||||
//{
|
|
||||||
// DOUT("enter");
|
|
||||||
// Q_UNUSED(pid);
|
|
||||||
// DOUT("leave");
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//DWORD Ihf::processNewHook(DWORD pid)
|
|
||||||
//{
|
|
||||||
// DOUT("enter");
|
|
||||||
// Q_UNUSED(pid);
|
|
||||||
// DOUT("leave");
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// See: HelloITH/main.cpp
|
|
||||||
// See: ThreadCreate in ITH/window.cpp
|
|
||||||
DWORD Ihf::threadCreate(TextThread *t)
|
|
||||||
{
|
|
||||||
Q_ASSERT(t);
|
|
||||||
DOUT("enter: pid =" << t->PID());
|
|
||||||
Q_ASSERT(hookManager_);
|
|
||||||
|
|
||||||
// Propagate UNICODE
|
|
||||||
// See: ThreadCreate in ITH/window.cpp
|
|
||||||
//if (ProcessRecord *pr = hookManager_->GetProcessRecord(t->PID())) {
|
|
||||||
// NtWaitForSingleObject(pr->hookman_mutex, 0, 0);
|
|
||||||
// Hook *hk = static_cast<Hook *>(pr->hookman_map);
|
|
||||||
// Q_ASSERT(!hk&&!MAX_HOOK || hk&&MAX_HOOK);
|
|
||||||
// for (int i = 0; i < MAX_HOOK; i++) {
|
|
||||||
// if (hk[i].Address() == t->Addr()) {
|
|
||||||
// if (hk[i].Type() & USING_UNICODE)
|
|
||||||
// t->Status() |= USING_UNICODE;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// NtReleaseMutant(pr->hookman_mutex, 0);
|
|
||||||
//}
|
|
||||||
auto d = new TextThreadDelegate(t);
|
|
||||||
bool init = true;
|
|
||||||
foreach (TextThreadDelegate *it, threadDelegates_)
|
|
||||||
if (d->signature() == it->signature()) {
|
|
||||||
TextThreadDelegate::release(d);
|
|
||||||
d = it;
|
|
||||||
d->retain();
|
|
||||||
init = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (init) {
|
|
||||||
d->setInterval(messageInterval_);
|
|
||||||
d->setParentWindow(parentWindow_);
|
|
||||||
updateLinkedDelegate(d);
|
|
||||||
}
|
|
||||||
threadDelegates_[t] = d;
|
|
||||||
t->RegisterOutputCallBack(threadOutput, d);
|
|
||||||
//t->RegisterFilterCallBack(threadFilter, d);
|
|
||||||
DOUT("leave");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See also: HelloITH/main.cpp
|
|
||||||
DWORD Ihf::threadRemove(TextThread *t)
|
|
||||||
{
|
|
||||||
DOUT("enter");
|
|
||||||
Q_ASSERT(t);
|
|
||||||
|
|
||||||
auto p = threadDelegates_.find(t);
|
|
||||||
if (p != threadDelegates_.end()) {
|
|
||||||
auto d = p.value();
|
|
||||||
//if (!linkedDelegates_.isEmpty()) {
|
|
||||||
// linkedDelegates_.remove(d);
|
|
||||||
// while (auto k = linkedDelegates_.key(d))
|
|
||||||
// linkedDelegates_.remove(k);
|
|
||||||
//}
|
|
||||||
threadDelegates_.erase(p);
|
|
||||||
TextThreadDelegate::release(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ITH_WITH_LINK
|
|
||||||
::Host_UnLinkAll(t->Number());
|
|
||||||
#endif // ITH_WITH_LINK
|
|
||||||
|
|
||||||
DOUT("leave");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See: HelloITH/main.cpp
|
|
||||||
DWORD Ihf::threadOutput(TextThread *t, BYTE *data, DWORD dataLength, DWORD newLine, PVOID pUserData, bool space)
|
|
||||||
{
|
|
||||||
DOUT("newLine =" << newLine << ", dataLength =" << dataLength << ", space =" << space);
|
|
||||||
Q_UNUSED(t)
|
|
||||||
Q_ASSERT(data);
|
|
||||||
Q_ASSERT(pUserData);
|
|
||||||
|
|
||||||
auto d = static_cast<TextThreadDelegate *>(pUserData);
|
|
||||||
//if (TextThreadDelegate *link = findLinkedDelegate(d))
|
|
||||||
// d = link;
|
|
||||||
Q_ASSERT(d);
|
|
||||||
if (!enabled_ ||
|
|
||||||
whitelistEnabled_ &&
|
|
||||||
!whitelistContains(d->signature()) &&
|
|
||||||
!(keptThreadName_[0] && d->nameEquals(keptThreadName_))) {
|
|
||||||
DOUT("leave: ignored");
|
|
||||||
return dataLength;
|
|
||||||
}
|
|
||||||
if (newLine)
|
|
||||||
d->touch();
|
|
||||||
//d->flush(); // new line data are ignored
|
|
||||||
else if (dataLength || space)
|
|
||||||
d->append(reinterpret_cast<char *>(data), dataLength, space);
|
|
||||||
//QString text = QString::fromLocal8Bit(reinterpret_cast<LPCSTR>(data), len);
|
|
||||||
DOUT("leave");
|
|
||||||
return dataLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TextThreadDelegate *Ihf::findLinkedDelegate(TextThreadDelegate *d)
|
|
||||||
//{
|
|
||||||
// Q_ASSERT(d);
|
|
||||||
// if (!linkedDelegates_.isEmpty()) {
|
|
||||||
// auto p = linkedDelegates_.find(d);
|
|
||||||
// if (p != linkedDelegates_.end())
|
|
||||||
// return p.value();
|
|
||||||
// }
|
|
||||||
// return nullptr;
|
|
||||||
//}
|
|
||||||
|
|
||||||
void Ihf::updateLinkedDelegate(TextThreadDelegate *d)
|
|
||||||
{
|
|
||||||
#ifdef ITH_WITH_LINK
|
|
||||||
Q_ASSERT(t);
|
|
||||||
foreach (TextThreadDelegate *it, threadDelegates_)
|
|
||||||
if (it->delegateOf(d))
|
|
||||||
::Host_AddLink(d->threadNumber(), it->threadNumber());
|
|
||||||
else if (d->delegateOf(it))
|
|
||||||
::Host_AddLink(it->threadNumber(), d->threadNumber());
|
|
||||||
#else
|
|
||||||
Q_UNUSED(d);
|
|
||||||
#endif // ITH_WITH_LINK
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Injection -
|
|
||||||
|
|
||||||
// See: Host_InjectByPID in IHF/main.cpp
|
|
||||||
// See: InjectThread in ITH/profile.cpp
|
|
||||||
bool Ihf::attachProcess(DWORD pid)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid);
|
|
||||||
bool ok = ::Host_InjectByPID(pid);
|
|
||||||
|
|
||||||
//enum { AttachDelay = 500 }; // in msec
|
|
||||||
//::Sleep(AttachDelay);
|
|
||||||
|
|
||||||
DOUT("leave: ret =" << ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See: Host_ActiveDetachProcess in IHF/main.cpp
|
|
||||||
bool Ihf::detachProcess(DWORD pid) { return ::Host_ActiveDetachProcess(pid); }
|
|
||||||
bool Ihf::hijackProcess(DWORD pid) { return ::Host_HijackProcess(pid); }
|
|
||||||
|
|
||||||
// - Hook -
|
|
||||||
|
|
||||||
// See: Host_ModifyHook in IHF/main.cpp
|
|
||||||
bool Ihf::updateHook(ulong pid, const QString &code)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid << ", code =" << code);
|
|
||||||
Q_ASSERT(pid);
|
|
||||||
HookParam hp = {};
|
|
||||||
if (!Ith::parseHookCode(code, &hp)) {
|
|
||||||
DOUT("leave: failed to parse hook code");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD hh = ::Host_ModifyHook(pid, &hp);
|
|
||||||
bool ok = ~hh;
|
|
||||||
DOUT("leave: ret =" << ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See: Host_InsertHook in IHF/main.cpp
|
|
||||||
bool Ihf::addHook(ulong pid, const QString &code, const QString &name, bool verbose)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid << ", name =" << name << ", code =" << code);
|
|
||||||
Q_ASSERT(pid);
|
|
||||||
if (hookAddresses_.contains(code)) {
|
|
||||||
DOUT("leave: already added");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HookParam hp = {};
|
|
||||||
if (!Ith::parseHookCode(code, &hp, verbose)) {
|
|
||||||
DOUT("leave: failed to parse hook code");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD hh = ::Host_InsertHook(pid, &hp, name.toAscii());
|
|
||||||
//DWORD hh = ::NewHook(hp, nameBuf);
|
|
||||||
bool ok = ~hh;
|
|
||||||
if (ok && hp.address) {
|
|
||||||
DOUT("hook address =" << hp.address);
|
|
||||||
hookAddresses_[code] = hp.address;
|
|
||||||
}
|
|
||||||
DOUT("leave: ok =" << ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See: Host_RemoveHook in IHF/main.cpp
|
|
||||||
bool Ihf::removeHook(ulong pid, const QString &code)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid << ", code =" << code);
|
|
||||||
Q_ASSERT(pid);
|
|
||||||
auto p = hookAddresses_.find(code);
|
|
||||||
if (p == hookAddresses_.end()) {
|
|
||||||
DOUT("leave: hook not added");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DWORD addr = p.value();
|
|
||||||
Q_ASSERT(addr);
|
|
||||||
hookAddresses_.erase(p);
|
|
||||||
|
|
||||||
DWORD hh = ::Host_RemoveHook(pid, addr);
|
|
||||||
bool ok = ~hh;
|
|
||||||
DOUT("leave: ret =" << ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Ihf::verifyHookCode(const QString &code)
|
|
||||||
{ return Ith::verifyHookCode(code); }
|
|
||||||
|
|
||||||
// - Whitelist -
|
|
||||||
|
|
||||||
QList<qint32> Ihf::whitelist()
|
|
||||||
{
|
|
||||||
QList<qint32> ret;
|
|
||||||
const qint32 *p = whitelist_;
|
|
||||||
while (*p)
|
|
||||||
ret.append(*p++);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Ihf::clearWhitelist() { *whitelist_= 0; }
|
|
||||||
|
|
||||||
void Ihf::setWhitelist(const QList<qint32> &l)
|
|
||||||
{
|
|
||||||
qint32 *p = whitelist_;
|
|
||||||
if (!l.isEmpty())
|
|
||||||
foreach (qint32 it, l) {
|
|
||||||
*p++ = it;
|
|
||||||
if (p >= whitelist_ + WhitelistSize)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
whitelist_[qMin(l.size(), WhitelistSize -1)] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Ihf::whitelistContains(qint32 signature)
|
|
||||||
{
|
|
||||||
const qint32 *p = whitelist_;
|
|
||||||
while (*p)
|
|
||||||
if (signature == *p++)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
||||||
|
|
||||||
/*
|
|
||||||
BYTE LeadByteTable[0x100] = {
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
||||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
||||||
2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
||||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
||||||
2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
|
|
||||||
};
|
|
||||||
|
|
||||||
DWORD Ihf::threadFilter(TextThread *thread, BYTE *out, DWORD len, DWORD new_line, PVOID data)
|
|
||||||
{
|
|
||||||
DWORD status = thread->Status();
|
|
||||||
|
|
||||||
if (!new_line && thread->Number() != 0)
|
|
||||||
{
|
|
||||||
if (status & USING_UNICODE)
|
|
||||||
{
|
|
||||||
DWORD i, j;
|
|
||||||
len >>= 1;
|
|
||||||
WCHAR c, *str = (LPWSTR)out;
|
|
||||||
for (i = 0, j = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
c = str[i];
|
|
||||||
//if (!uni_filter->Check(c))
|
|
||||||
str[j++] = c;
|
|
||||||
|
|
||||||
}
|
|
||||||
memset(str + j, 0, (len - j) << 1);
|
|
||||||
len = j << 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WORD c;
|
|
||||||
DWORD i, j;
|
|
||||||
for (i = 0, j = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
c = out[i];
|
|
||||||
if (LeadByteTable[c] == 1)
|
|
||||||
{
|
|
||||||
//if (!mb_filter->Check(c))
|
|
||||||
out[j++] = c & 0xFF;
|
|
||||||
}
|
|
||||||
else if (i + 1 < len)
|
|
||||||
{
|
|
||||||
|
|
||||||
c = out[i + 1];
|
|
||||||
c <<= 8;
|
|
||||||
c |= out[i];
|
|
||||||
//if (!mb_filter->Check(c))
|
|
||||||
{
|
|
||||||
out[j++] = c & 0xFF;
|
|
||||||
out[j++] = c >> 8;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memset(out + j, 0, len - j);
|
|
||||||
len = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// jichi: 10/15/2011: FIXME: This overload will infect the entire program,
|
|
||||||
// even source files that exclude this header, which is unexpected.
|
|
||||||
// No idea what is the trade off of this behavior on performance and liability.
|
|
||||||
// Lots of Qt stuff doesn't work such as QString::toStdString.
|
|
||||||
// I have to use dynamic linkage to avoid being polluted by this module.
|
|
||||||
//
|
|
||||||
// original author: HEAP_ZERO_MEMORY flag is critical. All new object are assumed with zero initialized.
|
|
||||||
// jichi: 10/20/2011: I think the only reason to use Rtl heap here is to ensure HEAP_ZERO_MEMORY,
|
|
||||||
// which is really a bad programming style and incur unstability on heap memory allocation.
|
|
||||||
// ::RtlFreeHeap crash on DLL debug mode. Replace it with standard malloc/free.
|
|
||||||
// ::hHeap handle is also removed from ith/sys.c.cc
|
|
||||||
|
|
||||||
inline void * __cdecl operator new(size_t lSize)
|
|
||||||
{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
|
|
||||||
|
|
||||||
inline void * __cdecl operator new[](size_t lSize)
|
|
||||||
{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
|
|
||||||
|
|
||||||
inline void __cdecl operator delete(void *pBlock)
|
|
||||||
{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
|
|
||||||
|
|
||||||
inline void __cdecl operator delete[](void* pBlock)
|
|
||||||
{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
|
|
||||||
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
inline void * __cdecl operator new(size_t size) throw()
|
|
||||||
{
|
|
||||||
if (!size) // When the value of the expression in a direct-new-declarator is zero,
|
|
||||||
size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
|
|
||||||
|
|
||||||
void *p = malloc(size);
|
|
||||||
if (p)
|
|
||||||
memset(p, 0, size);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void * __cdecl operator new[](size_t size) throw()
|
|
||||||
{
|
|
||||||
if (!size) // When the value of the expression in a direct-new-declarator is zero,
|
|
||||||
size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
|
|
||||||
|
|
||||||
void *p = malloc(size);
|
|
||||||
if (p)
|
|
||||||
memset(p, 0, size);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void __cdecl operator delete(void *p) throw() { free(p); }
|
|
||||||
inline void __cdecl operator delete[](void *p) throw() { free(p); }
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
//QString
|
|
||||||
//Ihf::getHookNameById(ulong hookId)
|
|
||||||
//{
|
|
||||||
// QString ret;
|
|
||||||
// if (hookId) {
|
|
||||||
// auto p = reinterpret_cast<TextThread *>(hookId);
|
|
||||||
// if (p->good())
|
|
||||||
// ret = p->name();
|
|
||||||
// }
|
|
||||||
// return ret;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//DWORD ProcessAttach(DWORD pid)
|
|
||||||
//{
|
|
||||||
// DOUT("process attached, pid =" << pid);
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
//DWORD ProcessDetach(DWORD pid)
|
|
||||||
//{
|
|
||||||
// DOUT("process detached, pid =" << pid);
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
//DWORD ProcessNewHook(DWORD pid)
|
|
||||||
//{
|
|
||||||
// DOUT("process has new hook inserted, pid =" << pid);
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
@ -1,117 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ihf_p.h
|
|
||||||
// 10/15/2011 jichi
|
|
||||||
// Internal header.
|
|
||||||
// Wrapper of IHF functions.
|
|
||||||
|
|
||||||
#include <QtCore/QHash>
|
|
||||||
#include <QtCore/QList>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
#include <QtGui/qwindowdefs.h> // for WId
|
|
||||||
|
|
||||||
//struct Settings; // opaque in ith/host/settings.h
|
|
||||||
class HookManager; // opaque in ith/host/hookman.h
|
|
||||||
class TextThread; // opaque in ith/host/textthread.h
|
|
||||||
class TextThreadDelegate;
|
|
||||||
|
|
||||||
enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
|
|
||||||
|
|
||||||
class Ihf
|
|
||||||
{
|
|
||||||
Ihf() {} // Singleton
|
|
||||||
|
|
||||||
static bool enabled_;
|
|
||||||
|
|
||||||
//static Settings *settings_;
|
|
||||||
static HookManager *hookManager_;
|
|
||||||
static qint64 messageInterval_;
|
|
||||||
static WId parentWindow_;
|
|
||||||
|
|
||||||
static QHash<TextThread *, TextThreadDelegate *> threadDelegates_;
|
|
||||||
//static QHash<TextThreadDelegate *, TextThreadDelegate *> linkedDelegates_;
|
|
||||||
static QHash<QString, ulong> hookAddresses_;
|
|
||||||
|
|
||||||
enum { WhitelistSize = 0x20 + 1 }; // ITH capacity is 0x20
|
|
||||||
static qint32 whitelist_[WhitelistSize]; // List of signatures. The last element is zero. I.e., at most BlackSize-1 threads.
|
|
||||||
static bool whitelistEnabled_;
|
|
||||||
static char keptThreadName_[ITH_THREAD_NAME_CAPACITY];
|
|
||||||
//static QString userDefinedThreadName_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// - Initialization -
|
|
||||||
static void init();
|
|
||||||
static void destroy();
|
|
||||||
|
|
||||||
static bool load();
|
|
||||||
static bool isLoaded() { return hookManager_; }
|
|
||||||
static void unload();
|
|
||||||
|
|
||||||
// - Properties -
|
|
||||||
|
|
||||||
static bool isEnabled() { return enabled_; }
|
|
||||||
static void setEnabled(bool t) { enabled_ = t; }
|
|
||||||
|
|
||||||
/// A valid window handle is required to make ITH work
|
|
||||||
static WId parentWindow() { return parentWindow_; }
|
|
||||||
static void setParentWindow(WId hwnd) { parentWindow_ = hwnd; }
|
|
||||||
|
|
||||||
/// Timeout (msecs) for a text message
|
|
||||||
static qint64 messageInterval() { return messageInterval_; }
|
|
||||||
static void setMessageInterval(qint64 msecs) { messageInterval_ = msecs; }
|
|
||||||
|
|
||||||
// - Injection -
|
|
||||||
static bool attachProcess(ulong pid);
|
|
||||||
static bool detachProcess(ulong pid);
|
|
||||||
static bool hijackProcess(ulong pid);
|
|
||||||
|
|
||||||
/// Add hook code
|
|
||||||
static bool addHook(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
|
|
||||||
static bool updateHook(ulong pid, const QString &code); // not used
|
|
||||||
static bool removeHook(ulong pid, const QString &code);
|
|
||||||
static bool verifyHookCode(const QString &code);
|
|
||||||
|
|
||||||
// - Whitelist -
|
|
||||||
static bool isWhitelistEnabled() { return whitelistEnabled_; }
|
|
||||||
static void setWhitelistEnabled(bool t) { whitelistEnabled_ = t; }
|
|
||||||
|
|
||||||
static QList<qint32> whitelist();
|
|
||||||
static void setWhitelist(const QList<qint32> &l);
|
|
||||||
static void clearWhitelist();
|
|
||||||
|
|
||||||
//static QString userDefinedThreadName() { return userDefinedThreadName_; }
|
|
||||||
//static void setUserDefinedThreadName(const QString &val) { userDefinedThreadName_ = val; }
|
|
||||||
static const char *keptThreadName() { return keptThreadName_; }
|
|
||||||
|
|
||||||
static void setKeptThreadName(const QString &v)
|
|
||||||
{
|
|
||||||
if (v.size() < ITH_THREAD_NAME_CAPACITY)
|
|
||||||
::strcpy(keptThreadName_, v.toAscii());
|
|
||||||
else
|
|
||||||
setKeptThreadName(v.left(ITH_THREAD_NAME_CAPACITY - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool whitelistContains(qint32 signature);
|
|
||||||
|
|
||||||
// - Callbacks -
|
|
||||||
//static ulong processAttach(ulong pid);
|
|
||||||
//static ulong processDetach(ulong pid);
|
|
||||||
//static ulong processNewHook(ulong pid);
|
|
||||||
|
|
||||||
static ulong threadCreate(_In_ TextThread *t);
|
|
||||||
static ulong threadRemove(_In_ TextThread *t);
|
|
||||||
static ulong threadOutput(_In_ TextThread *t, _In_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData, _In_ bool space);
|
|
||||||
//static ulong threadFilter(_In_ TextThread *t, _Out_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData);
|
|
||||||
//static ulong threadReset(TextThread *t);
|
|
||||||
//static void consoleOutput(const char *text);
|
|
||||||
//static void consoleOutputW(const wchar_t *text);
|
|
||||||
|
|
||||||
// - Linked threasds -
|
|
||||||
private:
|
|
||||||
//static TextThreadDelegate *findLinkedDelegate(TextThreadDelegate *d);
|
|
||||||
static void updateLinkedDelegate(TextThreadDelegate *d);
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,275 +0,0 @@
|
|||||||
// ith_p.cc
|
|
||||||
// 10/15/2011 jichi
|
|
||||||
|
|
||||||
#include "texthook/ith_p.h"
|
|
||||||
#include "vnrhook/include/const.h"
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#define DEBUG "ith_p.cc"
|
|
||||||
#include "sakurakit/skdebug.h"
|
|
||||||
|
|
||||||
// HookParam copied from ITH/common.h:
|
|
||||||
// struct HookParam // size = 40 (0x24)
|
|
||||||
// {
|
|
||||||
// typedef void (*DataFun)(DWORD, HookParam*, DWORD*, DWORD*, DWORD*);
|
|
||||||
//
|
|
||||||
// DWORD addr; // 4
|
|
||||||
// DWORD off, // 8
|
|
||||||
// ind, // 12
|
|
||||||
// split, // 16
|
|
||||||
// split_ind; // 20
|
|
||||||
// DWORD module, // 24
|
|
||||||
// function; // 28
|
|
||||||
// DataFun text_fun; // 32, jichi: is this the same in x86 and x86_64?
|
|
||||||
// DWORD type; // 36
|
|
||||||
// WORD length_offset; // 38
|
|
||||||
// BYTE hook_len, // 39
|
|
||||||
// recover_len; // 40
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
// - Implementation Details -
|
|
||||||
|
|
||||||
namespace { namespace detail { // unnamed
|
|
||||||
|
|
||||||
// ITH ORIGINAL CODE BEGIN
|
|
||||||
|
|
||||||
// See: ITH/ITH.h
|
|
||||||
// Revision: 133
|
|
||||||
inline DWORD Hash(_In_ LPWSTR module, int length = -1)
|
|
||||||
{
|
|
||||||
bool flag = length == -1;
|
|
||||||
DWORD hash = 0;
|
|
||||||
for (; *module && (flag || length--); module++)
|
|
||||||
hash = _rotr(hash,7) + *module; //hash=((hash>>7)|(hash<<25))+(*module);
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
// See: ITH/command.cpp
|
|
||||||
// Revision: 133
|
|
||||||
//
|
|
||||||
// jichi note: str[0xF] will be modified and restored.
|
|
||||||
// So, the buffer of str must be larger than 0xF.
|
|
||||||
int Convert(_In_ LPWSTR str, _Out_ DWORD *num, _In_ LPWSTR delim)
|
|
||||||
{
|
|
||||||
if (!num)
|
|
||||||
return -1;
|
|
||||||
WCHAR t = *str,
|
|
||||||
tc = *(str + 0xF);
|
|
||||||
WCHAR temp[0x10] = {};
|
|
||||||
LPWSTR it = temp,
|
|
||||||
istr = str,
|
|
||||||
id = temp;
|
|
||||||
if (delim) {
|
|
||||||
id = wcschr(delim, t);
|
|
||||||
str[0xF] = delim[0]; // reset str[0xF] in case of out-of-bound iteration
|
|
||||||
}
|
|
||||||
else
|
|
||||||
str[0xF] = 0; // reset str[0xF] in case of out-of-bound iteration
|
|
||||||
while (!id && t) {
|
|
||||||
*it = t;
|
|
||||||
it++; istr++;
|
|
||||||
t = *istr;
|
|
||||||
if (delim)
|
|
||||||
id = wcschr(delim, t);
|
|
||||||
}
|
|
||||||
swscanf(temp, L"%x", num);
|
|
||||||
str[0xF] = tc; // restore the str[0xF]
|
|
||||||
if (!id || istr - str == 0xF)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!t)
|
|
||||||
return istr - str; // >= 0
|
|
||||||
else
|
|
||||||
return id - delim; // >= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// See: ITH/command.cpp
|
|
||||||
// Revision: 133
|
|
||||||
//
|
|
||||||
// jichi note: str[0xF] will be modified and restored.
|
|
||||||
// So, the buffer of cmd must be larger than 0xF*2 = 0x1F.
|
|
||||||
bool Parse(_In_ LPWSTR cmd, _Out_ HookParam &hp)
|
|
||||||
{
|
|
||||||
::memset(&hp, 0, sizeof(hp));
|
|
||||||
|
|
||||||
int t;
|
|
||||||
bool accept = false;
|
|
||||||
DWORD *data = &hp.offset; //
|
|
||||||
LPWSTR offset = cmd + 1;
|
|
||||||
LPWSTR delim_str = L":*@!";
|
|
||||||
LPWSTR delim = delim_str;
|
|
||||||
if (*offset == L'n' || *offset == 'N') {
|
|
||||||
offset++;
|
|
||||||
hp.type |= NO_CONTEXT;
|
|
||||||
}
|
|
||||||
// jichi 4/25/2015: Add support for fixing hook
|
|
||||||
if (*offset == L'f' || *offset == 'F') {
|
|
||||||
offset++;
|
|
||||||
hp.type |= FIXING_SPLIT;
|
|
||||||
}
|
|
||||||
if (*offset == L'j' || *offset == 'J') { // 11/22/2015: J stands for Japanese only
|
|
||||||
offset++;
|
|
||||||
hp.type |= NO_ASCII;
|
|
||||||
}
|
|
||||||
while (!accept) {
|
|
||||||
t = Convert(offset, data, delim);
|
|
||||||
if (t < 0)
|
|
||||||
return false; //ConsoleOutput(L"Syntax error.");
|
|
||||||
offset = ::wcschr(offset , delim[t]);
|
|
||||||
if (offset)
|
|
||||||
offset++; // skip the current delim
|
|
||||||
else //goto _error;
|
|
||||||
return false; //ConsoleOutput(L"Syntax error.");
|
|
||||||
switch (delim[t]) {
|
|
||||||
case L':':
|
|
||||||
data = &hp.split;
|
|
||||||
delim = delim_str + 1;
|
|
||||||
hp.type |= USING_SPLIT;
|
|
||||||
break;
|
|
||||||
case L'*':
|
|
||||||
if (hp.split) {
|
|
||||||
data = &hp.split_index;
|
|
||||||
delim = delim_str + 2;
|
|
||||||
hp.type |= SPLIT_INDIRECT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hp.type |= DATA_INDIRECT;
|
|
||||||
data = &hp.index;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case L'@':
|
|
||||||
accept = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t = Convert(offset, &hp.address, delim_str);
|
|
||||||
if (t < 0)
|
|
||||||
return false;
|
|
||||||
if (hp.offset & 0x80000000)
|
|
||||||
hp.offset -= 4;
|
|
||||||
if (hp.split & 0x80000000)
|
|
||||||
hp.split -= 4;
|
|
||||||
LPWSTR temp = offset;
|
|
||||||
offset = ::wcschr(offset, L':');
|
|
||||||
if (offset) {
|
|
||||||
hp.type |= MODULE_OFFSET;
|
|
||||||
offset++;
|
|
||||||
delim = ::wcschr(offset, L':');
|
|
||||||
|
|
||||||
if (delim) {
|
|
||||||
*delim = 0;
|
|
||||||
delim++;
|
|
||||||
_wcslwr(offset);
|
|
||||||
hp.function = Hash(delim);
|
|
||||||
hp.module = Hash(offset, delim - offset - 1);
|
|
||||||
hp.type |= FUNCTION_OFFSET;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
hp.module = Hash(_wcslwr(offset));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
offset = ::wcschr(temp, L'!');
|
|
||||||
if (offset) {
|
|
||||||
hp.type |= MODULE_OFFSET;
|
|
||||||
swscanf(offset + 1, L"%x", &hp.module);
|
|
||||||
offset = ::wcschr(offset + 1, L'!');
|
|
||||||
if (offset) {
|
|
||||||
hp.type |= FUNCTION_OFFSET;
|
|
||||||
swscanf(offset + 1, L"%x", &hp.function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (*cmd) {
|
|
||||||
case L's':
|
|
||||||
case L'S':
|
|
||||||
hp.type |= USING_STRING;
|
|
||||||
break;
|
|
||||||
case L'e':
|
|
||||||
case L'E':
|
|
||||||
hp.type |= STRING_LAST_CHAR;
|
|
||||||
case L'a':
|
|
||||||
case L'A':
|
|
||||||
hp.type |= BIG_ENDIAN;
|
|
||||||
hp.length_offset = 1;
|
|
||||||
break;
|
|
||||||
case L'b':
|
|
||||||
case L'B':
|
|
||||||
hp.length_offset = 1;
|
|
||||||
break;
|
|
||||||
// jichi 12/7/2014: Disabled
|
|
||||||
//case L'h':
|
|
||||||
//case L'H':
|
|
||||||
// hp.type |= PRINT_DWORD;
|
|
||||||
case L'q':
|
|
||||||
case L'Q':
|
|
||||||
hp.type |= USING_STRING | USING_UNICODE;
|
|
||||||
break;
|
|
||||||
case L'l':
|
|
||||||
case L'L':
|
|
||||||
hp.type |= STRING_LAST_CHAR;
|
|
||||||
case L'w':
|
|
||||||
case L'W':
|
|
||||||
hp.type |= USING_UNICODE;
|
|
||||||
hp.length_offset = 1;
|
|
||||||
break;
|
|
||||||
default: ;
|
|
||||||
}
|
|
||||||
//ConsoleOutput(L"Try to insert additional hook.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ITH ORIGINAL CODE END
|
|
||||||
|
|
||||||
}} // unnamed detail
|
|
||||||
|
|
||||||
// - ITH API -
|
|
||||||
|
|
||||||
// Sample code: L"/HS-4:-14@4383C0" (WHITE ALBUM 2)
|
|
||||||
bool Ith::parseHookCode(const QString &code, HookParam *hp, bool verbose)
|
|
||||||
{
|
|
||||||
#define HCODE_PREFIX "/H"
|
|
||||||
enum { HCODE_PREFIX_LEN = sizeof(HCODE_PREFIX) -1 }; // 2
|
|
||||||
if (!hp || !code.startsWith(HCODE_PREFIX))
|
|
||||||
return false;
|
|
||||||
if (verbose)
|
|
||||||
DOUT("enter: code =" << code);
|
|
||||||
else
|
|
||||||
DOUT("enter");
|
|
||||||
|
|
||||||
size_t bufsize = qMax(0xFF, code.size() + 1); // in case detail::Convert modify the buffer
|
|
||||||
auto buf = new wchar_t[bufsize];
|
|
||||||
code.toWCharArray(buf);
|
|
||||||
buf[code.size()] = 0;
|
|
||||||
|
|
||||||
bool ret = detail::Parse(buf + HCODE_PREFIX_LEN, *hp);
|
|
||||||
delete[] buf;
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (ret && verbose)
|
|
||||||
qDebug()
|
|
||||||
<< "addr:" << hp->address
|
|
||||||
<< ", text_fun:" << hp->text_fun
|
|
||||||
<< ", function:"<< hp->function
|
|
||||||
<< ", hook_len:" << hp->hook_len
|
|
||||||
<< ", ind:" << hp->index
|
|
||||||
<< ", length_offset:" << hp->length_offset
|
|
||||||
<< ", module:" << hp->module
|
|
||||||
<< ", off:" <<hp->offset
|
|
||||||
<< ", recover_len:" << hp->recover_len
|
|
||||||
<< ", split:" << hp->split
|
|
||||||
<< ", split_ind:" << hp->split_index
|
|
||||||
<< ", type:" << hp->type;
|
|
||||||
#endif // DEBUG
|
|
||||||
DOUT("leave: ret =" << ret);
|
|
||||||
return ret;
|
|
||||||
#undef HOOK_CODE_PREFIX
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Ith::verifyHookCode(const QString &code)
|
|
||||||
{
|
|
||||||
HookParam hp = {};
|
|
||||||
return parseHookCode(code, &hp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith_p.h
|
|
||||||
// 10/15/2011 jichi
|
|
||||||
// Internal header.
|
|
||||||
// Wrapper of functions from ITH.
|
|
||||||
|
|
||||||
#include <QtCore/QString>
|
|
||||||
|
|
||||||
struct HookParam; // opaque, declared in ITH/common.h
|
|
||||||
|
|
||||||
namespace Ith {
|
|
||||||
|
|
||||||
/// Parse hook code, and save the result to hook param if succeeded.
|
|
||||||
bool parseHookCode(_In_ const QString &code, _Out_ HookParam *hp, bool verbose = true);
|
|
||||||
bool verifyHookCode(_In_ const QString &code);
|
|
||||||
|
|
||||||
} // namespace Ith
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,414 +0,0 @@
|
|||||||
// texthook.cc
|
|
||||||
// 10/14/2011 jichi
|
|
||||||
|
|
||||||
#include "texthook/texthook.h"
|
|
||||||
#include "texthook/texthook_p.h"
|
|
||||||
#include "texthook/ihf_p.h"
|
|
||||||
#include "texthook/textthread_p.h"
|
|
||||||
#include "texthook/winapi_p.h"
|
|
||||||
#include <QtCore>
|
|
||||||
|
|
||||||
//#define DEBUG "texthook.cc"
|
|
||||||
#include "sakurakit/skdebug.h"
|
|
||||||
|
|
||||||
//#include <ITH/IHF_SYS.h>
|
|
||||||
//namespace { int _ = IthInitSystemService(); }
|
|
||||||
|
|
||||||
/** Private class */
|
|
||||||
|
|
||||||
TextHookPrivate *TextHookPrivate::instance_;
|
|
||||||
|
|
||||||
/** Public class */
|
|
||||||
|
|
||||||
// - Construction -
|
|
||||||
|
|
||||||
//TextHook *TextHook::g_;
|
|
||||||
//TextHook *TextHook::globalInstance() { static Self g; return &g; }
|
|
||||||
|
|
||||||
//TextHook::TextHook(QObject *parent)
|
|
||||||
// : Base(parent), d_(new D)
|
|
||||||
//{}
|
|
||||||
|
|
||||||
TextHook::TextHook(QObject *parent)
|
|
||||||
: Base(parent), d_(new D(this))
|
|
||||||
{
|
|
||||||
Ihf::init();
|
|
||||||
//Ihf::setUserDefinedThreadName(d_->source);
|
|
||||||
DOUT("pass");
|
|
||||||
}
|
|
||||||
|
|
||||||
TextHook::~TextHook()
|
|
||||||
{
|
|
||||||
DOUT("enter");
|
|
||||||
if (isActive())
|
|
||||||
stop();
|
|
||||||
delete d_;
|
|
||||||
|
|
||||||
Ihf::destroy();
|
|
||||||
DOUT("leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Properties -
|
|
||||||
|
|
||||||
int TextHook::dataCapacity() const
|
|
||||||
{ return TextThreadDelegate::capacity(); }
|
|
||||||
|
|
||||||
void TextHook::setDataCapacity(int value)
|
|
||||||
{ TextThreadDelegate::setCapacity(value); }
|
|
||||||
|
|
||||||
bool TextHook::removesRepeat() const
|
|
||||||
{ return TextThreadDelegate::removesRepeat(); }
|
|
||||||
|
|
||||||
void TextHook::setRemovesRepeat(bool value)
|
|
||||||
{ TextThreadDelegate::setRemovesRepeat(value); }
|
|
||||||
|
|
||||||
bool TextHook::keepsSpace() const
|
|
||||||
{ return TextThreadDelegate::keepsSpace(); }
|
|
||||||
|
|
||||||
void TextHook::setKeepsSpace(bool value)
|
|
||||||
{ TextThreadDelegate::setKeepsSpace(value); }
|
|
||||||
|
|
||||||
bool TextHook::wideCharacter() const
|
|
||||||
{ return TextThreadDelegate::wideCharacter(); }
|
|
||||||
|
|
||||||
void TextHook::setWideCharacter(bool value)
|
|
||||||
{ TextThreadDelegate::setWideCharacter(value); }
|
|
||||||
|
|
||||||
// see: ITH/common.h
|
|
||||||
int TextHook::capacity() const
|
|
||||||
{ return 0x20; }
|
|
||||||
|
|
||||||
QString TextHook::defaultHookName() const
|
|
||||||
{ return d_->source; }
|
|
||||||
|
|
||||||
void TextHook::setDefaultHookName(const QString &name)
|
|
||||||
{
|
|
||||||
d_->source = name;
|
|
||||||
//Ihf::setUserDefinedThreadName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextHook::isEnabled() const
|
|
||||||
{ return d_->enabled; }
|
|
||||||
|
|
||||||
void TextHook::setEnabled(bool t)
|
|
||||||
{
|
|
||||||
d_->enabled = t;
|
|
||||||
Ihf::setEnabled(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextHook::isActive() const
|
|
||||||
{ return Ihf::isLoaded(); }
|
|
||||||
|
|
||||||
void TextHook::start()
|
|
||||||
{ Ihf::load(); }
|
|
||||||
|
|
||||||
void TextHook::stop()
|
|
||||||
{
|
|
||||||
if (!isEmpty())
|
|
||||||
clear();
|
|
||||||
Ihf::unload();
|
|
||||||
}
|
|
||||||
|
|
||||||
WId TextHook::parentWinId() const { return Ihf::parentWindow(); }
|
|
||||||
void TextHook::setParentWinId(WId hwnd) { Ihf::setParentWindow(hwnd); }
|
|
||||||
|
|
||||||
int TextHook::interval() const
|
|
||||||
{ return Ihf::messageInterval(); }
|
|
||||||
|
|
||||||
void TextHook::setInterval(int msecs)
|
|
||||||
{ Ihf::setMessageInterval(msecs); }
|
|
||||||
|
|
||||||
// - Injection -
|
|
||||||
|
|
||||||
void TextHook::clear()
|
|
||||||
{
|
|
||||||
DOUT("enter");
|
|
||||||
foreach (ulong pid, d_->pids)
|
|
||||||
detachProcess(pid);
|
|
||||||
if (!d_->hooks.isEmpty())
|
|
||||||
d_->hooks.clear();
|
|
||||||
clearThreadWhitelist();
|
|
||||||
DOUT("leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextHook::containsProcess(ulong pid) const { return d_->pids.contains(pid); }
|
|
||||||
bool TextHook::isEmpty() const { return d_->pids.isEmpty(); }
|
|
||||||
|
|
||||||
//QList<ulong> TextHook::attachedProcesses(bool checkActive) const
|
|
||||||
//{
|
|
||||||
// if (isEmpty() || !checkActive)
|
|
||||||
// return d_->pids;
|
|
||||||
//
|
|
||||||
// QList<ulong> ret;
|
|
||||||
// foreach (ulong pid, d_->pids)
|
|
||||||
// if (winapi::IsProcessActiveWithId(pid))
|
|
||||||
// ret.append(pid);
|
|
||||||
// return ret;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//ulong TextHook::currentProccess() const
|
|
||||||
//{ return anyAttachedProcess(true); } // check active = true
|
|
||||||
|
|
||||||
//ulong TextHook::anyAttachedProcess(bool checkActive) const
|
|
||||||
//{
|
|
||||||
// if (isEmpty())
|
|
||||||
// return 0;
|
|
||||||
// if (!checkActive)
|
|
||||||
// return d_->pids.first();
|
|
||||||
//
|
|
||||||
// foreach (ulong pid, d_->pids)
|
|
||||||
// if (winapi::IsProcessActiveWithId(pid))
|
|
||||||
// return pid;
|
|
||||||
// return 0;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//bool TextHook::attachOneProcess(ulong pid, bool checkActive)
|
|
||||||
//{
|
|
||||||
// DOUT("enter: pid =" << pid);
|
|
||||||
// DOUT("isAttached =" << containsProcess(pid));
|
|
||||||
// if (!isActive())
|
|
||||||
// start();
|
|
||||||
//
|
|
||||||
// if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
|
|
||||||
// DOUT("leave: ret = false, isActive = false");
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (containsProcess(pid)) {
|
|
||||||
// DOUT("leave: pid already attached");
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// detachAllProcesses();
|
|
||||||
//
|
|
||||||
// bool ret = Ihf::attachProcess(pid);
|
|
||||||
// if (ret && !containsProcess(pid)) {
|
|
||||||
// d_->pids.removeAll(pid);
|
|
||||||
// d_->pids.append(pid);
|
|
||||||
// emit processAttached(pid);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// DOUT("leave: ret =" << ret);
|
|
||||||
// return ret;
|
|
||||||
//}
|
|
||||||
|
|
||||||
bool TextHook::attachProcess(ulong pid, bool checkActive)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
|
|
||||||
if (!isActive())
|
|
||||||
start();
|
|
||||||
Q_ASSERT(isActive());
|
|
||||||
DOUT("isActive =" << isActive());
|
|
||||||
|
|
||||||
if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
|
|
||||||
DOUT("leave: ret = false, isActive = false");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = Ihf::attachProcess(pid);
|
|
||||||
if (ret) {
|
|
||||||
d_->pids.insert(pid);
|
|
||||||
emit processAttached(pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
DOUT("leave: ret =" << ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextHook::detachProcess(ulong pid, bool checkActive)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
|
|
||||||
Q_ASSERT(isActive());
|
|
||||||
|
|
||||||
auto it = d_->pids.find(pid);
|
|
||||||
if (it == d_->pids.end()) {
|
|
||||||
DOUT("leave: ret = false, not attached");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d_->pids.erase(it);
|
|
||||||
d_->hooks.remove(pid);
|
|
||||||
|
|
||||||
if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
|
|
||||||
emit processDetached(pid);
|
|
||||||
DOUT("leave: ret = false, isActive = false");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = Ihf::detachProcess(pid);
|
|
||||||
//try {
|
|
||||||
// ret = Ihf::detachProcess(pid);
|
|
||||||
//} catch (...) {
|
|
||||||
// DOUT("warning: detach exception");
|
|
||||||
//}
|
|
||||||
|
|
||||||
emit processDetached(pid);
|
|
||||||
DOUT("leave: ret =" << ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextHook::hijackProcess(ulong pid)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid);
|
|
||||||
Q_ASSERT(isActive());
|
|
||||||
|
|
||||||
if (!containsProcess(pid)) {
|
|
||||||
DOUT("leave: aborted, process not attached");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7/12/2015: Function disabled
|
|
||||||
return true;
|
|
||||||
|
|
||||||
//bool ret = Ihf::hijackProcess(pid);
|
|
||||||
//DOUT("leave: ret =" << ret);
|
|
||||||
//return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//void TextHook::detachAllProcesses()
|
|
||||||
//{
|
|
||||||
// DOUT("enter");
|
|
||||||
// foreach (ulong pid, d_->pids)
|
|
||||||
// detachProcess(pid);
|
|
||||||
// if (!d_->hooks.isEmpty())
|
|
||||||
// d_->hooks.clear();
|
|
||||||
// DOUT("leave");
|
|
||||||
//}
|
|
||||||
|
|
||||||
// - Hook -
|
|
||||||
|
|
||||||
//bool TextHook::containsHook(ulong pid) const
|
|
||||||
//{ return d_->hooks.contains(pid); }
|
|
||||||
//
|
|
||||||
//bool TextHook::containsHook(ulong pid, const QString &code) const
|
|
||||||
//{ return processHook(pid) == code; }
|
|
||||||
|
|
||||||
bool TextHook::addHookCode(ulong pid, const QString &code, const QString &name, bool verbose)
|
|
||||||
{
|
|
||||||
DOUT("enter: pid =" << pid << ", code =" << code);
|
|
||||||
if (isEmpty() || !containsProcess(pid)) {
|
|
||||||
DOUT("leave: failed, process not attached");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (d_->hooks.contains(pid)) {
|
|
||||||
DOUT("leave: failed, hook already exists");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool ok = Ihf::addHook(pid, code,
|
|
||||||
name.isEmpty() ? defaultHookName() : name,
|
|
||||||
verbose);
|
|
||||||
if (ok)
|
|
||||||
d_->hooks[pid] = code;
|
|
||||||
DOUT("leave: ret =" << ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextHook::verifyHookCode(const QString &code) { return Ihf::verifyHookCode(code); }
|
|
||||||
|
|
||||||
bool TextHook::removeHookCode(ulong pid)
|
|
||||||
{
|
|
||||||
DOUT("enter");
|
|
||||||
auto p = d_->hooks.find(pid);
|
|
||||||
if (p == d_->hooks.end()) {
|
|
||||||
DOUT("leave: not hooked");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//DOUT("remove existing hook, THIS SHOULD NOT HAPPEN");
|
|
||||||
bool ok = Ihf::removeHook(pid, p.value());
|
|
||||||
d_->hooks.erase(p);
|
|
||||||
DOUT("leave: ret =" << ok);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
//QString TextHook::processHook(ulong pid) const
|
|
||||||
//{
|
|
||||||
// auto p = d_->hooks.find(pid);
|
|
||||||
// return p == d_->hooks.end() ? QString() : p.value();
|
|
||||||
//}
|
|
||||||
|
|
||||||
bool TextHook::isThreadWhitelistEnabled() const { return Ihf::isWhitelistEnabled(); }
|
|
||||||
|
|
||||||
void TextHook::setThreadWhitelistEnabled(bool t) { Ihf::setWhitelistEnabled(t); }
|
|
||||||
|
|
||||||
QList<qint32> TextHook::threadWhitelist() const { return Ihf::whitelist(); }
|
|
||||||
|
|
||||||
void TextHook::setThreadWhitelist(const QList<qint32> &sigs) { Ihf::setWhitelist(sigs); }
|
|
||||||
|
|
||||||
void TextHook::clearThreadWhitelist() { Ihf::clearWhitelist(); }
|
|
||||||
|
|
||||||
QString TextHook::keptThreadName() const { return Ihf::keptThreadName(); }
|
|
||||||
|
|
||||||
void TextHook::setKeptThreadName(const QString &v) { Ihf::setKeptThreadName(v); }
|
|
||||||
|
|
||||||
// EOF
|
|
||||||
|
|
||||||
/*
|
|
||||||
QString
|
|
||||||
TextHook::guessEncodingForFile(const QString &fileName)
|
|
||||||
{
|
|
||||||
static QHash<QString, QString> db;
|
|
||||||
if (db.isEmpty()) {
|
|
||||||
db["malie.exe"] = "UTF-16";
|
|
||||||
}
|
|
||||||
auto p = db.find(fileName);
|
|
||||||
return p == db.end() ? QString() : p.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Helpers -
|
|
||||||
|
|
||||||
bool
|
|
||||||
TextHook::isStandardHookName(const QString &name) const
|
|
||||||
{
|
|
||||||
static QSet<uint> hashes;
|
|
||||||
if (hashes.isEmpty()) {
|
|
||||||
#define ADD(_text) hashes.insert(qHash(QString(_text)))
|
|
||||||
ADD("ConsoleOutput");
|
|
||||||
ADD("GetTextExtentPoint32A");
|
|
||||||
ADD("GetGlyphOutlineA");
|
|
||||||
ADD("ExtTextOutA");
|
|
||||||
ADD("TextOutA");
|
|
||||||
ADD("GetCharABCWidthsA");
|
|
||||||
ADD("DrawTextA");
|
|
||||||
ADD("DrawTextExA");
|
|
||||||
ADD("GetTextExtentPoint32W");
|
|
||||||
ADD("GetGlyphOutlineW");
|
|
||||||
ADD("ExtTextOutW");
|
|
||||||
ADD("TextOutW");
|
|
||||||
ADD("GetCharABCWidthsW");
|
|
||||||
ADD("DrawTextW");
|
|
||||||
ADD("DrawTextExW");
|
|
||||||
#undef ADD
|
|
||||||
}
|
|
||||||
uint h = qHash(name);
|
|
||||||
return hashes.contains(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
TextHook::isKnownHookForProcess(const QString &hook, const QString &proc) const
|
|
||||||
{
|
|
||||||
// TODO: update database on line periodically
|
|
||||||
qDebug() << "qth::isKnownHookForProcess: hook =" << hook << ", proc =" << proc;
|
|
||||||
|
|
||||||
static QSet<uint> hashes;
|
|
||||||
if (hashes.isEmpty()) {
|
|
||||||
#define ADD(_hook, _proc) hashes.insert(qHash(QString(_hook) + "\n" + _proc))
|
|
||||||
//ADD("Malie", "malie"); // light
|
|
||||||
ADD("GetGlyphOutlineA", "STEINSGATE");
|
|
||||||
ADD("StuffScriptEngine", "EVOLIMIT");
|
|
||||||
#undef ADD
|
|
||||||
}
|
|
||||||
uint h = qHash(hook + "\n" + proc);
|
|
||||||
return hashes.contains(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString
|
|
||||||
TextHook::hookNameById(ulong hookId) const
|
|
||||||
{
|
|
||||||
//return Ihf::getHookNameById(hookId);
|
|
||||||
// FIXME: supposed to be the engine name, unimplemented
|
|
||||||
Q_UNUSED(hookId)
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
@ -1,101 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// texthook.h
|
|
||||||
// 10/14/2011 jichi
|
|
||||||
|
|
||||||
#include "texthook_config.h"
|
|
||||||
#include "sakurakit/skglobal.h"
|
|
||||||
#include <QtCore/QByteArray>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QList>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
#include <QtGui/qwindowdefs.h> // for WId
|
|
||||||
|
|
||||||
class TextHookPrivate;
|
|
||||||
/// Singleton class. Only one instance is allowed.
|
|
||||||
class TEXTHOOK_EXPORT TextHook : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_DISABLE_COPY(TextHook)
|
|
||||||
SK_EXTEND_CLASS(TextHook, QObject)
|
|
||||||
SK_DECLARE_PRIVATE(TextHookPrivate)
|
|
||||||
|
|
||||||
// - Construction -
|
|
||||||
public:
|
|
||||||
explicit TextHook(QObject *parent = nullptr);
|
|
||||||
~TextHook();
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void dataReceived(QByteArray raw, QByteArray rendered, qint32 signature, QString source);
|
|
||||||
void processAttached(qint64 pid);
|
|
||||||
void processDetached(qint64 pid);
|
|
||||||
|
|
||||||
// - Properties -
|
|
||||||
public:
|
|
||||||
/// Limited by ITH
|
|
||||||
int capacity() const;
|
|
||||||
|
|
||||||
bool isEnabled() const;
|
|
||||||
void setEnabled(bool t);
|
|
||||||
|
|
||||||
WId parentWinId() const; ///< Must be set to a valid window so that ::SetTimer works
|
|
||||||
void setParentWinId(WId hwnd);
|
|
||||||
|
|
||||||
int interval() const; ///< Time to differentiate sentences
|
|
||||||
void setInterval(int msecs);
|
|
||||||
|
|
||||||
int dataCapacity() const; ///< Maximum text length
|
|
||||||
void setDataCapacity(int value);
|
|
||||||
|
|
||||||
bool removesRepeat() const;
|
|
||||||
void setRemovesRepeat(bool value);
|
|
||||||
|
|
||||||
bool keepsSpace() const;
|
|
||||||
void setKeepsSpace(bool value);
|
|
||||||
|
|
||||||
bool wideCharacter() const;
|
|
||||||
void setWideCharacter(bool value);
|
|
||||||
|
|
||||||
QString defaultHookName() const; ///< The default one is "H-code"
|
|
||||||
void setDefaultHookName(const QString &name);
|
|
||||||
|
|
||||||
bool isActive() const;
|
|
||||||
void start();
|
|
||||||
void stop();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
// - Injection -
|
|
||||||
public:
|
|
||||||
//bool attachOneProcess(ulong pid, bool checkActive = false);
|
|
||||||
bool attachProcess(ulong pid, bool checkActive = false);
|
|
||||||
bool detachProcess(ulong pid, bool checkActive = false);
|
|
||||||
bool hijackProcess(ulong pid);
|
|
||||||
//void detachAllProcesses();
|
|
||||||
//QList<ulong> attachedProcesses(bool checkActive = false) const;
|
|
||||||
//ulong anyAttachedProcess(bool checkActive = false) const;
|
|
||||||
//ulong currentProccess() const;
|
|
||||||
|
|
||||||
bool containsProcess(ulong pid) const;
|
|
||||||
bool isEmpty() const; ///< Return true if at least one process is attached
|
|
||||||
|
|
||||||
bool addHookCode(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
|
|
||||||
static bool verifyHookCode(const QString &code); ///< Return if hcode is valid
|
|
||||||
//bool containsHook(ulong pid) const;
|
|
||||||
//bool containsHook(ulong pid, const QString &code) const;
|
|
||||||
//QString processHook(ulong pid) const;
|
|
||||||
//QString currentHook() const { return processHook(currentProccess()); }
|
|
||||||
bool removeHookCode(ulong pid); ///< Assume atmost one hcode per process
|
|
||||||
|
|
||||||
// - Whitelist -
|
|
||||||
public:
|
|
||||||
bool isThreadWhitelistEnabled() const;
|
|
||||||
void setThreadWhitelistEnabled(bool t);
|
|
||||||
QList<qint32> threadWhitelist() const;
|
|
||||||
void setThreadWhitelist(const QList<qint32> &signatures);
|
|
||||||
void clearThreadWhitelist();
|
|
||||||
// Note: len(v) must be smaller than 0x200
|
|
||||||
void setKeptThreadName(const QString &v);
|
|
||||||
QString keptThreadName() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* texthook.rc
|
|
||||||
* 10/20/2011 jichi
|
|
||||||
*/
|
|
||||||
#if defined(UNDER_CE)
|
|
||||||
# include <winbase.h>
|
|
||||||
#else
|
|
||||||
# include <winver.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
|
||||||
FILEVERSION 1,0,0,0
|
|
||||||
PRODUCTVERSION 1,0,0,0
|
|
||||||
FILEFLAGSMASK 0x3fL
|
|
||||||
#ifdef _DEBUG
|
|
||||||
FILEFLAGS VS_FF_DEBUG
|
|
||||||
#else
|
|
||||||
FILEFLAGS 0x0L
|
|
||||||
#endif
|
|
||||||
FILEOS VOS__WINDOWS32
|
|
||||||
FILETYPE VFT_DLL
|
|
||||||
FILESUBTYPE 0x0L
|
|
||||||
BEGIN
|
|
||||||
BLOCK "StringFileInfo"
|
|
||||||
BEGIN
|
|
||||||
BLOCK "040904B0"
|
|
||||||
BEGIN
|
|
||||||
VALUE "CompanyName", "Sakuradite\0"
|
|
||||||
VALUE "FileDescription", "Text Hook\0"
|
|
||||||
VALUE "FileVersion", "1.0.0.0\0"
|
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2012.\0"
|
|
||||||
VALUE "OriginalFilename", "texthook.dll\0"
|
|
||||||
VALUE "ProductName", "texthook\0"
|
|
||||||
END
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
/* End of Version info */
|
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// texthook_config.h
|
|
||||||
// 10/20/2011 jichi
|
|
||||||
|
|
||||||
//#define TEXTHOOK_EXPORT
|
|
||||||
|
|
||||||
#ifndef TEXTHOOK_EXPORT
|
|
||||||
# ifdef TEXTHOOK_STATIC_LIB
|
|
||||||
# define TEXTHOOK_EXPORT
|
|
||||||
# elif defined(TEXTHOOK_BUILD_LIB)
|
|
||||||
# define TEXTHOOK_EXPORT Q_DECL_EXPORT
|
|
||||||
# else
|
|
||||||
# define TEXTHOOK_EXPORT Q_DECL_IMPORT
|
|
||||||
# endif
|
|
||||||
#endif // TEXTHOOK_EXPORT
|
|
||||||
|
|
||||||
#define TEXTHOOK_DEFAULT_NAME "H-code"
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,39 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// texthook_p.h
|
|
||||||
// 10/14/2011 jichi
|
|
||||||
// Internal header.
|
|
||||||
// Defines TextHook private data.
|
|
||||||
|
|
||||||
#include "texthook/texthook.h"
|
|
||||||
#include <QtCore/QHash>
|
|
||||||
#include <QtCore/QSet>
|
|
||||||
|
|
||||||
// - Private -
|
|
||||||
|
|
||||||
class TextHookPrivate
|
|
||||||
{
|
|
||||||
SK_CLASS(TextHookPrivate)
|
|
||||||
SK_DECLARE_PUBLIC(TextHook)
|
|
||||||
|
|
||||||
static Self *instance_; // global instance
|
|
||||||
|
|
||||||
bool enabled;
|
|
||||||
QString source;
|
|
||||||
QSet<ulong> pids;
|
|
||||||
QHash<ulong, QString> hooks; // ITH hook code, indexed by pid
|
|
||||||
|
|
||||||
explicit TextHookPrivate(Q *q)
|
|
||||||
: q_(q), enabled(true), source(TEXTHOOK_DEFAULT_NAME) { instance_ = this; }
|
|
||||||
|
|
||||||
~TextHookPrivate() { instance_ = nullptr; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void sendData(const QByteArray &rawData, const QByteArray &renderedData, qint32 signature, const QString &name)
|
|
||||||
{
|
|
||||||
if (instance_ && instance_->q_->isEnabled())
|
|
||||||
emit instance_->q_->dataReceived(rawData, renderedData, signature, name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,376 +0,0 @@
|
|||||||
// textthread_p.cc
|
|
||||||
// 6/6/2012 jichi
|
|
||||||
|
|
||||||
#include "texthook/textthread_p.h"
|
|
||||||
#include "texthook/texthook_p.h"
|
|
||||||
#include "winmutex/winmutex.h"
|
|
||||||
#include "wintimer/wintimer.h"
|
|
||||||
#include "host/textthread.h"
|
|
||||||
#include <QtCore/QRegExp>
|
|
||||||
|
|
||||||
//#define DEBUG "textthread_p.cc"
|
|
||||||
#include "sakurakit/skdebug.h"
|
|
||||||
|
|
||||||
enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
|
|
||||||
|
|
||||||
#define REPEAT_RX_1 "(.{2,})\\1+$" // The pattern has at least 2 bytes, and repeats at least once
|
|
||||||
|
|
||||||
/** Private class */
|
|
||||||
|
|
||||||
#define D_LOCK win_mutex_lock<D::mutex_type> d_lock(D::globalMutex) // Synchronized scope for accessing private data
|
|
||||||
|
|
||||||
class TextThreadDelegatePrivate
|
|
||||||
{
|
|
||||||
SK_CLASS(TextThreadDelegatePrivate)
|
|
||||||
SK_DISABLE_COPY(TextThreadDelegatePrivate)
|
|
||||||
|
|
||||||
public:
|
|
||||||
typedef win_mutex<CRITICAL_SECTION> mutex_type;
|
|
||||||
|
|
||||||
static mutex_type globalMutex; // Used only in public class. Because ITH is running in another single thread
|
|
||||||
static int globalCapacity; // maximum text size
|
|
||||||
static bool globalRemovesRepeat;
|
|
||||||
static bool globalKeepsSpace;
|
|
||||||
static bool globalWideCharacter;
|
|
||||||
|
|
||||||
TextThread *t;
|
|
||||||
WinTimer flushTimer; // as QTimer does not work with windows remote thread, use native WM_TIMER instead
|
|
||||||
|
|
||||||
ulong signature; // buffered
|
|
||||||
char sourceBuffer[ITH_THREAD_NAME_CAPACITY]; // buffered
|
|
||||||
QString source; // buffered
|
|
||||||
|
|
||||||
int bufferSize;
|
|
||||||
int bufferCapacity;
|
|
||||||
char *buffer;
|
|
||||||
bool removesRepeat;
|
|
||||||
|
|
||||||
QByteArray spaceBuffer;
|
|
||||||
int spaceCount;
|
|
||||||
|
|
||||||
struct Repeat
|
|
||||||
{
|
|
||||||
QRegExp rx; // cached
|
|
||||||
char *buffer; // repeated string
|
|
||||||
int size;
|
|
||||||
int pos; // >= 0, current pos of repeating string
|
|
||||||
int offset; // offset of repeated string
|
|
||||||
|
|
||||||
Repeat() : rx(REPEAT_RX_1), buffer(nullptr), size(0), pos(0), offset(-1) {}
|
|
||||||
~Repeat() { if (buffer) delete[] buffer; }
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
size = pos = 0;
|
|
||||||
offset = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isRepeating(const char *data, int len) const
|
|
||||||
{
|
|
||||||
if (!size || !buffer)
|
|
||||||
return false;
|
|
||||||
switch (len) {
|
|
||||||
case 1: return pos < size && buffer[pos] == *data;
|
|
||||||
case 2: return pos < size + 1 && buffer[pos] == data[0] && buffer[pos +1] == data[1];
|
|
||||||
default:
|
|
||||||
if (pos + len >= size)
|
|
||||||
return false;
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
if (buffer[pos + i] != data[i])
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} repeat;
|
|
||||||
|
|
||||||
// - Construction -
|
|
||||||
public:
|
|
||||||
explicit TextThreadDelegatePrivate(TextThread *thread) : t(thread),
|
|
||||||
bufferSize(0), bufferCapacity(globalCapacity), buffer(new char[globalCapacity]),
|
|
||||||
spaceCount(0),
|
|
||||||
removesRepeat(false)
|
|
||||||
{
|
|
||||||
signature = signatureOf(t);
|
|
||||||
|
|
||||||
//size_t size =
|
|
||||||
t->GetThreadString(sourceBuffer, ITH_THREAD_NAME_CAPACITY);
|
|
||||||
source = sourceBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
~TextThreadDelegatePrivate() { delete[] buffer; }
|
|
||||||
|
|
||||||
// - Properties -
|
|
||||||
public:
|
|
||||||
//QString text() const { return QString::fromLocal8Bit(buffer); }
|
|
||||||
//ulong context() const { return t->GetThreadParameter()->retn; }
|
|
||||||
//ulong subcontext() const { return t->GetThreadParameter()->spl; }
|
|
||||||
|
|
||||||
//ulong processId() const { return t->PID(); }
|
|
||||||
|
|
||||||
// - Actions -
|
|
||||||
public:
|
|
||||||
void flush()
|
|
||||||
{
|
|
||||||
if (flushTimer.isActive())
|
|
||||||
flushTimer.stop();
|
|
||||||
if (bufferSize) {
|
|
||||||
send();
|
|
||||||
bufferSize = 0;
|
|
||||||
}
|
|
||||||
if (!spaceBuffer.isEmpty())
|
|
||||||
spaceBuffer.clear();
|
|
||||||
spaceCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void syncGlobal()
|
|
||||||
{
|
|
||||||
if (bufferCapacity < globalCapacity) {
|
|
||||||
delete[] buffer;
|
|
||||||
bufferCapacity = globalCapacity;
|
|
||||||
buffer = new char[bufferCapacity];
|
|
||||||
if (repeat.buffer) {
|
|
||||||
delete[] repeat.buffer;
|
|
||||||
if (!removesRepeat)
|
|
||||||
repeat.buffer = nullptr;
|
|
||||||
else {
|
|
||||||
char *largerBuffer = new char[bufferCapacity];
|
|
||||||
if (repeat.size)
|
|
||||||
qMemCopy(largerBuffer, repeat.buffer, repeat.size);
|
|
||||||
repeat.buffer = largerBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//bufferSize = repeatOffset = 0; // already reset in flush
|
|
||||||
//if (removesRepeat)
|
|
||||||
// repeat.reset();
|
|
||||||
}
|
|
||||||
if (removesRepeat != globalRemovesRepeat) {
|
|
||||||
removesRepeat = globalRemovesRepeat;
|
|
||||||
if (removesRepeat)
|
|
||||||
repeat.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendSpace()
|
|
||||||
{
|
|
||||||
flushTimer.start();
|
|
||||||
spaceCount++;
|
|
||||||
if (spaceBuffer.isEmpty())
|
|
||||||
spaceBuffer.append(buffer, bufferSize);
|
|
||||||
spaceBuffer.append(' ');
|
|
||||||
if (globalWideCharacter)
|
|
||||||
spaceBuffer.append('\0'); // L' ' = {'\x20', '\0'};
|
|
||||||
}
|
|
||||||
|
|
||||||
void append(const char *data, int len)
|
|
||||||
{
|
|
||||||
flushTimer.start();
|
|
||||||
if (bufferSize < qMin(bufferCapacity, globalCapacity))
|
|
||||||
switch (len) {
|
|
||||||
case 1: buffer[bufferSize++] = *data; break;
|
|
||||||
case 2: buffer[bufferSize++] = *data;
|
|
||||||
if (bufferSize < bufferCapacity)
|
|
||||||
buffer[bufferSize++] = data[1];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
int diff = qMin(len, bufferCapacity - bufferSize);
|
|
||||||
qMemCopy(buffer + bufferSize, data, diff);
|
|
||||||
bufferSize += diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!spaceBuffer.isEmpty())
|
|
||||||
spaceBuffer.append(data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void appendRepeat(const char *data, int len)
|
|
||||||
{
|
|
||||||
if (bufferSize + len >= qMin(bufferCapacity, globalCapacity)) // overflow
|
|
||||||
return;
|
|
||||||
if (repeat.isRepeating(data, len)) {
|
|
||||||
repeat.pos += len;
|
|
||||||
if (repeat.pos >= repeat.size)
|
|
||||||
repeat.pos = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
repeat.clear();
|
|
||||||
|
|
||||||
append(data, len);
|
|
||||||
|
|
||||||
if (bufferSize >= 6) { // at least 2 characters
|
|
||||||
// Use fromLatin1 to prevent the data from being decoded
|
|
||||||
QString t = QString::fromLatin1(buffer, bufferSize);
|
|
||||||
repeat.offset = repeat.rx.indexIn(t);
|
|
||||||
if (repeat.offset >= 0) {
|
|
||||||
repeat.size = repeat.rx.cap(1).size();
|
|
||||||
if (!repeat.buffer)
|
|
||||||
repeat.buffer = new char[bufferCapacity];
|
|
||||||
qMemCopy(repeat.buffer, buffer + repeat.offset, repeat.size);
|
|
||||||
//bufferSize = repeat.offset repeat.size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void send()
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
if (removesRepeat && repeat.offset >= 0 && repeat.size)
|
|
||||||
size = repeat.offset + repeat.size;
|
|
||||||
else
|
|
||||||
size = bufferSize;
|
|
||||||
if (!spaceBuffer.isEmpty() && spaceBuffer.size() != size)
|
|
||||||
spaceBuffer.truncate(size + spaceCount);
|
|
||||||
TextHookPrivate::sendData(
|
|
||||||
QByteArray(buffer, size), spaceBuffer,
|
|
||||||
signature, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
static qint32 signatureOf(TextThread *t)
|
|
||||||
{
|
|
||||||
qint32 ret =
|
|
||||||
(t->GetThreadParameter()->retn & 0xffff) | // context
|
|
||||||
(t->GetThreadParameter()->spl & 0xffff) << 16; // subcontext
|
|
||||||
return ret ? ret : t->Addr();
|
|
||||||
}
|
|
||||||
|
|
||||||
//static QString sourceOf(TextThread *t);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static ulong contextOf(TextThread *t)
|
|
||||||
{ return t->GetThreadParameter()->retn; }
|
|
||||||
|
|
||||||
static ulong subcontextOf(TextThread *t)
|
|
||||||
{ return t->GetThreadParameter()->spl; }
|
|
||||||
};
|
|
||||||
|
|
||||||
TextThreadDelegatePrivate::mutex_type TextThreadDelegatePrivate::globalMutex;
|
|
||||||
int TextThreadDelegatePrivate::globalCapacity = 512;
|
|
||||||
bool TextThreadDelegatePrivate::globalRemovesRepeat = false;
|
|
||||||
bool TextThreadDelegatePrivate::globalKeepsSpace = false;
|
|
||||||
bool TextThreadDelegatePrivate::globalWideCharacter = false;
|
|
||||||
|
|
||||||
//QString TextThreadDelegatePrivate::sourceOf(TextThread *t)
|
|
||||||
//{
|
|
||||||
// Q_ASSERT(t);
|
|
||||||
// QString ret;
|
|
||||||
// enum { buf_size = 0x200 }; // 0x200 is used by ITH internally
|
|
||||||
// wchar_t buf[buf_size];
|
|
||||||
// ulong len = t->GetThreadString(buf, buf_size);
|
|
||||||
// if (len)
|
|
||||||
// ret = QString::fromWCharArray(buf, len);
|
|
||||||
// return ret;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/** Public class */
|
|
||||||
|
|
||||||
// - Constructions -
|
|
||||||
|
|
||||||
TextThreadDelegate::TextThreadDelegate(TextThread *t)
|
|
||||||
: d_(new D(t))
|
|
||||||
{
|
|
||||||
d_->flushTimer.setMethod(this, &Self::flush);
|
|
||||||
d_->flushTimer.setSingleShot(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextThreadDelegate::~TextThreadDelegate()
|
|
||||||
{
|
|
||||||
if (d_->flushTimer.isActive())
|
|
||||||
d_->flushTimer.stop();
|
|
||||||
delete d_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextThreadDelegate::delegateOf(const Self *that) const
|
|
||||||
{
|
|
||||||
Q_ASSERT(t);
|
|
||||||
// Both have no context, and my subcontext is smaller
|
|
||||||
return that
|
|
||||||
&& !D::contextOf(that->d_->t) && !D::contextOf(d_->t)
|
|
||||||
&& D::subcontextOf(that->d_->t) >= D::subcontextOf(d_->t)
|
|
||||||
&& ::strcmp(d_->sourceBuffer, that->d_->sourceBuffer) == 0
|
|
||||||
&& nameEquals("Malie");
|
|
||||||
}
|
|
||||||
|
|
||||||
// - Properties -
|
|
||||||
|
|
||||||
//TextThread *TextThreadDelegate::t() const { return d_->t; }
|
|
||||||
int TextThreadDelegate::threadNumber() const
|
|
||||||
{ return d_->t->Number(); }
|
|
||||||
|
|
||||||
qint32 TextThreadDelegate::signature() const
|
|
||||||
{ return d_->signature; }
|
|
||||||
|
|
||||||
QString TextThreadDelegate::name() const
|
|
||||||
{ return d_->source; }
|
|
||||||
|
|
||||||
bool TextThreadDelegate::nameEquals(const char *that) const
|
|
||||||
{ return !::strcmp(d_->sourceBuffer, that); }
|
|
||||||
|
|
||||||
int TextThreadDelegate::capacity() { return D::globalCapacity; }
|
|
||||||
void TextThreadDelegate::setCapacity(int value) { D::globalCapacity = value; }
|
|
||||||
|
|
||||||
bool TextThreadDelegate::removesRepeat() { return D::globalRemovesRepeat; }
|
|
||||||
void TextThreadDelegate::setRemovesRepeat(bool value) { D::globalRemovesRepeat = value; }
|
|
||||||
|
|
||||||
bool TextThreadDelegate::wideCharacter() { return D::globalWideCharacter; }
|
|
||||||
void TextThreadDelegate::setWideCharacter(bool value) { D::globalWideCharacter = value; }
|
|
||||||
|
|
||||||
bool TextThreadDelegate::keepsSpace() { return D::globalKeepsSpace; }
|
|
||||||
void TextThreadDelegate::setKeepsSpace(bool value) { D::globalKeepsSpace = value; }
|
|
||||||
|
|
||||||
void TextThreadDelegate::setInterval(int msecs)
|
|
||||||
{ d_->flushTimer.setInterval(msecs); }
|
|
||||||
|
|
||||||
void TextThreadDelegate::setParentWindow(WId winId)
|
|
||||||
{ d_->flushTimer.setParentWindow(winId); }
|
|
||||||
|
|
||||||
// - Actions -
|
|
||||||
|
|
||||||
void TextThreadDelegate::flush()
|
|
||||||
{
|
|
||||||
D_LOCK;
|
|
||||||
d_->flush();
|
|
||||||
d_->syncGlobal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextThreadDelegate::touch()
|
|
||||||
{
|
|
||||||
D_LOCK;
|
|
||||||
d_->flushTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextThreadDelegate::append(const char *data, int len, bool space)
|
|
||||||
{
|
|
||||||
D_LOCK;
|
|
||||||
if (space && D::globalKeepsSpace)
|
|
||||||
d_->appendSpace();
|
|
||||||
if (data && len) {
|
|
||||||
if (d_->removesRepeat)
|
|
||||||
d_->appendRepeat(data, len);
|
|
||||||
else
|
|
||||||
d_->append(data, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
||||||
/*
|
|
||||||
void TextThreadDelegate::append(const QByteArray &data)
|
|
||||||
{
|
|
||||||
D::mutex_lock_type locker(D::mutex);
|
|
||||||
|
|
||||||
d_->flushTimer.start();
|
|
||||||
if (d_->buffer.size() <= D::capacity)
|
|
||||||
d_->buffer.append(data);
|
|
||||||
}
|
|
||||||
void TextThreadDelegatePrivate::send()
|
|
||||||
{
|
|
||||||
#ifdef DEBUG
|
|
||||||
qDebug()<< source()
|
|
||||||
<< t->Number()
|
|
||||||
<< t->PID()
|
|
||||||
<< QString::number(t->Addr(), 16)
|
|
||||||
<< QString::number(t->GetThreadParameter()->retn, 16)
|
|
||||||
<< QString::number(t->GetThreadParameter()->spl, 16)
|
|
||||||
<< QTextCodec::codecForName("SHIFT-JIS")->makeDecoder()->toUnicode(buffer);
|
|
||||||
#endif // DEBUG
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,81 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// textthread_p.h
|
|
||||||
// 6/6/2012 jichi
|
|
||||||
// Internal header.
|
|
||||||
// Defines TextHook delegate class.
|
|
||||||
|
|
||||||
#include "sakurakit/skglobal.h"
|
|
||||||
#include <QtGui/qwindowdefs.h>
|
|
||||||
|
|
||||||
//QT_FORWARD_DECLARE_CLASS(QByteArray)
|
|
||||||
|
|
||||||
class SharedRef
|
|
||||||
{
|
|
||||||
SK_CLASS(SharedRef)
|
|
||||||
int count_;
|
|
||||||
public:
|
|
||||||
SharedRef(): count_(1) {}
|
|
||||||
int retainCount() const { return count_; }
|
|
||||||
void retain() { count_++; }
|
|
||||||
//void release() { count_--; }
|
|
||||||
static void release(Self *x) { if (--x->count_ <= 0) delete x; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: This class is not thread-safe!
|
|
||||||
class TextThread;
|
|
||||||
class TextThreadDelegatePrivate;
|
|
||||||
class TextThreadDelegate : public SharedRef
|
|
||||||
{
|
|
||||||
SK_EXTEND_CLASS(TextThreadDelegate, SharedRef)
|
|
||||||
SK_DISABLE_COPY(TextThreadDelegate)
|
|
||||||
SK_DECLARE_PRIVATE(TextThreadDelegatePrivate)
|
|
||||||
public:
|
|
||||||
explicit TextThreadDelegate(TextThread *t);
|
|
||||||
~TextThreadDelegate();
|
|
||||||
|
|
||||||
bool delegateOf(const Self *t) const;
|
|
||||||
|
|
||||||
// - Properties -
|
|
||||||
|
|
||||||
//TextThread *t() const;
|
|
||||||
int threadNumber() const;
|
|
||||||
qint32 signature() const;
|
|
||||||
QString name() const;
|
|
||||||
bool nameEquals(const char *that) const; // optimized
|
|
||||||
|
|
||||||
// Maximum text size
|
|
||||||
static int capacity();
|
|
||||||
static void setCapacity(int value);
|
|
||||||
|
|
||||||
static bool wideCharacter();
|
|
||||||
static void setWideCharacter(bool value);
|
|
||||||
|
|
||||||
static bool removesRepeat();
|
|
||||||
static void setRemovesRepeat(bool value);
|
|
||||||
|
|
||||||
static bool keepsSpace();
|
|
||||||
static void setKeepsSpace(bool value);
|
|
||||||
|
|
||||||
//TextThread *t() const;
|
|
||||||
|
|
||||||
//int interval() const;
|
|
||||||
void setInterval(int msecs);
|
|
||||||
|
|
||||||
//WId parentWindow() const;
|
|
||||||
void setParentWindow(WId winId);
|
|
||||||
|
|
||||||
// - Actions -
|
|
||||||
|
|
||||||
//void append(const QByteArray &data);
|
|
||||||
/** Add data to the text thread
|
|
||||||
* @param data raw data
|
|
||||||
* @param len length of the data
|
|
||||||
* @param space Whether have LEADING space
|
|
||||||
*/
|
|
||||||
void append(const char *data, int len, bool space=false);
|
|
||||||
void flush();
|
|
||||||
void touch(); // keep timer running
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,21 +0,0 @@
|
|||||||
// apiwin_p.cc
|
|
||||||
// 10/6/2012 jichi
|
|
||||||
#include "texthook/winapi_p.h"
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
WINAPI_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
bool IsProcessActiveWithId(DWORD dwProcessId)
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
if (HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId)) {
|
|
||||||
DWORD dwExitCode;
|
|
||||||
ret = ::GetExitCodeProcess(hProc, &dwExitCode) && (dwExitCode == STILL_ACTIVE);
|
|
||||||
::CloseHandle(hProc);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
WINAPI_END_NAMESPACE
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
// winapi_p.h
|
|
||||||
// 10/5/2012 jichi
|
|
||||||
// Internal header.
|
|
||||||
// Wrapper of <windows.h>
|
|
||||||
|
|
||||||
#ifndef WINAPI_BEGIN_NAMESPACE
|
|
||||||
# define WINAPI_BEGIN_NAMESPACE namespace winapi {
|
|
||||||
#endif
|
|
||||||
#ifndef WINAPI_END_NAMESPACE
|
|
||||||
# define WINAPI_END_NAMESPACE } // namespace winapi
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WINAPI_BEGIN_NAMESPACE
|
|
||||||
bool IsProcessActiveWithId(unsigned long dwProcessId);
|
|
||||||
WINAPI_END_NAMESPACE
|
|
||||||
|
|
||||||
// EOF
|
|
Loading…
x
Reference in New Issue
Block a user