/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com) * This file is part of the Interactive Text Hooker. * Interactive Text Hooker is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "window.h" #include "ProcessWindow.h" #include "resource.h" #include "language.h" #include "host/host.h" #include "host/hookman.h" #include "vnrhook/include/const.h" #include "version.h" #include "ProfileManager.h" #include "host/settings.h" #include "profile/Profile.h" #include "TextBuffer.h" #include "profile/misc.h" #define CMD_SIZE 512 static WNDPROC proc, proccmd, procChar; static WCHAR last_cmd[CMD_SIZE]; extern HINSTANCE hIns; // main.cpp HWND hMainWnd, hwndCombo, hwndProcessComboBox, hwndEdit, hwndCmd; HWND hwndProcess; HWND hwndOption, hwndTop, hwndClear, hwndSave, hwndExtensions, hwndRemoveHook; HWND hProcDlg, hOptionDlg; HBRUSH hWhiteBrush; DWORD background; ProcessWindow* pswnd; TextBuffer* texts; extern ProfileManager* pfman; // ProfileManager.cpp extern HookManager* man; // main.cpp extern Settings* setman; // main.cpp #define COMMENT_BUFFER_LENGTH 512 static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH]; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void SaveSettings(); // main.cpp extern LONG split_time, process_time, inject_delay, insert_delay, auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp static int last_select, last_edit; ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = NULL; wcex.hbrBackground = GetStockBrush(WHITE_BRUSH); wcex.lpszMenuName = NULL; wcex.lpszClassName = ClassName; wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1); return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, DWORD nAdmin, RECT* rc) { hIns = hInstance; LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName; hMainWnd = CreateWindow(ClassName, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, NULL, NULL, hInstance, 0); if (!hMainWnd) return FALSE; ShowWindow(hMainWnd, SW_SHOWNORMAL); UpdateWindow(hMainWnd); return TRUE; } DWORD SaveProcessProfile(DWORD pid); // ProfileManager.cpp BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { SetWindowText(GetDlgItem(hDlg, IDC_EDIT1), std::to_wstring((long long)split_time).c_str()); } return TRUE; case WM_COMMAND: { DWORD wmId = LOWORD(wParam); DWORD wmEvent = HIWORD(wParam); switch (wmId) { case IDOK: { WCHAR str[128]; GetWindowText(GetDlgItem(hDlg, IDC_EDIT1), str, 0x80); DWORD st = std::stoul(str); split_time = st > 100 ? st : 100; setman->splittingInterval = split_time; } case IDCANCEL: EndDialog(hDlg, 0); hOptionDlg = NULL; break; } return TRUE; } default: return FALSE; } return FALSE; } BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: { pswnd = new ProcessWindow(hDlg); return TRUE; } case WM_COMMAND: { DWORD wmId, wmEvent; wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case WM_DESTROY: case IDOK: EndDialog(hDlg, NULL); hProcDlg = NULL; delete pswnd; pswnd = NULL; break; case IDC_BUTTON1: pswnd->RefreshProcess(); break; case IDC_BUTTON2: pswnd->AttachProcess(); break; case IDC_BUTTON3: pswnd->DetachProcess(); break; case IDC_BUTTON5: pswnd->CreateProfileForSelectedProcess(); break; case IDC_BUTTON6: pswnd->DeleteProfileForSelectedProcess(); break; } } return TRUE; case WM_NOTIFY: { LPNMHDR dr = (LPNMHDR)lParam; switch (dr->code) { case LVN_ITEMCHANGED: if (dr->idFrom == IDC_LIST1) { NMLISTVIEW *nmlv = (LPNMLISTVIEW)lParam; if (nmlv->uNewState & LVIS_SELECTED) pswnd->RefreshThread(nmlv->iItem); } break; } } return TRUE; default: return FALSE; } } LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CHAR: //Filter user input. if (GetKeyState(VK_CONTROL) & 0x8000) { if (wParam == 1) { Edit_SetSel(hwndEdit, 0, -1); SendMessage(hwndEdit, WM_COPY, 0, 0); } } return 0; case WM_LBUTTONUP: if (hwndEdit) SendMessage(hwndEdit, WM_COPY, 0, 0); default: { return proc(hWnd, message, wParam, lParam); } } } LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_KEYDOWN: if (wParam == VK_UP) { SetWindowText(hWnd, last_cmd); SetFocus(hWnd); return 0; } break; case WM_CHAR: if (wParam == VK_RETURN) { DWORD s = 0, pid = 0; WCHAR str[32]; if (GetWindowTextLength(hWnd) == 0) break; GetWindowText(hWnd, last_cmd, CMD_SIZE); //IthBreak(); if (GetWindowText(hwndProcessComboBox, str, 32)) pid = std::stoul(str); ProcessCommand(last_cmd, pid); Edit_SetSel(hWnd, 0, -1); Edit_ReplaceSel(hWnd, &s); SetFocus(hWnd); return 0; } default: break; } return CallWindowProc(proccmd, hWnd, message, wParam, lParam); } void CreateButtons(HWND hWnd) { hwndProcess = CreateWindow(L"Button", L"Process", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndOption = CreateWindow(L"Button", L"Option", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndClear = CreateWindow(L"Button", L"Clear", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndSave = CreateWindow(L"Button", L"Save", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndExtensions = CreateWindow(L"Button", L"Extensions", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndRemoveHook = CreateWindow(L"Button", L"Unhook", WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndTop = CreateWindow(L"Button", L"Top", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndProcessComboBox = CreateWindow(L"ComboBox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndCmd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_AUTOHSCROLL, 0, 0, 0, 0, hWnd, 0, hIns, NULL); hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 0, 0, 0, 0, hWnd, 0, hIns, NULL); } void ClickButton(HWND hWnd, HWND h) { if (h == hwndProcess) { if (hProcDlg) SetForegroundWindow(hProcDlg); else hProcDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG2, 0, ProcessDlgProc); } else if (h == hwndOption) { if (hOptionDlg) SetForegroundWindow(hOptionDlg); else hOptionDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG4, 0, OptionDlgProc); } else if (h == hwndClear) { man->ClearCurrent(); } else if (h == hwndTop) { if (Button_GetCheck(h) == BST_CHECKED) { Button_SetCheck(h, BST_UNCHECKED); SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); if (hProcDlg) SetWindowPos(hProcDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); if (hOptionDlg) SetWindowPos(hOptionDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); } else { Button_SetCheck(h, BST_CHECKED); SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); if (hProcDlg) SetWindowPos(hProcDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); if (hOptionDlg) SetWindowPos(hOptionDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); } } else if (h == hwndSave) { WCHAR str[32]; if (GetWindowText(hwndProcessComboBox, str, 32)) { DWORD pid = std::stoul(str); SaveProcessProfile(pid); } pfman->SaveProfiles(); } else if (h == hwndExtensions) { man->AddConsoleOutput(L"GUI for managing your extensions coming soon. Please do it manually for now."); } else if (h == hwndRemoveHook) { WCHAR str[32]; if (GetWindowText(hwndCombo, str, 32)) { std::wstring entry(str); std::size_t i; DWORD threadNumber = std::stoul(entry, &i, 16); entry = entry.substr(i + 1); DWORD pid = std::stoul(entry, &i); entry = entry.substr(i + 1); DWORD addr = std::stoul(entry, NULL, 16); if (threadNumber != 0) RemoveHook(pid, addr); } } } DWORD ThreadOutput(TextThread* thread, const BYTE* out, DWORD len, DWORD new_line) { if (len == 0) return len; DWORD status = thread->Status(); if (status & CURRENT_SELECT) { texts->AddText((LPWSTR)out, len / 2, false); } return len; } bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp) { if (!pid) return false; ProcessRecord *pr = ::man->GetProcessRecord(pid); if (!pr) return false; bool result = false; WaitForSingleObject(pr->hookman_mutex, 0); const Hook *hks = (Hook *)pr->hookman_map; for (int i = 0; i < MAX_HOOK; i++) { if (hks[i].Address() == hook_addr) { hp = hks[i].hp; result = true; break; } } ReleaseMutex(pr->hookman_mutex); return result; } std::wstring GetEntryString(TextThread& thread) { CHAR entry[512]; thread.GetEntryString(entry, 512); return toUnicodeString(entry); } std::wstring CreateEntryWithLink(TextThread& thread, std::wstring& entry) { std::wstring entryWithLink = entry; if (thread.PID() == 0) entryWithLink += L"ConsoleOutput"; HookParam hp = {}; if (GetHookParam(thread.PID(), thread.Addr(), hp)) entryWithLink += L" (" + GetCode(hp, thread.PID()) + L")"; return entryWithLink; } void AddToCombo(TextThread& thread, bool replace) { std::wstring entry = GetEntryString(thread); std::wstring entryWithLink = CreateEntryWithLink(thread, entry); int i = ComboBox_FindString(hwndCombo, -1, entry.c_str()); if (replace) { int sel = ComboBox_GetCurSel(hwndCombo); if (i != CB_ERR) ComboBox_DeleteString(hwndCombo, i); ComboBox_AddString(hwndCombo, entryWithLink.c_str()); ComboBox_SetCurSel(hwndCombo, sel); } else { if (i == CB_ERR) ComboBox_AddString(hwndCombo, entryWithLink.c_str()); // Why set current selection to 0 when the new thread is selected? if (thread.Status() & CURRENT_SELECT) ComboBox_SetCurSel(hwndCombo, 0); } } void RemoveFromCombo(TextThread* thread) { CHAR entry[512]; thread->GetEntryString(entry, 512); std::wstring unicodeEntry = toUnicodeString(entry); if (thread->PID() == 0) unicodeEntry += L"ConsoleOutput"; int i = ComboBox_FindString(hwndCombo, 0, unicodeEntry.c_str()); if (i != CB_ERR) { if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR) ConsoleOutput(ErrorDeleteCombo); } } DWORD SetEditText(LPWSTR wc) { DWORD line; Edit_SetText(hwndEdit, wc); line = Edit_GetLineCount(hwndEdit); SendMessage(hwndEdit, EM_LINESCROLL, 0, line); return 0; } DWORD ThreadReset(TextThread* thread) { texts->ClearBuffer(); man->SetCurrent(thread); thread->LockVector(); DWORD len = 0; LPWSTR wc = (LPWSTR)thread->GetStore(&len); len /= 2; wc[len] = L'\0'; SetEditText(wc); WCHAR buffer[16]; std::swprintf(buffer, L"%04X", thread->Number()); DWORD tmp = ComboBox_FindString(hwndCombo, 0, buffer); if (tmp != CB_ERR) ComboBox_SetCurSel(hwndCombo, tmp); thread->UnlockVector(); return 0; } DWORD AddRemoveLink(TextThread* thread) { AddToCombo(*thread, true); return 0; } bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook); DWORD ThreadCreate(TextThread* thread) { thread->RegisterOutputCallBack(ThreadOutput, 0); //thread->RegisterFilterCallBack(ThreadFilter, 0); AddToCombo(*thread, false); const auto& tp = thread->GetThreadParameter(); auto pr = man->GetProcessRecord(tp->pid); if (pr == NULL) return 0; if (IsUnicodeHook(*pr, tp->hook)) thread->Status() |= USING_UNICODE; auto pf = pfman->GetProfile(tp->pid); if (!pf) return 0; const std::wstring& hook_name = GetHookNameByAddress(*pr, thread->GetThreadParameter()->hook); auto thread_profile = pf->FindThread(thread->GetThreadParameter(), hook_name); if (thread_profile != pf->Threads().end()) { (*thread_profile)->HookManagerIndex() = thread->Number(); auto thread_index = thread_profile - pf->Threads().begin(); if (pf->IsThreadSelected(thread_profile)) ThreadReset(thread); } return 0; } bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook) { bool res = false; WaitForSingleObject(pr.hookman_mutex, 0); auto hooks = (const Hook*)pr.hookman_map; for (DWORD i = 0; i < MAX_HOOK; i++) { if (hooks[i].Address() == hook) { res = hooks[i].Type() & USING_UNICODE; break; } } ReleaseMutex(pr.hookman_mutex); return res; } DWORD ThreadRemove(TextThread* thread) { RemoveFromCombo(thread); return 0; } DWORD RegisterProcess(DWORD pid) { auto path = GetProcessPath(pid); if (!path.empty()) { WCHAR str[MAX_PATH]; std::swprintf(str, L"%04d:%s", pid, path.substr(path.rfind(L'\\') + 1).c_str()); ComboBox_AddString(hwndProcessComboBox, str); if (ComboBox_GetCount(hwndProcessComboBox) == 1) ComboBox_SetCurSel(hwndProcessComboBox, 0); } Profile* pf = pfman->GetProfile(pid); if (pf) { for (auto i = pf->Hooks().begin(); i != pf->Hooks().end(); ++i) { InsertHook(pid, &i->get()->HP(), toMultiByteString(i->get()->Name())); } } return 0; } DWORD RemoveProcessList(DWORD pid) { WCHAR str[MAX_PATH]; std::swprintf(str, L"%04d", pid); DWORD i = ComboBox_FindString(hwndProcessComboBox, 0, str); DWORD j = ComboBox_GetCurSel(hwndProcessComboBox); if (i != CB_ERR) { DWORD k = ComboBox_DeleteString(hwndProcessComboBox, i); if (i == j) ComboBox_SetCurSel(hwndProcessComboBox, 0); } return 0; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: CreateButtons(hWnd); // Add text to the window. Edit_LimitText(hwndEdit, -1); SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411); proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc); proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc); hwndCombo = CreateWindow(L"ComboBox", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP, 0, 0, 0, 0, hWnd, 0, hIns, NULL); { HDC hDC = GetDC(hWnd); int nHeight = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72); ReleaseDC(hWnd, hDC); HFONT hf = CreateFont(nHeight, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"MS Gothic"); hWhiteBrush = GetStockBrush(WHITE_BRUSH); SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0); SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0); SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0); SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0); texts = new TextBuffer(hwndEdit); man->RegisterThreadCreateCallback(ThreadCreate); man->RegisterThreadRemoveCallback(ThreadRemove); man->RegisterThreadResetCallback(ThreadReset); TextThread* console = man->FindSingle(0); console->RegisterOutputCallBack(ThreadOutput, NULL); AddToCombo(*console, false); man->RegisterProcessAttachCallback(RegisterProcess); man->RegisterProcessDetachCallback(RemoveProcessList); //man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook); Artikash 5/30/2018 TODO: Finish implementing this. man->RegisterAddRemoveLinkCallback(AddRemoveLink); StartHost(); { static const WCHAR program_name[] = L"NextHooker beta v"; //static const WCHAR program_version[] = L"3.0"; static WCHAR version_info[256]; std::swprintf(version_info, L"%s%s (%s)", program_name, program_version, build_date); man->AddConsoleOutput(version_info); man->AddConsoleOutput(InitMessage); } if (background == 0) man->AddConsoleOutput(BackgroundMsg); } return 0; case WM_COMMAND: { DWORD wmId, wmEvent, dwId; wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmEvent) { case EN_VSCROLL: { SCROLLBARINFO info = { sizeof(info) }; GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info); InvalidateRect(hwndEdit, 0, 1); ValidateRect(hwndEdit, &info.rcScrollBar); RedrawWindow(hwndEdit, 0, 0, RDW_ERASE); } break; case CBN_SELENDOK: { if ((HWND)lParam == hwndProcessComboBox) return 0; dwId = ComboBox_GetCurSel(hwndCombo); int len = ComboBox_GetLBTextLen(hwndCombo, dwId); if (len > 0) { LPWSTR pwcEntry = new WCHAR[len + 1]; len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry); DWORD num = std::stoul(pwcEntry, NULL, 16); man->SelectCurrent(num); delete[] pwcEntry; } } return 0; case BN_CLICKED: ClickButton(hWnd, (HWND)lParam); break; default: break; } } break; case WM_SETFOCUS: SetFocus(hwndEdit); return 0; case WM_SIZE: { WORD width = LOWORD(lParam); WORD height = HIWORD(lParam); DWORD l = width / 7; WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font h = h + (h / 2); HDC hDC = GetDC(hWnd); RECT rc; GetClientRect(hWnd, &rc); FillRect(hDC, &rc, hWhiteBrush); ReleaseDC(hWnd, hDC); MoveWindow(hwndProcess, 0, 0, l, h, TRUE); MoveWindow(hwndOption, l * 1, 0, l, h, TRUE); MoveWindow(hwndTop, l * 2, 0, l, h, TRUE); MoveWindow(hwndClear, l * 3, 0, l, h, TRUE); MoveWindow(hwndExtensions, l * 4, 0, l, h, TRUE); MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE); MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE); l *= 2; MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE); MoveWindow(hwndCmd, l, h, width - l, h, TRUE); MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE); h *= 3; MoveWindow(hwndEdit, 0, h, width, height - h, TRUE); } return 0; case WM_DESTROY: man->RegisterThreadCreateCallback(0); man->RegisterThreadRemoveCallback(0); man->RegisterThreadResetCallback(0); man->RegisterProcessAttachCallback(0); man->RegisterProcessDetachCallback(0); //delete texts; SaveSettings(); PostQuitMessage(0); return 0; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }