mireado

starting commit

/* 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;
};
#include "ProcessWindow.h"
#include "resource.h"
#include "ith/host/srv.h"
#include "ith/host/hookman.h"
#include "ProfileManager.h"
#include "Profile.h"
extern HookManager* man; // main.cpp
extern ProfileManager* pfman; // ProfileManager.cpp
ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog)
{
hbRefresh = GetDlgItem(hDlg, IDC_BUTTON1);
hbAttach = GetDlgItem(hDlg, IDC_BUTTON2);
hbDetach = GetDlgItem(hDlg, IDC_BUTTON3);
hbAddProfile = GetDlgItem(hDlg, IDC_BUTTON5);
hbRemoveProfile = GetDlgItem(hDlg, IDC_BUTTON6);
EnableWindow(hbAddProfile, FALSE);
EnableWindow(hbRemoveProfile, FALSE);
hlProcess = GetDlgItem(hDlg, IDC_LIST1);
heOutput = GetDlgItem(hDlg, IDC_EDIT1);
ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
InitProcessDlg();
RefreshProcess();
}
void ProcessWindow::InitProcessDlg()
{
LVCOLUMN lvc = {};
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lvc.fmt = LVCFMT_RIGHT; // left-aligned column
lvc.cx = 40;
lvc.pszText = L"PID";
ListView_InsertColumn(hlProcess, 0, &lvc);
lvc.cx = 100;
lvc.fmt = LVCFMT_LEFT; // left-aligned column
lvc.pszText = L"Name";
ListView_InsertColumn(hlProcess, 1, &lvc);
}
void ProcessWindow::RefreshProcess()
{
ListView_DeleteAllItems(hlProcess);
LVITEM item = {};
item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
DWORD idProcess[1024], cbNeeded;
WCHAR path[MAX_PATH];
if (EnumProcesses(idProcess, sizeof(idProcess), &cbNeeded))
{
DWORD len = cbNeeded / sizeof(DWORD);
for (DWORD i = 0; i < len; ++i)
{
DWORD pid = idProcess[i];
UniqueHandle hProcess(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
if (hProcess)
{
if (GetProcessImageFileName(hProcess.get(), path, MAX_PATH))
{
WCHAR buffer[256];
std::swprintf(buffer, L"%d", pid);
PWCHAR name = wcsrchr(path, L'\\') + 1;
item.pszText = buffer;
item.lParam = pid;
ListView_InsertItem(hlProcess, &item);
ListView_SetItemText(hlProcess, item.iItem, 1, name);
}
}
}
}
}
void ProcessWindow::AttachProcess()
{
DWORD pid = GetSelectedPID();
if (IHF_InjectByPID(pid) != -1)
RefreshThreadWithPID(pid, true);
}
void ProcessWindow::DetachProcess()
{
DWORD pid = GetSelectedPID();
if (IHF_ActiveDetachProcess(pid) == 0)
RefreshThreadWithPID(pid, false);
}
void ProcessWindow::AddCurrentToProfile()
{
DWORD pid = GetSelectedPID();
auto path = GetProcessPath(pid);
if (!path.empty())
{
Profile* pf = pfman->AddProfile(path, pid);
pfman->FindProfileAndUpdateHookAddresses(pid, path);
RefreshThread(ListView_GetSelectionMark(hlProcess));
}
}
void ProcessWindow::RemoveCurrentFromProfile()
{
DWORD pid = GetSelectedPID();
auto path = GetProcessPath(pid);
if (!path.empty())
{
pfman->DeleteProfile(path);
RefreshThread(ListView_GetSelectionMark(hlProcess));
}
}
void ProcessWindow::RefreshThread(int index)
{
LVITEM item = {};
item.mask = LVIF_PARAM;
item.iItem = index;
ListView_GetItem(hlProcess, &item);
DWORD pid = item.lParam;
bool isAttached = man->GetProcessRecord(pid) != NULL;
RefreshThreadWithPID(pid, isAttached);
}
void ProcessWindow::RefreshThreadWithPID(DWORD pid, bool isAttached)
{
EnableWindow(hbDetach, isAttached);
EnableWindow(hbAttach, !isAttached);
auto path = GetProcessPath(pid);
bool hasProfile = !path.empty() && pfman->HasProfile(path);
EnableWindow(hbAddProfile, isAttached && !hasProfile);
EnableWindow(hbRemoveProfile, hasProfile);
if (pid == GetCurrentProcessId())
EnableWindow(hbAttach, FALSE);
}
DWORD ProcessWindow::GetSelectedPID()
{
LVITEM item={};
item.mask = LVIF_PARAM;
item.iItem = ListView_GetSelectionMark(hlProcess);
ListView_GetItem(hlProcess, &item);
return item.lParam;
}
#pragma once
#include "ITH.h"
class ProcessWindow
{
public:
ProcessWindow(HWND hDialog);
void InitProcessDlg();
void RefreshProcess();
void AttachProcess();
void DetachProcess();
void AddCurrentToProfile();
void RemoveCurrentFromProfile();
void RefreshThread(int index);
private:
void RefreshThreadWithPID(DWORD pid, bool isAttached);
DWORD GetSelectedPID();
HWND hDlg;
HWND hlProcess;
HWND hbRefresh,hbAttach,hbDetach,hbAddProfile,hbRemoveProfile;
HWND heOutput;
};
/* 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;
}
/* 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;
};
#include "ProfileManager.h"
#include "Profile.h"
#include "ith/host/srv.h"
#include "ith/host/hookman.h"
#include "ith/common/types.h"
#include "ith/common/const.h"
extern HookManager* man; // main.cpp
extern LONG auto_inject, auto_insert, inject_delay; // main.cpp
extern LONG insert_delay, process_time; // main.cpp
bool MonitorFlag;
ProfileManager* pfman;
DWORD WINAPI MonitorThread(LPVOID lpThreadParameter);
void AddHooksToProfile(Profile& pf, const ProcessRecord& pr);
void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid);
DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread);
void MakeHookRelative(const ProcessRecord& pr, HookParam& hp);
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address);
void GetHookNameToAddressMap(const ProcessRecord& pr, std::map<std::wstring, DWORD>& hookNameToAddress);
ProfileManager::ProfileManager():
hMonitorThread(IthCreateThread(MonitorThread, 0))
{
LoadProfile();
}
ProfileManager::~ProfileManager()
{
SaveProfile();
WaitForSingleObject(hMonitorThread.get(), 0);
}
Profile* ProfileManager::GetProfile(DWORD pid)
{
std::wstring path = GetProcessPath(pid);
if (!path.empty())
{
auto node = profile_tree.find(path);
if (node != profile_tree.end())
return node->second.get();
}
return NULL;
}
bool ProfileManager::AddProfile(pugi::xml_node game)
{
auto file = game.child(L"File");
auto profile = game.child(L"Profile");
if (!file || !profile)
return false;
auto path = file.attribute(L"Path");
if (!path)
return false;
auto profile_title = game.attribute(L"Title");
auto title = profile_title ? profile_title.value() : L"";
auto pf = new Profile(title);
if (!pf->XmlReadProfile(profile))
return false;
AddProfile(path.value(), profile_ptr(pf));
return true;
}
Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid)
{
CSLock lock(cs);
auto& pf = profile_tree[path];
if (!pf)
{
std::wstring title = GetProcessTitle(pid);
pf.reset(new Profile(title));
}
return pf.get();
}
Profile* ProfileManager::AddProfile(const std::wstring& path, profile_ptr new_profile)
{
CSLock lock(cs);
auto& pf = profile_tree[path];
if (!pf)
pf.swap(new_profile);
return pf.get();
}
void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root)
{
auto game = root.append_child(L"Game");
auto file_node = game.append_child(L"File");
file_node.append_attribute(L"Path") = path.c_str();
auto profile_node = game.append_child(L"Profile");
pf.XmlWriteProfile(profile_node);
if (!pf.Title().empty())
{
if (!game.attribute(L"Title"))
game.append_attribute(L"Title");
game.attribute(L"Title") = pf.Title().c_str();
}
}
void ProfileManager::LoadProfile()
{
pugi::xml_document doc;
UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
if (hFile.get() == INVALID_HANDLE_VALUE)
return;
DWORD size = GetFileSize(hFile.get(), NULL);
std::unique_ptr<char[]> buffer(new char[size]);
ReadFile(hFile.get(), buffer.get(), size, &size, NULL);
auto result = doc.load_buffer(buffer.get(), size);
if (!result)
return;
auto root = doc.root().child(L"ITH_Profile");
if (!root)
return;
for (auto game = root.begin(); game != root.end(); ++game)
AddProfile(*game);
}
void ProfileManager::SaveProfile()
{
pugi::xml_document doc;
auto root = doc.append_child(L"ITH_Profile");
for (auto it = profile_tree.begin(); it != profile_tree.end(); ++it) {
auto& path = it->first;
auto& profile = it->second;
WriteProfileXml(path, *profile, root);
}
UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_WRITE, 0, CREATE_ALWAYS));
if (hFile.get() != INVALID_HANDLE_VALUE)
{
FileWriter fw(hFile.get());
doc.save(fw);
}
}
void ProfileManager::DeleteProfile(const std::wstring& path)
{
CSLock lock(cs);
profile_tree.erase(profile_tree.find(path));
}
void ProfileManager::FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path)
{
if (path.empty())
return;
auto it = profile_tree.find(path);
if (it == profile_tree.end())
return;
auto& pf = it->second;
const ProcessRecord* pr = man->GetProcessRecord(pid);
if (pr == NULL)
return;
// hook name -> hook address
std::map<std::wstring, DWORD> hookNameToAddress;
GetHookNameToAddressMap(*pr, hookNameToAddress);
for (auto thread_profile = pf->Threads().begin(); thread_profile != pf->Threads().end();
++thread_profile)
{
auto it = hookNameToAddress.find((*thread_profile)->HookName());
if (it != hookNameToAddress.end())
(*thread_profile)->HookAddress() = it->second;
}
}
void GetHookNameToAddressMap(const ProcessRecord& pr,
std::map<std::wstring, DWORD>& hookNameToAddress)
{
WaitForSingleObject(pr.hookman_mutex, 0);
auto hooks = (const Hook*)pr.hookman_map;
for (DWORD i = 0; i < MAX_HOOK; ++i)
{
if (hooks[i].Address() == 0)
continue;
auto& hook = hooks[i];
std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL))
hookNameToAddress[name.get()] = hook.Address();
}
ReleaseMutex(pr.hookman_mutex);
}
bool ProfileManager::HasProfile(const std::wstring& path)
{
return profile_tree.find(path) != profile_tree.end();
}
DWORD ProfileManager::ProfileCount()
{
return profile_tree.size();
}
DWORD WINAPI InjectThread(LPVOID lpThreadParameter)
{
DWORD pid = (DWORD)lpThreadParameter;
Sleep(inject_delay);
if (man == NULL)
return 0;
DWORD status = IHF_InjectByPID(pid);
if (!auto_insert)
return status;
if (status == -1)
return status;
Sleep(insert_delay);
const Profile* pf = pfman->GetProfile(pid);
if (pf)
{
SendParam sp;
sp.type = 0;
for (auto hp = pf->Hooks().begin(); hp != pf->Hooks().end(); ++hp)
IHF_InsertHook(pid, const_cast<HookParam*>(&(*hp)->HP()), (*hp)->Name().c_str());
}
return status;
}
DWORD WINAPI MonitorThread(LPVOID lpThreadParameter)
{
while (MonitorFlag)
{
DWORD aProcesses[1024], cbNeeded, cProcesses;
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
break;
cProcesses = cbNeeded / sizeof(DWORD);
for (size_t i = 0; i < cProcesses; ++i)
{
Sleep(process_time);
if (!auto_inject || man == NULL || man->GetProcessRecord(aProcesses[i]))
continue;
std::wstring process_path = GetProcessPath(aProcesses[i]);
if (!process_path.empty() && pfman->HasProfile(process_path))
{
UniqueHandle hThread(IthCreateThread(InjectThread, aProcesses[i]));
WaitForSingleObject(hThread.get(), 0);
}
}
}
return 0;
}
DWORD SaveProcessProfile(DWORD pid)
{
const ProcessRecord* pr = man->GetProcessRecord(pid);
if (pr == NULL)
return 0;
std::wstring path = GetProcessPath(pid);
if (path.empty())
return 0;
Profile* pf = pfman->GetProfile(pid);
if (pf != NULL)
pf->Clear();
else
pf = pfman->AddProfile(path, pid);
AddHooksToProfile(*pf, *pr);
AddThreadsToProfile(*pf, *pr, pid);
return 0;
}
void AddHooksToProfile(Profile& pf, const ProcessRecord& pr)
{
WaitForSingleObject(pr.hookman_mutex, 0);
auto hooks = (const Hook*)pr.hookman_map;
for (DWORD i = 0; i < MAX_HOOK; ++i)
{
if (hooks[i].Address() == 0)
continue;
auto& hook = hooks[i];
DWORD type = hook.Type();
if ((type & HOOK_ADDITIONAL) && (type & HOOK_ENGINE) == 0)
{
std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL))
{
if (hook.hp.module)
{
HookParam hp = hook.hp;
MakeHookRelative(pr, hp);
pf.AddHook(hp, name.get());
}
else
pf.AddHook(hook.hp, name.get());
}
}
}
ReleaseMutex(pr.hookman_mutex);
}
void MakeHookRelative(const ProcessRecord& pr, HookParam& hp)
{
MEMORY_BASIC_INFORMATION info;
VirtualQueryEx(pr.process_handle, (LPCVOID)hp.addr, &info, sizeof(info));
hp.addr -= (DWORD)info.AllocationBase;
hp.function = 0;
}
void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid)
{
man->LockHookman();
ThreadTable* table = man->Table();
for (int i = 0; i < table->Used(); ++i)
{
TextThread* tt = table->FindThread(i);
if (tt == NULL || tt->GetThreadParameter()->pid != pid)
continue;
//if (tt->Status() & CURRENT_SELECT || tt->Link() || tt->GetComment())
if (tt->Status() & CURRENT_SELECT || tt->Link())
AddThreadToProfile(pf, pr, *tt);
}
man->UnlockHookman();
}
DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread)
{
const ThreadParameter* tp = thread.GetThreadParameter();
std::wstring hook_name = GetHookNameByAddress(pr, tp->hook);
if (hook_name.empty())
return -1;
auto thread_profile = new ThreadProfile(hook_name, tp->retn, tp->spl, 0, 0,
THREAD_MASK_RETN | THREAD_MASK_SPLIT, L"");
DWORD threads_size = pf.Threads().size();
int thread_profile_index = pf.AddThread(thread_ptr(thread_profile));
if (thread_profile_index == threads_size) // new thread
{
WORD iw = thread_profile_index & 0xFFFF;
if (thread.Status() & CURRENT_SELECT)
pf.SelectedIndex() = iw;
if (thread.Link())
{
WORD to_index = AddThreadToProfile(pf, pr, *(thread.Link())) & 0xFFFF;
if (iw >= 0)
pf.AddLink(link_ptr(new LinkProfile(iw, to_index)));
}
}
return thread_profile_index; // in case more than one thread links to the same thread.
}
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address)
{
std::wstring hook_name;
WaitForSingleObject(pr.hookman_mutex, 0);
auto hooks = (const Hook*)pr.hookman_map;
for (int i = 0; i < MAX_HOOK; ++i)
{
auto& hook = hooks[i];
if (hook.Address() == hook_address)
{
std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength() * 2, NULL))
hook_name = name.get();
break;
}
}
ReleaseMutex(pr.hookman_mutex);
return hook_name;
}
#pragma once
#include "ITH.h"
#include "utility.h" // UniqueHandle, CriticalSection
class Profile;
class ProfileManager
{
public:
ProfileManager();
~ProfileManager();
Profile* AddProfile(const std::wstring& path, DWORD pid);
void DeleteProfile(const std::wstring& path);
void LoadProfile();
void SaveProfile();
void FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path);
bool HasProfile(const std::wstring& path);
Profile* GetProfile(DWORD pid);
DWORD ProfileCount();
private:
typedef std::unique_ptr<Profile> profile_ptr;
typedef std::map<std::wstring, profile_ptr> profile_map;
ProfileManager(const ProfileManager&);
ProfileManager operator=(const ProfileManager&);
bool AddProfile(pugi::xml_node game);
Profile* AddProfile(const std::wstring& path, profile_ptr new_profile);
void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc);
// locate profile with executable path
profile_map profile_tree;
CriticalSection cs;
UniqueHandle hMonitorThread;
};
#include "TextBuffer.h"
DWORD WINAPI FlushThread(LPVOID lParam); // window.cpp
TextBuffer::TextBuffer(HWND edit) : hThread(IthCreateThread(FlushThread, (DWORD)this)),
hEdit(edit),
running(true)
{
}
TextBuffer::~TextBuffer()
{
running = false;
WaitForSingleObject(hThread.get(), 0);
}
void TextBuffer::AddText(LPCWSTR str, int len, bool line)
{
CSLock lock(cs);
if (len > 0)
this->str.append(str, len);
line_break = line;
}
void TextBuffer::Flush()
{
CSLock lock(cs);
if (line_break || str.empty())
return;
DWORD t = Edit_GetTextLength(hEdit);
Edit_SetSel(hEdit, t, -1);
Edit_ReplaceSel(hEdit, str.c_str());
str.clear();
}
void TextBuffer::ClearBuffer()
{
CSLock lock(cs);
str.clear();
line_break = false;
}
#pragma once
#include "ITH.h"
#include "utility.h" // UniqueHandle, CriticalSection
class TextBuffer
{
public:
TextBuffer(HWND edit);
~TextBuffer();
void Flush();
void AddText(LPCWSTR str, int len, bool line);
void ClearBuffer();
bool Running() { return running; }
private:
CriticalSection cs;
bool line_break, running;
UniqueHandle hThread;
HWND hEdit;
std::wstring str;
};
/* 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/>.
*/
const wchar_t* Warning=L"Warning!";
//command.cpp
const wchar_t* ErrorSyntax=L"Syntax error";
const wchar_t* Usage = L"Syntax:\r\n\
\r\n\
:H[ELP] - print help\r\n\
:Lfrom-to - link from thread 'from' to thread 'to'\r\n\
:Ufrom - unlink link from thread 'from'\r\n\
\r\n\
'from' and 'to' and hexadecimal thread numbers. The thread number is the first number in the combo box.\r\n\
\r\n\
Loader options:\r\n\
/P[{process_id|Nprocess_name}] - attach to process\r\n\
\r\n\
Hook options:\r\n\
/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\
\r\n\
All numbers in /H (except ordinal) are hexadecimal without any prefixes";
const wchar_t* ExtendedUsage = L"/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]\r\n\
\r\n\
Set additional custom hook\r\n\
\r\n\
Hook types :\r\n\
A - DBCS char\r\n\
B - DBCS char(big-endian)\r\n\
W - UCS2 char\r\n\
S - MBCS string\r\n\
Q - UTF-16 string\r\n\
\r\n\
Parameters:\r\n\
X - use hardware breakpoints\r\n\
N - don't use contexts\r\n\
data_offset - stack offset to char / string pointer\r\n\
drdo - add a level of indirection to data_offset\r\n\
sub_offset - stack offset to subcontext\r\n\
drso - add a level of indirection to sub_offset\r\n\
addr - address of the hook\r\n\
module - name of the module to use as base for 'addr'\r\n\
name - name of the 'module' export to use as base for 'addr'\r\n\
ordinal - number of the 'module' export ordinal to use as base for 'addr'\r\n\
\r\n\
Negative values of 'data_offset' and 'sub_offset' refer to registers: \r\n\
- 4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI\r\n\
\r\n\
\"Add a level of indirection\" means in C/C++ style: (*(ESP+data_offset)+drdo) instead of (ESP+data_offset)\r\n\
\r\n\
All numbers except ordinal are hexadecimal without any prefixes";
//inject.cpp
const wchar_t* ErrorRemoteThread=L"Can't create remote thread.";
const wchar_t* ErrorOpenProcess=L"Can't open process.";
const wchar_t* ErrorNoProcess=L"Process not found";
const wchar_t* SelfAttach=L"Please do not attach to ITH.exe";
const wchar_t* AlreadyAttach=L"Process already attached.";
const wchar_t* FormatInject=L"Inject process %d. Module base %.8X";
//main.cpp
const wchar_t* NotAdmin=L"Can't enable SeDebugPrevilege. ITH might malfunction.\r\n\
Please run ITH as administrator or turn off UAC.";
//pipe.cpp
const wchar_t* ErrorCreatePipe=L"Can't create text pipe or too many instance.";
const wchar_t* FormatDetach=L"Process %d detached.";
const wchar_t* ErrorCmdQueueFull=L"Command queue full.";
const wchar_t* ErrorNoAttach=L"No process attached.";
//profile.cpp
const wchar_t* ErrorMonitor=L"Can't monitor process.";
//utility.cpp
const wchar_t* InitMessage=L"Copyright (C) 2010-2012 kaosu <qiupf2000@gmail.com>\r\n\
Copyright (C) 2015 zorkzero <zorkzero@hotmail.com>\r\n\
Source code <https://code.google.com/p/interactive-text-hooker/>\r\n\
General discussion <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>";
const wchar_t* BackgroundMsg=L"Type \":h\" or \":help\" for help.";
const wchar_t* ErrorLinkExist=L"Link exist.";
const wchar_t* ErrorCylicLink=L"Link failed. No cyclic link allowed.";
const wchar_t* FormatLink=L"Link from thread%.4x to thread%.4x.";
const wchar_t* ErrorLink=L"Link failed. Source or/and destination thread not found.";
const wchar_t* ErrorDeleteCombo=L"Error delete from combo.";
//window.cpp
const wchar_t* ClassName=L"ITH";
const wchar_t* ClassNameAdmin=L"ITH (Administrator)";
const wchar_t* ErrorNotSplit=L"Need to enable split first!";
const wchar_t* ErrorNotModule=L"Need to enable module first!";
//Main window buttons
const wchar_t* ButtonTitleProcess=L"Process";
const wchar_t* ButtonTitleThread=L"Thread";
const wchar_t* ButtonTitleHook=L"Hook";
const wchar_t* ButtonTitleProfile=L"Profile";
const wchar_t* ButtonTitleOption=L"Option";
const wchar_t* ButtonTitleClear=L"Clear";
const wchar_t* ButtonTitleSave=L"Save";
const wchar_t* ButtonTitleTop=L"Top";
//Hook window
const wchar_t* SpecialHook=L"Special hook, no AGTH equivalent.";
//Process window
const wchar_t* TabTitlePID=L"PID";
const wchar_t* TabTitleMemory=L"Memory";
const wchar_t* TabTitleName=L"Name";
const wchar_t* TabTitleTID=L"TID";
const wchar_t* TabTitleStart=L"Start";
const wchar_t* TabTitleModule=L"Module";
const wchar_t* TabTitleState=L"State";
const wchar_t* SuccessAttach=L"Attach ITH to process successfully.";
const wchar_t* FailAttach=L"Failed to attach ITH to process.";
const wchar_t* SuccessDetach=L"ITH detach from process.";
const wchar_t* FailDetach=L"Detach failed.";
//Profile window
const wchar_t* ProfileExist=L"Profile already exists.";
const wchar_t* SuccessAddProfile=L"Profile added.";
const wchar_t* FailAddProfile=L"Fail to add profile";
const wchar_t* TabTitleNumber=L"No.";
const wchar_t* NoFile=L"Can't find file.";
const wchar_t* PathDismatch=L"Process name dismatch, continue?";
const wchar_t* SuccessImportProfile=L"Import profile success";
//const wchar_t* SuccessAddProfile=L"Profile added.";
\ No newline at end of file
/* 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
extern const wchar_t* Warning;
//command.cpp
extern const wchar_t* ErrorSyntax;
extern const wchar_t* Usage;
extern const wchar_t* ExtendedUsage;
//inject.cpp
extern const wchar_t* ErrorRemoteThread;
extern const wchar_t* ErrorOpenProcess;
extern const wchar_t* ErrorNoProcess;
extern const wchar_t* SelfAttach;
extern const wchar_t* AlreadyAttach;
extern const wchar_t* FormatInject;
//main.cpp
extern const wchar_t* NotAdmin;
//pipe.cpp
extern const wchar_t* ErrorCreatePipe;
extern const wchar_t* FormatDetach;
extern const wchar_t* ErrorCmdQueueFull;
extern const wchar_t* ErrorNoAttach;
//profile.cpp
extern const wchar_t* ErrorMonitor;
//utility.cpp
extern const wchar_t* InitMessage;
extern const wchar_t* BackgroundMsg;
extern const wchar_t* ErrorLinkExist;
extern const wchar_t* ErrorCylicLink;
extern const wchar_t* FormatLink;
extern const wchar_t* ErrorLink;
extern const wchar_t* ErrorDeleteCombo;
//window.cpp
extern const wchar_t* ClassName;
extern const wchar_t* ClassNameAdmin;
extern const wchar_t* ErrorNotSplit;
extern const wchar_t* ErrorNotModule;
//Main window buttons
extern const wchar_t* ButtonTitleProcess;
extern const wchar_t* ButtonTitleThread;
extern const wchar_t* ButtonTitleHook;
extern const wchar_t* ButtonTitleProfile;
extern const wchar_t* ButtonTitleOption;
extern const wchar_t* ButtonTitleClear;
extern const wchar_t* ButtonTitleSave;
extern const wchar_t* ButtonTitleTop;
//Hook window
extern const wchar_t* SpecialHook;
//Process window
extern const wchar_t* TabTitlePID;
extern const wchar_t* TabTitleMemory;
extern const wchar_t* TabTitleName;
extern const wchar_t* TabTitleTID;
extern const wchar_t* TabTitleStart;
extern const wchar_t* TabTitleModule;
extern const wchar_t* TabTitleState;
extern const wchar_t* SuccessAttach;
extern const wchar_t* FailAttach;
extern const wchar_t* SuccessDetach;
extern const wchar_t* FailDetach;
//Profile window
extern const wchar_t* ProfileExist;
extern const wchar_t* SuccessAddProfile;
extern const wchar_t* FailAddProfile;
extern const wchar_t* TabTitleNumber;
extern const wchar_t* NoFile;
extern const wchar_t* PathDismatch;
extern const wchar_t* SuccessImportProfile;
\ No newline at end of file
/* 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/host/SettingManager.h"
#include "CustomFilter.h"
#include "profile.h"
#include "ProfileManager.h"
HINSTANCE hIns;
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE hInstance, DWORD nCmdShow, RECT *rc);
RECT window;
extern HWND hMainWnd; // windows.cpp
extern bool MonitorFlag; // ProfileManager.cpp
extern ProfileManager* pfman; // ProfileManager.cpp
extern "C" {
BOOL IthInitSystemService();
void IthCloseSystemService();
}
CustomFilter* uni_filter;
CustomFilter* mb_filter;
HookManager* man;
SettingManager* setman;
LONG split_time, cyclic_remove, global_filter;
LONG process_time, inject_delay, insert_delay,
auto_inject, auto_insert, clipboard_flag;
std::map<std::wstring, long> setting;
void RecordMBChar(WORD mb, PVOID f)
{
auto filter = (pugi::xml_node*)f;
DWORD m = mb;
WCHAR buffer[16];
std::swprintf(buffer, L"m%04X", m);
filter->append_attribute(buffer) = L"0";
}
void RecordUniChar(WORD uni, PVOID f)
{
auto filter = (pugi::xml_node*)f;
DWORD m = uni;
WCHAR buffer[16];
std::swprintf(buffer, L"u%04X", m);
filter->append_attribute(buffer) = L"0";
std::wstring text = filter->text().get();
text += (wchar_t)m;
filter->text().set(text.c_str());
}
void SaveSettings()
{
GetWindowRect(hMainWnd, &window);
setting[L"window_left"] = window.left;
setting[L"window_right"] = window.right;
setting[L"window_top"] = window.top;
setting[L"window_bottom"] = window.bottom;
setting[L"split_time"] = split_time;
setting[L"process_time"] = process_time;
setting[L"inject_delay"] = inject_delay;
setting[L"insert_delay"] = insert_delay;
setting[L"auto_inject"] = auto_inject;
setting[L"auto_insert"] = auto_insert;
setting[L"auto_copy"] = clipboard_flag;
setting[L"auto_suppress"] = cyclic_remove;
setting[L"global_filter"] = global_filter;
UniqueHandle hFile(IthCreateFile(L"ITH.xml", GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS));
if (hFile.get() != INVALID_HANDLE_VALUE)
{
FileWriter fw(hFile.get());
pugi::xml_document doc;
auto root = doc.root().append_child(L"ITH_Setting");
for (auto it = setting.begin(); it != setting.end(); ++it)
root.append_attribute(it->first.c_str()).set_value(it->second);
auto filter = root.append_child(L"SingleCharFilter");
filter.append_child(pugi::xml_node_type::node_pcdata);
mb_filter->Traverse(RecordMBChar, &filter);
uni_filter->Traverse(RecordUniChar, &filter);
doc.save(fw);
}
}
void DefaultSettings()
{
setting[L"split_time"] = 200;
setting[L"process_time"] = 50;
setting[L"inject_delay"] = 3000;
setting[L"insert_delay"] = 500;
setting[L"auto_inject"] = 1;
setting[L"auto_insert"] = 1;
setting[L"auto_copy"] = 0;
setting[L"auto_suppress"] = 0;
setting[L"global_filter"] = 0;
setting[L"window_left"] = 100;
setting[L"window_right"] = 800;
setting[L"window_top"] = 100;
setting[L"window_bottom"] = 600;
}
void InitializeSettings()
{
split_time = setting[L"split_time"];
process_time = setting[L"process_time"];
inject_delay = setting[L"inject_delay"];
insert_delay = setting[L"insert_delay"];
auto_inject = setting[L"auto_inject"];
auto_insert = setting[L"auto_insert"];
clipboard_flag = setting[L"auto_copy"];
cyclic_remove = setting[L"auto_suppress"];
global_filter = setting[L"global_filter"];
window.left = setting[L"window_left"];
window.right = setting[L"window_right"];
window.top = setting[L"window_top"];
window.bottom = setting[L"window_bottom"];
if (auto_inject > 1)
auto_inject = 1;
if (auto_insert > 1)
auto_insert = 1;
if (clipboard_flag > 1)
clipboard_flag = 1;
if (cyclic_remove > 1)
cyclic_remove = 1;
if (window.right < window.left || window.right - window.left < 600)
window.right = window.left + 600;
if (window.bottom < window.top || window.bottom - window.top < 200)
window.bottom = window.top + 200;
}
void LoadSettings()
{
UniqueHandle hFile(IthCreateFile(L"ITH.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
if (hFile.get() != INVALID_HANDLE_VALUE)
{
DWORD size = GetFileSize(hFile.get(), NULL);
std::unique_ptr<char[]> buffer(new char[size]);
ReadFile(hFile.get(), buffer.get(), size, &size, NULL);
pugi::xml_document doc;
auto result = doc.load_buffer_inplace(buffer.get(), size);
if (!result)
return;
auto root = doc.root().child(L"ITH_Setting");
for (auto attr = root.attributes_begin(); attr != root.attributes_end(); ++attr)
{
auto it = setting.find(attr->name());
if (it != setting.end())
it->second = std::stoul(attr->value());
}
auto filter = root.child(L"SingleCharFilter");
if (filter)
{
for (auto attr = filter.attributes_begin(); attr != filter.attributes_end(); ++attr)
{
if (attr->name()[0] == L'm')
{
DWORD c = std::stoul(attr->name() + 1, NULL, 16);
mb_filter->Insert(c & 0xFFFF);
}
else if (attr->name()[0] == L'u')
{
DWORD c = std::stoul(attr->name() + 1, NULL, 16);
uni_filter->Insert(c & 0xFFFF);
}
}
std::wstring filter_value = filter.text().get();
for (auto it = filter_value.begin(); it != filter_value.end(); ++it)
{
WCHAR filter_unichar[2] = { *it, L'\0' };
char filter_mbchar[4];
WC_MB(filter_unichar, filter_mbchar, 4);
mb_filter->Insert(*(WORD*)filter_mbchar);
uni_filter->Insert(*it);
}
}
}
}
extern LPCWSTR ClassName, ClassNameAdmin;
static WCHAR mutex[] = L"ITH_RUNNING";
DWORD FindITH()
{
HWND hwnd = FindWindow(ClassName, ClassName);
if (hwnd == NULL)
hwnd = FindWindow(ClassName, ClassNameAdmin);
if (hwnd)
{
ShowWindow(hwnd, SW_SHOWNORMAL);
SetForegroundWindow(hwnd);
return 0;
}
return 1;
}
LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo)
{
wchar_t path_name[512]; // fully qualified path name
WCHAR code[16];
EXCEPTION_RECORD* rec = ExceptionInfo->ExceptionRecord;
std::swprintf(code, L"%08X", rec->ExceptionCode);
MEMORY_BASIC_INFORMATION info;
if (VirtualQuery(rec->ExceptionAddress, &info, sizeof(info)))
{
if (GetModuleFileName((HMODULE)info.AllocationBase, path_name, 512))
{
LPWSTR name = wcsrchr(path_name, L'\\');
if (name)
{
DWORD addr = (DWORD)rec->ExceptionAddress;
std::swprintf(name, L"%s:%08X", name + 1, addr - (DWORD)info.AllocationBase);
MessageBox(NULL, name, code, MB_OK);
TerminateProcess(GetCurrentProcess(), 0);
}
}
}
std::swprintf(path_name, L"%08X", rec->ExceptionAddress);
MessageBox(NULL, path_name, code, MB_OK);
TerminateProcess(GetCurrentProcess(), 0);
return 0;
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (!IthInitSystemService())
TerminateProcess(GetCurrentProcess(), 0);
CreateMutex(NULL, TRUE, L"ITH_MAIN_RUNNING");
if (IHF_Init())
{
SetUnhandledExceptionFilter(UnhandledExcept);
IHF_GetHookManager(&man);
IHF_GetSettingManager(&setman);
setman->SetValue(SETTING_SPLIT_TIME, 200);
MonitorFlag = true;
pfman = new ProfileManager();
mb_filter = new CustomFilter();
uni_filter = new CustomFilter();
DefaultSettings();
LoadSettings();
InitializeSettings();
setman->SetValue(SETTING_SPLIT_TIME, split_time);
setman->SetValue(SETTING_CLIPFLAG, clipboard_flag);
hIns = hInstance;
MyRegisterClass(hIns);
InitInstance(hIns, IHF_IsAdmin(), &window);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//delete mb_filter;
//delete uni_filter;
delete pfman;
MonitorFlag = false;
man = NULL;
}
else
{
FindITH();
}
IHF_Cleanup();
IthCloseSystemService();
TerminateProcess(GetCurrentProcess(), 0);
}
This diff could not be displayed because it is too large.
This diff is collapsed. Click to expand it.
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif
#define IDD_DIALOG2 102
#define IDD_DIALOG4 104
#define IDI_ICON1 110
#define IDC_CHECK1 1000
#define IDC_CHECK2 1001
#define IDC_CHECK3 1002
#define IDC_CHECK4 1003
#define IDC_CHECK5 1004
#define IDC_EDIT1 1011
#define IDC_EDIT2 1012
#define IDC_EDIT3 1013
#define IDC_EDIT4 1014
#define IDC_BUTTON1 1020
#define IDC_BUTTON2 1021
#define IDC_BUTTON3 1022
#define IDC_BUTTON5 1024
#define IDC_LIST1 1028
#define IDC_BUTTON6 40000
#define IDC_CHECK6 40001
/* 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 "utility.h"
#include "ith/host/srv.h"
#include "ith/host/hookman.h"
#include "ith/common/types.h"
#include "ith/common/const.h"
extern HookManager* man; // main.cpp
std::wstring GetDriveLetter(const std::wstring& devicePath);
std::wstring GetWindowsPath(const std::wstring& fileObjectPath);
PVOID GetAllocationBase(DWORD pid, LPCVOID);
std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase);
std::wstring GetModuleFileNameAsString();
std::wstring GetProcessPath(HANDLE hProc);
void ConsoleOutput(LPCWSTR text)
{
man->AddConsoleOutput(text);
}
void ConsoleOutput(LPCSTR text)
{
int wc_length = MB_WC_count(text, -1);
LPWSTR wc = new WCHAR[wc_length];
MB_WC(text, wc, wc_length);
man->AddConsoleOutput(wc);
delete wc;
}
std::wstring GetProcessPath(DWORD pid)
{
UniqueHandle hProc(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
if (hProc)
return GetProcessPath(hProc.get());
else
return L"";
}
std::wstring GetProcessPath(HANDLE hProc)
{
wchar_t path[MAX_PATH];
GetProcessImageFileName(hProc, path, MAX_PATH);
return GetWindowsPath(path);
}
std::wstring GetWindowsPath(const std::wstring& path)
{
// path is in device form
// \Device\HarddiskVolume2\Windows\System32\taskhost.exe
auto pathOffset = path.find(L'\\', 1) + 1;
pathOffset = path.find(L'\\', pathOffset);
std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2
std::wstring dosDrive = GetDriveLetter(devicePath); // C:
if (dosDrive.empty())
return L"";
std::wstring dosPath = dosDrive; // C:
dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe
return dosPath;
}
std::wstring GetDriveLetter(const std::wstring& devicePath)
{
for (wchar_t drive = L'A'; drive <= L'Z'; drive++)
{
wchar_t szDriveName[3] = { drive, L':', L'\0' };
wchar_t szTarget[512];
if (QueryDosDevice(szDriveName, szTarget, 512))
if (devicePath.compare(szTarget) == 0)
return szDriveName;
}
return L"";
}
std::wstring GetCode(const HookParam& hp, DWORD pid)
{
std::wstring code(L"/H");
WCHAR c;
if (hp.type & PRINT_DWORD)
c = L'H';
else 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';
}
code += c;
if (hp.type & NO_CONTEXT)
code += L'N';
if (hp.off >> 31)
code += L"-" + ToHexString(-(hp.off + 4));
else
code += ToHexString(hp.off);
if (hp.type & DATA_INDIRECT)
{
if (hp.ind >> 31)
code += L"*-" + ToHexString(-hp.ind);
else
code += L"*" + ToHexString(hp.ind);
}
if (hp.type & USING_SPLIT)
{
if (hp.split >> 31)
code += L":-" + ToHexString(-(4 + hp.split));
else
code += L":" + ToHexString(hp.split);
}
if (hp.type & SPLIT_INDIRECT)
{
if (hp.split_ind >> 31)
code += L"*-" + ToHexString(-hp.split_ind);
else
code += L"*" + ToHexString(hp.split_ind);
}
if (pid)
{
PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.addr);
if (allocationBase)
{
std::wstring path = GetModuleFileNameAsString(pid, allocationBase);
if (!path.empty())
{
auto fileName = path.substr(path.rfind(L'\\') + 1);
DWORD relativeHookAddress = hp.addr - (DWORD)allocationBase;
code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName;
return code;
}
}
}
if (hp.module)
{
code += L"@" + ToHexString(hp.addr) + L"!" + ToHexString(hp.module);
if (hp.function)
code += L"!" + ToHexString(hp.function);
}
else
{
// hack, the original address is stored in the function field
// if (module == NULL && function != NULL)
// in TextHook::UnsafeInsertHookCode() MODULE_OFFSET and FUNCTION_OFFSET are removed from
// HookParam.type
if (hp.function)
code += L"@" + ToHexString(hp.function);
else
code += L"@" + ToHexString(hp.addr) + L":";
}
return code;
}
std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase)
{
const ProcessRecord* pr = man->GetProcessRecord(pid);
if (pr)
{
HANDLE hProc = pr->process_handle;
WCHAR path[MAX_PATH];
if (GetModuleFileNameEx(hProc, (HMODULE)allocationBase, path, MAX_PATH))
return path;
}
return L"";
}
PVOID GetAllocationBase(DWORD pid, LPCVOID addr)
{
const ProcessRecord *pr = man->GetProcessRecord(pid);
if (pr)
{
MEMORY_BASIC_INFORMATION info;
HANDLE hProc = pr->process_handle;
if (VirtualQueryEx(hProc, addr, &info, sizeof(info)))
{
if (info.Type & MEM_IMAGE)
return info.AllocationBase;
}
}
return NULL;
}
struct TitleParam
{
DWORD pid, buffer_len, retn_len;
std::wstring buffer;
};
BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lParam)
{
TitleParam* p = (TitleParam*)lParam;
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
if (pid == p->pid)
{
if (GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE)
{
int len = GetWindowTextLength(hwnd);
std::unique_ptr<wchar_t[]> result(new wchar_t[len + 1]);
GetWindowText(hwnd, result.get(), len + 1);
p->buffer = result.get();
p->retn_len = p->buffer.size();
if (!p->buffer.empty())
return FALSE;
}
}
return TRUE;
}
std::wstring GetProcessTitle(DWORD pid)
{
TitleParam p;
p.pid = pid;
p.buffer_len = 0;
p.retn_len = 0;
EnumWindows(EnumProc, (LPARAM)&p);
return p.buffer;
}
WindowsError::WindowsError(DWORD error_code) : error_code(error_code), msg("")
{
CHAR str[512];
std::sprintf(str, "error code 0x%8x", error_code);
msg = str;
}
const char *WindowsError::what() const
{
return msg.c_str();
}
HANDLE IthCreateThread(LPVOID start_addr, DWORD param)
{
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_addr, (LPVOID)param, 0, NULL);
}
std::wstring GetModuleFileNameAsString()
{
WCHAR path[MAX_PATH];
GetModuleFileName(NULL, path, MAX_PATH);
return path;
}
bool IthCreateDirectory(LPCWSTR name)
{
std::wstring path = GetModuleFileNameAsString();
path = path.substr(0, path.rfind(L'\\') + 1) + name;
BOOL error_code = CreateDirectory(path.c_str(), NULL);
return error_code != 0 || GetLastError() == ERROR_ALREADY_EXISTS;
}
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
{
std::wstring path = GetModuleFileNameAsString();
path = path.substr(0, path.rfind(L'\\') + 1) + name;
return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL);
}
//SJIS->Unicode. 'mb' must be null-terminated. 'wc_length' is the length of 'wc' in characters.
int MB_WC(const char* mb, wchar_t* wc, int wc_length)
{
return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length);
}
// Count characters in wide string. 'mb_length' is the number of bytes from 'mb' to convert or
// -1 if the string is null terminated.
int MB_WC_count(const char* mb, int mb_length)
{
return MultiByteToWideChar(932, 0, mb, mb_length, NULL, 0);
}
// Unicode->SJIS. Analogous to MB_WC.
int WC_MB(const wchar_t *wc, char* mb, int mb_length)
{
return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL);
}
DWORD Hash(const std::wstring& module, int length)
{
DWORD hash = 0;
auto end = length < 0 || static_cast<std::size_t>(length) > module.length() ? module.end() : module.begin() + length;
for (auto it = module.begin(); it != end; ++it)
hash = _rotr(hash, 7) + *it;
return hash;
}
#pragma once
#include "ITH.h"
struct HookParam;
struct ProcessRecord;
DWORD Hash(const std::wstring& module, int length = -1);
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid);
std::wstring GetProcessPath(DWORD pid);
void ConsoleOutput(LPCWSTR);
void ConsoleOutput(LPCSTR text);
std::wstring GetProcessTitle(DWORD pid);
std::wstring GetCode(const HookParam& hp, DWORD pid = 0);
// http://codesequoia.wordpress.com/2012/08/26/stdunique_ptr-for-windows-handles/
struct HandleDeleter
{
typedef HANDLE pointer;
void operator() (HANDLE h)
{
if (h != INVALID_HANDLE_VALUE) {
CloseHandle(h);
}
}
};
typedef std::unique_ptr<HANDLE, HandleDeleter> UniqueHandle;
class FileWriter : public pugi::xml_writer
{
HANDLE hFile;
public:
FileWriter(HANDLE hFile) : hFile(hFile) {};
~FileWriter() {};
virtual void write(const void* data, size_t size)
{
DWORD dwNumberOfBytesWritten;
WriteFile(hFile, data, size, &dwNumberOfBytesWritten, NULL);
}
};
class WindowsError : public std::exception
{
private:
std::string msg;
DWORD error_code;
public:
WindowsError(DWORD error_code);
virtual const char *what() const;
};
HANDLE IthCreateThread(LPVOID start_addr, DWORD param);
bool IthCreateDirectory(LPCWSTR name);
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition);
int MB_WC(const char* mb, wchar_t* wc, int wc_length);
int MB_WC_count(const char* mb, int mb_length);
int WC_MB(const wchar_t *wc, char* mb, int mb_length);
bool Parse(const std::wstring& cmd, HookParam& hp);
template <typename T>
std::wstring ToHexString(T i) {
std::wstringstream ss;
ss << std::uppercase << std::hex << i;
return ss.str();
}
// http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html
class CriticalSection
{
public:
CriticalSection()
{
::InitializeCriticalSection(&m_rep);
}
~CriticalSection()
{
::DeleteCriticalSection(&m_rep);
}
void Enter()
{
::EnterCriticalSection(&m_rep);
}
void Leave()
{
::LeaveCriticalSection(&m_rep);
}
private:
CriticalSection(const CriticalSection&);
CriticalSection& operator=(const CriticalSection&);
CRITICAL_SECTION m_rep;
};
class CSLock
{
public:
CSLock(CriticalSection& a_section)
: m_section(a_section)
{
m_section.Enter();
}
~CSLock()
{
m_section.Leave();
}
private:
CSLock(const CSLock&);
CSLock& operator=(const CSLock&);
CriticalSection& m_section;
};
const wchar_t* build_date=L"27.01.2013";
const WCHAR program_version[] = L"@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@";
This diff is collapsed. Click to expand it.
/* 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"