(re)add remove hooks feature skeleton

This commit is contained in:
Akash Mozumdar 2019-06-10 01:49:11 -04:00
parent ae0398a243
commit c808e7594d
12 changed files with 90 additions and 33 deletions

View File

@ -16,7 +16,6 @@ namespace
{ {
public: public:
ProcessRecord(DWORD processId, HANDLE pipe) : ProcessRecord(DWORD processId, HANDLE pipe) :
processId(processId),
pipe(pipe), pipe(pipe),
mappedFile(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())), mappedFile(OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(processId)).c_str())),
view(*(const TextHook(*)[MAX_HOOK])MapViewOfFile(mappedFile, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2)), // jichi 1/16/2015: Changed to half to hook section size view(*(const TextHook(*)[MAX_HOOK])MapViewOfFile(mappedFile, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2)), // jichi 1/16/2015: Changed to half to hook section size
@ -47,23 +46,19 @@ namespace
}).detach(); }).detach();
} }
Host::HookEventHandler OnHookFound = [](HookParam hp, DWORD processId, const std::wstring& text) Host::HookEventHandler OnHookFound = [](HookParam hp, const std::wstring& text)
{ {
Host::AddConsoleOutput(Util::GenerateCode(hp, 0) + L": " + text); Host::AddConsoleOutput(Util::GenerateCode(hp, 0) + L": " + text);
}; };
private: private:
DWORD processId;
HANDLE pipe; HANDLE pipe;
AutoHandle<> mappedFile; AutoHandle<> mappedFile;
const TextHook(&view)[MAX_HOOK]; const TextHook(&view)[MAX_HOOK];
WinMutex viewMutex; WinMutex viewMutex;
}; };
size_t HashThreadParam(ThreadParam tp) size_t HashThreadParam(ThreadParam tp) { return std::hash<int64_t>()(tp.processId + tp.addr) + std::hash<int64_t>()(tp.ctx + tp.ctx2); }
{
return std::hash<int64_t>()(tp.processId + tp.addr) + std::hash<int64_t>()(tp.ctx + tp.ctx2);
}
Synchronized<std::unordered_map<ThreadParam, TextThread, Functor<HashThreadParam>>, std::recursive_mutex> textThreadsByParams; Synchronized<std::unordered_map<ThreadParam, TextThread, Functor<HashThreadParam>>, std::recursive_mutex> textThreadsByParams;
Synchronized<std::unordered_map<DWORD, ProcessRecord>, std::recursive_mutex> processRecordsByIds; Synchronized<std::unordered_map<DWORD, ProcessRecord>, std::recursive_mutex> processRecordsByIds;
@ -109,13 +104,13 @@ namespace
auto info = *(HookFoundNotif*)buffer; auto info = *(HookFoundNotif*)buffer;
auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound; auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound;
std::wstring wide = info.text; std::wstring wide = info.text;
if (wide.size() > STRING) OnHookFound(info.hp, processId, info.text); if (wide.size() > STRING) OnHookFound(info.hp, info.text);
info.hp.type = USING_STRING; info.hp.type = USING_STRING;
if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage)) if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage))
if (converted->size() > STRING) OnHookFound(info.hp, processId, converted.value()); if (converted->size() > STRING) OnHookFound(info.hp, converted.value());
info.hp.codepage = CP_UTF8; info.hp.codepage = CP_UTF8;
if (auto converted = Util::StringToWideString((char*)info.text, CP_UTF8)) if (auto converted = Util::StringToWideString((char*)info.text, CP_UTF8))
if (converted->size() > STRING) OnHookFound(info.hp, processId, converted.value()); if (converted->size() > STRING) OnHookFound(info.hp, converted.value());
} }
break; break;
case HOST_NOTIFICATION_RMVHOOK: case HOST_NOTIFICATION_RMVHOOK:
@ -134,13 +129,14 @@ namespace
{ {
auto tp = *(ThreadParam*)buffer; auto tp = *(ThreadParam*)buffer;
auto textThreadsByParams = ::textThreadsByParams.Acquire(); auto textThreadsByParams = ::textThreadsByParams.Acquire();
if (textThreadsByParams->count(tp) == 0) try auto textThread = textThreadsByParams->find(tp);
if (textThread == textThreadsByParams->end())
{ {
TextThread& created = textThreadsByParams->try_emplace(tp, tp, Host::GetHookParam(tp)).first->second; try { textThread = textThreadsByParams->try_emplace(tp, tp, Host::GetHookParam(tp)).first; }
OnCreate(created); catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again
OnCreate(textThread->second);
} }
catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again textThread->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
textThreadsByParams->find(tp)->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
} }
break; break;
} }
@ -226,6 +222,11 @@ namespace Host
processRecordsByIds->at(processId).Send(InsertHookCmd(hp)); processRecordsByIds->at(processId).Send(InsertHookCmd(hp));
} }
void RemoveHook(DWORD processId, uint64_t address)
{
processRecordsByIds->at(processId).Send(RemoveHookCmd(address));
}
void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound) void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound)
{ {
if (HookFound) processRecordsByIds->at(processId).OnHookFound = HookFound; if (HookFound) processRecordsByIds->at(processId).OnHookFound = HookFound;

View File

@ -7,17 +7,18 @@ namespace Host
{ {
using ProcessEventHandler = std::function<void(DWORD)>; using ProcessEventHandler = std::function<void(DWORD)>;
using ThreadEventHandler = std::function<void(TextThread&)>; using ThreadEventHandler = std::function<void(TextThread&)>;
using HookEventHandler = std::function<void(HookParam, DWORD processId, const std::wstring& text)>; using HookEventHandler = std::function<void(HookParam, const std::wstring& text)>;
void Start(ProcessEventHandler Connect, ProcessEventHandler Disconnect, ThreadEventHandler Create, ThreadEventHandler Destroy, TextThread::OutputCallback Output); void Start(ProcessEventHandler Connect, ProcessEventHandler Disconnect, ThreadEventHandler Create, ThreadEventHandler Destroy, TextThread::OutputCallback Output);
void InjectProcess(DWORD processId); void InjectProcess(DWORD processId);
void DetachProcess(DWORD processId); void DetachProcess(DWORD processId);
void InsertHook(DWORD processId, HookParam hp); void InsertHook(DWORD processId, HookParam hp);
void RemoveHook(DWORD processId, uint64_t address);
void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound = {}); void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound = {});
HookParam GetHookParam(ThreadParam tp); HookParam GetHookParam(ThreadParam tp);
TextThread& GetThread(ThreadParam tp); TextThread& GetThread(ThreadParam tp);
void AddConsoleOutput(std::wstring text); void AddConsoleOutput(std::wstring text);
inline int defaultCodepage = SHIFT_JIS; inline int defaultCodepage = SHIFT_JIS;

View File

@ -10,6 +10,7 @@
#include <QPushButton> #include <QPushButton>
#include <QCheckBox> #include <QCheckBox>
#include <QSpinBox> #include <QSpinBox>
#include <QListWidget>
#include <QMessageBox> #include <QMessageBox>
#include <QInputDialog> #include <QInputDialog>
#include <QFileDialog> #include <QFileDialog>
@ -18,6 +19,7 @@ extern const char* ATTACH;
extern const char* LAUNCH; extern const char* LAUNCH;
extern const char* DETACH; extern const char* DETACH;
extern const char* ADD_HOOK; extern const char* ADD_HOOK;
extern const char* REMOVE_HOOKS;
extern const char* SAVE_HOOKS; extern const char* SAVE_HOOKS;
extern const char* FIND_HOOKS; extern const char* FIND_HOOKS;
extern const char* SETTINGS; extern const char* SETTINGS;
@ -61,6 +63,7 @@ MainWindow::MainWindow(QWidget *parent) :
{ LAUNCH, &MainWindow::LaunchProcess }, { LAUNCH, &MainWindow::LaunchProcess },
{ DETACH, &MainWindow::DetachProcess }, { DETACH, &MainWindow::DetachProcess },
{ ADD_HOOK, &MainWindow::AddHook }, { ADD_HOOK, &MainWindow::AddHook },
{ REMOVE_HOOKS, &MainWindow::RemoveHooks },
{ SAVE_HOOKS, &MainWindow::SaveHooks }, { SAVE_HOOKS, &MainWindow::SaveHooks },
{ FIND_HOOKS, &MainWindow::FindHooks }, { FIND_HOOKS, &MainWindow::FindHooks },
{ SETTINGS, &MainWindow::Settings }, { SETTINGS, &MainWindow::Settings },
@ -267,6 +270,7 @@ void MainWindow::LaunchProcess()
PROCESS_INFORMATION info = {}; PROCESS_INFORMATION info = {};
if (QMessageBox::question(this, SELECT_PROCESS, USE_JP_LOCALE) == QMessageBox::Yes) if (QMessageBox::question(this, SELECT_PROCESS, USE_JP_LOCALE) == QMessageBox::Yes)
{
if (HMODULE localeEmulator = LoadLibraryOnce(L"LoaderDll")) if (HMODULE localeEmulator = LoadLibraryOnce(L"LoaderDll"))
{ {
// see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252 // see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252
@ -285,6 +289,7 @@ void MainWindow::LaunchProcess()
((LONG(__stdcall*)(decltype(&LEB), LPCWSTR appName, LPWSTR commandLine, LPCWSTR currentDir, void*, void*, PROCESS_INFORMATION*, void*, void*, void*, void*)) ((LONG(__stdcall*)(decltype(&LEB), LPCWSTR appName, LPWSTR commandLine, LPCWSTR currentDir, void*, void*, PROCESS_INFORMATION*, void*, void*, void*, void*))
GetProcAddress(localeEmulator, "LeCreateProcess"))(&LEB, process.c_str(), NULL, path.c_str(), NULL, NULL, &info, NULL, NULL, NULL, NULL); GetProcAddress(localeEmulator, "LeCreateProcess"))(&LEB, process.c_str(), NULL, path.c_str(), NULL, NULL, &info, NULL, NULL, NULL, NULL);
} }
}
if (info.hProcess == NULL) if (info.hProcess == NULL)
{ {
STARTUPINFOW DUMMY = { sizeof(DUMMY) }; STARTUPINFOW DUMMY = { sizeof(DUMMY) };
@ -308,6 +313,32 @@ void MainWindow::AddHook()
else Host::AddConsoleOutput(INVALID_CODE); else Host::AddConsoleOutput(INVALID_CODE);
} }
void MainWindow::RemoveHooks()
{
DWORD processId = GetSelectedProcessId();
std::unordered_map<uint64_t, HookParam> hooks;
for (int i = 0; i < ui->ttCombo->count(); ++i)
{
ThreadParam tp = ParseTextThreadString(ui->ttCombo->itemText(i));
if (tp.processId == GetSelectedProcessId()) hooks[tp.addr] = Host::GetHookParam(tp);
}
auto hookList = new QListWidget(this);
hookList->setWindowFlag(Qt::Window, true);
hookList->setWindowTitle(REMOVE_HOOKS);
for (auto[address, hp] : hooks)
new QListWidgetItem(QString(hp.name) + "@" + QString::number(address, 16), hookList);
connect(hookList, &QListWidget::itemDoubleClicked, [processId, hookList](QListWidgetItem* item)
{
try
{
Host::RemoveHook(processId, item->text().split("@")[1].toULongLong(nullptr, 16));
delete item;
}
catch (std::out_of_range) { hookList->close(); }
});
hookList->show();
}
void MainWindow::SaveHooks() void MainWindow::SaveHooks()
{ {
if (auto processName = Util::GetModuleFilename(GetSelectedProcessId())) if (auto processName = Util::GetModuleFilename(GetSelectedProcessId()))
@ -378,7 +409,8 @@ void MainWindow::FindHooks()
if (!filterInput->text().isEmpty()) try { filter = std::wregex(S(filterInput->text())); } catch (std::regex_error) {}; if (!filterInput->text().isEmpty()) try { filter = std::wregex(S(filterInput->text())); } catch (std::regex_error) {};
memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), 25)); memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), 25));
auto hooks = std::make_shared<QString>(); auto hooks = std::make_shared<QString>();
Host::FindHooks(processId, sp, [hooks, filter](HookParam hp, DWORD processId, const std::wstring& text) DWORD processId = this->processId;
Host::FindHooks(processId, sp, [processId, hooks, filter](HookParam hp, const std::wstring& text)
{ {
if (std::regex_search(text, filter)) hooks->append(S(Util::GenerateCode(hp, processId)) + ": " + S(text) + "\n"); if (std::regex_search(text, filter)) hooks->append(S(Util::GenerateCode(hp, processId)) + ": " + S(text) + "\n");
}); });

