From c89b05cf428ea5b2f6752fdbf1c3a4d248f11ca2 Mon Sep 17 00:00:00 2001 From: Akash Mozumdar Date: Thu, 31 Jan 2019 11:41:43 -0500 Subject: [PATCH] add cli, move exception handlers and code parsers into host --- CMakeLists.txt | 1 + GUI/CMakeLists.txt | 7 +- GUI/host/CMakeLists.txt | 8 + GUI/host/cli.cpp | 29 ++++ GUI/{ => host}/exception.cpp | 2 +- GUI/host/util.cpp | 267 +++++++++++++++++++++++++++++++- GUI/host/util.h | 3 + GUI/mainwindow.cpp | 8 +- GUI/misc.cpp | 286 ----------------------------------- GUI/misc.h | 5 +- 10 files changed, 315 insertions(+), 301 deletions(-) create mode 100644 GUI/host/CMakeLists.txt create mode 100644 GUI/host/cli.cpp rename GUI/{ => host}/exception.cpp (98%) delete mode 100644 GUI/misc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e53e351..2e88715 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,5 +31,6 @@ set(CMAKE_CONFIGURATION_TYPES Debug Release) file(COPY ${LIBS} DESTINATION ${CMAKE_FINAL_OUTPUT_DIRECTORY}) add_subdirectory(GUI) +add_subdirectory(GUI/host) add_subdirectory(vnrhook) add_subdirectory(extensions) diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index aced78d..10c1fb6 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -6,18 +6,17 @@ set(AUTOMOC OFF) set(RESOURCE_FILES Textractor.rc Textractor.ico) add_compile_options(/GL) # Populate a CMake variable with the sources -set(gui_SRCS +set(gui_src main.cpp mainwindow.cpp - misc.cpp - exception.cpp extenwindow.cpp + host/exception.cpp host/host.cpp host/textthread.cpp host/util.cpp ${RESOURCE_FILES} ) -add_executable(${PROJECT_NAME} WIN32 ${gui_SRCS}) +add_executable(${PROJECT_NAME} WIN32 ${gui_src}) target_link_libraries(${PROJECT_NAME} Qt5::Widgets winhttp) install_qt5_libs(${PROJECT_NAME}) diff --git a/GUI/host/CMakeLists.txt b/GUI/host/CMakeLists.txt new file mode 100644 index 0000000..fcd83f7 --- /dev/null +++ b/GUI/host/CMakeLists.txt @@ -0,0 +1,8 @@ +# The CLI isn't used by Textractor itself, but is here for other people that want to build projects on top of Textractor +add_executable(TextractorCLI + cli.cpp + exception.cpp + host.cpp + textthread.cpp + util.cpp +) diff --git a/GUI/host/cli.cpp b/GUI/host/cli.cpp new file mode 100644 index 0000000..8b4dd86 --- /dev/null +++ b/GUI/host/cli.cpp @@ -0,0 +1,29 @@ +#include "host.h" +#include "util.h" +#include +#include +#include + +int main() +{ + _setmode(_fileno(stdout), _O_U16TEXT); + _setmode(_fileno(stdin), _O_U16TEXT); + wprintf_s(L"Usage: {'attach'|'detach'|hookcode} -Pprocessid\n"); + Host::Start([](auto) {}, [](auto) {}, [](auto) {}, [](auto) {}, [](TextThread* thread, std::wstring& output) + { + wprintf_s(L"[%I64X:%I32X:%I64X:%I64X:%I64X:%s] %s\n", thread->handle, thread->tp.processId, thread->tp.addr, thread->tp.ctx, thread->tp.ctx2, thread->name.c_str(), output.c_str()); + return false; + }); + wchar_t input[500] = {}; + while (fgetws(input, 500, stdin)) + { + wchar_t command[500] = {}; + DWORD processId = 0; + if (swscanf(input, L"%500s -P%d", command, &processId) != 2) ExitProcess(0); + if (_wcsicmp(command, L"attach") == 0) Host::InjectProcess(processId); + else if (_wcsicmp(command, L"detach") == 0) Host::DetachProcess(processId); + else if (auto hp = Util::ParseCode(command)) Host::InsertHook(processId, hp.value()); + else ExitProcess(0); + } + ExitProcess(0); +} diff --git a/GUI/exception.cpp b/GUI/host/exception.cpp similarity index 98% rename from GUI/exception.cpp rename to GUI/host/exception.cpp index ba39979..93a7f0f 100644 --- a/GUI/exception.cpp +++ b/GUI/host/exception.cpp @@ -1,4 +1,4 @@ -#include "host/util.h" +#include "util.h" #include namespace diff --git a/GUI/host/util.cpp b/GUI/host/util.cpp index 3b2a9cf..71d3bc7 100644 --- a/GUI/host/util.cpp +++ b/GUI/host/util.cpp @@ -1,7 +1,246 @@ -#include "util.h" -#include "types.h" +#include "util.h" +#include "defs.h" +#include "host.h" +#include #include +namespace +{ + std::optional ParseRCode(std::wstring RCode) + { + std::wsmatch match; + HookParam hp = {}; + hp.type |= DIRECT_READ; + + // {S|Q|V} + switch (RCode[0]) + { + case L'S': + break; + case L'Q': + hp.type |= USING_UNICODE; + break; + case L'V': + hp.type |= USING_UTF8; + break; + default: + return {}; + } + RCode.erase(0, 1); + + // [codepage#] + if (std::regex_search(RCode, match, std::wregex(L"^([0-9]+)#"))) + { + hp.codepage = std::stoi(match[1]); + RCode.erase(0, match[0].length()); + } + + // [*deref_offset] + if (RCode[0] == L'0') RCode.erase(0, 1); // Legacy + if (std::regex_search(RCode, match, std::wregex(L"^\\*(-?[[:xdigit:]]+)"))) + { + hp.type |= DATA_INDIRECT; + hp.index = std::stoi(match[1], nullptr, 16); + RCode.erase(0, match[0].length()); + } + + // @addr + if (!std::regex_match(RCode, match, std::wregex(L"@([[:xdigit:]]+)"))) return {}; + hp.address = std::stoull(match[1], nullptr, 16); + return hp; + } + + std::optional ParseSCode(std::wstring SCode) + { + std::wsmatch match; + HookParam hp = {}; + hp.type |= READ_SEARCH; + + // [codepage#] + if (std::regex_search(SCode, match, std::wregex(L"^([0-9]+)#"))) + { + hp.codepage = std::stoi(match[1]); + SCode.erase(0, match[0].length()); + } + else + { + hp.codepage = Host::defaultCodepage; + } + + wcscpy_s(hp.text, SCode.c_str()); + + return hp; + } + + std::optional ParseHCode(std::wstring HCode) + { + std::wsmatch match; + HookParam hp = {}; + + // {A|B|W|S|Q|V} + switch (HCode[0]) + { + case L'S': + hp.type |= USING_STRING; + break; + case L'A': + hp.type |= BIG_ENDIAN; + hp.length_offset = 1; + break; + case L'B': + hp.length_offset = 1; + break; + case L'Q': + hp.type |= USING_STRING | USING_UNICODE; + break; + case L'W': + hp.type |= USING_UNICODE; + hp.length_offset = 1; + break; + case L'V': + hp.type |= USING_STRING | USING_UTF8; + break; + default: + return {}; + } + HCode.erase(0, 1); + + // [N] + if (HCode[0] == L'N') + { + hp.type |= NO_CONTEXT; + HCode.erase(0, 1); + } + + // [codepage#] + if (std::regex_search(HCode, match, std::wregex(L"^([0-9]+)#"))) + { + hp.codepage = std::stoi(match[1]); + HCode.erase(0, match[0].length()); + } + + // data_offset + if (!std::regex_search(HCode, match, std::wregex(L"^-?[[:xdigit:]]+"))) return {}; + hp.offset = std::stoi(match[0], nullptr, 16); + HCode.erase(0, match[0].length()); + + // [*deref_offset1] + if (std::regex_search(HCode, match, std::wregex(L"^\\*(-?[[:xdigit:]]+)"))) + { + hp.type |= DATA_INDIRECT; + hp.index = std::stoi(match[1], nullptr, 16); + HCode.erase(0, match[0].length()); + } + + // [:split_offset[*deref_offset2]] + if (std::regex_search(HCode, match, std::wregex(L"^:(-?[[:xdigit:]]+)"))) + { + hp.type |= USING_SPLIT; + hp.split = std::stoi(match[1], nullptr, 16); + HCode.erase(0, match[0].length()); + + if (std::regex_search(HCode, match, std::wregex(L"^\\*(-?[[:xdigit:]]+)"))) + { + hp.type |= SPLIT_INDIRECT; + hp.split_index = std::stoi(match[1], nullptr, 16); + HCode.erase(0, match[0].length()); + } + } + + // @addr[:module[:func]] + if (!std::regex_match(HCode, match, std::wregex(L"@([[:xdigit:]]+)(:.+?)?(:.+)?"))) return {}; + hp.address = std::stoull(match[1], nullptr, 16); + if (match[2].matched) + { + hp.type |= MODULE_OFFSET; + wcscpy_s(hp.module, match[2].str().erase(0, 1).c_str()); + } + if (match[3].matched) + { + hp.type |= FUNCTION_OFFSET; + std::wstring func = match[3]; + strcpy_s(hp.function, std::string(func.begin(), func.end()).erase(0, 1).c_str()); + } + + // ITH has registers offset by 4 vs AGTH: need this to correct + if (hp.offset < 0) hp.offset -= 4; + if (hp.split < 0) hp.split -= 4; + + return hp; + } + + std::wstring GenerateRCode(HookParam hp) + { + std::wstringstream RCode(L"R"); + + if (hp.type & USING_UNICODE) + { + RCode << "Q"; + } + else + { + RCode << "S"; + if (hp.codepage != 0) RCode << hp.codepage << "#"; + } + + RCode << std::uppercase << std::hex; + + if (hp.type & DATA_INDIRECT) RCode << "*" << hp.index; + + RCode << "@" << hp.address; + + return RCode.str(); + } + + std::wstring GenerateHCode(HookParam hp, DWORD processId) + { + std::wstringstream HCode(L"H"); + + if (hp.type & USING_UNICODE) + { + if (hp.type & USING_STRING) HCode << "Q"; + else HCode << "W"; + } + else + { + if (hp.type & USING_STRING) HCode << "S"; + else if (hp.type & BIG_ENDIAN) HCode << "A"; + else HCode << "B"; + } + if (hp.type & NO_CONTEXT) HCode << "N"; + if (hp.text_fun || hp.filter_fun || hp.hook_fun) HCode << "X"; // no AGTH equivalent + + if (hp.codepage != 0 && !(hp.type & USING_UNICODE)) HCode << hp.codepage << "#"; + + HCode << std::uppercase << std::hex; + + if (hp.offset < 0) hp.offset += 4; + if (hp.split < 0) hp.split += 4; + + HCode << hp.offset; + if (hp.type & DATA_INDIRECT) HCode << "*" << hp.index; + if (hp.type & USING_SPLIT) HCode << ":" << hp.split; + if (hp.type & SPLIT_INDIRECT) HCode << "*" << hp.split_index; + + // Attempt to make the address relative + if (!(hp.type & MODULE_OFFSET)) + if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId)) + if (MEMORY_BASIC_INFORMATION info = {}; VirtualQueryEx(process, (LPCVOID)hp.address, &info, sizeof(info))) + if (auto moduleName = Util::GetModuleFilename(processId, (HMODULE)info.AllocationBase)) + { + hp.type |= MODULE_OFFSET; + hp.address -= (uint64_t)info.AllocationBase; + wcscpy_s(hp.module, moduleName->c_str() + moduleName->rfind(L'\\') + 1); + } + + HCode << "@" << hp.address; + if (hp.type & MODULE_OFFSET) HCode << ":" << hp.module; + if (hp.type & FUNCTION_OFFSET) HCode << ":" << hp.function; + + return HCode.str(); + } +} + namespace Util { std::optional GetModuleFilename(DWORD processId, HMODULE module) @@ -45,4 +284,28 @@ namespace Util return RemoveRepetition(text = end - len), true; return false; } + + std::optional ParseCode(std::wstring code) + { + if (code[0] == L'/') code.erase(0, 1); // legacy/AGTH compatibility + if (code[0] == L'R') return ParseRCode(code.erase(0, 1)); + else if (code[0] == L'S') return ParseSCode(code.erase(0, 1)); + else if (code[0] == L'H') return ParseHCode(code.erase(0, 1)); + return {}; + } + + std::wstring GenerateCode(HookParam hp, DWORD processId) + { + return hp.type & DIRECT_READ ? GenerateRCode(hp) : GenerateHCode(hp, processId); + } + + TEST( + assert(StringToWideString(u8"こんにちは").value() == L"こんにちは"), + assert(ParseCode(L"/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")), + assert(ParseCode(L"HB4@0")), + assert(ParseCode(L"/RS*10@44")), + assert(!ParseCode(L"HQ@4")), + assert(!ParseCode(L"/RW@44")), + assert(!ParseCode(L"/HWG@33")) + ); } diff --git a/GUI/host/util.h b/GUI/host/util.h index 46ff6af..5133339 100644 --- a/GUI/host/util.h +++ b/GUI/host/util.h @@ -1,6 +1,7 @@ #pragma once #include "common.h" +#include "types.h" namespace Util { @@ -10,4 +11,6 @@ namespace Util std::optional StringToWideString(std::string text, UINT encoding = CP_UTF8); // return true if repetition found (see https://github.com/Artikash/Textractor/issues/40) bool RemoveRepetition(std::wstring& text); + std::optional ParseCode(std::wstring code); + std::wstring GenerateCode(HookParam hp, DWORD processId); } diff --git a/GUI/mainwindow.cpp b/GUI/mainwindow.cpp index 6adefdf..4582feb 100644 --- a/GUI/mainwindow.cpp +++ b/GUI/mainwindow.cpp @@ -103,7 +103,7 @@ void MainWindow::ProcessConnected(DWORD processId) auto hookList = std::find_if(allProcesses.rbegin(), allProcesses.rend(), [&](QString hookList) { return hookList.contains(process); }); if (hookList != allProcesses.rend()) for (auto hookCode : hookList->split(" , ")) - if (auto hp = ParseCode(hookCode)) Host::InsertHook(processId, hp.value()); + if (auto hp = Util::ParseCode(S(hookCode))) Host::InsertHook(processId, hp.value()); }); } @@ -117,7 +117,7 @@ void MainWindow::ProcessDisconnected(DWORD processId) void MainWindow::ThreadAdded(TextThread* thread) { - QString ttString = TextThreadString(thread) + S(thread->name) + " (" + GenerateCode(thread->hp, thread->tp.processId) + ")"; + QString ttString = TextThreadString(thread) + S(thread->name) + " (" + S(Util::GenerateCode(thread->hp, thread->tp.processId)) + ")"; QMetaObject::invokeMethod(this, [this, ttString] { ui->ttCombo->addItem(ttString); @@ -261,7 +261,7 @@ void MainWindow::DetachProcess() void MainWindow::AddHook() { if (QString hookCode = QInputDialog::getText(this, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, "", &ok, Qt::WindowCloseButtonHint); ok) - if (auto hp = ParseCode(hookCode)) Host::InsertHook(GetSelectedProcessId(), hp.value()); + if (auto hp = Util::ParseCode(S(hookCode))) Host::InsertHook(GetSelectedProcessId(), hp.value()); else Host::AddConsoleOutput(INVALID_CODE); } @@ -276,7 +276,7 @@ void MainWindow::SaveHooks() if (tp.processId == GetSelectedProcessId()) { HookParam hp = Host::GetHookParam(tp); - if (!(hp.type & HOOK_ENGINE)) hookCodes[tp.addr] = GenerateCode(hp, tp.processId); + if (!(hp.type & HOOK_ENGINE)) hookCodes[tp.addr] = S(Util::GenerateCode(hp, tp.processId)); } } QTextFile(HOOK_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Append).write((S(processName.value()) + " , " + QStringList(hookCodes.values()).join(" , ") + "\n").toUtf8()); diff --git a/GUI/misc.cpp b/GUI/misc.cpp deleted file mode 100644 index a7ab1ee..0000000 --- a/GUI/misc.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include "misc.h" -#include "const.h" -#include "defs.h" -#include "host/host.h" -#include "host/util.h" -#include -#include - -namespace -{ - std::optional ParseRCode(QString RCode) - { - HookParam hp = {}; - hp.type |= DIRECT_READ; - - // {S|Q|V} - switch (RCode.at(0).unicode()) - { - case L'S': - break; - case L'Q': - hp.type |= USING_UNICODE; - break; - case L'V': - hp.type |= USING_UTF8; - break; - default: - return {}; - } - RCode.remove(0, 1); - - // [codepage#] - QRegularExpressionMatch codepage = QRegularExpression("^([0-9]+)#").match(RCode); - if (codepage.hasMatch()) - { - hp.codepage = codepage.captured(1).toInt(); - RCode.remove(0, codepage.captured(0).length()); - } - - // [*deref_offset] - if (RCode.at(0).unicode() == L'0') RCode.remove(0, 1); // Legacy - QRegularExpressionMatch deref = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(RCode); - if (deref.hasMatch()) - { - hp.type |= DATA_INDIRECT; - hp.index = deref.captured(1).toInt(nullptr, 16); - RCode.remove(0, deref.captured(0).length()); - } - - // @addr - QRegularExpressionMatch address = QRegularExpression("^@([[:xdigit:]]+)$").match(RCode); - if (!address.hasMatch()) return {}; - hp.address = address.captured(1).toULongLong(nullptr, 16); - return hp; - } - - std::optional ParseSCode(QString SCode) - { - HookParam hp = {}; - hp.type |= READ_SEARCH; - - // [codepage#] - QRegularExpressionMatch codepage = QRegularExpression("^([0-9]+)#").match(SCode); - if (codepage.hasMatch()) - { - hp.codepage = codepage.captured(1).toInt(); - SCode.remove(0, codepage.captured(0).length()); - } - else - { - hp.codepage = Host::defaultCodepage; - } - - wcscpy_s(hp.text, S(SCode).c_str()); - - return hp; - } - - std::optional ParseHCode(QString HCode) - { - HookParam hp = {}; - - // {A|B|W|S|Q|V} - switch (HCode.at(0).unicode()) - { - case L'S': - hp.type |= USING_STRING; - break; - case L'A': - hp.type |= BIG_ENDIAN; - hp.length_offset = 1; - break; - case L'B': - hp.length_offset = 1; - break; - case L'Q': - hp.type |= USING_STRING | USING_UNICODE; - break; - case L'W': - hp.type |= USING_UNICODE; - hp.length_offset = 1; - break; - case L'V': - hp.type |= USING_STRING | USING_UTF8; - break; - default: - return {}; - } - HCode.remove(0, 1); - - // [N] - if (HCode.at(0).unicode() == L'N') - { - hp.type |= NO_CONTEXT; - HCode.remove(0, 1); - } - - // [codepage#] - QRegularExpressionMatch codepage = QRegularExpression("^([0-9]+)#").match(HCode); - if (codepage.hasMatch()) - { - hp.codepage = codepage.captured(1).toInt(); - HCode.remove(0, codepage.captured(0).length()); - } - - // data_offset - QRegularExpressionMatch dataOffset = QRegularExpression("^\\-?[[:xdigit:]]+").match(HCode); - if (!dataOffset.hasMatch()) return {}; - hp.offset = dataOffset.captured(0).toInt(nullptr, 16); - HCode.remove(0, dataOffset.captured(0).length()); - - // [*deref_offset1] - QRegularExpressionMatch deref1 = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(HCode); - if (deref1.hasMatch()) - { - hp.type |= DATA_INDIRECT; - hp.index = deref1.captured(1).toInt(nullptr, 16); - HCode.remove(0, deref1.captured(0).length()); - } - - // [:split_offset[*deref_offset2]] - QRegularExpressionMatch splitOffset = QRegularExpression("^\\:(\\-?[[:xdigit:]]+)").match(HCode); - if (splitOffset.hasMatch()) - { - hp.type |= USING_SPLIT; - hp.split = splitOffset.captured(1).toInt(nullptr, 16); - HCode.remove(0, splitOffset.captured(0).length()); - - QRegularExpressionMatch deref2 = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(HCode); - if (deref2.hasMatch()) - { - hp.type |= SPLIT_INDIRECT; - hp.split_index = deref2.captured(1).toInt(nullptr, 16); - HCode.remove(0, deref2.captured(0).length()); - } - } - - // @addr[:module[:func]] - QStringList addressPieces = HCode.split(":"); - QRegularExpressionMatch address = QRegularExpression("^@([[:xdigit:]]+)$").match(addressPieces.at(0)); - if (!address.hasMatch()) return {}; - hp.address = address.captured(1).toULongLong(nullptr, 16); - if (addressPieces.size() > 1) - { - hp.type |= MODULE_OFFSET; - wcscpy_s(hp.module, S(addressPieces.at(1)).c_str()); - } - if (addressPieces.size() > 2) - { - hp.type |= FUNCTION_OFFSET; - strcpy_s(hp.function, addressPieces.at(2).toStdString().c_str()); - } - - // ITH has registers offset by 4 vs AGTH: need this to correct - if (hp.offset < 0) hp.offset -= 4; - if (hp.split < 0) hp.split -= 4; - - return hp; - } - - QString GenerateRCode(HookParam hp) - { - QString RCode = "R"; - QTextStream codeBuilder(&RCode); - - if (hp.type & USING_UNICODE) - { - codeBuilder << "Q"; - } - else - { - codeBuilder << "S"; - if (hp.codepage != 0) codeBuilder << hp.codepage << "#"; - } - - codeBuilder.setIntegerBase(16); - codeBuilder.setNumberFlags(QTextStream::UppercaseDigits); - - if (hp.type & DATA_INDIRECT) codeBuilder << "*" << hp.index; - - codeBuilder << "@" << hp.address; - - return RCode; - } - - QString GenerateHCode(HookParam hp, DWORD processId) - { - QString HCode = "H"; - QTextStream codeBuilder(&HCode); - - if (hp.type & USING_UNICODE) - { - if (hp.type & USING_STRING) codeBuilder << "Q"; - else codeBuilder << "W"; - } - else - { - if (hp.type & USING_STRING) codeBuilder << "S"; - else if (hp.type & BIG_ENDIAN) codeBuilder << "A"; - else codeBuilder << "B"; - } - if (hp.type & NO_CONTEXT) codeBuilder << "N"; - if (hp.text_fun || hp.filter_fun || hp.hook_fun) codeBuilder << "X"; // no AGTH equivalent - - if (hp.codepage != 0 && !(hp.type & USING_UNICODE)) codeBuilder << hp.codepage << "#"; - - codeBuilder.setIntegerBase(16); - codeBuilder.setNumberFlags(QTextStream::UppercaseDigits); - - if (hp.offset < 0) hp.offset += 4; - if (hp.split < 0) hp.split += 4; - - codeBuilder << hp.offset; - if (hp.type & DATA_INDIRECT) codeBuilder << "*" << hp.index; - if (hp.type & USING_SPLIT) codeBuilder << ":" << hp.split; - if (hp.type & SPLIT_INDIRECT) codeBuilder << "*" << hp.split_index; - - // Attempt to make the address relative - if (!(hp.type & MODULE_OFFSET)) - if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId)) - if (MEMORY_BASIC_INFORMATION info = {}; VirtualQueryEx(process, (LPCVOID)hp.address, &info, sizeof(info))) - if (auto moduleName = Util::GetModuleFilename(processId, (HMODULE)info.AllocationBase)) - { - hp.type |= MODULE_OFFSET; - hp.address -= (uint64_t)info.AllocationBase; - wcscpy_s(hp.module, moduleName->c_str() + moduleName->rfind(L'\\') + 1); - } - - codeBuilder << "@" << hp.address; - if (hp.type & MODULE_OFFSET) codeBuilder << ":" << S(hp.module); - if (hp.type & FUNCTION_OFFSET) codeBuilder << ":" << hp.function; - - return HCode; - } -} - -std::optional ParseCode(QString code) -{ - if (code.startsWith("/")) code.remove(0, 1); // legacy/AGTH compatibility - if (code.startsWith("R")) return ParseRCode(code.remove(0, 1)); - else if (code.startsWith("S")) return ParseSCode(code.remove(0, 1)); - else if (code.startsWith("H")) return ParseHCode(code.remove(0, 1)); - else return {}; -} - -QString GenerateCode(HookParam hp, DWORD processId) -{ - if (hp.type & DIRECT_READ) return GenerateRCode(hp); - else return GenerateHCode(hp, processId); -} - -HMODULE LoadLibraryOnce(std::wstring fileName) -{ - HMODULE module = GetModuleHandleW(fileName.c_str()); - if (!module) module = LoadLibraryW(fileName.c_str()); - return module; -} - -TEST( - assert(ParseCode("/HQN936#-c*C:C*1C@4AA:gdi.dll:GetTextOutA")), - assert(ParseCode("HB4@0")), - assert(ParseCode("/RS*10@44")), - assert(!ParseCode("HQ@4")), - assert(!ParseCode("/RW@44")), - assert(!ParseCode("/HWG@33")) -); diff --git a/GUI/misc.h b/GUI/misc.h index 8b48496..c932300 100644 --- a/GUI/misc.h +++ b/GUI/misc.h @@ -1,7 +1,6 @@ #pragma once #include "qtcommon.h" -#include "types.h" struct QTextFile : QFile { @@ -11,6 +10,4 @@ struct QTextFile : QFile inline std::wstring S(const QString& S) { return { S.toStdWString() }; } inline QString S(const std::wstring& S) { return QString::fromStdWString(S); } -std::optional ParseCode(QString HCode); -QString GenerateCode(HookParam hp, DWORD processId); -HMODULE LoadLibraryOnce(std::wstring fileName); +inline HMODULE LoadLibraryOnce(std::wstring fileName) { if (HMODULE module = GetModuleHandleW(fileName.c_str())) return module; return LoadLibraryW(fileName.c_str()); }