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:
Akash Mozumdar 2019-09-04 12:23:23 -04:00
parent 3794afc7c4
commit 96f235732c
3 changed files with 62 additions and 16 deletions

View File

@ -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();
}); });

View File

@ -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;
}; };

View File

@ -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);