View File

@ -36,6 +36,7 @@ private:
void LaunchProcess(); void LaunchProcess();
void DetachProcess(); void DetachProcess();
void AddHook(); void AddHook();
void RemoveHooks();
void SaveHooks(); void SaveHooks();
void FindHooks(); void FindHooks();
void Settings(); void Settings();

View File

@ -31,7 +31,7 @@ constexpr bool x64 = false;
template <typename T> using Array = T[]; template <typename T> using Array = T[];
template<typename E, typename M = std::mutex> template<typename T, typename M = std::mutex>
class Synchronized class Synchronized
{ {
public: public:
@ -40,17 +40,17 @@ public:
struct Locker struct Locker
{ {
E* operator->() { return ptr; } T* operator->() { return &contents; }
std::unique_lock<M> lock; std::unique_lock<M> lock;
E* ptr; T& contents;
}; };
Locker Acquire() { return { std::unique_lock(mtx), &contents }; } Locker Acquire() { return { std::unique_lock(m), contents }; }
Locker operator->() { return Acquire(); } Locker operator->() { return Acquire(); }
private: private:
E contents; T contents;
M mtx; M m;
}; };
template <auto F> template <auto F>

View File

@ -27,5 +27,6 @@ enum HookParamType : unsigned
FIXING_SPLIT = 0x1000, FIXING_SPLIT = 0x1000,
DIRECT_READ = 0x2000, // /R read code instead of classic /H hook code DIRECT_READ = 0x2000, // /R read code instead of classic /H hook code
HOOK_ENGINE = 0x4000, HOOK_ENGINE = 0x4000,
HOOK_ADDITIONAL = 0x8000 HOOK_ADDITIONAL = 0x8000,
HOOK_REMOVED = 0x10000, // hook was intentionally removed by the user
}; };

