hook search through all exported functions, hook search deeper into stack, fix bug with dlls being loaded next to each other
This commit is contained in:
parent
3794afc7c4
commit
96f235732c
@ -500,8 +500,16 @@ void MainWindow::FindHooks()
|
|||||||
layout.addWidget(&startButton);
|
layout.addWidget(&startButton);
|
||||||
connect(&startButton, &QPushButton::clicked, &dialog, &QDialog::accept);
|
connect(&startButton, &QPushButton::clicked, &dialog, &QDialog::accept);
|
||||||
if (!dialog.exec()) return;
|
if (!dialog.exec()) return;
|
||||||
QByteArray pattern = QByteArray::fromHex(patternInput.text().replace("??", QString::number(XX, 16)).toUtf8());
|
if (patternInput.text().contains('.'))
|
||||||
memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), 25));
|
{
|
||||||
|
wcsncpy_s(sp.module, S(patternInput.text()).c_str(), MAX_MODULE_SIZE - 1);
|
||||||
|
sp.length = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QByteArray pattern = QByteArray::fromHex(patternInput.text().replace("??", QString::number(XX, 16)).toUtf8());
|
||||||
|
memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), PATTERN_SIZE));
|
||||||
|
}
|
||||||
try { filter = S(filterInput.text()); } catch (std::regex_error) {}
|
try { filter = S(filterInput.text()); } catch (std::regex_error) {}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -510,10 +518,13 @@ void MainWindow::FindHooks()
|
|||||||
filter = std::wregex(cjkCheckbox.isChecked() ? L"[\\u3000-\\ua000]{4,}" : L"[\\u0020-\\u1000]{4,}");
|
filter = std::wregex(cjkCheckbox.isChecked() ? L"[\\u3000-\\ua000]{4,}" : L"[\\u0020-\\u1000]{4,}");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hooks = std::make_shared<std::vector<std::pair<HookParam, std::wstring>>>();
|
auto hooks = std::make_shared<std::vector<QString>>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Host::FindHooks(processId, sp, [hooks, filter](HookParam hp, const std::wstring& text) { if (std::regex_search(text, filter)) hooks->push_back({ hp, text }); });
|
Host::FindHooks(processId, sp, [hooks, processId, filter](HookParam hp, const std::wstring& text)
|
||||||
|
{
|
||||||
|
if (std::regex_search(text, filter)) hooks->push_back(S(Util::GenerateCode(hp, processId) + L" => " + text));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (std::out_of_range) { return; }
|
catch (std::out_of_range) { return; }
|
||||||
std::thread([this, hooks, processId]
|
std::thread([this, hooks, processId]
|
||||||
@ -529,8 +540,7 @@ void MainWindow::FindHooks()
|
|||||||
hookList->setAttribute(Qt::WA_DeleteOnClose);
|
hookList->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
hookList->resize({ 750, 300 });
|
hookList->resize({ 750, 300 });
|
||||||
hookList->setWindowTitle(SEARCH_FOR_HOOKS);
|
hookList->setWindowTitle(SEARCH_FOR_HOOKS);
|
||||||
for (auto [hp, text] : *hooks)
|
for (const auto& hook : *hooks) new QListWidgetItem(hook, hookList);
|
||||||
new QListWidgetItem(S(Util::GenerateCode(hp, processId) + L" => " + text), hookList);
|
|
||||||
connect(hookList, &QListWidget::itemClicked, [this](QListWidgetItem* item) { AddHook(item->text().split(" => ")[0]); });
|
connect(hookList, &QListWidget::itemClicked, [this](QListWidgetItem* item) { AddHook(item->text().split(" => ")[0]); });
|
||||||
hookList->show();
|
hookList->show();
|
||||||
|
|
||||||
@ -538,8 +548,11 @@ void MainWindow::FindHooks()
|
|||||||
if (!saveFileName.isEmpty())
|
if (!saveFileName.isEmpty())
|
||||||
{
|
{
|
||||||
QTextFile saveFile(saveFileName, QIODevice::WriteOnly | QIODevice::Truncate);
|
QTextFile saveFile(saveFileName, QIODevice::WriteOnly | QIODevice::Truncate);
|
||||||
for (auto [hp, text] : *hooks)
|
for (const auto& hook : *hooks)
|
||||||
saveFile.write(S(Util::GenerateCode(hp, processId) + L" => " + text + L"\n").toUtf8());
|
{
|
||||||
|
saveFile.write(hook.toUtf8());
|
||||||
|
saveFile.write("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hooks->clear();
|
hooks->clear();
|
||||||
});
|
});
|
||||||
|
@ -67,6 +67,7 @@ struct SearchParam
|
|||||||
maxRecords = 100000,
|
maxRecords = 100000,
|
||||||
codepage = SHIFT_JIS;
|
codepage = SHIFT_JIS;
|
||||||
uintptr_t padding = 0, minAddress = 0, maxAddress = (uintptr_t)-1;
|
uintptr_t padding = 0, minAddress = 0, maxAddress = (uintptr_t)-1;
|
||||||
|
wchar_t module[MAX_MODULE_SIZE] = {};
|
||||||
wchar_t text[PATTERN_SIZE] = {}; // text to search for
|
wchar_t text[PATTERN_SIZE] = {}; // text to search for
|
||||||
void(*hookPostProcessor)(HookParam&) = nullptr;
|
void(*hookPostProcessor)(HookParam&) = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@ namespace
|
|||||||
{
|
{
|
||||||
SearchParam sp;
|
SearchParam sp;
|
||||||
|
|
||||||
constexpr int CACHE_SIZE = 500'000;
|
constexpr int MAX_STRING_SIZE = 500, CACHE_SIZE = 300'000;
|
||||||
struct HookRecord
|
struct HookRecord
|
||||||
{
|
{
|
||||||
~HookRecord()
|
~HookRecord()
|
||||||
@ -32,7 +32,7 @@ namespace
|
|||||||
}
|
}
|
||||||
uint64_t address = 0;
|
uint64_t address = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
char text[500] = {};
|
char text[MAX_STRING_SIZE] = {};
|
||||||
};
|
};
|
||||||
std::unique_ptr<HookRecord[]> records;
|
std::unique_ptr<HookRecord[]> records;
|
||||||
long recordsAvailable;
|
long recordsAvailable;
|
||||||
@ -114,18 +114,31 @@ namespace
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBadStrPtr(void* str)
|
||||||
|
{
|
||||||
|
if (str < (void*)0x1000) return true;
|
||||||
|
|
||||||
|
MEMORY_BASIC_INFORMATION info;
|
||||||
|
if (VirtualQuery(str, &info, sizeof(info)) == 0 || info.Protect < PAGE_READONLY || info.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true;
|
||||||
|
|
||||||
|
void* regionEnd = (BYTE*)info.BaseAddress + info.RegionSize;
|
||||||
|
if ((BYTE*)str + MAX_STRING_SIZE <= regionEnd) return false;
|
||||||
|
return IsBadStrPtr(regionEnd);
|
||||||
|
}
|
||||||
|
|
||||||
void Send(char** stack, uintptr_t address)
|
void Send(char** stack, uintptr_t address)
|
||||||
{
|
{
|
||||||
// it is unsafe to call ANY external functions from this, as they may have been hooked (if called the hook would call this function making an infinite loop)
|
// it is unsafe to call ANY external functions from this, as they may have been hooked (if called the hook would call this function making an infinite loop)
|
||||||
// the exceptions are compiler intrinsics like _InterlockedDecrement
|
// the exceptions are compiler intrinsics like _InterlockedDecrement
|
||||||
if (recordsAvailable <= 0) return;
|
if (recordsAvailable <= 0) return;
|
||||||
for (int i = -registers; i < 6; ++i)
|
for (int i = -registers; i < 10; ++i)
|
||||||
{
|
{
|
||||||
int length = 0, sum = 0;
|
int length = 0, sum = 0;
|
||||||
char* str = stack[i] + sp.padding;
|
char* str = stack[i] + sp.padding;
|
||||||
__try { for (; (str[length] || str[length + 1]) && length < 500; length += 2) sum += str[length] + str[length + 1]; }
|
if (IsBadStrPtr(str)) return; // seems to improve performance; TODO: more tests and benchmarks to confirm
|
||||||
|
__try { for (; (str[length] || str[length + 1]) && length < MAX_STRING_SIZE; length += 2) sum += str[length] + str[length + 1]; }
|
||||||
__except (EXCEPTION_EXECUTE_HANDLER) {}
|
__except (EXCEPTION_EXECUTE_HANDLER) {}
|
||||||
if (length > STRING && length < 499)
|
if (length > STRING && length < MAX_STRING_SIZE - 1)
|
||||||
{
|
{
|
||||||
__try
|
__try
|
||||||
{
|
{
|
||||||
@ -156,6 +169,24 @@ void Send(char** stack, uintptr_t address)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint64_t> GetFunctions(uintptr_t module)
|
||||||
|
{
|
||||||
|
if (!module) return {};
|
||||||
|
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)module;
|
||||||
|
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) return {};
|
||||||
|
IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)(module + dosHeader->e_lfanew);
|
||||||
|
if (ntHeader->Signature != IMAGE_NT_SIGNATURE) return {};
|
||||||
|
DWORD exportAddress = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||||
|
if (!exportAddress) return {};
|
||||||
|
IMAGE_EXPORT_DIRECTORY* exportDirectory = (IMAGE_EXPORT_DIRECTORY*)(module + exportAddress);
|
||||||
|
std::vector<uint64_t> functions;
|
||||||
|
for (int i = 0; i < exportDirectory->NumberOfNames; ++i)
|
||||||
|
//char* funcName = (char*)(module + *(DWORD*)(module + exportDirectory->AddressOfNames + i * sizeof(DWORD)));
|
||||||
|
functions.push_back(module + *(DWORD*)(module + exportDirectory->AddressOfFunctions +
|
||||||
|
sizeof(DWORD) * *(WORD*)(module + exportDirectory->AddressOfNameOrdinals + i * sizeof(WORD))));
|
||||||
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
void SearchForHooks(SearchParam spUser)
|
void SearchForHooks(SearchParam spUser)
|
||||||
{
|
{
|
||||||
std::thread([=]
|
std::thread([=]
|
||||||
@ -175,12 +206,13 @@ void SearchForHooks(SearchParam spUser)
|
|||||||
{
|
{
|
||||||
VirtualQuery((void*)moduleStopAddress, &info, sizeof(info));
|
VirtualQuery((void*)moduleStopAddress, &info, sizeof(info));
|
||||||
moduleStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize;
|
moduleStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize;
|
||||||
} while (info.Protect > PAGE_NOACCESS);
|
} while (info.Protect > PAGE_EXECUTE);
|
||||||
moduleStopAddress -= info.RegionSize;
|
moduleStopAddress -= info.RegionSize;
|
||||||
|
|
||||||
ConsoleOutput(STARTING_SEARCH);
|
ConsoleOutput(STARTING_SEARCH);
|
||||||
std::vector<uint64_t> addresses = Util::SearchMemory(sp.pattern, sp.length, PAGE_EXECUTE, sp.minAddress, sp.maxAddress);
|
std::vector<uint64_t> addresses;
|
||||||
for (auto& addr : addresses) addr += sp.offset;
|
if (*sp.module) addresses = GetFunctions((uintptr_t)GetModuleHandleW(sp.module));
|
||||||
|
else for (auto& addr : addresses = Util::SearchMemory(sp.pattern, sp.length, PAGE_EXECUTE, sp.minAddress, sp.maxAddress)) addr += sp.offset;
|
||||||
addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) { return addr > moduleStartAddress && addr < moduleStopAddress; }), addresses.end());
|
addresses.erase(std::remove_if(addresses.begin(), addresses.end(), [&](uint64_t addr) { return addr > moduleStartAddress && addr < moduleStopAddress; }), addresses.end());
|
||||||
*(void**)(trampoline + send_offset) = Send;
|
*(void**)(trampoline + send_offset) = Send;
|
||||||
auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE);
|
auto trampolines = (decltype(trampoline)*)VirtualAlloc(NULL, sizeof(trampoline) * addresses.size(), MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
Loading…
Reference in New Issue
Block a user