353 lines
9.7 KiB
C++
353 lines
9.7 KiB
C++
|
#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;
|
||
|
}
|