View File

@ -75,6 +75,13 @@ struct InsertHookCmd // From host
HookParam hp; HookParam hp;
}; };
struct RemoveHookCmd // From host
{
RemoveHookCmd(uint64_t address) : address(address) {}
HostCommandType command = HOST_COMMAND_REMOVE_HOOK;
uint64_t address;
};
struct FindHookCmd // From host struct FindHookCmd // From host
{ {
FindHookCmd(SearchParam sp) : sp(sp) {} FindHookCmd(SearchParam sp) : sp(sp) {}

View File

@ -10,6 +10,7 @@ const char* ATTACH = u8"Attach to game";
const char* LAUNCH = u8"Launch game"; const char* LAUNCH = u8"Launch game";
const char* DETACH = u8"Detach from game"; const char* DETACH = u8"Detach from game";
const char* ADD_HOOK = u8"Add hook"; const char* ADD_HOOK = u8"Add hook";
const char* REMOVE_HOOKS = u8"Remove hook(s)";
const char* SAVE_HOOKS = u8"Save hook(s)"; const char* SAVE_HOOKS = u8"Save hook(s)";
const char* FIND_HOOKS = u8"Find hooks"; const char* FIND_HOOKS = u8"Find hooks";
const char* SETTINGS = u8"Settings"; const char* SETTINGS = u8"Settings";

View File

@ -61,7 +61,14 @@ DWORD WINAPI Pipe(LPVOID)
case HOST_COMMAND_NEW_HOOK: case HOST_COMMAND_NEW_HOOK:
{ {
auto info = *(InsertHookCmd*)buffer; auto info = *(InsertHookCmd*)buffer;
NewHook(info.hp, "UserHook", 0); static int userHooks = 0;
NewHook(info.hp, ("UserHook" + std::to_string(userHooks += 1)).c_str(), 0);
}
break;
case HOST_COMMAND_REMOVE_HOOK:
{
auto info = *(RemoveHookCmd*)buffer;
RemoveHook(info.address, 0, true);
} }
break; break;
case HOST_COMMAND_FIND_HOOK: case HOST_COMMAND_FIND_HOOK:
@ -180,14 +187,18 @@ void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
if (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS); if (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS);
if (lpname && *lpname) strncpy_s(hp.name, lpname, HOOK_NAME_SIZE - 1); if (lpname && *lpname) strncpy_s(hp.name, lpname, HOOK_NAME_SIZE - 1);
ConsoleOutput(INSERTING_HOOK, hp.name); ConsoleOutput(INSERTING_HOOK, hp.name);
if (hp.address) RemoveHook(hp.address, 0); RemoveHook(hp.address, 0);
if (!(*hooks)[currentHook].Insert(hp, flag)) ConsoleOutput(HOOK_FAILED); if (!(*hooks)[currentHook].Insert(hp, flag))
{
ConsoleOutput(HOOK_FAILED);
(*hooks)[currentHook].Clear();
}
} }
} }
void RemoveHook(uint64_t addr, int maxOffset) void RemoveHook(uint64_t addr, int maxOffset, bool markRemoved)
{ {
for (auto& hook : *hooks) if (abs((long long)(hook.address - addr)) <= maxOffset) return hook.Clear(); for (auto& hook : *hooks) if (abs((long long)(hook.address - addr)) <= maxOffset) return hook.Clear(markRemoved);
} }
// EOF // EOF

