Skip to content

Commit

Permalink
Fix the main part of search history (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chaoses-Ib committed Sep 22, 2021
1 parent cde77a9 commit 2255112
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Hijacker/Hijacker.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
<ClInclude Include="pch.h" />
<ClInclude Include="pinyin.hpp" />
<ClInclude Include="resource.h" />
<ClInclude Include="search_history.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
Expand All @@ -187,6 +188,7 @@
</ClCompile>
<ClCompile Include="pinyin.cpp" />
<ClCompile Include="pinyin_data.cpp" />
<ClCompile Include="search_history.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Hijacker.rc" />
Expand Down
6 changes: 6 additions & 0 deletions Hijacker/Hijacker.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="search_history.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
Expand All @@ -44,6 +47,9 @@
<ClCompile Include="pinyin_data.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="search_history.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Hijacker.rc">
Expand Down
41 changes: 34 additions & 7 deletions Hijacker/dllmain.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#include "pch.h"
#include <unordered_map>
#include <string_view>
#include <thread>
#include "helper.hpp"
#include "pinyin.hpp"
#include "search_history.hpp"

constexpr wchar_t prop_edit_content[] = L"IbEverythingExt.Content";
constexpr wchar_t prop_edit_processed_content[] = L"IbEverythingExt.ProcessedContent";
constexpr wchar_t prop_main_title[] = L"IbEverythingExt.Title";

std::unordered_map<std::wstring, std::wstring> content_map{};

bool is_modifier_in_blacklist(std::wstring_view modifier) {
using namespace std::literals;

Expand Down Expand Up @@ -408,6 +412,18 @@ LRESULT CALLBACK edit_window_proc(
}
}
break;
case WM_KILLFOCUS:
{
if constexpr (ib::debug_runtime)
DebugOStream() << L"WM_KILLFOCUS\n";

// save the content to map
if (auto content = (std::wstring*)GetPropW(hwnd, prop_edit_content)) {
auto processed_content = (std::wstring*)GetPropW(hwnd, prop_edit_processed_content);
content_map[*processed_content] = *content;
}
}
break;
}
return CallWindowProcW(edit_window_proc_prev, hwnd, uMsg, wParam, lParam);
}
Expand Down Expand Up @@ -474,8 +490,15 @@ BOOL WINAPI SetWindowTextW_detour(
return true;
}
} else if (sv == L"Edit"sv) {
// prevent modifying edit
return true;
auto processed_content = (std::wstring*)GetPropW(hWnd, prop_edit_processed_content);
if (processed_content && *processed_content == lpString) {
// prevent modifying edit
return true;
}

auto iter = content_map.find<std::wstring>(lpString);
if (iter != content_map.end())
return SetWindowTextW_real(hWnd, iter->second.c_str());
}
}
return SetWindowTextW_real(hWnd, lpString);
Expand Down Expand Up @@ -512,25 +535,29 @@ BOOL APIENTRY DllMain( HMODULE hModule,
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if constexpr (ib::debug_runtime)
DebugOStream() << L"DLL_PROCESS_ATTACH\n";

IbDetourAttach(&CreateWindowExW_real, CreateWindowExW_detour);
IbDetourAttach(&SetWindowTextW_real, SetWindowTextW_detour);

// may be loaded after creating windows? it seems that only netutil.dll does
//EnumWindows(enum_window_proc,GetCurrentThreadId());

if constexpr (ib::debug_runtime)
DebugOStream() << L"DLL_PROCESS_ATTACH\n";
search_history_init();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
IbDetourDetach(&SetWindowTextW_real, SetWindowTextW_detour);
IbDetourDetach(&CreateWindowExW_real, CreateWindowExW_detour);

if constexpr (ib::debug_runtime)
DebugOStream() << L"DLL_PROCESS_DETACH\n";

search_history_destroy();

IbDetourDetach(&SetWindowTextW_real, SetWindowTextW_detour);
IbDetourDetach(&CreateWindowExW_real, CreateWindowExW_detour);
break;
}
return TRUE;
Expand Down
169 changes: 169 additions & 0 deletions Hijacker/search_history.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#include "pch.h"
#include "search_history.hpp"
#include <unordered_map>
#include <string>
#include "helper.hpp"

extern std::unordered_map<std::wstring, std::wstring> content_map;

