Merge pull request #149 from Artikash/remove-hooks

Remove hooks feature
This commit is contained in:
Akash Mozumdar 2019-06-10 22:49:06 -04:00 committed by GitHub
commit 58c80c82ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 86 additions and 28 deletions

View File

@ -16,7 +16,6 @@ namespace
{
public:
ProcessRecord(DWORD processId, HANDLE pipe) :
processId(processId),
pipe(pipe),
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
@ -47,23 +46,19 @@ namespace
}).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);
};
private:
DWORD processId;
HANDLE pipe;
AutoHandle<> mappedFile;
const TextHook(&view)[MAX_HOOK];
WinMutex viewMutex;
};
size_t HashThreadParam(ThreadParam tp)
{
return std::hash<int64_t>()(tp.processId + tp.addr) + std::hash<int64_t>()(tp.ctx + tp.ctx2);
}
size_t HashThreadParam(ThreadParam tp) { 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<DWORD, ProcessRecord>, std::recursive_mutex> processRecordsByIds;
@ -109,13 +104,13 @@ namespace
auto info = *(HookFoundNotif*)buffer;
auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound;
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;
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;
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;
case HOST_NOTIFICATION_RMVHOOK:
@ -134,13 +129,14 @@ namespace
{
auto tp = *(ThreadParam*)buffer;
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;
OnCreate(created);
try { textThread = textThreadsByParams->try_emplace(tp, tp, Host::GetHookParam(tp)).first; }
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
textThreadsByParams->find(tp)->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
textThread->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
}
break;
}
@ -226,6 +222,11 @@ namespace Host
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)
{
if (HookFound) processRecordsByIds->at(processId).OnHookFound = HookFound;

View File

@ -7,17 +7,18 @@ namespace Host
{
using ProcessEventHandler = std::function<void(DWORD)>;
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 InjectProcess(DWORD processId);
void DetachProcess(DWORD processId);
void InsertHook(DWORD processId, HookParam hp);
void RemoveHook(DWORD processId, uint64_t address);
void FindHooks(DWORD processId, SearchParam sp, HookEventHandler HookFound = {});
HookParam GetHookParam(ThreadParam tp);
TextThread& GetThread(ThreadParam tp);
void AddConsoleOutput(std::wstring text);
inline int defaultCodepage = SHIFT_JIS;

View File

@ -10,6 +10,7 @@
#include <QPushButton>
#include <QCheckBox>
#include <QSpinBox>
#include <QListWidget>
#include <QMessageBox>
#include <QInputDialog>
#include <QFileDialog>
@ -18,6 +19,7 @@ extern const char* ATTACH;
extern const char* LAUNCH;
extern const char* DETACH;
extern const char* ADD_HOOK;
extern const char* REMOVE_HOOKS;
extern const char* SAVE_HOOKS;
extern const char* FIND_HOOKS;
extern const char* SETTINGS;
@ -38,6 +40,7 @@ extern const char* HOOK_SEARCH_FILTER;
extern const char* START_HOOK_SEARCH;
extern const char* SAVE_SEARCH_RESULTS;
extern const char* TEXT_FILES;
extern const char* DOUBLE_CLICK_TO_REMOVE_HOOK;
extern const char* SAVE_SETTINGS;
extern const char* USE_JP_LOCALE;
extern const char* FILTER_REPETITION;
@ -61,6 +64,7 @@ MainWindow::MainWindow(QWidget *parent) :
{ LAUNCH, &MainWindow::LaunchProcess },
{ DETACH, &MainWindow::DetachProcess },
{ ADD_HOOK, &MainWindow::AddHook },
{ REMOVE_HOOKS, &MainWindow::RemoveHooks },
{ SAVE_HOOKS, &MainWindow::SaveHooks },
{ FIND_HOOKS, &MainWindow::FindHooks },
{ SETTINGS, &MainWindow::Settings },
@ -267,6 +271,7 @@ void MainWindow::LaunchProcess()
PROCESS_INFORMATION info = {};
if (QMessageBox::question(this, SELECT_PROCESS, USE_JP_LOCALE) == QMessageBox::Yes)
{
if (HMODULE localeEmulator = LoadLibraryOnce(L"LoaderDll"))
{
// see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252
@ -285,6 +290,7 @@ void MainWindow::LaunchProcess()
((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);
}
}
if (info.hProcess == NULL)
{
STARTUPINFOW DUMMY = { sizeof(DUMMY) };
@ -308,6 +314,33 @@ void MainWindow::AddHook()
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->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint);
hookList->setMinimumSize({ 300, 50 });
hookList->setWindowTitle(DOUBLE_CLICK_TO_REMOVE_HOOK);
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()
{
if (auto processName = Util::GetModuleFilename(GetSelectedProcessId()))
@ -378,7 +411,8 @@ void MainWindow::FindHooks()
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));
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");
});

View File

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

View File

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

View File

@ -27,5 +27,5 @@ enum HookParamType : unsigned
FIXING_SPLIT = 0x1000,
DIRECT_READ = 0x2000, // /R read code instead of classic /H hook code
HOOK_ENGINE = 0x4000,
HOOK_ADDITIONAL = 0x8000
HOOK_ADDITIONAL = 0x8000,
};

View File

@ -75,6 +75,13 @@ struct InsertHookCmd // From host
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
{
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* DETACH = u8"Detach from game";
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* FIND_HOOKS = u8"Find hooks";
const char* SETTINGS = u8"Settings";
@ -60,6 +61,7 @@ const char* HOOK_SEARCH_FILTER = u8"Results must match this regex";
const char* START_HOOK_SEARCH = u8"Start hook search";
const char* SAVE_SEARCH_RESULTS = u8"Save search results";
const char* TEXT_FILES = u8"Text (*.txt)";
const char* DOUBLE_CLICK_TO_REMOVE_HOOK = u8"Double click a hook to remove it";
const char* FILTER_REPETITION = u8"Repetition Filter";
const char* DEFAULT_CODEPAGE = u8"Default Codepage";
const char* FLUSH_DELAY = u8"Flush Delay";

View File

@ -61,7 +61,14 @@ DWORD WINAPI Pipe(LPVOID)
case HOST_COMMAND_NEW_HOOK:
{
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);
}
break;
case HOST_COMMAND_FIND_HOOK:
@ -180,8 +187,12 @@ void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
if (++currentHook >= MAX_HOOK) return ConsoleOutput(TOO_MANY_HOOKS);
if (lpname && *lpname) strncpy_s(hp.name, lpname, HOOK_NAME_SIZE - 1);
ConsoleOutput(INSERTING_HOOK, hp.name);
if (hp.address) RemoveHook(hp.address, 0);
if (!(*hooks)[currentHook].Insert(hp, flag)) ConsoleOutput(HOOK_FAILED);
RemoveHook(hp.address, 0);
if (!(*hooks)[currentHook].Insert(hp, flag))
{
ConsoleOutput(HOOK_FAILED);
(*hooks)[currentHook].Clear();
}
}
}

View File

@ -286,6 +286,7 @@ void TextHook::RemoveReadCode()
void TextHook::Clear()
{
std::scoped_lock lock(viewMutex);
if (address == 0) return;
if (hp.type & DIRECT_READ) RemoveReadCode();
else RemoveHookCode();
NotifyHookRemove(address, hp.name);