View File

@ -12,7 +12,7 @@ void ConsoleOutput(LPCSTR text, ...);
void NotifyHookFound(HookParam hp, wchar_t* text); void NotifyHookFound(HookParam hp, wchar_t* text);
void NotifyHookRemove(uint64_t addr, LPCSTR name); void NotifyHookRemove(uint64_t addr, LPCSTR name);
void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE); void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
void RemoveHook(uint64_t addr, int maxOffset = 9); void RemoveHook(uint64_t addr, int maxOffset = 9, bool markRemoved = false);
extern "C" // minhook library extern "C" // minhook library
{ {

View File

@ -283,13 +283,15 @@ void TextHook::RemoveReadCode()
CloseHandle(readerThread); CloseHandle(readerThread);
} }
void TextHook::Clear() void TextHook::Clear(bool markRemoved)
{ {
std::scoped_lock lock(viewMutex); std::scoped_lock lock(viewMutex);
if (address == 0) return;
if (hp.type & DIRECT_READ) RemoveReadCode(); if (hp.type & DIRECT_READ) RemoveReadCode();
else RemoveHookCode(); else RemoveHookCode();
NotifyHookRemove(address, hp.name); NotifyHookRemove(address, hp.name);
memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
if (markRemoved) hp.type = HOOK_REMOVED;
} }
int TextHook::GetLength(uintptr_t base, uintptr_t in) int TextHook::GetLength(uintptr_t base, uintptr_t in)

View File

@ -26,7 +26,7 @@ public:
}; // Absolute address }; // Absolute address
bool Insert(HookParam hp, DWORD set_flag); bool Insert(HookParam hp, DWORD set_flag);
void Clear(); void Clear(bool markRemoved = false);
private: private:
void Read(); void Read();