exclude useless file
This commit is contained in:
parent
90613f5039
commit
80193634f9
@ -1,119 +0,0 @@
|
|||||||
/* 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
|
|
||||||
|
|
||||||
template <class T, unsigned int default_size>
|
|
||||||
class PointerTable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PointerTable()
|
|
||||||
{
|
|
||||||
assert((default_size & (default_size - 1)) == 0);
|
|
||||||
size = default_size;
|
|
||||||
table = new T*[size];
|
|
||||||
used = 0;
|
|
||||||
next = 0;
|
|
||||||
memset(table, 0, size * sizeof(T*));
|
|
||||||
}
|
|
||||||
~PointerTable()
|
|
||||||
{
|
|
||||||
delete table;
|
|
||||||
}
|
|
||||||
T* Set(unsigned int number, T* ptr)
|
|
||||||
{
|
|
||||||
if (number >= size - 2)
|
|
||||||
{
|
|
||||||
unsigned int new_size = size;
|
|
||||||
while (number >= new_size - 2) new_size <<= 1;
|
|
||||||
Resize(new_size);
|
|
||||||
}
|
|
||||||
T* original = table[number + 1];
|
|
||||||
table[number + 1] = ptr;
|
|
||||||
if (ptr == 0) //Clear pointer.
|
|
||||||
{
|
|
||||||
if (number < next) next = number;
|
|
||||||
if (number == used - 1) //Last used position is cleared.
|
|
||||||
{
|
|
||||||
table[0] = (T*)1;
|
|
||||||
for (used--; table[used] == 0; used--);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else //Set pointer.
|
|
||||||
{
|
|
||||||
__assume(number < size - 2); //Otherwise a resize operation is invoked.
|
|
||||||
if (number == next)
|
|
||||||
{
|
|
||||||
next++; //Next position is occupied.
|
|
||||||
for (next++; table[next]; next++); //There is always a zero in the end.
|
|
||||||
next--; //next is zero based but the table start at one(zero is used as sentry).
|
|
||||||
}
|
|
||||||
if (number >= used) used = number + 1;
|
|
||||||
}
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
T* Get(unsigned int number)
|
|
||||||
{
|
|
||||||
number++;
|
|
||||||
if (number <= used) return table[number];
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
T* operator [](unsigned int number)
|
|
||||||
{
|
|
||||||
number++;
|
|
||||||
if (number <= used) return table[number];
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
void Append(T* ptr)
|
|
||||||
{
|
|
||||||
Set(next,ptr);
|
|
||||||
}
|
|
||||||
void Resize(unsigned int new_size)
|
|
||||||
{
|
|
||||||
assert(new_size > size);
|
|
||||||
assert((new_size & (new_size - 1)) == 0);
|
|
||||||
assert(new_size < 0x10000);
|
|
||||||
|
|
||||||
T** temp = new T*[new_size];
|
|
||||||
memcpy(temp, table, size * sizeof(T*));
|
|
||||||
memset(temp + size, 0, (new_size - size) * sizeof(T*));
|
|
||||||
delete table;
|
|
||||||
size = new_size;
|
|
||||||
table = temp;
|
|
||||||
}
|
|
||||||
void DeleteAll() //Release all pointers on demand.
|
|
||||||
{
|
|
||||||
T* p;
|
|
||||||
next = 0;
|
|
||||||
while (used)
|
|
||||||
{
|
|
||||||
p = table[used];
|
|
||||||
if (p) delete p;
|
|
||||||
table[used] = 0;
|
|
||||||
used--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Reset() //Reset without release pointers.
|
|
||||||
{
|
|
||||||
memset(table, 0, sizeof(T*) * (used + 1));
|
|
||||||
next = 0;
|
|
||||||
used = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
unsigned int size,next,used;
|
|
||||||
T** table;
|
|
||||||
};
|
|
341
gui/Profile.cpp
341
gui/Profile.cpp
@ -1,341 +0,0 @@
|
|||||||
/* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "ITH.h"
|
|
||||||
#include "ith/host/srv.h"
|
|
||||||
#include "ith/host/hookman.h"
|
|
||||||
#include "ith/common/types.h"
|
|
||||||
#include "ith/common/const.h"
|
|
||||||
#include "Profile.h"
|
|
||||||
#include "utility.h"
|
|
||||||
|
|
||||||
Profile::Profile(const std::wstring& title) :
|
|
||||||
select_index(-1),
|
|
||||||
title(title)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::vector<thread_ptr>::const_iterator Profile::FindThreadProfile(const ThreadParameter& tp) const
|
|
||||||
{
|
|
||||||
auto thread_profile = std::find_if(threads.begin(), threads.end(),
|
|
||||||
[&tp](const thread_ptr& thread_profile) -> bool
|
|
||||||
{
|
|
||||||
if (thread_profile->HookAddress() != tp.hook)
|
|
||||||
return false;
|
|
||||||
DWORD t1 = thread_profile->Return();
|
|
||||||
DWORD t2 = tp.retn;
|
|
||||||
if (thread_profile->Flags() & THREAD_MASK_RETN)
|
|
||||||
{
|
|
||||||
t1 &= 0xFFFF;
|
|
||||||
t2 &= 0xFFFF;
|
|
||||||
}
|
|
||||||
if (t1 != t2)
|
|
||||||
return false;
|
|
||||||
t1 = thread_profile->Split();
|
|
||||||
t2 = tp.spl;
|
|
||||||
if (thread_profile->Flags() & THREAD_MASK_SPLIT)
|
|
||||||
{
|
|
||||||
t1 &= 0xFFFF;
|
|
||||||
t2 &= 0xFFFF;
|
|
||||||
}
|
|
||||||
return t1 == t2;
|
|
||||||
});
|
|
||||||
return thread_profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<hook_ptr>& Profile::Hooks() const
|
|
||||||
{
|
|
||||||
return hooks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<thread_ptr>& Profile::Threads() const
|
|
||||||
{
|
|
||||||
return threads;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<link_ptr>& Profile::Links() const
|
|
||||||
{
|
|
||||||
return links;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlReadProfile(pugi::xml_node profile)
|
|
||||||
{
|
|
||||||
auto hooks_node = profile.child(L"Hooks");
|
|
||||||
auto threads_node = profile.child(L"Threads");
|
|
||||||
auto links_node = profile.child(L"Links");
|
|
||||||
if (hooks_node && !XmlReadProfileHook(hooks_node))
|
|
||||||
return false;
|
|
||||||
if (threads_node && !XmlReadProfileThread(threads_node))
|
|
||||||
return false;
|
|
||||||
if (links_node && !XmlReadProfileLink(links_node))
|
|
||||||
return false;
|
|
||||||
auto select_node = profile.child(L"Select");
|
|
||||||
if (select_node)
|
|
||||||
{
|
|
||||||
auto thread_index = select_node.attribute(L"ThreadIndex");
|
|
||||||
if (!thread_index)
|
|
||||||
return false;
|
|
||||||
DWORD tmp_select = std::stoul(thread_index.value(), NULL, 16);
|
|
||||||
select_index = tmp_select & 0xFFFF;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlReadProfileHook(pugi::xml_node hooks_node)
|
|
||||||
{
|
|
||||||
for (auto hook = hooks_node.begin(); hook != hooks_node.end(); ++hook)
|
|
||||||
{
|
|
||||||
std::wstring name = hook->name();
|
|
||||||
if (name.empty() || name.compare(L"Hook") != 0)
|
|
||||||
return false;
|
|
||||||
auto type = hook->attribute(L"Type");
|
|
||||||
if (!type || type.empty())
|
|
||||||
return false;
|
|
||||||
auto code = hook->attribute(L"Code");
|
|
||||||
if (!code)
|
|
||||||
return false;
|
|
||||||
std::wstring code_value = code.value();
|
|
||||||
HookParam hp = {};
|
|
||||||
switch (type.value()[0])
|
|
||||||
{
|
|
||||||
case L'H':
|
|
||||||
if (code_value[0] != L'/')
|
|
||||||
return false;
|
|
||||||
if (code_value[1] != L'H' && code_value[1] != L'h')
|
|
||||||
return false;
|
|
||||||
if (Parse(code_value.substr(2), hp))
|
|
||||||
{
|
|
||||||
auto name = hook->attribute(L"Name");
|
|
||||||
if (!name || name.empty())
|
|
||||||
AddHook(hp, L"");
|
|
||||||
else
|
|
||||||
AddHook(hp, name.value());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlReadProfileThread(pugi::xml_node threads_node)
|
|
||||||
{
|
|
||||||
std::wstring hook_name_buffer;
|
|
||||||
for (auto thread = threads_node.begin(); thread != threads_node.end(); ++thread)
|
|
||||||
{
|
|
||||||
std::wstring name = thread->name();
|
|
||||||
if (name.empty() || name.compare(L"Thread") != 0)
|
|
||||||
return false;
|
|
||||||
auto hook_name = thread->attribute(L"HookName");
|
|
||||||
if (!hook_name)
|
|
||||||
return false;
|
|
||||||
auto context = thread->attribute(L"Context");
|
|
||||||
if (!context)
|
|
||||||
return false;
|
|
||||||
auto sub_context = thread->attribute(L"SubContext");
|
|
||||||
if (!sub_context)
|
|
||||||
return false;
|
|
||||||
auto mask = thread->attribute(L"Mask");
|
|
||||||
if (!mask)
|
|
||||||
return false;
|
|
||||||
DWORD mask_tmp = std::stoul(mask.value(), NULL, 16);
|
|
||||||
auto comment = thread->attribute(L"Comment");
|
|
||||||
auto retn = std::stoul(context.value(), NULL, 16);
|
|
||||||
WORD hm_index = 0;
|
|
||||||
auto hook_addr = 0;
|
|
||||||
auto split = std::stoul(sub_context.value(), NULL, 16);
|
|
||||||
WORD flags = mask_tmp & 0xFFFF;
|
|
||||||
auto tp = new ThreadProfile(hook_name.value(), retn, split, hook_addr, hm_index, flags,
|
|
||||||
comment.value());
|
|
||||||
AddThread(thread_ptr(tp));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlReadProfileLink(pugi::xml_node links_node)
|
|
||||||
{
|
|
||||||
for (auto link = links_node.begin(); link != links_node.end(); ++link)
|
|
||||||
{
|
|
||||||
std::wstring name = link->name();
|
|
||||||
if (name.empty() || name.compare(L"Link") != 0)
|
|
||||||
return false;
|
|
||||||
auto from = link->attribute(L"From");
|
|
||||||
if (!from)
|
|
||||||
return false;
|
|
||||||
DWORD link_from = std::stoul(from.value(), NULL, 16);
|
|
||||||
auto to = link->attribute(L"To");
|
|
||||||
if (!to)
|
|
||||||
return false;
|
|
||||||
DWORD link_to = std::stoul(to.value(), NULL, 16);
|
|
||||||
auto lp = new LinkProfile(link_from & 0xFFFF, link_to & 0xFFFF);
|
|
||||||
AddLink(link_ptr(lp));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlWriteProfile(pugi::xml_node profile_node)
|
|
||||||
{
|
|
||||||
if (!hooks.empty())
|
|
||||||
{
|
|
||||||
auto node = profile_node.append_child(L"Hooks");
|
|
||||||
XmlWriteProfileHook(node);
|
|
||||||
}
|
|
||||||
if (!threads.empty())
|
|
||||||
{
|
|
||||||
auto node = profile_node.append_child(L"Threads");
|
|
||||||
XmlWriteProfileThread(node);
|
|
||||||
}
|
|
||||||
if (!links.empty())
|
|
||||||
{
|
|
||||||
auto node = profile_node.append_child(L"Links");
|
|
||||||
XmlWriteProfileLink(node);
|
|
||||||
}
|
|
||||||
if (select_index != 0xFFFF)
|
|
||||||
{
|
|
||||||
auto node = profile_node.append_child(L"Select");
|
|
||||||
node.append_attribute(L"ThreadIndex") = select_index;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlWriteProfileHook(pugi::xml_node hooks_node)
|
|
||||||
{
|
|
||||||
for (auto hook = hooks.begin(); hook != hooks.end(); ++hook)
|
|
||||||
{
|
|
||||||
auto hook_node = hooks_node.append_child(L"Hook");
|
|
||||||
hook_node.append_attribute(L"Type") = L"H";
|
|
||||||
hook_node.append_attribute(L"Code") = GetCode((*hook)->HP()).c_str();
|
|
||||||
if (!(*hook)->Name().empty())
|
|
||||||
hook_node.append_attribute(L"Name") = (*hook)->Name().c_str();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlWriteProfileThread(pugi::xml_node threads_node)
|
|
||||||
{
|
|
||||||
for (auto thread = threads.begin(); thread != threads.end(); ++thread)
|
|
||||||
{
|
|
||||||
const std::wstring& name = (*thread)->HookName();
|
|
||||||
if (name.empty())
|
|
||||||
return false;
|
|
||||||
auto node = threads_node.append_child(L"Thread");
|
|
||||||
node.append_attribute(L"HookName") = name.c_str();
|
|
||||||
node.append_attribute(L"Mask") = ToHexString((*thread)->Flags() & 3).c_str();
|
|
||||||
node.append_attribute(L"SubContext") = ToHexString((*thread)->Split()).c_str();
|
|
||||||
node.append_attribute(L"Context") = ToHexString((*thread)->Return()).c_str();
|
|
||||||
if (!(*thread)->Comment().empty())
|
|
||||||
node.append_attribute(L"Comment") = (*thread)->Comment().c_str();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Profile::XmlWriteProfileLink(pugi::xml_node links_node)
|
|
||||||
{
|
|
||||||
for (auto link = links.begin(); link != links.end(); ++link)
|
|
||||||
{
|
|
||||||
auto node = links_node.append_child(L"Link");
|
|
||||||
node.append_attribute(L"From") = ToHexString((*link)->FromIndex()).c_str();
|
|
||||||
node.append_attribute(L"To") = ToHexString((*link)->ToIndex()).c_str();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profile::Clear()
|
|
||||||
{
|
|
||||||
title = L"";
|
|
||||||
select_index = -1;
|
|
||||||
hooks.clear();
|
|
||||||
threads.clear();
|
|
||||||
links.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Profile::AddHook(const HookParam& hp, const std::wstring& name)
|
|
||||||
{
|
|
||||||
//if (hook_count == 4) return;
|
|
||||||
auto it = std::find_if(hooks.begin(), hooks.end(), [&hp](hook_ptr& hook)
|
|
||||||
{
|
|
||||||
return hook->HP().addr == hp.addr &&
|
|
||||||
hook->HP().module == hp.module &&
|
|
||||||
hook->HP().function == hp.function;
|
|
||||||
});
|
|
||||||
if (it != hooks.end())
|
|
||||||
return it - hooks.begin();
|
|
||||||
hooks.emplace_back(new HookProfile(hp, name));
|
|
||||||
return hooks.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the thread profile and return its index
|
|
||||||
int Profile::AddThread(thread_ptr tp)
|
|
||||||
{
|
|
||||||
auto it = std::find_if(threads.begin(), threads.end(), [&tp](thread_ptr& thread)
|
|
||||||
{
|
|
||||||
return thread->HookName().compare(tp->HookName()) == 0 &&
|
|
||||||
thread->Return() == tp->Return() &&
|
|
||||||
thread->Split() == tp->Split();
|
|
||||||
});
|
|
||||||
if (it != threads.end())
|
|
||||||
return it - threads.begin();
|
|
||||||
threads.push_back(std::move(tp));
|
|
||||||
return threads.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Profile::AddLink(link_ptr lp)
|
|
||||||
{
|
|
||||||
auto it = std::find_if(links.begin(), links.end(), [&lp] (link_ptr& link)
|
|
||||||
{
|
|
||||||
return link->FromIndex() == lp->FromIndex() &&
|
|
||||||
link->ToIndex() == lp->ToIndex();
|
|
||||||
});
|
|
||||||
if (it != links.end())
|
|
||||||
return it - links.begin();
|
|
||||||
links.push_back(std::move(lp));
|
|
||||||
return links.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profile::RemoveHook(DWORD index)
|
|
||||||
{
|
|
||||||
if (index >= 0 && index < hooks.size())
|
|
||||||
hooks.erase(hooks.begin() + index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profile::RemoveThread(DWORD index)
|
|
||||||
{
|
|
||||||
if (index >= 0 && index < threads.size())
|
|
||||||
{
|
|
||||||
links.erase(std::remove_if(links.begin(), links.end(), [index](link_ptr& link)
|
|
||||||
{
|
|
||||||
return link->FromIndex() == index + 1 || link->ToIndex() == index + 1;
|
|
||||||
}), links.end());
|
|
||||||
if (select_index == index)
|
|
||||||
select_index = -1;
|
|
||||||
threads.erase(threads.begin() + index);
|
|
||||||
if (index < select_index)
|
|
||||||
select_index--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profile::RemoveLink(DWORD index)
|
|
||||||
{
|
|
||||||
if (index >= 0 && index < links.size())
|
|
||||||
links.erase(links.begin() + index);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::wstring& Profile::Title() const
|
|
||||||
{
|
|
||||||
return title;
|
|
||||||
}
|
|
125
gui/Profile.h
125
gui/Profile.h
@ -1,125 +0,0 @@
|
|||||||
/* 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 "ITH.h"
|
|
||||||
#include "ith/common/types.h" // HookParam
|
|
||||||
|
|
||||||
struct ThreadParameter;
|
|
||||||
|
|
||||||
#define THREAD_MASK_RETN 1
|
|
||||||
#define THREAD_MASK_SPLIT 2
|
|
||||||
|
|
||||||
class HookProfile
|
|
||||||
{
|
|
||||||
HookParam hp;
|
|
||||||
std::wstring name;
|
|
||||||
public:
|
|
||||||
HookProfile(const HookParam& hp, const std::wstring& name):
|
|
||||||
hp(hp),
|
|
||||||
name(name)
|
|
||||||
{}
|
|
||||||
const HookParam& HP() const { return hp; };
|
|
||||||
const std::wstring& Name() const { return name; };
|
|
||||||
};
|
|
||||||
|
|
||||||
class ThreadProfile
|
|
||||||
{
|
|
||||||
std::wstring hook_name;
|
|
||||||
DWORD retn;
|
|
||||||
DWORD split;
|
|
||||||
DWORD hook_addr;
|
|
||||||
WORD hm_index, flags;
|
|
||||||
std::wstring comment;
|
|
||||||
public:
|
|
||||||
ThreadProfile(const std::wstring& hook_name,
|
|
||||||
DWORD retn,
|
|
||||||
DWORD split,
|
|
||||||
DWORD hook_addr,
|
|
||||||
WORD hm_index,
|
|
||||||
WORD flags,
|
|
||||||
const std::wstring& comment) :
|
|
||||||
hook_name(hook_name),
|
|
||||||
retn(retn),
|
|
||||||
split(split),
|
|
||||||
hook_addr(hook_addr),
|
|
||||||
hm_index(hm_index),
|
|
||||||
flags(flags),
|
|
||||||
comment(comment)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
const std::wstring& HookName() const { return hook_name; }
|
|
||||||
const std::wstring& Comment() const { return comment; }
|
|
||||||
DWORD Return() const { return retn; }
|
|
||||||
DWORD Split() const { return split; }
|
|
||||||
DWORD& HookAddress() { return hook_addr; }
|
|
||||||
WORD& HookManagerIndex() { return hm_index; }
|
|
||||||
WORD Flags() const { return flags; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class LinkProfile
|
|
||||||
{
|
|
||||||
WORD from_index, to_index;
|
|
||||||
public:
|
|
||||||
LinkProfile(WORD from_index, WORD to_index):
|
|
||||||
from_index(from_index),
|
|
||||||
to_index(to_index)
|
|
||||||
{}
|
|
||||||
WORD FromIndex() const { return from_index; }
|
|
||||||
WORD ToIndex() const { return to_index; }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::unique_ptr<HookProfile> hook_ptr;
|
|
||||||
typedef std::unique_ptr<ThreadProfile> thread_ptr;
|
|
||||||
typedef std::unique_ptr<LinkProfile> link_ptr;
|
|
||||||
|
|
||||||
class Profile
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Profile(const std::wstring& title);
|
|
||||||
bool XmlReadProfile(pugi::xml_node profile_node);
|
|
||||||
bool XmlWriteProfile(pugi::xml_node profile_node);
|
|
||||||
int AddHook(const HookParam& hp, const std::wstring& name);
|
|
||||||
int AddThread(thread_ptr tp);
|
|
||||||
int AddLink(link_ptr lp);
|
|
||||||
void Clear();
|
|
||||||
const std::vector<hook_ptr>& Hooks() const;
|
|
||||||
const std::vector<thread_ptr>& Threads() const;
|
|
||||||
const std::vector<link_ptr>& Links() const;
|
|
||||||
const std::wstring& Title() const;
|
|
||||||
std::vector<thread_ptr>::const_iterator FindThreadProfile(const ThreadParameter& tp) const;
|
|
||||||
WORD& SelectedIndex() { return select_index; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void RemoveLink(DWORD index);
|
|
||||||
void RemoveHook(DWORD index);
|
|
||||||
void RemoveThread(DWORD index);
|
|
||||||
|
|
||||||
bool XmlReadProfileHook(pugi::xml_node hooks_node);
|
|
||||||
bool XmlReadProfileThread(pugi::xml_node threads_node);
|
|
||||||
bool XmlReadProfileLink(pugi::xml_node links_node);
|
|
||||||
bool XmlWriteProfileHook(pugi::xml_node hooks_node);
|
|
||||||
bool XmlWriteProfileThread(pugi::xml_node threads_node);
|
|
||||||
bool XmlWriteProfileLink(pugi::xml_node links_node);
|
|
||||||
|
|
||||||
std::wstring title;
|
|
||||||
std::vector<hook_ptr> hooks;
|
|
||||||
std::vector<thread_ptr> threads;
|
|
||||||
std::vector<link_ptr> links;
|
|
||||||
|
|
||||||
WORD select_index;
|
|
||||||
};
|
|
@ -1,72 +0,0 @@
|
|||||||
/**
|
|
||||||
* pugixml parser - version 1.5
|
|
||||||
* --------------------------------------------------------
|
|
||||||
* Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
|
||||||
* Report bugs and download new versions at http://pugixml.org/
|
|
||||||
*
|
|
||||||
* This library is distributed under the MIT License. See notice at the end
|
|
||||||
* of this file.
|
|
||||||
*
|
|
||||||
* This work is based on the pugxml parser, which is:
|
|
||||||
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef HEADER_PUGICONFIG_HPP
|
|
||||||
#define HEADER_PUGICONFIG_HPP
|
|
||||||
|
|
||||||
// Uncomment this to enable wchar_t mode
|
|
||||||
#define PUGIXML_WCHAR_MODE
|
|
||||||
|
|
||||||
// Uncomment this to disable XPath
|
|
||||||
// #define PUGIXML_NO_XPATH
|
|
||||||
|
|
||||||
// Uncomment this to disable STL
|
|
||||||
// #define PUGIXML_NO_STL
|
|
||||||
|
|
||||||
// Uncomment this to disable exceptions
|
|
||||||
// #define PUGIXML_NO_EXCEPTIONS
|
|
||||||
|
|
||||||
// Set this to control attributes for public classes/functions, i.e.:
|
|
||||||
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
|
|
||||||
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
|
|
||||||
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
|
|
||||||
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
|
|
||||||
|
|
||||||
// Tune these constants to adjust memory-related behavior
|
|
||||||
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
|
|
||||||
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
|
|
||||||
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
|
|
||||||
|
|
||||||
// Uncomment this to switch to header-only version
|
|
||||||
// #define PUGIXML_HEADER_ONLY
|
|
||||||
// #include "pugixml.cpp"
|
|
||||||
|
|
||||||
// Uncomment this to enable long long support
|
|
||||||
// #define PUGIXML_HAS_LONG_LONG
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (c) 2006-2014 Arseny Kapoulkine
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated documentation
|
|
||||||
* files (the "Software"), to deal in the Software without
|
|
||||||
* restriction, including without limitation the rights to use,
|
|
||||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following
|
|
||||||
* conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
11484
gui/pugixml.cpp
11484
gui/pugixml.cpp
File diff suppressed because it is too large
Load Diff
1355
gui/pugixml.hpp
1355
gui/pugixml.hpp
File diff suppressed because it is too large
Load Diff
@ -1,28 +0,0 @@
|
|||||||
# ith/common/common.pri
|
|
||||||
# 8/9/2011 jichi
|
|
||||||
# Overwrite ITH headers
|
|
||||||
|
|
||||||
#DEFINES += ITH_HAS_CRT # whether ITH is linked with msvcrt
|
|
||||||
#DEFINES += ITH_HAS_CXX # whether ITH has access to native C++ syntax
|
|
||||||
|
|
||||||
DEPENDPATH += $$PWD
|
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
$$PWD/const.h \
|
|
||||||
$$PWD/defs.h \
|
|
||||||
$$PWD/except.h \
|
|
||||||
$$PWD/growl.h \
|
|
||||||
$$PWD/memory.h \
|
|
||||||
$$PWD/string.h \
|
|
||||||
$$PWD/types.h
|
|
||||||
|
|
||||||
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
|
||||||
|
|
||||||
# jichi 9/14/2013: Whether using SEH exception handle.
|
|
||||||
# msvcrt on Windows XP is missin EH
|
|
||||||
#DEFINES += ITH_HAS_SEH
|
|
||||||
|
|
||||||
# jichi 9/22/2013: Whether let ITH manage heap
|
|
||||||
#DEFINES += ITH_HAS_HEAP
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,253 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/common/const.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// Branch: ITH/common.h, rev 128
|
|
||||||
|
|
||||||
// jichi 9/9/2013: Another importnat function is lstrcatA, which is already handled by
|
|
||||||
// Debonosu hooks. Wait until it is really needed by certain games.
|
|
||||||
// The order of the functions is used in several place.
|
|
||||||
// I need to recompile all of the dlls to modify the order.
|
|
||||||
enum HookFunType {
|
|
||||||
HF_Null = -1
|
|
||||||
, HF_GetTextExtentPoint32A
|
|
||||||
, HF_GetGlyphOutlineA
|
|
||||||
, HF_ExtTextOutA
|
|
||||||
, HF_TextOutA
|
|
||||||
, HF_GetCharABCWidthsA
|
|
||||||
, HF_DrawTextA
|
|
||||||
, HF_DrawTextExA
|
|
||||||
//, HF_lstrlenA
|
|
||||||
, HF_GetTextExtentPoint32W
|
|
||||||
, HF_GetGlyphOutlineW
|
|
||||||
, HF_ExtTextOutW
|
|
||||||
, HF_TextOutW
|
|
||||||
, HF_GetCharABCWidthsW
|
|
||||||
, HF_DrawTextW
|
|
||||||
, HF_DrawTextExW
|
|
||||||
//, HF_lstrlenW
|
|
||||||
, HookFunCount // 14
|
|
||||||
};
|
|
||||||
|
|
||||||
// jichi 10/14/2014
|
|
||||||
#define HOOK_GDI_FUNCTION_LIST \
|
|
||||||
GetTextExtentPoint32A \
|
|
||||||
, GetGlyphOutlineA \
|
|
||||||
, ExtTextOutA \
|
|
||||||
, TextOutA \
|
|
||||||
, GetCharABCWidthsA \
|
|
||||||
, GetTextExtentPoint32W \
|
|
||||||
, GetGlyphOutlineW \
|
|
||||||
, ExtTextOutW \
|
|
||||||
, TextOutW \
|
|
||||||
, GetCharABCWidthsW \
|
|
||||||
, DrawTextA \
|
|
||||||
, DrawTextExA \
|
|
||||||
, DrawTextW \
|
|
||||||
, DrawTextExW
|
|
||||||
|
|
||||||
enum { HOOK_FUN_COUNT = HookFunCount };
|
|
||||||
// jichi 1/16/2015: Though called max hook, it means max number of text threads
|
|
||||||
enum { MAX_HOOK = 32 }; // must be larger than HookFunCount
|
|
||||||
//enum { HOOK_SECTION_SIZE = 0x2000 }; // default ITH value
|
|
||||||
// jichi 1/16/2015: Change to a very large number to prevent crash
|
|
||||||
//enum { MAX_HOOK = 0x100 }; // must be larger than HookFunCount
|
|
||||||
enum { HOOK_SECTION_SIZE = MAX_HOOK * 0x100 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook)
|
|
||||||
|
|
||||||
// jichi 375/2014: Add offset of pusha/pushad
|
|
||||||
// http://faydoc.tripod.com/cpu/pushad.htm
|
|
||||||
// http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
|
|
||||||
//
|
|
||||||
// Warning: The offset in ITH has -4 offset comparing to pusha and AGTH
|
|
||||||
enum pusha_off {
|
|
||||||
pusha_eax_off = -0x4
|
|
||||||
, pusha_ecx_off = -0x8
|
|
||||||
, pusha_edx_off = -0xc
|
|
||||||
, pusha_ebx_off = -0x10
|
|
||||||
, pusha_esp_off = -0x14
|
|
||||||
, pusha_ebp_off = -0x18
|
|
||||||
, pusha_esi_off = -0x1c
|
|
||||||
, pusha_edi_off = -0x20
|
|
||||||
, pusha_off = -0x24 // pushad offset
|
|
||||||
};
|
|
||||||
|
|
||||||
enum IhfCommandType {
|
|
||||||
IHF_COMMAND = -1 // null type
|
|
||||||
, IHF_COMMAND_NEW_HOOK = 0
|
|
||||||
, IHF_COMMAND_REMOVE_HOOK = 1
|
|
||||||
, IHF_COMMAND_MODIFY_HOOK = 2
|
|
||||||
, IHF_COMMAND_DETACH = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
enum IhfNotificationType {
|
|
||||||
IHF_NOTIFICATION = -1 // null type
|
|
||||||
, IHF_NOTIFICATION_TEXT = 0
|
|
||||||
, IHF_NOTIFICATION_NEWHOOK = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
// jichi 9/8/2013: The meaning are guessed
|
|
||||||
// Values must be within DWORD
|
|
||||||
// Unused values are as follows:
|
|
||||||
// - 0x100
|
|
||||||
enum HookParamType : unsigned long {
|
|
||||||
USING_STRING = 0x1 // type(data) is char* or wchar_t* and has length
|
|
||||||
, USING_UTF8 = USING_STRING // jichi 10/21/2014: temporarily handled the same way as USING_STRING
|
|
||||||
, USING_UNICODE = 0x2 // type(data) is wchar_t or wchar_t*
|
|
||||||
, BIG_ENDIAN = 0x4 // type(data) is char
|
|
||||||
, DATA_INDIRECT = 0x8
|
|
||||||
, USING_SPLIT = 0x10 // aware of split time?
|
|
||||||
, SPLIT_INDIRECT = 0x20
|
|
||||||
, MODULE_OFFSET = 0x40 // do hash module, and the address is relative to module
|
|
||||||
, FUNCTION_OFFSET = 0x80 // do hash function, and the address is relative to funccion
|
|
||||||
, PRINT_DWORD = 0x100 // jichi 12/7/2014: Removed
|
|
||||||
, STRING_LAST_CHAR = 0x200
|
|
||||||
, NO_CONTEXT = 0x400
|
|
||||||
//, EXTERN_HOOK = 0x800 // jichi 10/24/2014: Removed
|
|
||||||
//, HOOK_AUXILIARY = 0x2000 // jichi 12/13/2013: None of known hooks are auxiliary
|
|
||||||
, HOOK_ENGINE = 0x4000
|
|
||||||
, HOOK_ADDITIONAL = 0x8000
|
|
||||||
|
|
||||||
// jichi 10/24/2014: Only trigger the dynamic function, do not return any data
|
|
||||||
, HOOK_EMPTY = 0x800
|
|
||||||
// jichi 6/1/2014: fix the split value to 0x10001
|
|
||||||
, FIXING_SPLIT = 0x1000
|
|
||||||
, RELATIVE_SPLIT = 0x2000 // relative split return address
|
|
||||||
};
|
|
||||||
|
|
||||||
// 6/1/2014: Fixed split value for hok parameter
|
|
||||||
// Fuse all threads, and prevent floating
|
|
||||||
enum { FIXED_SPLIT_VALUE = 0x10001 };
|
|
||||||
|
|
||||||
// jichi 12/18/2013:
|
|
||||||
// These dlls are used to guess the range for non-NO_CONTEXT hooks.
|
|
||||||
//
|
|
||||||
// Disabling uxtheme.dll would crash certain system: http://tieba.baidu.com/p/2764436254
|
|
||||||
#define IHF_FILTER_DLL_LIST \
|
|
||||||
/* ITH original filters */ \
|
|
||||||
L"gdiplus.dll" /* Graphics functions like TextOutA */ \
|
|
||||||
, L"lpk.dll" /* Language package scripts and fonts */ \
|
|
||||||
, L"msctf.dll" /* Text service */ \
|
|
||||||
, L"psapi.dll" /* Processes */ \
|
|
||||||
, L"usp10.dll" /* UNICODE rendering */ \
|
|
||||||
, L"user32.dll" /* Non-graphics functions like lstrlenA */ \
|
|
||||||
, L"uxtheme.dll" /* Theme */ \
|
|
||||||
\
|
|
||||||
/* Windows DLLs */ \
|
|
||||||
, L"advapi32.dll" /* Advanced services */ \
|
|
||||||
, L"apphelp.dll" /* Appliation help */ \
|
|
||||||
, L"audioses.dll" /* Audios */ \
|
|
||||||
, L"avrt.dll" /* Audio video runtime */ \
|
|
||||||
, L"cfgmgr32.dll" /* Configuration manager */ \
|
|
||||||
, L"clbcatq.dll" /* COM query service */ \
|
|
||||||
, L"comctl32.dll" /* Common control library */ \
|
|
||||||
, L"comdlg32.dll" /* Common dialogs */ \
|
|
||||||
, L"crypt32.dll" /* Security cryption */ \
|
|
||||||
, L"cryptbase.dll"/* Security cryption */ \
|
|
||||||
, L"cryptsp.dll" /* Security cryption */ \
|
|
||||||
, L"d3d8thk.dll" /* Direct3D 8 */ \
|
|
||||||
, L"d3d9.dll" /* Direct3D 9 */ \
|
|
||||||
, L"dbghelp.dll" /* Debug help */ \
|
|
||||||
, L"dciman32.dll" /* Display cotrol */ \
|
|
||||||
, L"devobj.dll" /* Device object */ \
|
|
||||||
, L"ddraw.dll" /* Direct draw */ \
|
|
||||||
, L"dinput.dll" /* Diret input */ \
|
|
||||||
, L"dsound.dll" /* Direct sound */ \
|
|
||||||
, L"DShowRdpFilter.dll" /* Direct show */ \
|
|
||||||
, L"dwmapi.dll" /* Windows manager */ \
|
|
||||||
, L"gdi32.dll" /* GDI32 */ \
|
|
||||||
, L"hid.dll" /* HID user library */ \
|
|
||||||
, L"iertutil.dll" /* IE runtime */ \
|
|
||||||
, L"imagehlp.dll" /* Image help */ \
|
|
||||||
, L"imm32.dll" /* Input method */ \
|
|
||||||
, L"ksuser.dll" /* Kernel service */ \
|
|
||||||
, L"ole32.dll" /* COM OLE */ \
|
|
||||||
, L"oleacc.dll" /* OLE access */ \
|
|
||||||
, L"oleaut32.dll" /* COM OLE */ \
|
|
||||||
, L"kernel.dll" /* Kernel functions */ \
|
|
||||||
, L"kernelbase.dll" /* Kernel functions */ \
|
|
||||||
, L"midimap.dll" /* MIDI */ \
|
|
||||||
, L"mmdevapi.dll" /* Audio device */ \
|
|
||||||
, L"mpr.dll" /* Winnet */ \
|
|
||||||
, L"msacm32.dll" /* MS ACM */ \
|
|
||||||
, L"msacm32.drv" /* MS ACM */ \
|
|
||||||
, L"msasn1.dll" /* Encoding/decoding */ \
|
|
||||||
, L"msimg32.dll" /* Image */ \
|
|
||||||
, L"msvfw32.dll" /* Media play */ \
|
|
||||||
, L"netapi32.dll" /* Network service */ \
|
|
||||||
, L"normaliz.dll" /* Normalize */ \
|
|
||||||
, L"nsi.dll" /* NSI */ \
|
|
||||||
, L"ntdll.dll" /* NT functions */ \
|
|
||||||
, L"ntmarta.dll" /* NT MARTA */ \
|
|
||||||
, L"nvd3dum.dll" /* Direct 3D */ \
|
|
||||||
, L"powerprof.dll"/* Power profile */ \
|
|
||||||
, L"profapi.dll" /* Profile API */ \
|
|
||||||
, L"propsys.dll" /* System properties */ \
|
|
||||||
, L"quartz.dll" /* OpenGL */ \
|
|
||||||
, L"rpcrt4.dll" /* RPC runtime */ \
|
|
||||||
, L"rpcrtremote.dll" /* RPC runtime */ \
|
|
||||||
, L"rsabase.dll" /* RSA cryption */ \
|
|
||||||
, L"rsaenh.dll" /* RSA cryption */ \
|
|
||||||
, L"schannel.dll" /* Security channel */ \
|
|
||||||
, L"sechost.dll" /* Service host */ \
|
|
||||||
, L"setupapi.dll" /* Setup service */ \
|
|
||||||
, L"shell32.dll" /* Windows shell */ \
|
|
||||||
, L"shlwapi.dll" /* Light-weighted shell */ \
|
|
||||||
, L"slc.dll" /* SLC */ \
|
|
||||||
, L"srvcli.dll" /* Service client */ \
|
|
||||||
, L"version.dll" /* Windows version */ \
|
|
||||||
, L"wdmaud.drv" /* Wave output */ \
|
|
||||||
, L"wldap32.dll" /* Wireless */ \
|
|
||||||
, L"wininet.dll" /* Internet access */ \
|
|
||||||
, L"winmm.dll" /* Windows sound */ \
|
|
||||||
, L"winsta.dll" /* Connection system */ \
|
|
||||||
, L"wtsapi32.dll" /* Windows terminal server */ \
|
|
||||||
, L"wintrust.dll" /* Windows trust */ \
|
|
||||||
, L"wsock32.dll" /* Windows sock */ \
|
|
||||||
, L"ws2_32.dll" /* Terminal server */ \
|
|
||||||
, L"wkscli.dll" /* ACIS */ \
|
|
||||||
\
|
|
||||||
/* MSVCRT */ \
|
|
||||||
, L"msvcrt.dll" /* VC rutime */ \
|
|
||||||
, L"msvcr80.dll" /* VC rutime 8 */ \
|
|
||||||
, L"msvcp80.dll" /* VC rutime 8 */ \
|
|
||||||
, L"msvcr90.dll" /* VC rutime 9 */ \
|
|
||||||
, L"msvcp90.dll" /* VC rutime 9 */ \
|
|
||||||
, L"msvcr100.dll" /* VC rutime 10 */ \
|
|
||||||
, L"msvcp100.dll" /* VC rutime 10 */ \
|
|
||||||
, L"msvcr110.dll" /* VC rutime 11 */ \
|
|
||||||
, L"msvcp110.dll" /* VC rutime 11 */ \
|
|
||||||
\
|
|
||||||
/* VNR */ \
|
|
||||||
, L"vnrhook.dll" \
|
|
||||||
, L"vnrhookxp.dll" \
|
|
||||||
\
|
|
||||||
/* Sogou IME */ \
|
|
||||||
, L"sogoupy.ime" \
|
|
||||||
, L"PicFace.dll" \
|
|
||||||
, L"AddressSearch.dll" \
|
|
||||||
\
|
|
||||||
/* QQ IME */ \
|
|
||||||
, L"QQPINYIN.IME" \
|
|
||||||
\
|
|
||||||
/* AlphaROM */ \
|
|
||||||
, L"kDays.dll" \
|
|
||||||
\
|
|
||||||
/* 360Safe */ \
|
|
||||||
, L"safemon.dll" \
|
|
||||||
\
|
|
||||||
/* Locale changers */ \
|
|
||||||
, L"AlLayer.dll" /* AppLocale */ \
|
|
||||||
, L"LocaleEmulator.dll" /* Locale Emulator */ \
|
|
||||||
, L"LSH.dll" /* LocaleSwitch */ \
|
|
||||||
, L"ntleah.dll" /* NTLEA */
|
|
||||||
|
|
||||||
// Google Japanese IME
|
|
||||||
//, L"GoogleIMEJaTIP32.dll"
|
|
||||||
|
|
||||||
enum {
|
|
||||||
//IHF_FILTER_COUNT = 7
|
|
||||||
IHF_FILTER_COUNT = 7 + 72 + 9 + 4 + 3 + 1 + 1 + 1 + 4 // count of total dlls to filter
|
|
||||||
, IHF_FILTER_CAPACITY = IHF_FILTER_COUNT + 1 // one more than the dll count
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,47 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/common/defs.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
|
|
||||||
// DLL files
|
|
||||||
|
|
||||||
//#define ITH_SERVER_DLL L"vnrsrv.dll"
|
|
||||||
//#define ITH_CLIENT_DLL L"vnrcli.dll"
|
|
||||||
//#define ITH_CLIENT_XP_DLL L"vnrclixp.dll"
|
|
||||||
////#define ITH_CLIENT_UX_DLL L"vnrcliux.dll"
|
|
||||||
//#define ITH_ENGINE_DLL L"vnreng.dll"
|
|
||||||
//#define ITH_ENGINE_XP_DLL L"vnrengxp.dll"
|
|
||||||
//#define ITH_ENGINE_UX_DLL L"vnrengux.dll"
|
|
||||||
|
|
||||||
#define ITH_DLL L"vnrhook.dll"
|
|
||||||
#define ITH_DLL_XP L"vnrhookxp.dll"
|
|
||||||
|
|
||||||
// Pipes
|
|
||||||
|
|
||||||
#define ITH_TEXT_PIPE L"\\??\\pipe\\VNR_TEXT"
|
|
||||||
#define ITH_COMMAND_PIPE L"\\??\\pipe\\VNR_COMMAND"
|
|
||||||
|
|
||||||
// Sections
|
|
||||||
|
|
||||||
#define ITH_SECTION_ L"VNR_SECTION_" // _%d
|
|
||||||
|
|
||||||
// Mutex
|
|
||||||
|
|
||||||
#define ITH_PROCESS_MUTEX_ L"VNR_PROCESS_" // ITH_%d
|
|
||||||
#define ITH_HOOKMAN_MUTEX_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
|
|
||||||
#define ITH_DETACH_MUTEX_ L"VNR_DETACH_" // ITH_DETACH_%d
|
|
||||||
|
|
||||||
#define ITH_GRANTPIPE_MUTEX L"VNR_GRANT_PIPE" // ITH_GRANT_PIPE
|
|
||||||
|
|
||||||
//#define ITH_ENGINE_MUTEX L"VNR_ENGINE" // ITH_ENGINE
|
|
||||||
#define ITH_CLIENT_MUTEX L"VNR_CLIENT" // ITH_DLL_RUNNING
|
|
||||||
#define ITH_SERVER_MUTEX L"VNR_SERVER" // ITH_RUNNING
|
|
||||||
#define ITH_SERVER_HOOK_MUTEX L"VNR_SERVER_HOOK" // original
|
|
||||||
|
|
||||||
// Events
|
|
||||||
|
|
||||||
#define ITH_REMOVEHOOK_EVENT L"VNR_REMOVE_HOOK" // ITH_REMOVE_HOOK
|
|
||||||
#define ITH_MODIFYHOOK_EVENT L"VNR_MODIFY_HOOK" // ITH_MODIFY_HOOK
|
|
||||||
#define ITH_PIPEEXISTS_EVENT L"VNR_PIPE_EXISTS" // ITH_PIPE_EXIST
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/common/except.h
|
|
||||||
// 9/17/2013 jichi
|
|
||||||
|
|
||||||
#define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only
|
|
||||||
|
|
||||||
#ifdef ITH_HAS_SEH
|
|
||||||
|
|
||||||
# define ITH_TRY __try
|
|
||||||
# define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
# define ITH_WITH_SEH(...) \
|
|
||||||
ITH_TRY { __VA_ARGS__; } ITH_EXCEPT {}
|
|
||||||
|
|
||||||
#else // for old msvcrt.dll on Windows XP that does not have exception handler
|
|
||||||
|
|
||||||
// Currently, only with_seh is implemented. Try and catch are not.
|
|
||||||
# define ITH_TRY if (true)
|
|
||||||
# define ITH_EXCEPT else
|
|
||||||
# include "winseh/winseh.h"
|
|
||||||
# define ITH_WITH_SEH(...) seh_with(__VA_ARGS__)
|
|
||||||
|
|
||||||
#endif // ITH_HAS_SEH
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,85 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/common/growl.h
|
|
||||||
// 9/17/2013 jichi
|
|
||||||
|
|
||||||
//#ifdef ITH_HAS_GROWL
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include "ith/common/string.h"
|
|
||||||
|
|
||||||
#define ITH_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
|
|
||||||
#define ITH_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
|
|
||||||
#define ITH_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
|
|
||||||
#define ITH_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD(DWORD value)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD: %x", value);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD2(DWORD v, DWORD v2)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD2: %x,%x", v, v2);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD3(DWORD v, DWORD v2, DWORD v3)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD3: %x,%x,%x", v, v2, v3);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD4(DWORD v, DWORD v2, DWORD v3, DWORD v4)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD4: %x,%x,%x,%x", v, v2, v3, v4);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD5(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD5: %x,%x,%x,%x,%x", v, v2, v3, v4, v5);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD6(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD6: %x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD7(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD7: %x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD8(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD8: %x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL_DWORD9(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8, DWORD v9)
|
|
||||||
{
|
|
||||||
WCHAR buf[100];
|
|
||||||
swprintf(buf, L"DWORD9: %x,%x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8, v9);
|
|
||||||
ITH_MSG(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ITH_GROWL(DWORD v) { ITH_GROWL_DWORD(v); }
|
|
||||||
inline void ITH_GROWL(LPCWSTR v) { ITH_MSG(v); }
|
|
||||||
inline void ITH_GROWL(LPCSTR v) { ITH_MSG_A(v); }
|
|
||||||
|
|
||||||
//#endif // ITH_HAS_GROWL
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,46 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/common/memory.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// Branch: ITH/mem.h, revision 66
|
|
||||||
|
|
||||||
#ifndef ITH_HAS_HEAP
|
|
||||||
# define ITH_MEMSET_HEAP(...) ::memset(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
# define ITH_MEMSET_HEAP(...) (void)0
|
|
||||||
|
|
||||||
// Defined in kernel32.lilb
|
|
||||||
extern "C" {
|
|
||||||
// PVOID RtlAllocateHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ SIZE_T Size);
|
|
||||||
__declspec(dllimport) void * __stdcall RtlAllocateHeap(void *HeapHandle, unsigned long Flags, unsigned long Size);
|
|
||||||
|
|
||||||
// BOOLEAN RtlFreeHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ PVOID HeapBase);
|
|
||||||
__declspec(dllimport) int __stdcall RtlFreeHeap(void *HeapHandle, unsigned long Flags, void *HeapBase);
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
//NTSYSAPI
|
|
||||||
//BOOL
|
|
||||||
//NTAPI
|
|
||||||
//RtlFreeHeap(
|
|
||||||
// _In_ HANDLE hHeap,
|
|
||||||
// _In_ DWORD dwFlags,
|
|
||||||
// _In_ LPVOID lpMem
|
|
||||||
//);
|
|
||||||
|
|
||||||
extern void *hHeap; // defined in ith/sys.cc
|
|
||||||
|
|
||||||
inline void * __cdecl operator new(size_t lSize)
|
|
||||||
{
|
|
||||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597%28v=vs.85%29.aspx
|
|
||||||
// HEAP_ZERO_MEMORY flag is critical. All new objects are assumed with zero initialized.
|
|
||||||
enum { HEAP_ZERO_MEMORY = 0x00000008 };
|
|
||||||
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); }
|
|
||||||
|
|
||||||
#endif // ITH_HAS_HEAP
|
|
@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/common/string.h
|
|
||||||
// 8/9/2013 jichi
|
|
||||||
// Branch: ITH/string.h, rev 66
|
|
||||||
|
|
||||||
#ifdef ITH_HAS_CRT // ITH is linked with msvcrt dlls
|
|
||||||
# include <cstdio>
|
|
||||||
# include <cstring>
|
|
||||||
|
|
||||||
#else
|
|
||||||
# define _INC_SWPRINTF_INL_
|
|
||||||
# define CRT_IMPORT __declspec(dllimport)
|
|
||||||
|
|
||||||
#include <windows.h> // for wchar_t
|
|
||||||
extern "C" {
|
|
||||||
CRT_IMPORT int swprintf(wchar_t *src, const wchar_t *fmt, ...);
|
|
||||||
CRT_IMPORT int sprintf(char *src, const char *fmt, ...);
|
|
||||||
CRT_IMPORT int swscanf(const wchar_t *src, const wchar_t *fmt, ...);
|
|
||||||
CRT_IMPORT int sscanf(const char *src, const char *fmt, ...);
|
|
||||||
CRT_IMPORT int wprintf(const wchar_t *fmt, ...);
|
|
||||||
CRT_IMPORT int printf(const char *fmt, ...);
|
|
||||||
CRT_IMPORT int _wputs(const wchar_t *src);
|
|
||||||
CRT_IMPORT int puts(const char *src);
|
|
||||||
CRT_IMPORT int _stricmp(const char *x, const char *y);
|
|
||||||
CRT_IMPORT int _wcsicmp(const wchar_t *x, const wchar_t *y);
|
|
||||||
//CRT_IMPORT size_t strlen(const char *);
|
|
||||||
//CRT_IMPORT size_t wcslen(const wchar_t *);
|
|
||||||
//CRT_IMPORT char *strcpy(char *,const char *);
|
|
||||||
//CRT_IMPORT wchar_t *wcscpy(wchar_t *,const wchar_t *);
|
|
||||||
CRT_IMPORT void *memmove(void *dst, const void *src, size_t sz);
|
|
||||||
CRT_IMPORT const char *strchr(const char *src, int val);
|
|
||||||
CRT_IMPORT int strncmp(const char *x, const char *y, size_t sz);
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
#endif // ITH_HAS_CRT
|
|
@ -1,91 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/common/types.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// Branch: ITH/common.h, rev 128
|
|
||||||
|
|
||||||
#include <windows.h> // needed for windef types
|
|
||||||
|
|
||||||
/** jichi 3/7/2014: Add guessed comment
|
|
||||||
*
|
|
||||||
* DWORD addr absolute or relative address
|
|
||||||
* DWORD split esp offset of the split character
|
|
||||||
*
|
|
||||||
* http://faydoc.tripod.com/cpu/pushad.htm
|
|
||||||
* http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
|
|
||||||
* The order is the same as pushd
|
|
||||||
* EAX, ECX, EDX, EBX, ESP (original value), EBP, ESI, and EDI (if the current operand-size attribute is 32) and AX, CX, DX, BX, SP
|
|
||||||
* Negative values of 'data_offset' and 'sub_offset' refer to registers:-4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI
|
|
||||||
*/
|
|
||||||
struct HookParam {
|
|
||||||
// jichi 8/24/2013: For special hooks. Original name: DataFun
|
|
||||||
typedef void (*text_fun_t)(DWORD esp, HookParam *hp, BYTE index, DWORD *data, DWORD *split, DWORD *len);
|
|
||||||
|
|
||||||
// jichi 10/24/2014: Add filter function. Return the if skip the text
|
|
||||||
typedef bool (*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index);
|
|
||||||
|
|
||||||
// jichi 10/24/2014: Add generic hook function, return false if stop execution.
|
|
||||||
typedef bool (*hook_fun_t)(DWORD esp, HookParam *hp);
|
|
||||||
|
|
||||||
DWORD addr; // absolute or relative address
|
|
||||||
DWORD off, // offset of the data in the memory
|
|
||||||
ind, // ?
|
|
||||||
split, // esp offset of the split character = pusha offset - 4
|
|
||||||
split_ind; // ?
|
|
||||||
DWORD module, // hash of the module
|
|
||||||
function;
|
|
||||||
text_fun_t text_fun;
|
|
||||||
filter_fun_t filter_fun;
|
|
||||||
hook_fun_t hook_fun;
|
|
||||||
DWORD type; // flags
|
|
||||||
WORD length_offset; // index of the string length
|
|
||||||
BYTE hook_len, // ?
|
|
||||||
recover_len; // ?
|
|
||||||
|
|
||||||
// 2/2/2015: jichi number of times - 1 to run the hook
|
|
||||||
BYTE extra_text_count;
|
|
||||||
BYTE _unused; // jichi 2/2/2015: add a BYTE type to make to total sizeof(HookParam) even.
|
|
||||||
|
|
||||||
// 7/20/2014: jichi additional parameters for PSP games
|
|
||||||
DWORD user_flags,
|
|
||||||
user_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// jichi 6/1/2014: Structure of the esp for extern functions
|
|
||||||
struct HookStack
|
|
||||||
{
|
|
||||||
// pushad
|
|
||||||
DWORD edi, // -0x24
|
|
||||||
esi, // -0x20
|
|
||||||
ebp, // -0x1c
|
|
||||||
esp, // -0x18
|
|
||||||
ebx, // -0x14
|
|
||||||
edx, // -0x10
|
|
||||||
ecx, // -0xc
|
|
||||||
eax; // -0x8
|
|
||||||
// pushfd
|
|
||||||
DWORD eflags; // -0x4
|
|
||||||
DWORD retaddr; // 0
|
|
||||||
DWORD args[1]; // 0x4
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SendParam {
|
|
||||||
DWORD type;
|
|
||||||
HookParam hp;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Hook { // size: 0x80
|
|
||||||
HookParam hp;
|
|
||||||
LPWSTR hook_name;
|
|
||||||
int name_length;
|
|
||||||
BYTE recover[0x68 - sizeof(HookParam)];
|
|
||||||
BYTE original[0x10];
|
|
||||||
|
|
||||||
DWORD Address() const { return hp.addr; }
|
|
||||||
DWORD Type() const { return hp.type; }
|
|
||||||
WORD Length() const { return hp.hook_len; }
|
|
||||||
LPWSTR Name() const { return hook_name; }
|
|
||||||
int NameLength() const { return name_length; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// dllconfig.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
|
|
||||||
#include "ith/common/memory.h"
|
|
||||||
#include "ith/common/string.h"
|
|
||||||
#include "ntdll/ntdll.h"
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,35 +0,0 @@
|
|||||||
# dllconfig.pri
|
|
||||||
# 8/9/2013 jichi
|
|
||||||
# For linking ITH injectable dlls.
|
|
||||||
# The dll is self-containd and Windows-independent.
|
|
||||||
|
|
||||||
CONFIG += dll noqt #noeh nosafeseh
|
|
||||||
CONFIG -= embed_manifest_dll # dynamically load dlls
|
|
||||||
win32 {
|
|
||||||
CONFIG(eh): DEFINES += ITH_HAS_SEH # Do have exception handler in msvcrt.dll on Windows Vista and later
|
|
||||||
CONFIG(noeh): DEFINES -= ITH_HAS_SEH # Do not have exception handler in msvcrt.dll on Windows XP and before
|
|
||||||
}
|
|
||||||
include(../../../config.pri)
|
|
||||||
#win32 {
|
|
||||||
# CONFIG(noeh): include($$LIBDIR/winseh/winseh_safe.pri)
|
|
||||||
#}
|
|
||||||
|
|
||||||
# jichi 11/24/2013: Disable manual heap
|
|
||||||
DEFINES -= ITH_HAS_HEAP
|
|
||||||
|
|
||||||
# jichi 11/13/2011: disable swprinf warning
|
|
||||||
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
|
||||||
|
|
||||||
## Libraries
|
|
||||||
|
|
||||||
#LIBS += -lkernel32 -luser32 -lgdi32
|
|
||||||
LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
|
|
||||||
LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
|
|
||||||
#LIBS += -L$$WDK7_HOME/lib/crt/i386 -lmsvcrt
|
|
||||||
#QMAKE_LFLAGS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # This will leave runtime flags in the dll
|
|
||||||
|
|
||||||
#LIBS += -L$$WDK8_HOME/lib/winv6.3/um/x86 -lntdll
|
|
||||||
|
|
||||||
HEADERS += $$PWD/dllconfig.h
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,135 +0,0 @@
|
|||||||
# hook.pro
|
|
||||||
# CONFIG += eh eha
|
|
||||||
# include(../dllconfig.pri)
|
|
||||||
|
|
||||||
# hookxp.pro
|
|
||||||
# CONFIG += noeh
|
|
||||||
# include(../dllconfig.pri)
|
|
||||||
|
|
||||||
# dllconfig.pri
|
|
||||||
# include(../../../config.pri)
|
|
||||||
# win32 {
|
|
||||||
# CONFIG(eh): DEFINES += ITH_HAS_SEH
|
|
||||||
# CONFIG(noeh): DEFINES -= ITH_HAS_SEH
|
|
||||||
# }
|
|
||||||
|
|
||||||
# config.pri
|
|
||||||
# CONFIG(eha) {
|
|
||||||
# message(CONFIG eha)
|
|
||||||
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
|
|
||||||
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
|
|
||||||
# QMAKE_CXXFLAGS_STL_ON += /EHa
|
|
||||||
# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# CONFIG(noeh) { # No Exception handler
|
|
||||||
# QMAKE_CXXFLAGS += /GR-
|
|
||||||
# QMAKE_CXXFLAGS_RTTI_ON -= /GR
|
|
||||||
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
|
|
||||||
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
|
|
||||||
# }
|
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
|
|
||||||
set(vnrhook_src
|
|
||||||
cli.h
|
|
||||||
config.h
|
|
||||||
hook.h
|
|
||||||
main.cc
|
|
||||||
engine/engine.cc
|
|
||||||
engine/engine.h
|
|
||||||
engine/hookdefs.h
|
|
||||||
engine/match.cc
|
|
||||||
engine/match.h
|
|
||||||
engine/pchooks.cc
|
|
||||||
engine/pchooks.h
|
|
||||||
engine/util.cc
|
|
||||||
engine/util.h
|
|
||||||
hijack/texthook.cc
|
|
||||||
rpc/pipe.cc
|
|
||||||
tree/avl.h
|
|
||||||
${PROJECT_SOURCE_DIR}/ccutil/ccmacro.h
|
|
||||||
${PROJECT_SOURCE_DIR}/cpputil/cpplocale.h
|
|
||||||
${PROJECT_SOURCE_DIR}/cpputil/cppmarshal.h
|
|
||||||
${PROJECT_SOURCE_DIR}/cpputil/cppmath.h
|
|
||||||
${PROJECT_SOURCE_DIR}/cpputil/cpppath.h
|
|
||||||
${PROJECT_SOURCE_DIR}/cpputil/cppstring.h
|
|
||||||
${PROJECT_SOURCE_DIR}/cpputil/cpptype.h
|
|
||||||
${PROJECT_SOURCE_DIR}/cpputil/cppunicode.h
|
|
||||||
${PROJECT_SOURCE_DIR}/disasm/disasm.cc
|
|
||||||
${PROJECT_SOURCE_DIR}/memdbg/memdbg.h
|
|
||||||
${PROJECT_SOURCE_DIR}/memdbg/memsearch.cc
|
|
||||||
${PROJECT_SOURCE_DIR}/memdbg/memsearch.h
|
|
||||||
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.cc
|
|
||||||
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.h
|
|
||||||
${PROJECT_SOURCE_DIR}/winversion/winversion.cc
|
|
||||||
${PROJECT_SOURCE_DIR}/winversion/winversion.h
|
|
||||||
${common_src}
|
|
||||||
${import_src}
|
|
||||||
)
|
|
||||||
|
|
||||||
source_group("common" FILES ${common_src})
|
|
||||||
source_group("import" FILES ${import_src})
|
|
||||||
|
|
||||||
add_library(vnrhook SHARED ${vnrhook_src})
|
|
||||||
|
|
||||||
set(vnrhookxp_src ${vnrhook_src}
|
|
||||||
${PROJECT_SOURCE_DIR}/winseh/winseh.cc
|
|
||||||
${PROJECT_SOURCE_DIR}/winseh/winseh_safe.cc
|
|
||||||
${PROJECT_SOURCE_DIR}/winseh/winseh.h
|
|
||||||
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
|
|
||||||
)
|
|
||||||
|
|
||||||
enable_language(ASM_MASM)
|
|
||||||
|
|
||||||
set_source_files_properties(
|
|
||||||
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
|
|
||||||
PROPERTIES
|
|
||||||
# CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711
|
|
||||||
COMPILE_FLAGS /safeseh
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(vnrhookxp SHARED ${vnrhookxp_src})
|
|
||||||
|
|
||||||
set_target_properties(vnrhook vnrhookxp PROPERTIES
|
|
||||||
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_options(vnrhook PRIVATE
|
|
||||||
/EHa
|
|
||||||
$<$<CONFIG:Release>:>
|
|
||||||
$<$<CONFIG:Debug>:>
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_options(vnrhookxp PRIVATE
|
|
||||||
/GR-
|
|
||||||
# /EHs-c- # disable exception handling # CMake bug 15243: http://www.cmake.org/Bug/view.php?id=15243
|
|
||||||
$<$<CONFIG:Release>:>
|
|
||||||
$<$<CONFIG:Debug>:>
|
|
||||||
)
|
|
||||||
|
|
||||||
if(TARGET vnrhookxp)
|
|
||||||
STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
|
||||||
endif(TARGET vnrhookxp)
|
|
||||||
|
|
||||||
set(vnrhook_libs
|
|
||||||
vnrsys
|
|
||||||
${WDK_HOME}/lib/wxp/i386/ntdll.lib
|
|
||||||
Version.lib
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(vnrhook ${vnrhook_libs})
|
|
||||||
target_link_libraries(vnrhookxp ${vnrhook_libs})
|
|
||||||
|
|
||||||
target_compile_definitions(vnrhook
|
|
||||||
PRIVATE
|
|
||||||
-DITH_HAS_SEH
|
|
||||||
)
|
|
||||||
target_compile_definitions(vnrhookxp
|
|
||||||
PRIVATE
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS vnrhook vnrhookxp RUNTIME
|
|
||||||
DESTINATION .
|
|
||||||
CONFIGURATIONS Release
|
|
||||||
)
|
|
@ -1,99 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// cli.h
|
|
||||||
// 8/24/2013 jichi
|
|
||||||
// Branch: IHF_DLL/IHF_CLIENT.h, rev 133
|
|
||||||
//
|
|
||||||
// 8/24/2013 TODO:
|
|
||||||
// - Clean up this file
|
|
||||||
// - Reduce global variables. Use namespaces or singleton classes instead.
|
|
||||||
|
|
||||||
//#include <windows.h>
|
|
||||||
//#define IHF
|
|
||||||
#include "config.h"
|
|
||||||
#include "hook.h"
|
|
||||||
|
|
||||||
// jichi 12/25/2013: Header in each message sent to vnrsrv
|
|
||||||
// There are totally three elements
|
|
||||||
// - 0x0 dwAddr hook address
|
|
||||||
// - 0x4 dwRetn return address
|
|
||||||
// - 0x8 dwSplit split value
|
|
||||||
#define HEADER_SIZE 0xc
|
|
||||||
|
|
||||||
extern int current_hook;
|
|
||||||
extern WCHAR dll_mutex[];
|
|
||||||
//extern WCHAR dll_name[];
|
|
||||||
extern DWORD trigger;
|
|
||||||
//extern DWORD current_process_id;
|
|
||||||
|
|
||||||
// jichi 6/3/2014: Get memory range of the current module
|
|
||||||
extern DWORD processStartAddress,
|
|
||||||
processStopAddress;
|
|
||||||
|
|
||||||
template <class T, class D, class fComp, class fCopy, class fLength>
|
|
||||||
class AVLTree;
|
|
||||||
struct FunctionInfo {
|
|
||||||
DWORD addr;
|
|
||||||
DWORD module;
|
|
||||||
DWORD size;
|
|
||||||
LPWSTR name;
|
|
||||||
};
|
|
||||||
struct SCMP;
|
|
||||||
struct SCPY;
|
|
||||||
struct SLEN;
|
|
||||||
extern AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
|
|
||||||
|
|
||||||
void InitFilterTable();
|
|
||||||
|
|
||||||
// jichi 9/25/2013: This class will be used by NtMapViewOfSectionfor
|
|
||||||
// interprocedure communication, where constructor/destructor will NOT work.
|
|
||||||
class TextHook : public Hook
|
|
||||||
{
|
|
||||||
int UnsafeInsertHookCode();
|
|
||||||
DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
|
|
||||||
public:
|
|
||||||
int InsertHook();
|
|
||||||
int InsertHookCode();
|
|
||||||
int InitHook(const HookParam &hp, LPCWSTR name = 0, WORD set_flag = 0);
|
|
||||||
int InitHook(LPVOID addr, DWORD data, DWORD data_ind,
|
|
||||||
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off = 0);
|
|
||||||
DWORD Send(DWORD dwDataBase, DWORD dwRetn);
|
|
||||||
int RecoverHook();
|
|
||||||
int RemoveHook();
|
|
||||||
int ClearHook();
|
|
||||||
int ModifyHook(const HookParam&);
|
|
||||||
int SetHookName(LPCWSTR name);
|
|
||||||
int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed
|
|
||||||
void CoolDown(); // jichi 9/28/2013: flush instruction cache on wine
|
|
||||||
};
|
|
||||||
|
|
||||||
extern TextHook *hookman,
|
|
||||||
*current_available;
|
|
||||||
|
|
||||||
//void InitDefaultHook();
|
|
||||||
|
|
||||||
struct FilterRange { DWORD lower, upper; };
|
|
||||||
extern FilterRange *filter;
|
|
||||||
|
|
||||||
extern bool running,
|
|
||||||
live;
|
|
||||||
|
|
||||||
extern HANDLE hPipe,
|
|
||||||
hmMutex;
|
|
||||||
|
|
||||||
DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter);
|
|
||||||
DWORD WINAPI CommandPipe(LPVOID lpThreadParameter);
|
|
||||||
|
|
||||||
//void RequestRefreshProfile();
|
|
||||||
|
|
||||||
//typedef DWORD (*InsertHookFun)(DWORD);
|
|
||||||
//typedef DWORD (*IdentifyEngineFun)();
|
|
||||||
//typedef DWORD (*InsertDynamicHookFun)(LPVOID addr, DWORD frame, DWORD stack);
|
|
||||||
//extern IdentifyEngineFun IdentifyEngine;
|
|
||||||
//extern InsertDynamicHookFun InsertDynamicHook;
|
|
||||||
|
|
||||||
// jichi 9/28/2013: Protect pipeline in wine
|
|
||||||
void CliLockPipe();
|
|
||||||
void CliUnlockPipe();
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,10 +0,0 @@
|
|||||||
#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"
|
|
||||||
|
|
||||||
// EOF
|
|
File diff suppressed because it is too large
Load Diff
@ -1,162 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// engine/engine.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
struct HookParam; // defined in ith types.h
|
|
||||||
|
|
||||||
namespace Engine {
|
|
||||||
|
|
||||||
// Global variables
|
|
||||||
extern wchar_t process_name_[MAX_PATH], // cached
|
|
||||||
process_path_[MAX_PATH]; // cached
|
|
||||||
extern DWORD module_base_,
|
|
||||||
module_limit_;
|
|
||||||
|
|
||||||
//extern LPVOID trigger_addr;
|
|
||||||
typedef bool (* trigger_fun_t)(LPVOID addr, DWORD frame, DWORD stack);
|
|
||||||
extern trigger_fun_t trigger_fun_;
|
|
||||||
|
|
||||||
bool InsertMonoHooks(); // Mono
|
|
||||||
|
|
||||||
// Wii engines
|
|
||||||
|
|
||||||
bool InsertGCHooks(); // Dolphin
|
|
||||||
bool InsertVanillawareGCHook();
|
|
||||||
|
|
||||||
// PS2 engines
|
|
||||||
|
|
||||||
bool InsertPCSX2Hooks(); // PCSX2
|
|
||||||
bool InsertMarvelousPS2Hook(); // http://marvelous.jp
|
|
||||||
bool InsertMarvelous2PS2Hook(); // http://marvelous.jp
|
|
||||||
bool InsertTypeMoonPS2Hook(); // http://typemoon.com
|
|
||||||
//bool InsertNamcoPS2Hook();
|
|
||||||
|
|
||||||
// PSP engines
|
|
||||||
|
|
||||||
void SpecialPSPHook(DWORD esp_base, HookParam *hp, DWORD *data, DWORD *split, DWORD *len); // General PSP extern hook
|
|
||||||
|
|
||||||
bool InsertPPSSPPHooks(); // PPSSPPWindows
|
|
||||||
|
|
||||||
bool InsertPPSSPPHLEHooks();
|
|
||||||
bool InsertOtomatePPSSPPHook(); // PSP otomate.jp, 0.9.9.0 only
|
|
||||||
|
|
||||||
bool Insert5pbPSPHook(); // PSP 5pb.jp
|
|
||||||
bool InsertAlchemistPSPHook(); // PSP Alchemist-net.co.jp, 0.9.8 only
|
|
||||||
bool InsertAlchemist2PSPHook(); // PSP Alchemist-net.co.jp
|
|
||||||
bool InsertBandaiNamePSPHook(); // PSP Bandai.co.jp
|
|
||||||
bool InsertBandaiPSPHook(); // PSP Bandai.co.jp
|
|
||||||
bool InsertBroccoliPSPHook(); // PSP Broccoli.co.jp
|
|
||||||
bool InsertFelistellaPSPHook(); // PSP felistella.co.jp
|
|
||||||
|
|
||||||
bool InsertCyberfrontPSPHook(); // PSP CYBERFRONT (closed)
|
|
||||||
bool InsertImageepochPSPHook(); // PSP Imageepoch.co.jp
|
|
||||||
bool InsertImageepoch2PSPHook();// PSP Imageepoch.co.jp
|
|
||||||
bool InsertKadokawaNamePSPHook(); // PSP Kadokawa.co.jp
|
|
||||||
bool InsertKonamiPSPHook(); // PSP Konami.jp
|
|
||||||
bool InsertTecmoPSPHook(); // PSP Koeitecmo.co.jp
|
|
||||||
//bool InsertTypeMoonPSPHook(); // PSP Typemoon.com
|
|
||||||
|
|
||||||
bool InsertOtomatePSPHook(); // PSP Otomate.jp, 0.9.8 only
|
|
||||||
//bool InsertOtomate2PSPHook(); // PSP otomate.jp >= 0.9.9.1
|
|
||||||
|
|
||||||
bool InsertIntensePSPHook(); // PSP Intense.jp
|
|
||||||
bool InsertKidPSPHook(); // PSP Kid-game.co.jp
|
|
||||||
bool InsertNippon1PSPHook(); // PSP Nippon1.jp
|
|
||||||
bool InsertNippon2PSPHook(); // PSP Nippon1.jp
|
|
||||||
bool InsertYetiPSPHook(); // PSP Yetigame.jp
|
|
||||||
bool InsertYeti2PSPHook(); // PSP Yetigame.jp
|
|
||||||
|
|
||||||
// PC engines
|
|
||||||
|
|
||||||
bool Insert2RMHook(); // 2RM - Adventure Engine
|
|
||||||
bool Insert5pbHook(); // 5pb.jp, PSP/PS3 games ported to PC
|
|
||||||
bool InsertAB2TryHook(); // Yane@AkabeiSoft2Try: YaneSDK.dll.
|
|
||||||
bool InsertAbelHook(); // Abel
|
|
||||||
bool InsertAdobeAirHook(); // Adobe AIR
|
|
||||||
bool InsertAdobeFlash10Hook(); // Adobe Flash Player 10
|
|
||||||
bool InsertAliceHook(); // System40@AliceSoft; do not work for latest alice games
|
|
||||||
bool InsertAmuseCraftHook(); // AMUSE CRAFT: *.pac
|
|
||||||
bool InsertAnex86Hook(); // Anex86: anex86.exe
|
|
||||||
bool InsertAOSHook(); // AOS: *.aos
|
|
||||||
bool InsertApricoTHook(); // Apricot: arc.a*
|
|
||||||
bool InsertArtemisHook(); // Artemis Engine: *.pfs
|
|
||||||
bool InsertAtelierHook(); // Atelier Kaguya: message.dat
|
|
||||||
bool InsertBGIHook(); // BGI: BGI.*
|
|
||||||
bool InsertC4Hook(); // C4: C4.EXE or XEX.EXE
|
|
||||||
bool InsertCaramelBoxHook(); // Caramel: *.bin
|
|
||||||
bool InsertCandyHook(); // SystemC@CandySoft: *.fpk
|
|
||||||
bool InsertCatSystemHook(); // CatSystem2: *.int
|
|
||||||
bool InsertCMVSHook(); // CMVS: data/pack/*.cpz; do not support the latest cmvs32.exe and cmvs64.exe
|
|
||||||
bool InsertCotophaHook(); // Cotopha: *.noa
|
|
||||||
bool InsertDebonosuHook(); // Debonosu: bmp.bak and dsetup.dll
|
|
||||||
bool InsertEaglsHook(); // E.A.G.L.S: EAGLES.dll
|
|
||||||
bool InsertEMEHook(); // EmonEngine: emecfg.ecf
|
|
||||||
bool InsertEushullyHook(); // Eushully: AGERC.DLL
|
|
||||||
bool InsertExpHook(); // EXP: http://www.exp-inc.jp
|
|
||||||
bool InsertFocasLensHook(); // FocasLens: Dat/*.arc, http://www.fo-lens.net
|
|
||||||
bool InsertGesen18Hook(); // Gsen18: *.szs
|
|
||||||
bool InsertGXPHook(); // GXP: *.gxp
|
|
||||||
bool InsertHorkEyeHook(); // HorkEye: resource string
|
|
||||||
bool InsertKAGParserHook(); // plugin/KAGParser.dll
|
|
||||||
bool InsertKAGParserExHook(); // plugin/KAGParserEx.dll
|
|
||||||
bool InsertKiriKiriHook(); // KiriKiri: *.xp3, resource string
|
|
||||||
bool InsertKiriKiriZHook(); // KiriKiri: *.xp3, resource string
|
|
||||||
bool InsertLeafHook(); // Leaf: *.pak
|
|
||||||
bool InsertLiveHook(); // Live: live.dll
|
|
||||||
bool InsertLunaSoftHook(); // LunaSoft: Pac/*.pac
|
|
||||||
bool InsertMalieHook(); // Malie@light: malie.ini
|
|
||||||
bool InsertMajiroHook(); // Majiro: *.arc
|
|
||||||
bool InsertMarineHeartHook(); // Marine Heart: SAISYS.exe
|
|
||||||
bool InsertMBLHook(); // MBL: *.mbl
|
|
||||||
bool InsertMEDHook(); // MED: *.med
|
|
||||||
bool InsertMinkHook(); // Mink: *.at2
|
|
||||||
//bool InsertMonoHook(); // Mono (Unity3D): */Mono/mono.dll
|
|
||||||
bool InsertNeXASHook(); // NeXAS: Thumbnail.pac
|
|
||||||
bool InsertNextonHook(); // NEXTON: aInfo.db
|
|
||||||
bool InsertNexton1Hook();
|
|
||||||
bool InsertNitroPlusHook(); // NitroPlus: *.npa
|
|
||||||
bool InsertPensilHook(); // Pensil: PSetup.exe
|
|
||||||
bool InsertQLIEHook(); // QLiE: GameData/*.pack
|
|
||||||
//bool InsertRai7Hook(); // Rai7puk: rai7.exe
|
|
||||||
bool InsertRejetHook(); // Rejet: Module/{gd.dat,pf.dat,sd.dat}
|
|
||||||
bool InsertRUGPHook(); // rUGP: rUGP.exe
|
|
||||||
bool InsertRetouchHook(); // Retouch: resident.dll
|
|
||||||
bool InsertRREHook(); // RunrunEngine: rrecfg.rcf
|
|
||||||
bool InsertShinaHook(); // ShinaRio: Rio.ini
|
|
||||||
bool InsertShinyDaysHook(); // ShinyDays
|
|
||||||
bool InsertElfHook(); // elf: Silky.exe
|
|
||||||
bool InsertScenarioPlayerHook();// sol-fa-soft: *.iar && *.sec5
|
|
||||||
bool InsertSiglusHook(); // SiglusEngine: SiglusEngine.exe
|
|
||||||
bool InsertSideBHook(); // SideB: Copyright side-B
|
|
||||||
bool InsertSyuntadaHook(); // Syuntada: dSoh.dat
|
|
||||||
bool InsertSystem43Hook(); // System43@AliceSoft: AliceStart.ini
|
|
||||||
bool InsertSystemAoiHook(); // SystemAoi: *.vfs
|
|
||||||
bool InsertTanukiHook(); // Tanuki: *.tak
|
|
||||||
bool InsertTaskforce2Hook(); // Taskforce2.exe
|
|
||||||
bool InsertTencoHook(); // Tenco: Check.mdx
|
|
||||||
bool InsertTriangleHook(); // Triangle: Execle.exe
|
|
||||||
bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc
|
|
||||||
bool InsertYurisHook(); // YU-RIS: *.ypf
|
|
||||||
bool InsertWillPlusHook(); // WillPlus: Rio.arc
|
|
||||||
bool InsertWolfHook(); // Wolf: Data.wolf
|
|
||||||
|
|
||||||
void InsertBrunsHook(); // Bruns: bruns.exe
|
|
||||||
void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe
|
|
||||||
void InsertLucifenHook(); // Lucifen@Navel: *.lpk
|
|
||||||
void InsertRyokuchaHook(); // Ryokucha: _checksum.exe
|
|
||||||
void InsertRealliveHook(); // RealLive: RealLive*.exe
|
|
||||||
void InsertStuffScriptHook(); // Stuff: *.mpk
|
|
||||||
void InsertTinkerBellHook(); // TinkerBell: arc00.dat
|
|
||||||
void InsertWaffleHook(); // WAFFLE: cg.pak
|
|
||||||
|
|
||||||
// CIRCUS: avdata/
|
|
||||||
bool InsertCircusHook1();
|
|
||||||
bool InsertCircusHook2();
|
|
||||||
|
|
||||||
} // namespace Engine
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// engine/hookdefs.h
|
|
||||||
// 7/20/2014 jichi
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
// For HookParam user flags
|
|
||||||
enum HookParamFlag : unsigned long {
|
|
||||||
HPF_Null = 0 // never used
|
|
||||||
, HPF_IgnoreSameAddress = 1 // ignore the last same text address
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,831 +0,0 @@
|
|||||||
// eng/match.cc
|
|
||||||
// 8/9/2013 jichi
|
|
||||||
// Branch: ITH_Engine/engine.cpp, revision 133
|
|
||||||
|
|
||||||
#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 "engine/match.h"
|
|
||||||
#include "engine/engine.h"
|
|
||||||
#include "engine/pchooks.h"
|
|
||||||
#include "engine/util.h"
|
|
||||||
#include "hook.h"
|
|
||||||
#include "ith/sys/sys.h"
|
|
||||||
#include "ith/common/except.h"
|
|
||||||
#include "ith/common/growl.h"
|
|
||||||
#include "ccutil/ccmacro.h"
|
|
||||||
|
|
||||||
//#define ConsoleOutput(...) (void)0 // jichi 8/18/2013: I don't need ConsoleOutput
|
|
||||||
|
|
||||||
enum { MAX_REL_ADDR = 0x200000 }; // jichi 8/18/2013: maximum relative address
|
|
||||||
|
|
||||||
// - Global variables -
|
|
||||||
|
|
||||||
namespace Engine {
|
|
||||||
|
|
||||||
WCHAR process_name_[MAX_PATH], // cached
|
|
||||||
process_path_[MAX_PATH]; // cached
|
|
||||||
|
|
||||||
DWORD module_base_,
|
|
||||||
module_limit_;
|
|
||||||
|
|
||||||
//LPVOID trigger_addr;
|
|
||||||
trigger_fun_t trigger_fun_;
|
|
||||||
|
|
||||||
} // namespace Engine
|
|
||||||
|
|
||||||
// - Methods -
|
|
||||||
|
|
||||||
namespace Engine { namespace { // unnamed
|
|
||||||
|
|
||||||
// jichi 7/17/2014: Disable GDI hooks for PPSSPP
|
|
||||||
bool DeterminePCEngine()
|
|
||||||
{
|
|
||||||
if (IthFindFile(L"PPSSPP*.exe")) { // jichi 7/12/2014 PPSSPPWindows.exe, PPSSPPEX.exe PPSSPPSP.exe
|
|
||||||
InsertPPSSPPHooks();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IthFindFile(L"pcsx2*.exe")) { // jichi 7/19/2014 PCSX2.exe or PCSX2WX.exe
|
|
||||||
if (!InsertPCSX2Hooks()) { // don't forget to rebuild vnrcli to inject SSE
|
|
||||||
// Always insert PC hooks so that user could add PCSX2 to VNR.
|
|
||||||
// TO BE REMOVED after more PS2 engines are added.
|
|
||||||
PcHooks::hookGDIFunctions();
|
|
||||||
PcHooks::hookLstrFunctions();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IthFindFile(L"Dolphin.exe")) { // jichi 7/20/2014
|
|
||||||
if (!InsertGCHooks()) {
|
|
||||||
// Always insert PC hooks so that user could add PCSX2 to VNR.
|
|
||||||
// TO BE REMOVED after more PS2 engines are added.
|
|
||||||
PcHooks::hookGDIFunctions();
|
|
||||||
PcHooks::hookLstrFunctions();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (IthFindFile(L"*\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
|
|
||||||
//if (IthCheckFile(L"bsz2_Data\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
|
|
||||||
// InsertMonoHook();
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
if (::GetModuleHandleA("mono.dll")) {
|
|
||||||
InsertMonoHooks();
|
|
||||||
|
|
||||||
// 3/20/2015 jichi
|
|
||||||
// Always insert GDI hooks even for Mono games
|
|
||||||
// For example: 新世黙示録 need GetGlyphOutlineA
|
|
||||||
PcHooks::hookGDIFunctions();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// PC games
|
|
||||||
PcHooks::hookGDIFunctions();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DetermineEngineByFile1()
|
|
||||||
{
|
|
||||||
if (IthFindFile(L"*.xp3") || Util::SearchResourceString(L"TVP(KIRIKIRI)")) {
|
|
||||||
if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
|
|
||||||
// jichi 11/24/2014: Disabled that might crash VBH
|
|
||||||
//if (IthCheckFile(L"plugin\\KAGParser.dll"))
|
|
||||||
// InsertKAGParserHook();
|
|
||||||
//else if (IthCheckFile(L"plugin\\KAGParserEx.dll"))
|
|
||||||
// InsertKAGParserExHook();
|
|
||||||
if (InsertKiriKiriZHook())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
InsertKiriKiriHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 8/2/2014 jichi: Game name shown as 2RM - Adventure Engine
|
|
||||||
if (Util::SearchResourceString(L"2RM") && Util::SearchResourceString(L"Adventure Engine")) {
|
|
||||||
Insert2RMHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 8/2/2014 jichi: Copyright is side-B, a conf.dat will be generated after the game is launched
|
|
||||||
// It also contains lua5.1.dll and lua5.dll
|
|
||||||
if (Util::SearchResourceString(L"side-B")) {
|
|
||||||
InsertSideBHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"bgi.*")) {
|
|
||||||
InsertBGIHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"AGERC.DLL")) { // 6/1/2014 jichi: Eushully, AGE.EXE
|
|
||||||
InsertEushullyHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"data*.arc") && IthFindFile(L"stream*.arc")) {
|
|
||||||
InsertMajiroHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 5/31/2014
|
|
||||||
if (//IthCheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
|
|
||||||
// data, effect, layer, mes, music
|
|
||||||
IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"mes.arc")) {
|
|
||||||
InsertElfHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"data\\pack\\*.cpz")) {
|
|
||||||
InsertCMVSHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 10/12/2013: Restore wolf engine
|
|
||||||
// jichi 10/18/2013: Check for data/*.wolf
|
|
||||||
if (IthFindFile(L"data.wolf") || IthFindFile(L"data\\*.wolf")) {
|
|
||||||
InsertWolfHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"advdata\\dat\\names.dat")) {
|
|
||||||
InsertCircusHook1();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"advdata\\grp\\names.dat")) {
|
|
||||||
InsertCircusHook2();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.noa")) {
|
|
||||||
InsertCotophaHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.pfs")) { // jichi 10/1/2013
|
|
||||||
InsertArtemisHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.int")) {
|
|
||||||
InsertCatSystemHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"message.dat")) {
|
|
||||||
InsertAtelierHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"Check.mdx")) { // jichi 4/1/2014: AUGame
|
|
||||||
InsertTencoHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 12/25/2013: It may or may not be QLIE.
|
|
||||||
// AlterEgo also has GameData/sound.pack but is not QLIE
|
|
||||||
if (IthFindFile(L"GameData\\*.pack") && InsertQLIEHook())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (IthFindFile(L"*.pac")) {
|
|
||||||
// jichi 6/3/2014: AMUSE CRAFT and SOFTPAL
|
|
||||||
// Selectively insert, so that lstrlenA can still get correct text if failed
|
|
||||||
if (IthCheckFile(L"dll\\resource.dll") && IthCheckFile(L"dll\\pal.dll") && InsertAmuseCraftHook())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (IthCheckFile(L"Thumbnail.pac")) {
|
|
||||||
//ConsoleOutput("vnreng: IGNORE NeXAS");
|
|
||||||
InsertNeXASHook(); // jichi 7/6/2014: GIGA
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Util::SearchResourceString(L"SOFTPAL")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE SoftPal UNiSONSHIFT");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// jichi 12/27/2014: LunaSoft
|
|
||||||
if (IthFindFile(L"Pac\\*.pac")) {
|
|
||||||
InsertLunaSoftHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 9/16/2013: Add Gesen18
|
|
||||||
if (IthFindFile(L"*.szs") || IthFindFile(L"Data\\*.szs")) {
|
|
||||||
InsertGesen18Hook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 12/22/2013: Add rejet
|
|
||||||
if (IthCheckFile(L"gd.dat") && IthCheckFile(L"pf.dat") && IthCheckFile(L"sd.dat")) {
|
|
||||||
InsertRejetHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Only examined with version 1.0
|
|
||||||
//if (IthFindFile(L"Adobe AIR\\Versions\\*\\Adobe AIR.dll")) { // jichi 4/15/2014: FIXME: Wildcard not working
|
|
||||||
if (IthCheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")) { // jichi 4/15/2014: Adobe AIR
|
|
||||||
InsertAdobeAirHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DetermineEngineByFile2()
|
|
||||||
{
|
|
||||||
if (IthCheckFile(L"resident.dll")) {
|
|
||||||
InsertRetouchHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"Malie.ini") || IthCheckFile(L"Malie.exe")) { // jichi: 9/9/2014: Add malie.exe in case malie.ini is missing
|
|
||||||
InsertMalieHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"live.dll")) {
|
|
||||||
InsertLiveHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 9/5/2013 jichi
|
|
||||||
if (IthCheckFile(L"aInfo.db")) {
|
|
||||||
InsertNextonHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.lpk")) {
|
|
||||||
InsertLucifenHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"cfg.pak")) {
|
|
||||||
InsertWaffleHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"Arc00.dat")) {
|
|
||||||
InsertTinkerBellHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.vfs")) { // jichi 7/6/2014: Better to test AoiLib.dll? ja.wikipedia.org/wiki/ソフトハウスキャラ
|
|
||||||
InsertSystemAoiHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.mbl")) {
|
|
||||||
InsertMBLHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 8/1/2014: YU-RIS engine, lots of clockup game also has this pattern
|
|
||||||
if (IthFindFile(L"pac\\*.ypf") || IthFindFile(L"*.ypf")) {
|
|
||||||
// jichi 8/14/2013: CLOCLUP: "ノーブレスオブリージュ" would crash the game.
|
|
||||||
if (!IthCheckFile(L"noblesse.exe"))
|
|
||||||
InsertYurisHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.npa")) {
|
|
||||||
InsertNitroPlusHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DetermineEngineByFile3()
|
|
||||||
{
|
|
||||||
//if (IthCheckFile(L"libscr.dll")) { // already checked
|
|
||||||
// InsertBrunsHook();
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// jichi 10/12/2013: Sample args.txt:
|
|
||||||
// See: http://tieba.baidu.com/p/2631413816
|
|
||||||
// -workdir
|
|
||||||
// .
|
|
||||||
// -loadpath
|
|
||||||
// .
|
|
||||||
// am.cfg
|
|
||||||
if (IthCheckFile(L"args.txt")) {
|
|
||||||
InsertBrunsHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"emecfg.ecf")) {
|
|
||||||
InsertEMEHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"rrecfg.rcf")) {
|
|
||||||
InsertRREHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.fpk") || IthFindFile(L"data\\*.fpk")) {
|
|
||||||
InsertCandyHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"arc.a*")) {
|
|
||||||
InsertApricoTHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.mpk")) {
|
|
||||||
InsertStuffScriptHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"Execle.exe")) {
|
|
||||||
InsertTriangleHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 2/28/2015: No longer work for "大正×対称アリス episode I" from Primula
|
|
||||||
//if (IthCheckFile(L"PSetup.exe")) {
|
|
||||||
// InsertPensilHook();
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
if (IthCheckFile(L"Yanesdk.dll")) {
|
|
||||||
InsertAB2TryHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.med")) {
|
|
||||||
InsertMEDHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DetermineEngineByFile4()
|
|
||||||
{
|
|
||||||
if (IthCheckFile(L"EAGLS.dll")) { // jichi 3/24/2014: E.A.G.L.S
|
|
||||||
//ConsoleOutput("vnreng: IGNORE EAGLS");
|
|
||||||
InsertEaglsHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"bmp.pak") && IthCheckFile(L"dsetup.dll")) {
|
|
||||||
InsertDebonosuHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"C4.EXE") || IthCheckFile(L"XEX.EXE")) {
|
|
||||||
InsertC4Hook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"Rio.arc") && IthFindFile(L"Chip*.arc")) {
|
|
||||||
InsertWillPlusHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.tac")) {
|
|
||||||
InsertTanukiHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.gxp")) {
|
|
||||||
InsertGXPHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.aos")) { // jichi 4/2/2014: AOS hook
|
|
||||||
InsertAOSHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.at2")) { // jichi 12/23/2014: Mink, sample files: voice.at2, voice.det, voice.nme
|
|
||||||
InsertMinkHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"*.ykc")) { // jichi 7/15/2014: YukaSystem1 is not supported, though
|
|
||||||
//ConsoleOutput("vnreng: IGNORE YKC:Feng/HookSoft(SMEE)");
|
|
||||||
InsertYukaSystem2Hook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"model\\*.hed")) { // jichi 9/8/2014: EXP
|
|
||||||
InsertExpHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 2/6/2015 平安亭
|
|
||||||
// dPi.dat, dPih.dat, dSc.dat, dSch.dat, dSo.dat, dSoh.dat, dSy.dat
|
|
||||||
//if (IthCheckFile(L"dSoh.dat")) { // no idea why this file does not work
|
|
||||||
if (IthCheckFile(L"dSch.dat")) {
|
|
||||||
InsertSyuntadaHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 2/28/2015: Delay checking Pensil in case something went wrong
|
|
||||||
// File pattern observed in [Primula] 大正×対称アリス episode I
|
|
||||||
// - PSetup.exe no longer exists
|
|
||||||
// - MovieTexture.dll information shows MovieTex dynamic library, copyright Pensil 2013
|
|
||||||
// - ta_trial.exe information shows 2XT - Primula Adventure Engine
|
|
||||||
if (IthFindFile(L"PSetup.exe") || IthFindFile(L"MovieTexture.dll") || Util::SearchResourceString(L"2XT -")) {
|
|
||||||
InsertPensilHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DetermineEngineByProcessName()
|
|
||||||
{
|
|
||||||
WCHAR str[MAX_PATH];
|
|
||||||
wcscpy(str, process_name_);
|
|
||||||
_wcslwr(str); // lower case
|
|
||||||
|
|
||||||
if (wcsstr(str,L"reallive") || IthCheckFile(L"Reallive.exe")) {
|
|
||||||
InsertRealliveHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 8/19/2013: DO NOT WORK for games like「ハピメア」
|
|
||||||
//if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
|
|
||||||
// InsertCMVSHook();
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// jichi 8/17/2013: Handle "~"
|
|
||||||
if (wcsstr(str, L"siglusengine") || !wcsncmp(str, L"siglus~", 7) || IthCheckFile(L"SiglusEngine.exe")) {
|
|
||||||
InsertSiglusHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wcsstr(str, L"taskforce2") || !wcsncmp(str, L"taskfo~", 7) || IthCheckFile(L"Faskforce2.exe")) {
|
|
||||||
InsertTaskforce2Hook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wcsstr(str,L"rugp") || IthCheckFile(L"rugp.exe")) {
|
|
||||||
InsertRUGPHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 8/17/2013: Handle "~"
|
|
||||||
if (wcsstr(str, L"igs_sample") || !wcsncmp(str, L"igs_sa~", 7) || IthCheckFile(L"igs_sample.exe")) {
|
|
||||||
InsertIronGameSystemHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wcsstr(str, L"bruns") || IthCheckFile(L"bruns.exe")) {
|
|
||||||
InsertBrunsHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wcsstr(str, L"anex86") || IthCheckFile(L"anex86.exe")) {
|
|
||||||
InsertAnex86Hook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 8/17/2013: Handle "~"
|
|
||||||
if (wcsstr(str, L"shinydays") || !wcsncmp(str, L"shinyd~", 7) || IthCheckFile(L"ShinyDays.exe")) {
|
|
||||||
InsertShinyDaysHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 10/3/2013: FIXME: Does not work
|
|
||||||
// Raise C0000005 even with admin priv
|
|
||||||
//if (wcsstr(str, L"bsz")) { // BALDRSKY ZERO
|
|
||||||
// InsertBaldrHook();
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (wcsstr(process_name_, L"SAISYS") || IthCheckFile(L"SaiSys.exe")) { // jichi 4/19/2014: Marine Heart
|
|
||||||
InsertMarineHeartHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD len = wcslen(str);
|
|
||||||
|
|
||||||
// jichi 8/24/2013: Checking for Rio.ini or $procname.ini
|
|
||||||
//wcscpy(str+len-4, L"_?.war");
|
|
||||||
//if (IthFindFile(str)) {
|
|
||||||
// InsertShinaHook();
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
if (InsertShinaHook())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
|
|
||||||
str[len - 3] = L'b';
|
|
||||||
str[len - 2] = L'i';
|
|
||||||
str[len - 1] = L'n';
|
|
||||||
str[len] = 0;
|
|
||||||
if (IthCheckFile(str) || IthCheckFile(L"trial.bin")) { // jichi 7/8/2014: add trial.bin
|
|
||||||
InsertCaramelBoxHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This must appear at last since str is modified
|
|
||||||
wcscpy(str + len - 4, L"_checksum.exe");
|
|
||||||
if (IthCheckFile(str)) {
|
|
||||||
InsertRyokuchaHook();
|
|
||||||
|
|
||||||
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
|
|
||||||
InsertScenarioPlayerHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DetermineEngineOther()
|
|
||||||
{
|
|
||||||
if (InsertAliceHook())
|
|
||||||
return true;
|
|
||||||
// jichi 1/19/2015: Disable inserting Lstr for System40
|
|
||||||
// See: http://sakuradite.com/topic/618
|
|
||||||
if (IthCheckFile(L"System40.ini")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE old System40.ini");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 12/26/2013: Add this after alicehook
|
|
||||||
if (IthCheckFile(L"AliceStart.ini")) {
|
|
||||||
InsertSystem43Hook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 8/24/2013: Move into functions
|
|
||||||
static BYTE static_file_info[0x1000];
|
|
||||||
if (IthGetFileInfo(L"*01", static_file_info))
|
|
||||||
if (*(DWORD*)static_file_info == 0) {
|
|
||||||
static WCHAR static_search_name[MAX_PATH];
|
|
||||||
LPWSTR name=(LPWSTR)(static_file_info+0x5E);
|
|
||||||
int len = wcslen(name);
|
|
||||||
name[len - 2] = L'*';
|
|
||||||
name[len - 1] = 0;
|
|
||||||
wcscpy(static_search_name, name);
|
|
||||||
IthGetFileInfo(static_search_name, static_file_info);
|
|
||||||
union {
|
|
||||||
FILE_BOTH_DIR_INFORMATION *both_info;
|
|
||||||
DWORD addr;
|
|
||||||
};
|
|
||||||
both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
|
|
||||||
//BYTE* ptr=static_file_info;
|
|
||||||
len = 0;
|
|
||||||
while (both_info->NextEntryOffset) {
|
|
||||||
addr += both_info->NextEntryOffset;
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
if (len > 3) {
|
|
||||||
InsertAbelHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 8/17/2014
|
|
||||||
// Put the patterns that might break other games at last
|
|
||||||
bool DetermineEngineAtLast()
|
|
||||||
{
|
|
||||||
if (IthFindFile(L"data\\*.cpk")) { // jichi 12/2/2014
|
|
||||||
Insert5pbHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// jichi 7/6/2014: named as ScenarioPlayer since resource string could be: scenario player program for xxx
|
|
||||||
// Do this at last as it is common
|
|
||||||
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) { // jichi 4/18/2014: Other game engine could also have *.iar such as Ryokucha
|
|
||||||
InsertScenarioPlayerHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//if (IthCheckFile(L"arc0.dat") && IthCheckFile(L"script.dat") // jichi 11/14/2014: too common
|
|
||||||
if (Util::SearchResourceString(L"HorkEye")) { // appear in copyright: Copyright (C) HorkEye, http://horkeye.com
|
|
||||||
InsertHorkEyeHook();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthCheckFile(L"comnArc.arc") // jichi 8/17/2014: this file might exist in multiple files
|
|
||||||
&& InsertNexton1Hook()) // old nexton game
|
|
||||||
return true;
|
|
||||||
if (IthCheckFile(L"arc.dat") // jichi 9/27/2014: too common
|
|
||||||
&& InsertApricoTHook())
|
|
||||||
return true;
|
|
||||||
if (IthFindFile(L"*.pak") // jichi 12/25/2014: too common
|
|
||||||
&& InsertLeafHook())
|
|
||||||
return true;
|
|
||||||
// jichi 10/31/2014
|
|
||||||
// File description: Adobe Flash Player 10.2r153
|
|
||||||
// Product name: Shockwave Flash
|
|
||||||
// Original filename: SAFlashPlayer.exe
|
|
||||||
// Legal trademarks: Adobe Flash Player
|
|
||||||
// No idea why, this must appear at last or it will crash
|
|
||||||
if (Util::SearchResourceString(L"Adobe Flash Player 10")) {
|
|
||||||
InsertAdobeFlash10Hook(); // only v10 might be supported. Otherwise, fallback to Lstr hooks
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IthFindFile(L"dat\\*.arc")) { // jichi 2/6/2015
|
|
||||||
InsertFocasLensHook(); // Touhou
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 6/1/2014
|
|
||||||
bool DetermineEngineGeneric()
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
if (IthCheckFile(L"AlterEgo.exe")) {
|
|
||||||
ConsoleOutput("vnreng: AlterEgo, INSERT WideChar hooks");
|
|
||||||
ret = true;
|
|
||||||
} else if (IthFindFile(L"data\\Sky\\*")) {
|
|
||||||
ConsoleOutput("vnreng: TEATIME, INSERT WideChar hooks");
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
//} else if (IthFindFile(L"image\\*.po2") || IthFindFile(L"image\\*.jo2")) {
|
|
||||||
// ConsoleOutput("vnreng: HarukaKanata, INSERT WideChar hooks"); // はるかかなた
|
|
||||||
// ret = true;
|
|
||||||
//}
|
|
||||||
if (ret)
|
|
||||||
PcHooks::hookWcharFunctions();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DetermineNoEngine()
|
|
||||||
{
|
|
||||||
//if (IthFindFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
|
|
||||||
// ConsoleOutput("vnreng: IGNORE Unity");
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
//if (IthCheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || IthCheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
|
|
||||||
// ConsoleOutput("vnreng: IGNORE Unity");
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// jichi 2/14/2015: Guilty+ RIN×SEN (PK)
|
|
||||||
if (IthCheckFile(L"rio.ini") || IthFindFile(L"*.war")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE unknown ShinaRio");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IthCheckFile(L"AdvHD.exe") || IthCheckFile(L"AdvHD.dll")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE Adv Player HD");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IthCheckFile(L"ScrPlayer.exe")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE ScrPlayer");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IthCheckFile(L"nnnConfig2.exe")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE Nya NNNConfig");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (IthCheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
|
|
||||||
// ConsoleOutput("vnreng: IGNORE Eushully");
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (IthCheckFile(L"game_sys.exe")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE Atelier Kaguya BY/TH");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IthFindFile(L"*.bsa")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE Bishop");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 3/19/2014: Escude game
|
|
||||||
// Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
|
|
||||||
if (IthCheckFile(L"gfx.bin") && IthCheckFile(L"snd.bin") && IthCheckFile(L"voc.bin")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE Escude");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 2/18/2015: Ignore if there is Nitro+ copyright
|
|
||||||
if (Util::SearchResourceString(L"Nitro+")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE unknown Nitro+");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 12/28/2014: "Chartreux Inc." in Copyright.
|
|
||||||
// Sublimary brands include Rosebleu, MORE, etc.
|
|
||||||
// GetGlyphOutlineA already works.
|
|
||||||
if (Util::SearchResourceString(L"Chartreux")) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE Chartreux");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wcsstr(process_name_, L"lcsebody") || !wcsncmp(process_name_, L"lcsebo~", 7) || IthCheckFile(L"lcsebody")) { // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
|
|
||||||
ConsoleOutput("vnreng: IGNORE lcsebody");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t str[MAX_PATH];
|
|
||||||
DWORD i;
|
|
||||||
for (i = 0; process_name_[i]; i++) {
|
|
||||||
str[i] = process_name_[i];
|
|
||||||
if (process_name_[i] == L'.')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*(DWORD *)(str + i + 1) = 0x630068; //.hcb
|
|
||||||
*(DWORD *)(str + i + 3) = 0x62;
|
|
||||||
if (IthCheckFile(str)) {
|
|
||||||
ConsoleOutput("vnreng: IGNORE FVP"); // jichi 10/3/2013: such like アトリエかぐや
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 12/13/2013: Declare it in a way compatible to EXCEPTION_PROCEDURE
|
|
||||||
EXCEPTION_DISPOSITION ExceptHandler(PEXCEPTION_RECORD ExceptionRecord, LPVOID, PCONTEXT, LPVOID)
|
|
||||||
{
|
|
||||||
if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) {
|
|
||||||
module_limit_ = ExceptionRecord->ExceptionInformation[1];
|
|
||||||
//OutputDWORD(module_limit_);
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,fs:[0x30] // jichi 12/13/2013: get PEB
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov ecx,module_limit_
|
|
||||||
sub ecx,module_base_
|
|
||||||
mov [eax+0x20],ecx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//ContextRecord->Esp = recv_esp;
|
|
||||||
//ContextRecord->Eip = recv_eip;
|
|
||||||
//return ExceptionContinueExecution; // jichi 3/11/2014: this will still crash. Not sure why ITH use this. Change to ExceptionContinueSearch
|
|
||||||
return ExceptionContinueSearch; // an unwind is in progress,
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 9/14/2013: Certain ITH functions like FindEntryAligned might raise exception without admin priv
|
|
||||||
// Return if succeeded.
|
|
||||||
bool UnsafeDetermineEngineType()
|
|
||||||
{
|
|
||||||
return DeterminePCEngine()
|
|
||||||
|| DetermineEngineByFile1()
|
|
||||||
|| DetermineEngineByFile2()
|
|
||||||
|| DetermineEngineByFile3()
|
|
||||||
|| DetermineEngineByFile4()
|
|
||||||
|| DetermineEngineByProcessName()
|
|
||||||
|| DetermineEngineOther()
|
|
||||||
|| DetermineEngineAtLast()
|
|
||||||
|| DetermineEngineGeneric()
|
|
||||||
|| DetermineNoEngine()
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 10/21/2014: Return whether found the game engine
|
|
||||||
bool DetermineEngineType()
|
|
||||||
{
|
|
||||||
// jichi 9/27/2013: disable game engine for debugging use
|
|
||||||
#ifdef ITH_DISABLE_ENGINE
|
|
||||||
PcHooks::hookLstrFunctions();
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
bool found = false;
|
|
||||||
#ifdef ITH_HAS_SEH
|
|
||||||
__try { found = UnsafeDetermineEngineType(); }
|
|
||||||
__except(ExceptHandler((GetExceptionInformation())->ExceptionRecord, 0, 0, 0)) {}
|
|
||||||
#else // use my own SEH
|
|
||||||
seh_with_eh(ExceptHandler,
|
|
||||||
found = UnsafeDetermineEngineType());
|
|
||||||
#endif // ITH_HAS_SEH
|
|
||||||
if (!found) // jichi 10/2/2013: Only enable it if no game engine is detected
|
|
||||||
PcHooks::hookLstrFunctions();
|
|
||||||
else
|
|
||||||
ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
|
|
||||||
return found;
|
|
||||||
#endif // ITH_DISABLE_ENGINE
|
|
||||||
}
|
|
||||||
|
|
||||||
// __asm
|
|
||||||
// {
|
|
||||||
// mov eax,seh_recover
|
|
||||||
// mov recv_eip,eax
|
|
||||||
// push ExceptHandler
|
|
||||||
// push fs:[0]
|
|
||||||
// mov fs:[0],esp
|
|
||||||
// pushad
|
|
||||||
// mov recv_esp,esp
|
|
||||||
// }
|
|
||||||
// DetermineEngineType();
|
|
||||||
// status++;
|
|
||||||
// __asm
|
|
||||||
// {
|
|
||||||
//seh_recover:
|
|
||||||
// popad
|
|
||||||
// mov eax,[esp]
|
|
||||||
// mov fs:[0],eax
|
|
||||||
// add esp,8
|
|
||||||
// }
|
|
||||||
// if (status == 0)
|
|
||||||
// ConsoleOutput("Fail to identify engine type.");
|
|
||||||
// else
|
|
||||||
// ConsoleOutput("Initialized successfully.");
|
|
||||||
//}
|
|
||||||
|
|
||||||
}} // namespace Engine unnamed
|
|
||||||
|
|
||||||
// - API -
|
|
||||||
|
|
||||||
bool Engine::IdentifyEngine()
|
|
||||||
{
|
|
||||||
// jichi 12/18/2013: Though FillRange could raise, it should never raise for he current process
|
|
||||||
// So, SEH is not used here.
|
|
||||||
FillRange(process_name_, &module_base_, &module_limit_);
|
|
||||||
return DetermineEngineType();
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Engine::InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
|
|
||||||
{ return trigger_fun_ ? !trigger_fun_(addr, frame, stack) : 0; }
|
|
||||||
|
|
||||||
void Engine::match(LPVOID lpThreadParameter)
|
|
||||||
{
|
|
||||||
CC_UNUSED(lpThreadParameter);
|
|
||||||
Util::GetProcessName(process_name_); // Initialize process name
|
|
||||||
Util::GetProcessPath(process_path_); // Initialize process path
|
|
||||||
::engine_registered = true;
|
|
||||||
//::RegisterEngineModule((DWORD)IdentifyEngine, (DWORD)InsertDynamicHook);
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
||||||
|
|
||||||
/*
|
|
||||||
extern "C" {
|
|
||||||
// http://gmogre3d.googlecode.com/svn-history/r815/trunk/OgreMain/src/WIN32/OgreMinGWSupport.cpp
|
|
||||||
// http://forum.osdev.org/viewtopic.php?f=8&t=22352
|
|
||||||
//#pragma data_seg()
|
|
||||||
//#pragma comment(linker, "/merge:.CRT=.data") // works fine in visual c++ 6
|
|
||||||
//#pragma data_seg()
|
|
||||||
//#pragma comment(linker, "/merge:.CRT=.rdata")
|
|
||||||
// MSVC libs use _chkstk for stack-probing. MinGW equivalent is _alloca.
|
|
||||||
//void _alloca();
|
|
||||||
//void _chkstk() { _alloca(); }
|
|
||||||
|
|
||||||
// MSVC uses security cookies to prevent some buffer overflow attacks.
|
|
||||||
// provide dummy implementations.
|
|
||||||
//void _fastcall __security_check_cookie(intptr_t i) {}
|
|
||||||
void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {}
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,24 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// engine/match.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// TODO: Clean up the interface to match game engines.
|
|
||||||
// Split the engine match logic out of hooks.
|
|
||||||
// Modify the game hook to allow replace functions for arbitary purpose
|
|
||||||
// instead of just extracting text.
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
namespace Engine {
|
|
||||||
|
|
||||||
void match(LPVOID lpThreadParameter);
|
|
||||||
|
|
||||||
// jichi 10/21/2014: Return whether found the engine
|
|
||||||
bool IdentifyEngine();
|
|
||||||
|
|
||||||
// jichi 10/21/2014: Return 0 if failed
|
|
||||||
DWORD InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack);
|
|
||||||
|
|
||||||
} // namespace Engine
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,192 +0,0 @@
|
|||||||
// pchooks.cc
|
|
||||||
// 8/1/2014 jichi
|
|
||||||
|
|
||||||
#include "engine/pchooks.h"
|
|
||||||
#include "hook.h"
|
|
||||||
|
|
||||||
#define DEBUG "vnrcli"
|
|
||||||
#define DPRINT(cstr) ConsoleOutput(DEBUG ":" __FUNCTION__ ":" cstr) // defined in vnrcli
|
|
||||||
|
|
||||||
// 8/1/2014 jichi: Split is not used.
|
|
||||||
// Although split is specified, USING_SPLIT is not assigned.
|
|
||||||
|
|
||||||
// Use LPASTE to convert to wchar_t
|
|
||||||
// http://bytes.com/topic/c/answers/135834-defining-wide-character-strings-macros
|
|
||||||
#define LPASTE(s) L##s
|
|
||||||
#define L(s) LPASTE(s)
|
|
||||||
#define NEW_HOOK(_fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
|
|
||||||
{ \
|
|
||||||
HookParam hp = {}; \
|
|
||||||
hp.addr = (DWORD)_fun; \
|
|
||||||
hp.off = _data; \
|
|
||||||
hp.ind = _data_ind; \
|
|
||||||
hp.split = _split_off; \
|
|
||||||
hp.split_ind = _split_ind; \
|
|
||||||
hp.type = _type; \
|
|
||||||
hp.length_offset = _len_off; \
|
|
||||||
NewHook(hp, L(#_fun)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 7/17/2014: Renamed from InitDefaultHook
|
|
||||||
void PcHooks::hookGDIFunctions()
|
|
||||||
{
|
|
||||||
DPRINT("enter");
|
|
||||||
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
|
|
||||||
//
|
|
||||||
// jichi 9/8/2013: Guessed meaning
|
|
||||||
// - data(off): 4 * the n-th (base 1) parameter representing the data of the string
|
|
||||||
// - len_off:
|
|
||||||
// - the n-th (base 1) parameter representing the length of the string
|
|
||||||
// - or 1 if is char
|
|
||||||
// - or 0 if detect on run time
|
|
||||||
// - type: USING_STRING if len_off != 1 else BIG_ENDIAN or USING_UNICODE
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
// int WINAPI lstrlenA(LPCSTR lpString)
|
|
||||||
// - data: 4 * 1 = 4, as lpString is the first
|
|
||||||
// - len_off: 0, as no parameter representing string length
|
|
||||||
// - type: BIG_ENDIAN, since len_off == 1
|
|
||||||
// BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
|
|
||||||
// - data: 4 * 2 = 0x8, as lpString is the second
|
|
||||||
// - len_off: 3, as nCount is the 3rd parameter
|
|
||||||
// - type: USING_STRING, since len_off != 1
|
|
||||||
//
|
|
||||||
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
|
|
||||||
|
|
||||||
enum stack {
|
|
||||||
s_retaddr = 0
|
|
||||||
, s_arg1 = 4 * 1 // 0x4
|
|
||||||
, s_arg2 = 4 * 2 // 0x8
|
|
||||||
, s_arg3 = 4 * 3 // 0xc
|
|
||||||
, s_arg4 = 4 * 4 // 0x10
|
|
||||||
, s_arg5 = 4 * 5 // 0x14
|
|
||||||
, s_arg6 = 4 * 6 // 0x18
|
|
||||||
};
|
|
||||||
|
|
||||||
//#define _(Name, ...) \
|
|
||||||
// hookman[HF_##Name].InitHook(Name, __VA_ARGS__); \
|
|
||||||
// hookman[HF_##Name].SetHookName(names[HF_##Name]);
|
|
||||||
|
|
||||||
// Always use s_arg1 = hDC as split_off
|
|
||||||
// 7/26/2014 jichi: Why there is no USING_SPLIT type?
|
|
||||||
|
|
||||||
// gdi32.dll
|
|
||||||
NEW_HOOK(GetTextExtentPoint32A, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
|
|
||||||
NEW_HOOK(GetGlyphOutlineA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // DWORD GetGlyphOutline(HDC hdc, UINT uChar, UINT uFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpvBuffer, const MAT2 *lpmat2);
|
|
||||||
NEW_HOOK(ExtTextOutA, s_arg6, 0,s_arg1,0, USING_STRING, 7) // BOOL ExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, const RECT *lprc, LPCTSTR lpString, UINT cbCount, const INT *lpDx);
|
|
||||||
NEW_HOOK(TextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cchString);
|
|
||||||
NEW_HOOK(GetCharABCWidthsA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidths(HDC hdc, UINT uFirstChar, UINT uLastChar, LPABC lpabc);
|
|
||||||
NEW_HOOK(GetTextExtentPoint32W, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
|
|
||||||
NEW_HOOK(GetGlyphOutlineW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
|
|
||||||
NEW_HOOK(ExtTextOutW, s_arg6, 0,s_arg1,0, USING_UNICODE|USING_STRING, 7)
|
|
||||||
NEW_HOOK(TextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
|
|
||||||
NEW_HOOK(GetCharABCWidthsW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
|
|
||||||
|
|
||||||
// user32.dll
|
|
||||||
NEW_HOOK(DrawTextA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawText(HDC hDC, LPCTSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat);
|
|
||||||
NEW_HOOK(DrawTextExA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawTextEx(HDC hdc, LPTSTR lpchText,int cchText, LPRECT lprc, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams);
|
|
||||||
NEW_HOOK(DrawTextW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
|
|
||||||
NEW_HOOK(DrawTextExW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
|
|
||||||
//#undef _
|
|
||||||
DPRINT("leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 10/2/2013
|
|
||||||
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
|
|
||||||
void PcHooks::hookLstrFunctions()
|
|
||||||
{
|
|
||||||
DPRINT("enter");
|
|
||||||
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
|
|
||||||
|
|
||||||
enum stack {
|
|
||||||
s_retaddr = 0
|
|
||||||
, s_arg1 = 4 * 1 // 0x4
|
|
||||||
//, s_arg2 = 4 * 2 // 0x8
|
|
||||||
//, s_arg3 = 4 * 3 // 0xc
|
|
||||||
//, s_arg4 = 4 * 4 // 0x10
|
|
||||||
//, s_arg5 = 4 * 5 // 0x14
|
|
||||||
//, s_arg6 = 4 * 6 // 0x18
|
|
||||||
};
|
|
||||||
|
|
||||||
// http://msdn.microsoft.com/en-us/library/78zh94ax.aspx
|
|
||||||
// int WINAPI lstrlen(LPCTSTR lpString);
|
|
||||||
// Lstr functions usually extracts rubbish, and might crash certain games like 「Magical Marriage Lunatics!!」
|
|
||||||
// Needed by Gift
|
|
||||||
// Use arg1 address for both split and data
|
|
||||||
NEW_HOOK(lstrlenA, s_arg1, 0,s_arg1,0, USING_STRING, 0) // 9/8/2013 jichi: int WINAPI lstrlen(LPCTSTR lpString);
|
|
||||||
NEW_HOOK(lstrlenW, s_arg1, 0,s_arg1,0, USING_UNICODE|USING_STRING, 0) // 9/8/2013 jichi: add lstrlen
|
|
||||||
|
|
||||||
// size_t strlen(const char *str);
|
|
||||||
// size_t strlen_l(const char *str, _locale_t locale);
|
|
||||||
// size_t wcslen(const wchar_t *str);
|
|
||||||
// size_t wcslen_l(const wchar_t *str, _locale_t locale);
|
|
||||||
// size_t _mbslen(const unsigned char *str);
|
|
||||||
// size_t _mbslen_l(const unsigned char *str, _locale_t locale);
|
|
||||||
// size_t _mbstrlen(const char *str);
|
|
||||||
// size_t _mbstrlen_l(const char *str, _locale_t locale);
|
|
||||||
|
|
||||||
// http://msdn.microsoft.com/en-us/library/ex0hs2ad.aspx
|
|
||||||
// Needed by 娘姉妹
|
|
||||||
//
|
|
||||||
// <tchar.h>
|
|
||||||
// char *_strinc(const char *current, _locale_t locale);
|
|
||||||
// wchar_t *_wcsinc(const wchar_t *current, _locale_t locale);
|
|
||||||
// <mbstring.h>
|
|
||||||
// unsigned char *_mbsinc(const unsigned char *current);
|
|
||||||
// unsigned char *_mbsinc_l(const unsigned char *current, _locale_t locale);
|
|
||||||
//_(L"_strinc", _strinc, 4, 0,4,0, USING_STRING, 0) // 12/13/2013 jichi
|
|
||||||
//_(L"_wcsinc", _wcsinc, 4, 0,4,0, USING_UNICODE|USING_STRING, 0)
|
|
||||||
DPRINT("leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
void PcHooks::hookWcharFunctions()
|
|
||||||
{
|
|
||||||
DPRINT("enter");
|
|
||||||
// 12/1/2013 jichi:
|
|
||||||
// AlterEgo
|
|
||||||
// http://tieba.baidu.com/p/2736475133
|
|
||||||
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page355
|
|
||||||
//
|
|
||||||
// MultiByteToWideChar
|
|
||||||
// http://blgames.proboards.com/thread/265
|
|
||||||
//
|
|
||||||
// WideCharToMultiByte
|
|
||||||
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page156
|
|
||||||
//
|
|
||||||
// int MultiByteToWideChar(
|
|
||||||
// _In_ UINT CodePage,
|
|
||||||
// _In_ DWORD dwFlags,
|
|
||||||
// _In_ LPCSTR lpMultiByteStr, // hook here
|
|
||||||
// _In_ int cbMultiByte,
|
|
||||||
// _Out_opt_ LPWSTR lpWideCharStr,
|
|
||||||
// _In_ int cchWideChar
|
|
||||||
// );
|
|
||||||
// int WideCharToMultiByte(
|
|
||||||
// _In_ UINT CodePage,
|
|
||||||
// _In_ DWORD dwFlags,
|
|
||||||
// _In_ LPCWSTR lpWideCharStr,
|
|
||||||
// _In_ int cchWideChar,
|
|
||||||
// _Out_opt_ LPSTR lpMultiByteStr,
|
|
||||||
// _In_ int cbMultiByte,
|
|
||||||
// _In_opt_ LPCSTR lpDefaultChar,
|
|
||||||
// _Out_opt_ LPBOOL lpUsedDefaultChar
|
|
||||||
// );
|
|
||||||
|
|
||||||
enum stack {
|
|
||||||
s_retaddr = 0
|
|
||||||
//, s_arg1 = 4 * 1 // 0x4
|
|
||||||
//, s_arg2 = 4 * 2 // 0x8
|
|
||||||
, s_arg3 = 4 * 3 // 0xc
|
|
||||||
//, s_arg4 = 4 * 4 // 0x10
|
|
||||||
//, s_arg5 = 4 * 5 // 0x14
|
|
||||||
//, s_arg6 = 4 * 6 // 0x18
|
|
||||||
};
|
|
||||||
|
|
||||||
// 3/17/2014 jichi: Temporarily disabled
|
|
||||||
// http://sakuradite.com/topic/159
|
|
||||||
NEW_HOOK(MultiByteToWideChar, s_arg3, 0,4,0, USING_STRING, 4)
|
|
||||||
NEW_HOOK(WideCharToMultiByte, s_arg3, 0,4,0, USING_UNICODE|USING_STRING, 4)
|
|
||||||
DPRINT("leave");
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// pchooks.h
|
|
||||||
// 8/1/2014 jichi
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
namespace PcHooks {
|
|
||||||
|
|
||||||
void hookGDIFunctions();
|
|
||||||
void hookLstrFunctions();
|
|
||||||
void hookWcharFunctions();
|
|
||||||
|
|
||||||
} // namespace PcHooks
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,305 +0,0 @@
|
|||||||
// util/util.cc
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// Branch: ITH_Engine/engine.cpp, revision 133
|
|
||||||
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
|
|
||||||
|
|
||||||
#include "engine/util.h"
|
|
||||||
#include "ith/sys/sys.h"
|
|
||||||
|
|
||||||
namespace { // unnamed
|
|
||||||
|
|
||||||
// jichi 4/19/2014: Return the integer that can mask the signature
|
|
||||||
DWORD SigMask(DWORD sig)
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
xor ecx,ecx
|
|
||||||
mov eax,sig
|
|
||||||
_mask:
|
|
||||||
shr eax,8
|
|
||||||
inc ecx
|
|
||||||
test eax,eax
|
|
||||||
jnz _mask
|
|
||||||
sub ecx,4
|
|
||||||
neg ecx
|
|
||||||
or eax,-1
|
|
||||||
shl ecx,3
|
|
||||||
shr eax,cl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace unnamed
|
|
||||||
|
|
||||||
// jichi 8/24/2013: binary search?
|
|
||||||
DWORD Util::GetCodeRange(DWORD hModule,DWORD *low, DWORD *high)
|
|
||||||
{
|
|
||||||
IMAGE_DOS_HEADER *DosHdr;
|
|
||||||
IMAGE_NT_HEADERS *NtHdr;
|
|
||||||
DWORD dwReadAddr;
|
|
||||||
IMAGE_SECTION_HEADER *shdr;
|
|
||||||
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) {
|
|
||||||
shdr = (PIMAGE_SECTION_HEADER)((DWORD)(&NtHdr->OptionalHeader) + NtHdr->FileHeader.SizeOfOptionalHeader);
|
|
||||||
while ((shdr->Characteristics & IMAGE_SCN_CNT_CODE) == 0)
|
|
||||||
shdr++;
|
|
||||||
*low = hModule + shdr->VirtualAddress;
|
|
||||||
*high = *low + (shdr->Misc.VirtualSize & 0xfffff000) + 0x1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Util::FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig)
|
|
||||||
{
|
|
||||||
//WCHAR str[0x40];
|
|
||||||
enum { reverse_length = 0x800 };
|
|
||||||
DWORD t, l;
|
|
||||||
DWORD mask = SigMask(sig);
|
|
||||||
bool flag2;
|
|
||||||
for (DWORD i = 0x1000; i < size-4; i++) {
|
|
||||||
bool flag1 = false;
|
|
||||||
if (*(BYTE *)(pt + i) == 0xe8) {
|
|
||||||
flag1 = flag2 = true;
|
|
||||||
t = *(DWORD *)(pt + i + 1);
|
|
||||||
} else if (*(WORD *)(pt + i) == 0x15ff) {
|
|
||||||
flag1 = true;
|
|
||||||
flag2 = false;
|
|
||||||
t = *(DWORD *)(pt + i + 2);
|
|
||||||
}
|
|
||||||
if (flag1) {
|
|
||||||
if (flag2) {
|
|
||||||
flag1 = (pt + i + 5 + t == fun);
|
|
||||||
l = 5;
|
|
||||||
} else if (t >= pt && t <= pt + size - 4) {
|
|
||||||
flag1 = fun == *(DWORD *)t;
|
|
||||||
l = 6;
|
|
||||||
} else
|
|
||||||
flag1 = false;
|
|
||||||
if (flag1)
|
|
||||||
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
|
|
||||||
//OutputConsole(str);
|
|
||||||
for (DWORD j = i; j > i - reverse_length; j--)
|
|
||||||
if ((*(WORD *)(pt + j)) == (sig & mask)) //Fun entry 1.
|
|
||||||
//swprintf(str,L"Entry: 0x%.8X",pt + j);
|
|
||||||
//OutputConsole(str);
|
|
||||||
return pt + j;
|
|
||||||
else
|
|
||||||
i += l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//OutputConsole(L"Find call and entry failed.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Util::FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp)
|
|
||||||
{
|
|
||||||
BYTE sig = (jmp) ? 0xe9 : 0xe8;
|
|
||||||
for (DWORD i = 0x1000; i < size - 4; i++)
|
|
||||||
if (sig == *(BYTE *)(pt + i)) {
|
|
||||||
DWORD t = *(DWORD *)(pt + i + 1);
|
|
||||||
if(fun == pt + i + 5 + t)
|
|
||||||
//OutputDWORD(pt + i);
|
|
||||||
return pt + i;
|
|
||||||
else
|
|
||||||
i += 5;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Util::FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp)
|
|
||||||
{
|
|
||||||
WORD sig = jmp ? 0x25ff : 0x15ff;
|
|
||||||
for (DWORD i = 0x1000; i < size - 4; i++)
|
|
||||||
if (sig == *(WORD *)(pt + i)) {
|
|
||||||
DWORD t = *(DWORD *)(pt + i + 2);
|
|
||||||
if (t > pt && t < pt + size) {
|
|
||||||
if (fun == *(DWORD *)t)
|
|
||||||
return pt + i;
|
|
||||||
else
|
|
||||||
i += 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Util::FindCallBoth(DWORD fun, DWORD size, DWORD pt)
|
|
||||||
{
|
|
||||||
for (DWORD i = 0x1000; i < size - 4; i++) {
|
|
||||||
if (*(BYTE *)(pt + i) == 0xe8) {
|
|
||||||
DWORD t = *(DWORD *)(pt + i + 1) + pt + i + 5;
|
|
||||||
if (t == fun)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
if (*(WORD *)(pt + i) == 0x15ff) {
|
|
||||||
DWORD t = *(DWORD *)(pt + i + 2);
|
|
||||||
if (t >= pt && t <= pt + size - 4) {
|
|
||||||
if (*(DWORD *)t == fun)
|
|
||||||
return i;
|
|
||||||
else
|
|
||||||
i += 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Util::FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig)
|
|
||||||
{
|
|
||||||
//WCHAR str[0x40];
|
|
||||||
enum { reverse_length = 0x800 };
|
|
||||||
DWORD mask = SigMask(sig);
|
|
||||||
for (DWORD i = 0x1000; i < size - 4; i++)
|
|
||||||
if (*(WORD *)(pt + i) == 0x15ff) {
|
|
||||||
DWORD t = *(DWORD *)(pt + i + 2);
|
|
||||||
if (t >= pt && t <= pt + size - 4) {
|
|
||||||
if (*(DWORD *)t == fun)
|
|
||||||
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
|
|
||||||
//OutputConsole(str);
|
|
||||||
for (DWORD j = i ; j > i - reverse_length; j--)
|
|
||||||
if ((*(DWORD *)(pt + j) & mask) == sig) // Fun entry 1.
|
|
||||||
//swprintf(str,L"Entry: 0x%.8X",pt + j);
|
|
||||||
//OutputConsole(str);
|
|
||||||
return pt + j;
|
|
||||||
|
|
||||||
} else
|
|
||||||
i += 6;
|
|
||||||
}
|
|
||||||
//OutputConsole(L"Find call and entry failed.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Util::FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig)
|
|
||||||
{
|
|
||||||
//WCHAR str[0x40];
|
|
||||||
enum { reverse_length = 0x800 };
|
|
||||||
if (DWORD i = FindCallOrJmpRel(fun, size, pt, false)) {
|
|
||||||
DWORD mask = SigMask(sig);
|
|
||||||
for (DWORD j = i; j > i - reverse_length; j--)
|
|
||||||
if (((*(DWORD *)j) & mask) == sig) //Fun entry 1.
|
|
||||||
//swprintf(str,L"Entry: 0x%.8X",j);
|
|
||||||
//OutputConsole(str);
|
|
||||||
return j;
|
|
||||||
//OutputConsole(L"Find call and entry failed.");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
DWORD Util::FindEntryAligned(DWORD start, DWORD back_range)
|
|
||||||
{
|
|
||||||
start &= ~0xf;
|
|
||||||
for (DWORD i = start, j = start - back_range; i > j; i-=0x10) {
|
|
||||||
DWORD k = *(DWORD *)(i-4);
|
|
||||||
if (k == 0xcccccccc
|
|
||||||
|| k == 0x90909090
|
|
||||||
|| k == 0xccccccc3
|
|
||||||
|| k == 0x909090c3
|
|
||||||
)
|
|
||||||
return i;
|
|
||||||
DWORD t = k & 0xff0000ff;
|
|
||||||
if (t == 0xcc0000c2 || t == 0x900000c2)
|
|
||||||
return i;
|
|
||||||
k >>= 8;
|
|
||||||
if (k == 0xccccc3 || k == 0x9090c3)
|
|
||||||
return i;
|
|
||||||
t = k & 0xff;
|
|
||||||
if (t == 0xc2)
|
|
||||||
return i;
|
|
||||||
k >>= 8;
|
|
||||||
if (k == 0xccc3 || k == 0x90c3)
|
|
||||||
return i;
|
|
||||||
k >>= 8;
|
|
||||||
if (k == 0xc3)
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD Util::FindImportEntry(DWORD hModule, DWORD fun)
|
|
||||||
{
|
|
||||||
IMAGE_DOS_HEADER *DosHdr;
|
|
||||||
IMAGE_NT_HEADERS *NtHdr;
|
|
||||||
DWORD IAT, end, pt, addr;
|
|
||||||
DosHdr = (IMAGE_DOS_HEADER *)hModule;
|
|
||||||
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
|
|
||||||
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
|
|
||||||
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
|
|
||||||
IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
|
|
||||||
end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
|
|
||||||
IAT += hModule;
|
|
||||||
end += IAT;
|
|
||||||
for (pt = IAT; pt < end; pt += 4) {
|
|
||||||
addr = *(DWORD *)pt;
|
|
||||||
if (addr == fun)
|
|
||||||
return pt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search string in rsrc section. This section usually contains version and copyright info.
|
|
||||||
bool Util::SearchResourceString(LPCWSTR str)
|
|
||||||
{
|
|
||||||
DWORD hModule = Util::GetModuleBase();
|
|
||||||
IMAGE_DOS_HEADER *DosHdr;
|
|
||||||
IMAGE_NT_HEADERS *NtHdr;
|
|
||||||
DosHdr = (IMAGE_DOS_HEADER *)hModule;
|
|
||||||
DWORD rsrc, size;
|
|
||||||
//__asm int 3
|
|
||||||
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
|
|
||||||
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
|
|
||||||
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
|
|
||||||
rsrc = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
|
|
||||||
if (rsrc) {
|
|
||||||
rsrc += hModule;
|
|
||||||
if (IthGetMemoryRange((LPVOID)rsrc, &rsrc ,&size) &&
|
|
||||||
SearchPattern(rsrc, size - 4, str, wcslen(str) << 1))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 4/15/2014: Copied from GetModuleBase in ITH CLI, for debugging purpose
|
|
||||||
DWORD Util::FindModuleBase(DWORD hash)
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,fs:[0x30]
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov esi,[eax+0x14]
|
|
||||||
mov edi,_wcslwr
|
|
||||||
listfind:
|
|
||||||
mov edx,[esi+0x28]
|
|
||||||
test edx,edx
|
|
||||||
jz notfound
|
|
||||||
push edx
|
|
||||||
call edi
|
|
||||||
pop edx
|
|
||||||
xor eax,eax
|
|
||||||
calc:
|
|
||||||
movzx ecx, word ptr [edx]
|
|
||||||
test cl,cl
|
|
||||||
jz fin
|
|
||||||
ror eax,7
|
|
||||||
add eax,ecx
|
|
||||||
add edx,2
|
|
||||||
jmp calc
|
|
||||||
fin:
|
|
||||||
cmp eax,[hash]
|
|
||||||
je found
|
|
||||||
mov esi,[esi]
|
|
||||||
jmp listfind
|
|
||||||
notfound:
|
|
||||||
xor eax,eax
|
|
||||||
jmp termin
|
|
||||||
found:
|
|
||||||
mov eax,[esi+0x10]
|
|
||||||
termin:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,76 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// util/util.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
namespace Util {
|
|
||||||
|
|
||||||
DWORD GetCodeRange(DWORD hModule,DWORD *low, DWORD *high);
|
|
||||||
DWORD FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig);
|
|
||||||
DWORD FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp);
|
|
||||||
DWORD FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp);
|
|
||||||
DWORD FindCallBoth(DWORD fun, DWORD size, DWORD pt);
|
|
||||||
DWORD FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig);
|
|
||||||
DWORD FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig);
|
|
||||||
DWORD FindEntryAligned(DWORD start, DWORD back_range);
|
|
||||||
DWORD FindImportEntry(DWORD hModule, DWORD fun);
|
|
||||||
|
|
||||||
// jichi 4/15/2014: Copied from ITH CLI, for debugging purpose
|
|
||||||
DWORD FindModuleBase(DWORD hash);
|
|
||||||
|
|
||||||
bool SearchResourceString(LPCWSTR str);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param name process name without path deliminator
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param path with process name and directy name
|
|
||||||
*/
|
|
||||||
inline void GetProcessPath(wchar_t *path)
|
|
||||||
{
|
|
||||||
//assert(path);
|
|
||||||
PLDR_DATA_TABLE_ENTRY it;
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,fs:[0x30]
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov it,eax
|
|
||||||
}
|
|
||||||
::wcscpy(path, it->FullDllName.Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return HANDLE module handle
|
|
||||||
*/
|
|
||||||
inline DWORD GetModuleBase()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,fs:[0x18]
|
|
||||||
mov eax,[eax+0x30]
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov eax,[eax+0x18]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Util
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,887 +0,0 @@
|
|||||||
// texthook.cc
|
|
||||||
// 8/24/2013 jichi
|
|
||||||
// Branch: ITH_DLL/texthook.cpp, rev 128
|
|
||||||
// 8/24/2013 TODO: Clean up this file
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
|
||||||
# pragma warning (disable:4018) // C4018: sign/unsigned mismatch
|
|
||||||
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
#include "cli.h"
|
|
||||||
#include "engine/match.h"
|
|
||||||
#include "ith/common/except.h"
|
|
||||||
//#include "ith/common/growl.h"
|
|
||||||
#include "ith/sys/sys.h"
|
|
||||||
#include "disasm/disasm.h"
|
|
||||||
//#include "winseh/winseh.h"
|
|
||||||
|
|
||||||
//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this ><
|
|
||||||
|
|
||||||
// - Global variables -
|
|
||||||
|
|
||||||
// 10/14/2014 jichi: disable GDI hooks
|
|
||||||
static bool gdi_hook_disabled_ = false;
|
|
||||||
void DisableGDIHooks()
|
|
||||||
{ ::gdi_hook_disabled_ = true; }
|
|
||||||
|
|
||||||
static bool IsGDIFunction(LPCVOID addr)
|
|
||||||
{
|
|
||||||
static LPVOID funcs[] = { HOOK_GDI_FUNCTION_LIST };
|
|
||||||
for (size_t i = 0; i < sizeof(funcs)/sizeof(*funcs); i++)
|
|
||||||
if (addr == funcs[i])
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//FilterRange filter[8];
|
|
||||||
|
|
||||||
DWORD flag,
|
|
||||||
enter_count;
|
|
||||||
|
|
||||||
TextHook *hookman,
|
|
||||||
*current_available;
|
|
||||||
|
|
||||||
// - Unnamed helpers -
|
|
||||||
|
|
||||||
namespace { // unnamed
|
|
||||||
//provide const time hook entry.
|
|
||||||
int userhook_count;
|
|
||||||
|
|
||||||
#if 0 // 3/6/2015 jichi: this hook is not used and hence disabled
|
|
||||||
const byte common_hook2[] = {
|
|
||||||
0x89, 0x3c,0xe4, // mov [esp],edi
|
|
||||||
0x60, // pushad
|
|
||||||
0x9c, // pushfd
|
|
||||||
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
|
|
||||||
0x8b,0x32, // mov esi,[edx] ; return address
|
|
||||||
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
|
|
||||||
0xe8, 0,0,0,0, // call @hook
|
|
||||||
0x9d, // popfd
|
|
||||||
0x61, // popad
|
|
||||||
0x5f, // pop edi ; skip return address on stack
|
|
||||||
}; //...
|
|
||||||
#endif // 0
|
|
||||||
|
|
||||||
const BYTE common_hook[] = {
|
|
||||||
0x9c, // pushfd
|
|
||||||
0x60, // pushad
|
|
||||||
0x9c, // pushfd
|
|
||||||
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
|
|
||||||
0x8b,0x32, // mov esi,[edx] ; return address
|
|
||||||
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TexHhook
|
|
||||||
0xe8, 0,0,0,0, // call @hook
|
|
||||||
0x9d, // popfd
|
|
||||||
0x61, // popad
|
|
||||||
0x9d // popfd
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* jichi 7/19/2014
|
|
||||||
*
|
|
||||||
* @param original_addr
|
|
||||||
* @param new_addr
|
|
||||||
* @param hook_len
|
|
||||||
* @param original_len
|
|
||||||
* @return -1 if failed, else 0 if ?, else ?
|
|
||||||
*/
|
|
||||||
int MapInstruction(DWORD original_addr, DWORD new_addr, BYTE &hook_len, BYTE &original_len)
|
|
||||||
{
|
|
||||||
int flag = 0;
|
|
||||||
DWORD l = 0;
|
|
||||||
const BYTE *r = (const BYTE *)original_addr; // 7/19/2014 jichi: original address is not modified
|
|
||||||
BYTE *c = (BYTE *)new_addr; // 7/19/2014 jichi: but new address might be modified
|
|
||||||
while((r - (BYTE *) original_addr) < 5) {
|
|
||||||
l = ::disasm(r);
|
|
||||||
if (l == 0) {
|
|
||||||
ConsoleOutput("vnrcli:MapInstruction: FAILED: failed to disasm");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
::memcpy(c, r, l);
|
|
||||||
if (*r >= 0x70 && *r < 0x80) {
|
|
||||||
c[0] = 0xf;
|
|
||||||
c[1] = *r + 0x10;
|
|
||||||
c += 6;
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,r
|
|
||||||
add eax,2
|
|
||||||
movsx edx,byte ptr [eax-1]
|
|
||||||
add edx,eax
|
|
||||||
mov eax,c
|
|
||||||
sub edx,eax
|
|
||||||
mov [eax-4],edx
|
|
||||||
}
|
|
||||||
} else if (*r == 0xeb) {
|
|
||||||
c[0] = 0xe9;
|
|
||||||
c += 5;
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,r
|
|
||||||
add eax,2
|
|
||||||
movsx edx,[eax-1]
|
|
||||||
add edx,eax
|
|
||||||
mov eax,c
|
|
||||||
sub edx,eax
|
|
||||||
mov [eax-4],edx
|
|
||||||
}
|
|
||||||
if (r - (BYTE *)original_addr < 5 - l) {
|
|
||||||
ConsoleOutput("vnrcli:MapInstruction: not safe to move instruction right after short jmp");
|
|
||||||
return -1; // Not safe to move instruction right after short jmp.
|
|
||||||
} else
|
|
||||||
flag = 1;
|
|
||||||
} else if (*r == 0xe8 || *r == 0xe9) {
|
|
||||||
c[0]=*r;
|
|
||||||
c += 5;
|
|
||||||
flag = (*r == 0xe9);
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,r
|
|
||||||
add eax,5
|
|
||||||
mov edx,[eax-4]
|
|
||||||
add edx,eax
|
|
||||||
mov eax,c
|
|
||||||
sub edx,eax
|
|
||||||
mov [eax-4],edx
|
|
||||||
}
|
|
||||||
} else if (*r == 0xf && (*(r + 1) >> 4) == 0x8) {
|
|
||||||
c += 6;
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,r
|
|
||||||
mov edx,dword ptr [eax+2]
|
|
||||||
add eax,6
|
|
||||||
add eax,edx
|
|
||||||
mov edx,c
|
|
||||||
sub eax,edx
|
|
||||||
mov [edx-4],eax
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
c += l;
|
|
||||||
r += l;
|
|
||||||
}
|
|
||||||
original_len = r - (BYTE *)original_addr;
|
|
||||||
hook_len = c - (BYTE *)new_addr;
|
|
||||||
return flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
//copy original instruction
|
|
||||||
//jmp back
|
|
||||||
DWORD GetModuleBase(DWORD hash)
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,fs:[0x30]
|
|
||||||
mov eax,[eax+0xc]
|
|
||||||
mov esi,[eax+0x14]
|
|
||||||
mov edi,_wcslwr
|
|
||||||
listfind:
|
|
||||||
mov edx,[esi+0x28]
|
|
||||||
test edx,edx
|
|
||||||
jz notfound
|
|
||||||
push edx
|
|
||||||
call edi
|
|
||||||
pop edx
|
|
||||||
xor eax,eax
|
|
||||||
calc:
|
|
||||||
movzx ecx, word ptr [edx]
|
|
||||||
test cl,cl
|
|
||||||
jz fin
|
|
||||||
ror eax,7
|
|
||||||
add eax,ecx
|
|
||||||
add edx,2
|
|
||||||
jmp calc
|
|
||||||
fin:
|
|
||||||
cmp eax,[hash]
|
|
||||||
je found
|
|
||||||
mov esi,[esi]
|
|
||||||
jmp listfind
|
|
||||||
notfound:
|
|
||||||
xor eax,eax
|
|
||||||
jmp termin
|
|
||||||
found:
|
|
||||||
mov eax,[esi+0x10]
|
|
||||||
termin:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD GetModuleBase()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax, fs:[0x18]
|
|
||||||
mov eax, [eax + 0x30]
|
|
||||||
mov eax, [eax + 0xc]
|
|
||||||
mov eax, [eax + 0xc]
|
|
||||||
mov eax, [eax + 0x18]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//void NotifyHookInsert()
|
|
||||||
//{
|
|
||||||
// if (live)
|
|
||||||
// {
|
|
||||||
// BYTE buffer[0x10];
|
|
||||||
// *(DWORD*)buffer=-1;
|
|
||||||
// *(DWORD*)(buffer+4)=1;
|
|
||||||
// IO_STATUS_BLOCK ios;
|
|
||||||
// NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
__declspec(naked) void SafeExit() // Return to eax
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov [esp+0x24], eax
|
|
||||||
popfd
|
|
||||||
popad
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// jichi 12/2/2013: This function mostly return 0.
|
|
||||||
// But sometimes return the hook address from TextHook::Send
|
|
||||||
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
|
|
||||||
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
|
|
||||||
{
|
|
||||||
//with_seh(hook->Send(dwDataBase, dwRetn));
|
|
||||||
seh_push_(seh_exit, 0, eax, ebx) // jichi 12/13/2013: only eax and ebx are available. ecx and edx are used.
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push esi
|
|
||||||
push edx
|
|
||||||
call TextHook::UnsafeSend
|
|
||||||
test eax, eax
|
|
||||||
jz seh_exit // label in seh_pop
|
|
||||||
mov ecx, SafeExit
|
|
||||||
mov [esp + 8], ecx // jichi 12/13/2013: change exit point if Send returns non-zero, not + 8 beause two elements has been pused
|
|
||||||
}
|
|
||||||
seh_pop_(seh_exit)
|
|
||||||
__asm retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
|
|
||||||
}
|
|
||||||
#endif // 0
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
|
|
||||||
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
|
|
||||||
{
|
|
||||||
// jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push esi
|
|
||||||
push edx
|
|
||||||
call TextHook::Send
|
|
||||||
test eax, eax
|
|
||||||
jz ok // label in seh_pop
|
|
||||||
mov ecx, SafeExit
|
|
||||||
mov [esp], ecx // jichi 12/13/2013: change exit point if Send returns non-zero
|
|
||||||
ok:
|
|
||||||
retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // 1
|
|
||||||
|
|
||||||
// jichi 12/13/2013: return if the retn address is within the filter dlls
|
|
||||||
inline bool HookFilter(DWORD retn)
|
|
||||||
{
|
|
||||||
for (DWORD i = 0; ::filter[i].lower; i++)
|
|
||||||
if (retn > ::filter[i].lower && retn < ::filter[i].upper)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // unnamed namespace
|
|
||||||
|
|
||||||
// - TextHook methods -
|
|
||||||
|
|
||||||
// jichi 12/2/2013: This function mostly return 0.
|
|
||||||
// It return the hook address only for auxiliary case.
|
|
||||||
// However, because no known hooks are auxiliary, this function always return 0.
|
|
||||||
//
|
|
||||||
// jichi 5/11/2014:
|
|
||||||
// - dwDataBase: the stack address
|
|
||||||
// - dwRetn: the return address of the hook
|
|
||||||
DWORD TextHook::Send(DWORD dwDataBase, DWORD dwRetn)
|
|
||||||
{
|
|
||||||
DWORD ret = 0;
|
|
||||||
//char b[0x100];
|
|
||||||
//::wcstombs(b, hook_name, 0x100);
|
|
||||||
//ConsoleOutput(b);
|
|
||||||
ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
|
|
||||||
{
|
|
||||||
enum { SMALL_BUFF_SIZE = 0x80 };
|
|
||||||
enum { MAX_DATA_SIZE = 0x10000 }; // jichi 12/25/2013: The same as the original ITH
|
|
||||||
DWORD dwCount,
|
|
||||||
dwAddr,
|
|
||||||
dwDataIn,
|
|
||||||
dwSplit;
|
|
||||||
BYTE *pbData,
|
|
||||||
pbSmallBuff[SMALL_BUFF_SIZE];
|
|
||||||
DWORD dwType = hp.type;
|
|
||||||
if (!live)
|
|
||||||
return 0;
|
|
||||||
if ((dwType & NO_CONTEXT) == 0 && HookFilter(dwRetn))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// jichi 10/24/2014: Skip GDI functions
|
|
||||||
if (::gdi_hook_disabled_ && ::IsGDIFunction((LPCVOID)hp.addr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
dwAddr = hp.addr;
|
|
||||||
|
|
||||||
/** jichi 12/24/2014
|
|
||||||
* @param addr function address
|
|
||||||
* @param frame real address of the function, supposed to be the same as addr
|
|
||||||
* @param stack address of current stack - 4
|
|
||||||
* @return If success, which is reverted
|
|
||||||
*/
|
|
||||||
if (::trigger)
|
|
||||||
::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
|
|
||||||
// jichi 10/21/2014: Directly invoke engine functions.
|
|
||||||
//if (trigger) {
|
|
||||||
// if (InsertDynamicHook)
|
|
||||||
// trigger = InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
|
|
||||||
// else
|
|
||||||
// trigger = 0;
|
|
||||||
//}
|
|
||||||
#if 0 // diasble HOOK_AUXILIARY
|
|
||||||
// jichi 12/13/2013: None of known hooks are auxiliary
|
|
||||||
if (dwType & HOOK_AUXILIARY) {
|
|
||||||
//Clean hook when dynamic hook finished.
|
|
||||||
//AUX hook is only used for a foothold of dynamic hook.
|
|
||||||
if (!trigger) {
|
|
||||||
ClearHook();
|
|
||||||
// jichi 12/13/2013: This is the only place where this function could return non-zero value
|
|
||||||
// However, I non of the known hooks are auxiliary
|
|
||||||
return dwAddr;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif // 0
|
|
||||||
// jichi 10/24/2014: generic hook function
|
|
||||||
if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp))
|
|
||||||
hp.hook_fun = nullptr;
|
|
||||||
|
|
||||||
if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// jichi 2/2/2015: Send multiple texts
|
|
||||||
for (BYTE textIndex = 0; textIndex <= hp.extra_text_count; textIndex++) {
|
|
||||||
dwCount = 0;
|
|
||||||
dwSplit = 0;
|
|
||||||
dwDataIn = *(DWORD *)(dwDataBase + hp.off); // default value
|
|
||||||
|
|
||||||
//if (dwType & EXTERN_HOOK) {
|
|
||||||
if (hp.text_fun) { // jichi 10/24/2014: remove EXTERN_HOOK
|
|
||||||
//DataFun fun=(DataFun)hp.text_fun;
|
|
||||||
//auto fun = hp.text_fun;
|
|
||||||
hp.text_fun(dwDataBase, &hp, textIndex, &dwDataIn, &dwSplit, &dwCount);
|
|
||||||
//if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
|
|
||||||
// return 0;
|
|
||||||
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
|
|
||||||
dwSplit -= ::processStartAddress;
|
|
||||||
} else {
|
|
||||||
if (dwDataIn == 0)
|
|
||||||
return 0;
|
|
||||||
if (dwType & FIXING_SPLIT)
|
|
||||||
dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating
|
|
||||||
else if (dwType & USING_SPLIT) {
|
|
||||||
dwSplit = *(DWORD *)(dwDataBase + hp.split);
|
|
||||||
if (dwType & SPLIT_INDIRECT) {
|
|
||||||
if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_ind), 0, 0))
|
|
||||||
dwSplit = *(DWORD *)(dwSplit + hp.split_ind);
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
|
|
||||||
dwSplit -= ::processStartAddress;
|
|
||||||
}
|
|
||||||
if (dwType & DATA_INDIRECT) {
|
|
||||||
if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.ind), 0, 0))
|
|
||||||
dwDataIn = *(DWORD *)(dwDataIn + hp.ind);
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
//if (dwType & PRINT_DWORD) {
|
|
||||||
// swprintf((WCHAR *)(pbSmallBuff + HEADER_SIZE), L"%.8X ", dwDataIn);
|
|
||||||
// dwDataIn = (DWORD)pbSmallBuff + HEADER_SIZE;
|
|
||||||
//}
|
|
||||||
dwCount = GetLength(dwDataBase, dwDataIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 12/25/2013: validate data size
|
|
||||||
if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
size_t sz = dwCount + HEADER_SIZE;
|
|
||||||
if (sz >= SMALL_BUFF_SIZE)
|
|
||||||
pbData = new BYTE[sz];
|
|
||||||
//ITH_MEMSET_HEAP(pbData, 0, sz * sizeof(BYTE)); // jichi 9/26/2013: zero memory
|
|
||||||
else
|
|
||||||
pbData = pbSmallBuff;
|
|
||||||
|
|
||||||
if (hp.length_offset == 1) {
|
|
||||||
if (dwType & STRING_LAST_CHAR) {
|
|
||||||
LPWSTR ts = (LPWSTR)dwDataIn;
|
|
||||||
dwDataIn = ts[::wcslen(ts) -1];
|
|
||||||
}
|
|
||||||
dwDataIn &= 0xffff;
|
|
||||||
if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8))
|
|
||||||
dwDataIn = _byteswap_ushort(dwDataIn & 0xffff);
|
|
||||||
if (dwCount == 1)
|
|
||||||
dwDataIn &= 0xff;
|
|
||||||
*(WORD *)(pbData + HEADER_SIZE) = dwDataIn & 0xffff;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
::memcpy(pbData + HEADER_SIZE, (void *)dwDataIn, dwCount);
|
|
||||||
|
|
||||||
// jichi 10/14/2014: Add filter function
|
|
||||||
if (hp.filter_fun && !hp.filter_fun(pbData + HEADER_SIZE, &dwCount, &hp, textIndex) || dwCount <= 0) {
|
|
||||||
if (pbData != pbSmallBuff)
|
|
||||||
delete[] pbData;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*(DWORD *)pbData = dwAddr;
|
|
||||||
if (dwType & (NO_CONTEXT|FIXING_SPLIT))
|
|
||||||
dwRetn = 0;
|
|
||||||
else if (dwRetn && (dwType & RELATIVE_SPLIT))
|
|
||||||
dwRetn -= ::processStartAddress;
|
|
||||||
|
|
||||||
*((DWORD *)pbData + 1) = dwRetn;
|
|
||||||
*((DWORD *)pbData + 2) = dwSplit;
|
|
||||||
if (dwCount) {
|
|
||||||
IO_STATUS_BLOCK ios = {};
|
|
||||||
|
|
||||||
IthCoolDown(); // jichi 9/28/2013: cool down to prevent parallelization in wine
|
|
||||||
//CliLockPipe();
|
|
||||||
if (STATUS_PENDING == NtWriteFile(hPipe, 0, 0, 0, &ios, pbData, dwCount + HEADER_SIZE, 0, 0)) {
|
|
||||||
NtWaitForSingleObject(hPipe, 0, 0);
|
|
||||||
NtFlushBuffersFile(hPipe, &ios);
|
|
||||||
}
|
|
||||||
//CliUnlockPipe();
|
|
||||||
}
|
|
||||||
if (pbData != pbSmallBuff)
|
|
||||||
delete[] pbData;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::InsertHook()
|
|
||||||
{
|
|
||||||
//ConsoleOutput("vnrcli:InsertHook: enter");
|
|
||||||
NtWaitForSingleObject(hmMutex, 0, 0);
|
|
||||||
int ok = InsertHookCode();
|
|
||||||
IthReleaseMutex(hmMutex);
|
|
||||||
if (hp.type & HOOK_ADDITIONAL) {
|
|
||||||
NotifyHookInsert(hp.addr);
|
|
||||||
//ConsoleOutput(hook_name);
|
|
||||||
//RegisterHookName(hook_name,hp.addr);
|
|
||||||
}
|
|
||||||
//ConsoleOutput("vnrcli:InsertHook: leave");
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::InsertHookCode()
|
|
||||||
{
|
|
||||||
enum : int { yes = 0, no = 1 };
|
|
||||||
DWORD ret = no;
|
|
||||||
// jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7
|
|
||||||
ITH_WITH_SEH(ret = UnsafeInsertHookCode());
|
|
||||||
//if (ret == no)
|
|
||||||
// ITH_WARN(L"Failed to insert hook");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::UnsafeInsertHookCode()
|
|
||||||
{
|
|
||||||
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
|
|
||||||
enum : int { yes = 0, no = 1 };
|
|
||||||
// MODULE_OFFSET is set, but there's no module address
|
|
||||||
// this means that this is an absolute address found on Windows 2000/XP
|
|
||||||
// we make the address relative to the process base
|
|
||||||
// we also store the original address in the function field because normally there can not
|
|
||||||
// exist a function address without a module address
|
|
||||||
if (hp.type & MODULE_OFFSET && !hp.module) {
|
|
||||||
DWORD base = GetModuleBase();
|
|
||||||
hp.function = hp.addr;
|
|
||||||
hp.addr -= 0x400000;
|
|
||||||
hp.addr += base;
|
|
||||||
hp.type &= ~MODULE_OFFSET;
|
|
||||||
}
|
|
||||||
else if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
|
|
||||||
if (DWORD base = GetModuleBase(hp.module)) {
|
|
||||||
if (hp.function && (hp.type & FUNCTION_OFFSET)) {
|
|
||||||
base = GetExportAddress(base, hp.function);
|
|
||||||
if (base)
|
|
||||||
hp.addr += base;
|
|
||||||
else {
|
|
||||||
current_hook--;
|
|
||||||
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: function not found in the export table");
|
|
||||||
return no;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
hp.addr += base;
|
|
||||||
}
|
|
||||||
hp.type &= ~(MODULE_OFFSET | FUNCTION_OFFSET);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
current_hook--;
|
|
||||||
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: module not present");
|
|
||||||
return no;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
TextHook *it = hookman;
|
|
||||||
for (int i = 0; (i < current_hook) && it; it++) { // Check if there is a collision.
|
|
||||||
if (it->Address())
|
|
||||||
i++;
|
|
||||||
//it = hookman + i;
|
|
||||||
if (it == this)
|
|
||||||
continue;
|
|
||||||
if (it->Address() <= hp.addr &&
|
|
||||||
it->Address() + it->Length() > hp.addr) {
|
|
||||||
it->ClearHook();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify hp.addr.
|
|
||||||
MEMORY_BASIC_INFORMATION info = {};
|
|
||||||
NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)hp.addr, MemoryBasicInformation, &info, sizeof(info), nullptr);
|
|
||||||
if (info.Type & PAGE_NOACCESS) {
|
|
||||||
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: page no access");
|
|
||||||
return no;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize common routine.
|
|
||||||
memcpy(recover, common_hook, sizeof(common_hook));
|
|
||||||
BYTE *c = (BYTE *)hp.addr,
|
|
||||||
*r = recover;
|
|
||||||
BYTE inst[8]; // jichi 9/27/2013: Why 8? Only 5 bytes will be written using NtWriteVirtualMemory
|
|
||||||
inst[0] = 0xe9; // jichi 9/27/2013: 0xe9 is jump, see: http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov edx,r // r = recover
|
|
||||||
mov eax,this
|
|
||||||
mov [edx+0xa],eax // push TextHook*, resolve to correspond hook.
|
|
||||||
lea eax,[edx+0x13]
|
|
||||||
mov edx,ProcessHook
|
|
||||||
sub edx,eax
|
|
||||||
mov [eax-4],edx // call ProcessHook
|
|
||||||
mov eax,c
|
|
||||||
add eax,5
|
|
||||||
mov edx,r
|
|
||||||
sub edx,eax
|
|
||||||
lea eax,inst+1
|
|
||||||
mov [eax],edx // jichi 12/17/2013: the parameter of jmp is in edx. So, ProcessHook must be naked.
|
|
||||||
}
|
|
||||||
r += sizeof(common_hook);
|
|
||||||
hp.hook_len = 5;
|
|
||||||
//bool jmpflag=false; // jichi 9/28/2013: nto used
|
|
||||||
// Copy original code.
|
|
||||||
switch (MapInstruction(hp.addr, (DWORD)r, hp.hook_len, hp.recover_len)) {
|
|
||||||
case -1:
|
|
||||||
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: failed to map instruction");
|
|
||||||
return no;
|
|
||||||
case 0:
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov ecx,this
|
|
||||||
movzx eax,[ecx]hp.hook_len
|
|
||||||
movzx edx,[ecx]hp.recover_len
|
|
||||||
add edx,[ecx]hp.addr
|
|
||||||
add eax,r
|
|
||||||
add eax,5
|
|
||||||
sub edx,eax
|
|
||||||
mov [eax-5],0xe9 // jichi 9/27/2013: 0xe9 is jump
|
|
||||||
mov [eax-4],edx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// jichi 9/27/2013: Save the original instructions in the memory
|
|
||||||
memcpy(original, (LPVOID)hp.addr, hp.recover_len);
|
|
||||||
//Check if the new hook range conflict with existing ones. Clear older if conflict.
|
|
||||||
{
|
|
||||||
TextHook *it = hookman;
|
|
||||||
for (int i = 0; i < current_hook; it++) {
|
|
||||||
if (it->Address())
|
|
||||||
i++;
|
|
||||||
if (it == this)
|
|
||||||
continue;
|
|
||||||
if (it->Address() >= hp.addr &&
|
|
||||||
it->Address() < hp.hook_len + hp.addr) {
|
|
||||||
it->ClearHook();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Insert hook and flush instruction cache.
|
|
||||||
enum {c8 = 0xcccccccc};
|
|
||||||
DWORD int3[] = {c8, c8};
|
|
||||||
DWORD t = 0x100,
|
|
||||||
old,
|
|
||||||
len;
|
|
||||||
// jichi 9/27/2013: Overwrite the memory with inst
|
|
||||||
// See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Memory%20Management/Virtual%20Memory/NtProtectVirtualMemory.html
|
|
||||||
// See: http://doxygen.reactos.org/d8/d6b/ndk_2mmfuncs_8h_af942709e0c57981d84586e74621912cd.html
|
|
||||||
DWORD addr = hp.addr;
|
|
||||||
NtProtectVirtualMemory(NtCurrentProcess(), (PVOID *)&addr, &t, PAGE_EXECUTE_READWRITE, &old);
|
|
||||||
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr, inst, 5, &t);
|
|
||||||
len = hp.recover_len - 5;
|
|
||||||
if (len)
|
|
||||||
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr + 5, int3, len, &t);
|
|
||||||
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
|
|
||||||
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)::hookman, 0x1000);
|
|
||||||
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: leave: succeed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind,
|
|
||||||
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
|
|
||||||
{
|
|
||||||
NtWaitForSingleObject(hmMutex, 0, 0);
|
|
||||||
hp.addr = (DWORD)addr;
|
|
||||||
hp.off = data;
|
|
||||||
hp.ind = data_ind;
|
|
||||||
hp.split = split_off;
|
|
||||||
hp.split_ind = split_ind;
|
|
||||||
hp.type = type;
|
|
||||||
hp.hook_len = 0;
|
|
||||||
hp.module = 0;
|
|
||||||
hp.length_offset = len_off & 0xffff;
|
|
||||||
current_hook++;
|
|
||||||
if (current_available >= this)
|
|
||||||
for (current_available = this + 1; current_available->Address(); current_available++);
|
|
||||||
IthReleaseMutex(hmMutex);
|
|
||||||
return this - hookman;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::InitHook(const HookParam &h, LPCWSTR name, WORD set_flag)
|
|
||||||
{
|
|
||||||
NtWaitForSingleObject(hmMutex, 0, 0);
|
|
||||||
hp = h;
|
|
||||||
hp.type |= set_flag;
|
|
||||||
if (name && name != hook_name) {
|
|
||||||
SetHookName(name);
|
|
||||||
}
|
|
||||||
current_hook++;
|
|
||||||
current_available = this+1;
|
|
||||||
while (current_available->Address())
|
|
||||||
current_available++;
|
|
||||||
IthReleaseMutex(hmMutex);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::RemoveHook()
|
|
||||||
{
|
|
||||||
enum : int { yes = 1, no = 0 };
|
|
||||||
if (!hp.addr)
|
|
||||||
return no;
|
|
||||||
ConsoleOutput("vnrcli:RemoveHook: enter");
|
|
||||||
const LONGLONG timeout = -50000000; // jichi 9/28/2012: in 100ns, wait at most for 5 seconds
|
|
||||||
NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
|
|
||||||
DWORD l = hp.hook_len;
|
|
||||||
//with_seh({ // jichi 9/17/2013: might crash ><
|
|
||||||
// jichi 12/25/2013: Actually, __try cannot catch such kind of exception
|
|
||||||
ITH_TRY {
|
|
||||||
NtWriteVirtualMemory(NtCurrentProcess(), (LPVOID)hp.addr, original, hp.recover_len, &l);
|
|
||||||
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
|
|
||||||
} ITH_EXCEPT {}
|
|
||||||
//});
|
|
||||||
hp.hook_len = 0;
|
|
||||||
IthReleaseMutex(hmMutex);
|
|
||||||
ConsoleOutput("vnrcli:RemoveHook: leave");
|
|
||||||
return yes;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::ClearHook()
|
|
||||||
{
|
|
||||||
NtWaitForSingleObject(hmMutex, 0, 0);
|
|
||||||
int err = RemoveHook();
|
|
||||||
if (hook_name) {
|
|
||||||
delete[] hook_name;
|
|
||||||
hook_name = nullptr;
|
|
||||||
}
|
|
||||||
memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
|
|
||||||
//if (current_available>this)
|
|
||||||
// current_available = this;
|
|
||||||
current_hook--;
|
|
||||||
IthReleaseMutex(hmMutex);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::ModifyHook(const HookParam &hp)
|
|
||||||
{
|
|
||||||
//WCHAR name[0x40];
|
|
||||||
DWORD len = 0;
|
|
||||||
if (hook_name)
|
|
||||||
len = wcslen(hook_name);
|
|
||||||
LPWSTR name = 0;
|
|
||||||
if (len) {
|
|
||||||
name = new wchar_t[len + 1];
|
|
||||||
//ITH_MEMSET_HEAP(name, 0, sizeof(wchar_t) * (len + 1)); // jichi 9/26/2013: zero memory
|
|
||||||
name[len] = 0;
|
|
||||||
wcscpy(name, hook_name);
|
|
||||||
}
|
|
||||||
ClearHook();
|
|
||||||
InitHook(hp,name);
|
|
||||||
InsertHook();
|
|
||||||
if (name)
|
|
||||||
delete[] name;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::RecoverHook()
|
|
||||||
{
|
|
||||||
if (hp.addr) {
|
|
||||||
// jichi 9/28/2013: Only enable TextOutA to debug Cross Channel
|
|
||||||
//if (hp.addr == (DWORD)TextOutA)
|
|
||||||
InsertHook();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::SetHookName(LPCWSTR name)
|
|
||||||
{
|
|
||||||
name_length = wcslen(name) + 1;
|
|
||||||
if (hook_name)
|
|
||||||
delete[] hook_name;
|
|
||||||
hook_name = new wchar_t[name_length];
|
|
||||||
//ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory
|
|
||||||
hook_name[name_length - 1] = 0;
|
|
||||||
wcscpy(hook_name, name);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextHook::GetLength(DWORD base, DWORD in)
|
|
||||||
{
|
|
||||||
if (base == 0)
|
|
||||||
return 0;
|
|
||||||
int len;
|
|
||||||
switch (hp.length_offset) {
|
|
||||||
default: // jichi 12/26/2013: I should not put this default branch to the end
|
|
||||||
len = *((int *)base + hp.length_offset);
|
|
||||||
if (len >= 0) {
|
|
||||||
if (hp.type & USING_UNICODE)
|
|
||||||
len <<= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (len != -1)
|
|
||||||
break;
|
|
||||||
//len == -1 then continue to case 0.
|
|
||||||
case 0:
|
|
||||||
if (hp.type & USING_UNICODE)
|
|
||||||
len = wcslen((const wchar_t *)in) << 1;
|
|
||||||
else
|
|
||||||
len = strlen((const char *)in);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if (hp.type & USING_UNICODE)
|
|
||||||
len = 2;
|
|
||||||
else {
|
|
||||||
if (hp.type & BIG_ENDIAN)
|
|
||||||
in >>= 8;
|
|
||||||
len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// jichi 12/25/2013: This function originally return -1 if failed
|
|
||||||
//return len;
|
|
||||||
return max(0, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
||||||
|
|
||||||
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
|
|
||||||
|
|
||||||
/*
|
|
||||||
DWORD recv_esp, recv_addr;
|
|
||||||
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
|
|
||||||
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
|
|
||||||
{
|
|
||||||
//WCHAR str[0x40],
|
|
||||||
// name[0x100];
|
|
||||||
//ConsoleOutput(L"Exception raised during hook processing.");
|
|
||||||
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
|
|
||||||
//ConsoleOutput(str);
|
|
||||||
//MEMORY_BASIC_INFORMATION info;
|
|
||||||
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
|
|
||||||
// MemoryBasicInformation,&info,sizeof(info),0)) &&
|
|
||||||
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
|
|
||||||
// MemorySectionName,name,0x200,0))) {
|
|
||||||
// swprintf(str, L"Exception offset: 0x%.8X:%s",
|
|
||||||
// ContextRecord->Eip-(DWORD)info.AllocationBase,
|
|
||||||
// wcsrchr(name,L'\\')+1);
|
|
||||||
// ConsoleOutput(str);
|
|
||||||
//}
|
|
||||||
ContextRecord->Esp = recv_esp;
|
|
||||||
ContextRecord->Eip = recv_addr;
|
|
||||||
return ExceptionContinueExecution;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
|
|
||||||
|
|
||||||
DWORD recv_esp, recv_addr;
|
|
||||||
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
|
|
||||||
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
|
|
||||||
{
|
|
||||||
//WCHAR str[0x40],
|
|
||||||
// name[0x100];
|
|
||||||
//ConsoleOutput(L"Exception raised during hook processing.");
|
|
||||||
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
|
|
||||||
//ConsoleOutput(str);
|
|
||||||
//MEMORY_BASIC_INFORMATION info;
|
|
||||||
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
|
|
||||||
// MemoryBasicInformation,&info,sizeof(info),0)) &&
|
|
||||||
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
|
|
||||||
// MemorySectionName,name,0x200,0))) {
|
|
||||||
// swprintf(str, L"Exception offset: 0x%.8X:%s",
|
|
||||||
// ContextRecord->Eip-(DWORD)info.AllocationBase,
|
|
||||||
// wcsrchr(name,L'\\')+1);
|
|
||||||
// ConsoleOutput(str);
|
|
||||||
//}
|
|
||||||
ContextRecord->Esp = recv_esp;
|
|
||||||
ContextRecord->Eip = recv_addr;
|
|
||||||
return ExceptionContinueExecution;
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
|
|
||||||
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,seh_recover
|
|
||||||
mov recv_addr,eax
|
|
||||||
push ExceptHandler
|
|
||||||
push fs:[0]
|
|
||||||
mov recv_esp,esp
|
|
||||||
mov fs:[0],esp
|
|
||||||
push esi
|
|
||||||
push edx
|
|
||||||
call TextHook::Send
|
|
||||||
test eax,eax
|
|
||||||
jz seh_recover
|
|
||||||
mov ecx,SafeExit
|
|
||||||
mov [esp + 0x8], ecx // change exit point
|
|
||||||
seh_recover:
|
|
||||||
pop dword ptr fs:[0]
|
|
||||||
pop ecx
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,38 +0,0 @@
|
|||||||
#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
|
|
@ -1,56 +0,0 @@
|
|||||||
# 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
|
|
@ -1,416 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,346 +0,0 @@
|
|||||||
// 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
|
|
@ -1,590 +0,0 @@
|
|||||||
#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
|
|
@ -1,59 +0,0 @@
|
|||||||
# 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
|
|
@ -1,66 +0,0 @@
|
|||||||
# 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
|
|
||||||
)
|
|
@ -1,46 +0,0 @@
|
|||||||
/* 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];
|
|
||||||
|
|
||||||
};
|
|
@ -1,587 +0,0 @@
|
|||||||
#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
|
|
@ -1,16 +0,0 @@
|
|||||||
#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
|
|
@ -1,945 +0,0 @@
|
|||||||
// 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
|
|
@ -1,144 +0,0 @@
|
|||||||
#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
|
|
@ -1,18 +0,0 @@
|
|||||||
# 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
|
|
@ -1,54 +0,0 @@
|
|||||||
# 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
|
|
@ -1,611 +0,0 @@
|
|||||||
// 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
|
|
@ -1,322 +0,0 @@
|
|||||||
// 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
|
|
@ -1,14 +0,0 @@
|
|||||||
#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
|
|
@ -1,36 +0,0 @@
|
|||||||
#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
|
|
@ -1,46 +0,0 @@
|
|||||||
#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
|
|
@ -1,787 +0,0 @@
|
|||||||
// 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
|
|
@ -1,136 +0,0 @@
|
|||||||
#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
|
|
@ -1,155 +0,0 @@
|
|||||||
#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
|
|
||||||
*/
|
|
@ -1,52 +0,0 @@
|
|||||||
#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
|
|
@ -1,10 +0,0 @@
|
|||||||
# mono.pri
|
|
||||||
# 12/26/2014 jichi
|
|
||||||
|
|
||||||
DEPENDPATH += $$PWD
|
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
$$PWD/funcinfo.h \
|
|
||||||
$$PWD/types.h
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,41 +0,0 @@
|
|||||||
#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
|
|
@ -1,106 +0,0 @@
|
|||||||
#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
|
|
@ -1,9 +0,0 @@
|
|||||||
# ppsspp.pri
|
|
||||||
# 12/26/2014 jichi
|
|
||||||
|
|
||||||
DEPENDPATH += $$PWD
|
|
||||||
|
|
||||||
HEADERS += \
|
|
||||||
$$PWD/funcinfo.h
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,18 +0,0 @@
|
|||||||
# ith.pro
|
|
||||||
# 10/13/2011 jichi
|
|
||||||
|
|
||||||
TEMPLATE = subdirs
|
|
||||||
|
|
||||||
# The order is important!
|
|
||||||
SUBDIRS += \
|
|
||||||
sys \
|
|
||||||
hook hookxp \
|
|
||||||
host
|
|
||||||
|
|
||||||
OTHER_FILES += dllconfig.pri
|
|
||||||
|
|
||||||
include(common/common.pri) # not used
|
|
||||||
include(import/mono/mono.pri) # not used
|
|
||||||
include(import/ppsspp/ppsspp.pri) # not used
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,37 +0,0 @@
|
|||||||
# sys.pro
|
|
||||||
# CONFIG += noqt noeh staticlib
|
|
||||||
|
|
||||||
# CONFIG(noeh) {
|
|
||||||
# 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(vnrsys_src
|
|
||||||
sys.h
|
|
||||||
sys.cc
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(vnrsys STATIC ${vnrsys_src})
|
|
||||||
|
|
||||||
target_compile_options(vnrsys PRIVATE
|
|
||||||
# http://msdn.microsoft.com/library/we6hfdy0.aspx
|
|
||||||
/GR- # disable RTTI
|
|
||||||
# http://msdn.microsoft.com/library/1deeycx5.aspx
|
|
||||||
# /EHs-c- # disable exception handling # CMake bug 15243: http://www.cmake.org/Bug/view.php?id=15243
|
|
||||||
$<$<CONFIG:Release>:>
|
|
||||||
$<$<CONFIG:Debug>:>
|
|
||||||
)
|
|
||||||
|
|
||||||
STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
|
||||||
|
|
||||||
target_link_libraries(vnrsys comctl32.lib)
|
|
||||||
|
|
||||||
target_compile_definitions(vnrsys
|
|
||||||
PRIVATE
|
|
||||||
)
|
|
1514
vnr/ith/sys/sys.cc
1514
vnr/ith/sys/sys.cc
File diff suppressed because it is too large
Load Diff
@ -1,132 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// ith/sys.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// Branch: ITH/IHF_SYS.h, rev 111
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning(disable:4800) // C4800: forcing value to bool
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
#include "ntdll/ntdll.h"
|
|
||||||
|
|
||||||
// jichi 8/24/2013: Why extern "C"? Any specific reason to use C instead of C++ naming?
|
|
||||||
extern "C" {
|
|
||||||
//int disasm(BYTE *opcode0); // jichi 8/15/2013: move disasm to separate file
|
|
||||||
extern WORD *NlsAnsiCodePage;
|
|
||||||
int FillRange(LPCWSTR name,DWORD *lower, DWORD *upper);
|
|
||||||
int MB_WC(char *mb, wchar_t *wc);
|
|
||||||
//int MB_WC_count(char *mb, int mb_length);
|
|
||||||
int WC_MB(wchar_t *wc, char *mb);
|
|
||||||
|
|
||||||
// jichi 10/1/2013: Return 0 if failed. So, it is ambiguous if the search pattern starts at 0
|
|
||||||
DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length); // KMP
|
|
||||||
|
|
||||||
// jichi 2/5/2014: The same as SearchPattern except it uses 0xff to match everything
|
|
||||||
// According to @Andys, 0xff seldom appear in the source code: http://sakuradite.com/topic/124
|
|
||||||
enum : BYTE { SP_ANY = 0xff };
|
|
||||||
#define SP_ANY_2 SP_ANY,SP_ANY
|
|
||||||
#define SP_ANY_3 SP_ANY,SP_ANY,SP_ANY
|
|
||||||
#define SP_ANY_4 SP_ANY,SP_ANY,SP_ANY,SP_ANY
|
|
||||||
DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard=SP_ANY);
|
|
||||||
|
|
||||||
BOOL IthInitSystemService();
|
|
||||||
void IthCloseSystemService();
|
|
||||||
DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size);
|
|
||||||
BOOL IthCheckFile(LPCWSTR file);
|
|
||||||
BOOL IthFindFile(LPCWSTR file);
|
|
||||||
BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size = 0x1000);
|
|
||||||
BOOL IthCheckFileFullPath(LPCWSTR file);
|
|
||||||
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition);
|
|
||||||
HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition);
|
|
||||||
HANDLE IthCreateDirectory(LPCWSTR name);
|
|
||||||
HANDLE IthCreateFileFullPath(LPCWSTR fullpath, DWORD option, DWORD share, DWORD disposition);
|
|
||||||
HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition);
|
|
||||||
HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right);
|
|
||||||
HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset=0, DWORD init_state=0);
|
|
||||||
HANDLE IthOpenEvent(LPCWSTR name);
|
|
||||||
void IthSetEvent(HANDLE hEvent);
|
|
||||||
void IthResetEvent(HANDLE hEvent);
|
|
||||||
HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist=0);
|
|
||||||
HANDLE IthOpenMutex(LPCWSTR name);
|
|
||||||
BOOL IthReleaseMutex(HANDLE hMutex);
|
|
||||||
//DWORD IthWaitForSingleObject(HANDLE hObject, DWORD dwTime);
|
|
||||||
HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc=(HANDLE)-1);
|
|
||||||
DWORD GetExportAddress(DWORD hModule,DWORD hash);
|
|
||||||
void IthSleep(int time); // jichi 9/28/2013: in ms
|
|
||||||
void IthSystemTimeToLocalTime(LARGE_INTEGER *ptime);
|
|
||||||
void FreeThreadStart(HANDLE hProc);
|
|
||||||
void CheckThreadStart();
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
#ifdef ITH_HAS_HEAP
|
|
||||||
extern HANDLE hHeap; // used in ith/common/memory.h
|
|
||||||
#endif // ITH_HAS_HEAP
|
|
||||||
|
|
||||||
extern DWORD current_process_id;
|
|
||||||
extern DWORD debug;
|
|
||||||
extern BYTE LeadByteTable[];
|
|
||||||
extern LPVOID page;
|
|
||||||
extern BYTE launch_time[];
|
|
||||||
|
|
||||||
inline DWORD GetHash(LPSTR str)
|
|
||||||
{
|
|
||||||
DWORD hash = 0;
|
|
||||||
//for (; *str; str++)
|
|
||||||
while (*str)
|
|
||||||
hash = ((hash>>7) | (hash<<25)) + *str++;
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline DWORD GetHash(LPCWSTR str)
|
|
||||||
{
|
|
||||||
DWORD hash = 0;
|
|
||||||
//for (; *str; str++)
|
|
||||||
while (*str)
|
|
||||||
hash = ((hash>>7) | (hash<<25)) + *str++;
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void IthBreak()
|
|
||||||
{ if (debug) __debugbreak(); }
|
|
||||||
|
|
||||||
inline LPCWSTR GetMainModulePath()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax, fs:[0x30]
|
|
||||||
mov eax, [eax + 0xC]
|
|
||||||
mov eax, [eax + 0xC]
|
|
||||||
mov eax, [eax + 0x28]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// jichi 9/28/2013: Add this to lock NtWriteFile in wine
|
|
||||||
class IthMutexLocker
|
|
||||||
{
|
|
||||||
HANDLE m;
|
|
||||||
public:
|
|
||||||
explicit IthMutexLocker(HANDLE mutex) : m(mutex)
|
|
||||||
{ NtWaitForSingleObject(m, 0, 0); }
|
|
||||||
|
|
||||||
~IthMutexLocker() { if (m != INVALID_HANDLE_VALUE) IthReleaseMutex(m); }
|
|
||||||
|
|
||||||
bool locked() const { return m != INVALID_HANDLE_VALUE; }
|
|
||||||
|
|
||||||
void unlock() { if (m != INVALID_HANDLE_VALUE) { IthReleaseMutex(m); m = INVALID_HANDLE_VALUE; } }
|
|
||||||
};
|
|
||||||
|
|
||||||
void IthCoolDown();
|
|
||||||
|
|
||||||
BOOL IthIsWine();
|
|
||||||
BOOL IthIsWindowsXp();
|
|
||||||
//BOOL IthIsWindows8OrGreater(); // not public
|
|
||||||
|
|
||||||
/** Get current dll path.
|
|
||||||
* @param buf
|
|
||||||
* @param len
|
|
||||||
* @return length of the path excluding \0
|
|
||||||
*/
|
|
||||||
size_t IthGetCurrentModulePath(wchar_t *buf, size_t len);
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,13 +0,0 @@
|
|||||||
# sys.pri
|
|
||||||
# 8/21/2013 jichi
|
|
||||||
|
|
||||||
DEFINES += WITH_LIB_ITH_SYS
|
|
||||||
LIBS += -lvnrsys
|
|
||||||
DEPENDPATH += $$PWD
|
|
||||||
HEADERS += $$PWD/sys.h
|
|
||||||
#SOURCES += $$PWD/sys.cc
|
|
||||||
|
|
||||||
#include($$LIBDIR/winddk/winddk.pri)
|
|
||||||
#LIBS += -L$$WDK/lib/wxp/i386
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,49 +0,0 @@
|
|||||||
# sys.pro
|
|
||||||
# 8/21/2013 jichi
|
|
||||||
# Build vnrsys.lib
|
|
||||||
|
|
||||||
CONFIG += noqt noeh staticlib
|
|
||||||
|
|
||||||
include(../../../../config.pri)
|
|
||||||
include($$LIBDIR/ntdll/ntdll.pri)
|
|
||||||
|
|
||||||
#include($$LIBDIR/winddk/winddk.pri)
|
|
||||||
#LIBS += -L$$WDK/lib/wxp/i386
|
|
||||||
|
|
||||||
# jichi 9/22/2013: When ITH is on wine, certain NT functions are replaced
|
|
||||||
#DEFINES += ITH_WINE
|
|
||||||
|
|
||||||
# jichi 9/14/2013: Windows XP's msvnrt does not have except handler
|
|
||||||
DEFINES -= ITH_HAS_SEH
|
|
||||||
|
|
||||||
# jichi 11/24/2013: Disable manual heap
|
|
||||||
DEFINES -= ITH_HAS_HEAP
|
|
||||||
|
|
||||||
## Libraries
|
|
||||||
|
|
||||||
#INCLUDEPATH += $$ITH_HOME/include
|
|
||||||
#INCLUDEPATH += $$WDK7_HOME/inc/ddk
|
|
||||||
|
|
||||||
#LIBS += -lgdi32 -luser32 -lkernel32
|
|
||||||
#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
|
|
||||||
#LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
|
|
||||||
|
|
||||||
#DEFINES += ITH_HAS_CXX
|
|
||||||
|
|
||||||
#LIBS += -lith_sys -lntdll
|
|
||||||
#LIBS += -lith_tls -lntdll
|
|
||||||
#LIBS += -lntoskrnl
|
|
||||||
|
|
||||||
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
|
||||||
|
|
||||||
## Sources
|
|
||||||
|
|
||||||
TEMPLATE = lib
|
|
||||||
TARGET = vnrsys
|
|
||||||
|
|
||||||
HEADERS += sys.h
|
|
||||||
SOURCES += sys.cc
|
|
||||||
|
|
||||||
OTHER_FILES += sys.pri
|
|
||||||
|
|
||||||
# EOF
|
|
@ -1,11 +0,0 @@
|
|||||||
12/16/2013
|
|
||||||
|
|
||||||
Differences between xp.dll and non-xp.dll for vnrhook.
|
|
||||||
|
|
||||||
non-xp:
|
|
||||||
CONFIG += eh
|
|
||||||
|
|
||||||
xp:
|
|
||||||
CONFIG += noeh
|
|
||||||
CONFIG -= embed_manifest_dll # Pure dynamic determined. The manifest would break Windows XP support
|
|
||||||
include($$LIBDIR/winseh/winseh_safe.pri)
|
|
Loading…
Reference in New Issue
Block a user