make thread linker asynchronous. also refactored, but in a way that makes me very concerned about the stability of host

This commit is contained in:
Akash Mozumdar 2019-06-16 22:57:41 -04:00
parent 3a103890ad
commit 566c0beb0a
4 changed files with 39 additions and 32 deletions

View File

@ -60,7 +60,7 @@ namespace
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<ThreadParam, TextThread, Functor<HashThreadParam>>, std::recursive_mutex> textThreadsByParams;
Synchronized<std::unordered_map<DWORD, ProcessRecord>, std::recursive_mutex> processRecordsByIds; Synchronized<std::unordered_map<DWORD, ProcessRecord>> processRecordsByIds;
Host::ProcessEventHandler OnConnect, OnDisconnect; Host::ProcessEventHandler OnConnect, OnDisconnect;
Host::ThreadEventHandler OnCreate, OnDestroy; Host::ThreadEventHandler OnCreate, OnDestroy;
@ -102,7 +102,7 @@ namespace
case HOST_NOTIFICATION_FOUND_HOOK: case HOST_NOTIFICATION_FOUND_HOOK:
{ {
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, info.text); if (wide.size() > STRING) OnHookFound(info.hp, info.text);
info.hp.type &= ~USING_UNICODE; info.hp.type &= ~USING_UNICODE;
@ -132,7 +132,7 @@ namespace
auto textThread = textThreadsByParams->find(tp); auto textThread = textThreadsByParams->find(tp);
if (textThread == textThreadsByParams->end()) if (textThread == textThreadsByParams->end())
{ {
try { textThread = textThreadsByParams->try_emplace(tp, tp, Host::GetHookParam(tp)).first; } try { textThread = textThreadsByParams->try_emplace(tp, tp, processRecordsByIds->at(tp.processId).GetHook(tp.addr).hp).first; }
catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again catch (std::out_of_range) { continue; } // probably garbage data in pipe, try again
OnCreate(textThread->second); OnCreate(textThread->second);
} }
@ -158,8 +158,6 @@ namespace Host
OnDestroy = [Destroy](TextThread& thread) { thread.Stop(); Destroy(thread); }; OnDestroy = [Destroy](TextThread& thread) { thread.Stop(); Destroy(thread); };
TextThread::Output = Output; TextThread::Output = Output;
processRecordsByIds->try_emplace(console.processId, console.processId, INVALID_HANDLE_VALUE);
OnConnect(console.processId);
textThreadsByParams->try_emplace(console, console, HookParam{}, CONSOLE); textThreadsByParams->try_emplace(console, console, HookParam{}, CONSOLE);
OnCreate(GetThread(console)); OnCreate(GetThread(console));
textThreadsByParams->try_emplace(clipboard, clipboard, HookParam{}, CLIPBOARD); textThreadsByParams->try_emplace(clipboard, clipboard, HookParam{}, CLIPBOARD);
@ -233,16 +231,18 @@ namespace Host
processRecordsByIds->at(processId).Send(FindHookCmd(sp)); processRecordsByIds->at(processId).Send(FindHookCmd(sp));
} }
HookParam GetHookParam(ThreadParam tp)
{
return processRecordsByIds->at(tp.processId).GetHook(tp.addr).hp;
}
TextThread& GetThread(ThreadParam tp) TextThread& GetThread(ThreadParam tp)
{ {
return textThreadsByParams->at(tp); return textThreadsByParams->at(tp);
} }
TextThread* GetThread(int64_t handle)
{
auto textThreadsByParams = ::textThreadsByParams.Acquire();
auto thread = std::find_if(textThreadsByParams->begin(), textThreadsByParams->end(), [&](const auto& thread) { return thread.second.handle == handle; });
return thread != textThreadsByParams->end() ? &thread->second : nullptr;
}
void AddConsoleOutput(std::wstring text) void AddConsoleOutput(std::wstring text)
{ {
GetThread(console).AddSentence(std::move(text)); GetThread(console).AddSentence(std::move(text));

View File

@ -12,11 +12,12 @@ namespace Host
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 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); TextThread* GetThread(int64_t handle);
TextThread& GetThread(ThreadParam tp); TextThread& GetThread(ThreadParam tp);
void AddConsoleOutput(std::wstring text); void AddConsoleOutput(std::wstring text);

View File

@ -234,6 +234,13 @@ DWORD MainWindow::GetSelectedProcessId()
std::array<InfoForExtension, 10> MainWindow::GetMiscInfo(TextThread& thread) std::array<InfoForExtension, 10> MainWindow::GetMiscInfo(TextThread& thread)
{ {
void(*AddSentence)(MainWindow*, int64_t, const wchar_t*) = [](MainWindow* This, int64_t number, const wchar_t* sentence)
{
std::wstring sentenceStr = sentence;
// pointer from Host::GetThread may not stay valid unless on main thread
QMetaObject::invokeMethod(This, [=]() mutable { if (TextThread* thread = Host::GetThread(number)) thread->AddSentence(std::move(sentenceStr)); });
};
return return
{ { { {
{ "current select", &thread == current }, { "current select", &thread == current },
@ -242,6 +249,8 @@ std::array<InfoForExtension, 10> MainWindow::GetMiscInfo(TextThread& thread)
{ "hook address", (int64_t)thread.tp.addr }, { "hook address", (int64_t)thread.tp.addr },
{ "text handle", thread.handle }, { "text handle", thread.handle },
{ "text name", (int64_t)thread.name.c_str() }, { "text name", (int64_t)thread.name.c_str() },
{ "this", (int64_t)this },
{ "void (*AddSentence)(void* this, int64_t number, const wchar_t* sentence)", (int64_t)AddSentence },
{ nullptr, 0 } // nullptr marks end of info array { nullptr, 0 } // nullptr marks end of info array
} }; } };
} }
@ -323,7 +332,7 @@ void MainWindow::RemoveHooks()
for (int i = 0; i < ui->ttCombo->count(); ++i) for (int i = 0; i < ui->ttCombo->count(); ++i)
{ {
ThreadParam tp = ParseTextThreadString(ui->ttCombo->itemText(i)); ThreadParam tp = ParseTextThreadString(ui->ttCombo->itemText(i));
if (tp.processId == GetSelectedProcessId()) hooks[tp.addr] = Host::GetHookParam(tp); if (tp.processId == GetSelectedProcessId()) hooks[tp.addr] = Host::GetThread(tp).hp;
} }
auto hookList = new QListWidget(this); auto hookList = new QListWidget(this);
hookList->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint); hookList->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint);
@ -346,24 +355,23 @@ void MainWindow::RemoveHooks()
void MainWindow::SaveHooks() void MainWindow::SaveHooks()
{ {
if (auto processName = Util::GetModuleFilename(GetSelectedProcessId())) auto processName = Util::GetModuleFilename(GetSelectedProcessId());
{ if (!processName) return;
QHash<uint64_t, QString> hookCodes; QHash<uint64_t, QString> hookCodes;
for (int i = 0; i < ui->ttCombo->count(); ++i) for (int i = 0; i < ui->ttCombo->count(); ++i)
{ {
ThreadParam tp = ParseTextThreadString(ui->ttCombo->itemText(i)); ThreadParam tp = ParseTextThreadString(ui->ttCombo->itemText(i));
if (tp.processId == GetSelectedProcessId()) if (tp.processId == GetSelectedProcessId())
{ {
HookParam hp = Host::GetHookParam(tp); HookParam hp = Host::GetThread(tp).hp;
if (!(hp.type & HOOK_ENGINE)) hookCodes[tp.addr] = S(Util::GenerateCode(hp, tp.processId)); if (!(hp.type & HOOK_ENGINE)) hookCodes[tp.addr] = S(Util::GenerateCode(hp, tp.processId));
} }
} }
auto hookInfo = QStringList() << S(processName.value()) << hookCodes.values(); auto hookInfo = QStringList() << S(processName.value()) << hookCodes.values();
ThreadParam tp = current.load()->tp; ThreadParam tp = current.load()->tp;
if (tp.processId == GetSelectedProcessId()) hookInfo << QString("|%1:%2:%3").arg(tp.ctx).arg(tp.ctx2).arg(S(Util::GenerateCode(Host::GetHookParam(tp), tp.processId))); if (tp.processId == GetSelectedProcessId()) hookInfo << QString("|%1:%2:%3").arg(tp.ctx).arg(tp.ctx2).arg(S(Util::GenerateCode(Host::GetThread(tp).hp, tp.processId)));
QTextFile(HOOK_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Append).write((hookInfo.join(" , ") + "\n").toUtf8()); QTextFile(HOOK_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Append).write((hookInfo.join(" , ") + "\n").toUtf8());
} }
}
void MainWindow::FindHooks() void MainWindow::FindHooks()
{ {

View File

@ -93,13 +93,11 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo) bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo)
{ {
std::lock_guard l(m); std::lock_guard l(m);
static std::unordered_map<int64_t, std::wstring> queuedWritesByHandle; int64_t textHandle = sentenceInfo["text number"];
int64_t textHandle = sentenceInfo["text handle"];
for (auto linkedHandle : linkedTextHandles[textHandle]) queuedWritesByHandle[linkedHandle] += L"\n" + sentence; for (auto linkedHandle : linkedTextHandles[textHandle])
((void(*)(void*, int64_t, const wchar_t*))sentenceInfo["void (*AddSentence)(void* this, int64_t number, const wchar_t* sentence)"])
((void*)sentenceInfo["this"], linkedHandle, sentence.c_str());
if (queuedWritesByHandle[textHandle].empty()) return false; return false;
sentence += queuedWritesByHandle[textHandle];
queuedWritesByHandle[textHandle].clear();
return true;
} }