(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:
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
textThreadsByParams->find(tp)->second.Push(buffer + sizeof(tp), bytesRead - sizeof(tp));
OnCreate(textThread->second);
}
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;
@ -61,6 +63,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 +270,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 +289,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 +313,32 @@ 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->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()
{
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) {};
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,6 @@ 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,
HOOK_REMOVED = 0x10000, // hook was intentionally removed by the user
};

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";

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, true);
}
break;
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 (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();
}
}
}
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

View File

@ -12,7 +12,7 @@ void ConsoleOutput(LPCSTR text, ...);
void NotifyHookFound(HookParam hp, wchar_t* text);
void NotifyHookRemove(uint64_t addr, LPCSTR name);
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
{

View File

@ -283,13 +283,15 @@ void TextHook::RemoveReadCode()
CloseHandle(readerThread);
}
void TextHook::Clear()
void TextHook::Clear(bool markRemoved)
{
std::scoped_lock lock(viewMutex);
if (address == 0) return;
if (hp.type & DIRECT_READ) RemoveReadCode();
else RemoveHookCode();
NotifyHookRemove(address, hp.name);
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)

View File

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