auto WriteFile_real = WriteFile;
BOOL WINAPI WriteFile_detour(
_In_ HANDLE hFile,
_In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
_In_ DWORD nNumberOfBytesToWrite,
_Out_opt_ LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ LPOVERLAPPED lpOverlapped
) {
using namespace std::literals;

do {
// UTF-8
if (nNumberOfBytesToWrite > sizeof "Search,Search Count,Last Search Date\r\n") {
std::string_view sv(static_cast<const char*>(lpBuffer), nNumberOfBytesToWrite);
if (!sv.starts_with("Search,Search Count,Last Search Date\r\n"sv))
break;

/*
Search,Search Count,Last Search Date
"a",4,132767094406609228
"case:regex:""[aA][bB][cC]""[dD单到的]",2,132767108299260960
*/
std::ostringstream out(std::ios_base::binary); // may be longer than buffer (such as "a" -> "nopy:a")
out << "Search,Search Count,Last Search Date\r\n"sv;

std::unordered_map<std::string, uint32_t> search_map{};
uint32_t i = "Search,Search Count,Last Search Date\r\n"sv.size();
while (i < sv.size()) {
if (sv[i++] != '"')
break;
out << '"';

// parse Search and convert processed content to raw content
std::string search;
while (i < sv.size()) {
char c = sv[i++];
if (c == '"') {
if (i < sv.size() && sv[i] == '"') { // ""
search.push_back('"');
i++;
} else
break;
} else
search.push_back(c);
}
{
// search to search_u16
// std::wstring_convert is deprecated in C++ 17
std::wstring search_u16(search.size(), L'\0');
search_u16.resize(MultiByteToWideChar(CP_UTF8, 0, search.data(), search.size(), search_u16.data(), search_u16.size()));

// processed content -> content
auto iter = content_map.find<std::wstring>(search_u16);
if (iter != content_map.end()) {
// content to content_u8
std::string content_u8(iter->second.size() * 3, '\0');
int length = WideCharToMultiByte(CP_UTF8, 0, iter->second.data(), iter->second.size(), content_u8.data(), content_u8.size(), nullptr, nullptr);
if (!length) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break;
content_u8.resize(iter->second.size() * 4);
length = WideCharToMultiByte(CP_UTF8, 0, iter->second.data(), iter->second.size(), content_u8.data(), content_u8.size(), nullptr, nullptr);
}
content_u8.resize(length);

search = content_u8;
}
}
out << search << '"' << ',';
i++; // ','

// parse Search Count and do sums
uint32_t j = i;
while (j < sv.size()) {
if (sv[j++] == ',')
break;
}
j--;
uint32_t count = std::atoi(sv.substr(i, j - i).data());
auto iter = search_map.find<std::string>(search);
if (iter == search_map.end()) {
search_map[search] = count;
} else {
count += iter->second;
search_map.erase(iter);
out << count;
i = j;
}

// the remaining
while (i < sv.size()) {
char c = sv[i++];
out << c;
if (c == '\n')
break;
}
}

const std::string& out_str = out.str();
bool result = WriteFile_real(hFile, out_str.data(), out_str.size(), lpNumberOfBytesWritten, lpOverlapped);
if (*lpNumberOfBytesWritten == out_str.size())
*lpNumberOfBytesWritten = nNumberOfBytesToWrite; // required
return result;
}
} while (false);

return WriteFile_real(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}

auto TextOutW_real = TextOutW;
BOOL WINAPI TextOutW_detour(_In_ HDC hdc, _In_ int x, _In_ int y, _In_reads_(c) LPCWSTR lpString, _In_ int c) {
// TextOutW will only be called from:
// DropdownList
// WindowFromDC() == NULL
// Options: Fonts and Colors
// WindowFromDC() != NULL

/*
using namespace std::literals;
static HDC last_hdc = 0;
static bool is_dropdown_list = false;
if (hdc != last_hdc) { // there may be a collision?
last_hdc = hdc;
is_dropdown_list = false;
HWND hwnd = WindowFromDC(hdc);
wchar_t buf[std::size(L"EVERYTHING_DROPDOWNLIST")];
if (int len = GetClassNameW(hwnd, buf, std::size(buf))) {
if (std::wstring_view(buf, len) == L"EVERYTHING_DROPDOWNLIST"sv) {
is_dropdown_list = true;
}
}
}
if (is_dropdown_list) {
}
*/

if (!WindowFromDC(hdc)) {
std::wstring_view sv(lpString, c);
if (c == 4096) { // max length
sv = std::wstring_view(lpString); // #TODO: is it always zero-terminated?
}

auto iter = content_map.find<std::wstring>(std::wstring(sv)); // #TODO: directly use sv
if (iter != content_map.end()) {
return TextOutW_real(hdc, x, y, iter->second.c_str(), iter->second.size());
}
}
return TextOutW_real(hdc, x, y, lpString, c);
}

void search_history_init() {
IbDetourAttach(&WriteFile_real, WriteFile_detour);
IbDetourAttach(&TextOutW_real, TextOutW_detour);
}

void search_history_destroy() {
IbDetourDetach(&TextOutW_real, TextOutW_detour);
IbDetourDetach(&WriteFile_real, WriteFile_detour);
}
4 changes: 4 additions & 0 deletions Hijacker/search_history.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

void search_history_init();
void search_history_destroy();

0 comments on commit 2255112

Please sign in to comment.