Principle and realization of all keyboard and mouse simulation methods

Foreword:

There are some game programs that use the DirectX interface, they bypass the windows message mechanism when reading keyboard operations, and use DirectInput. This is because some games have higher requirements for real-time control, such as racing games, which require the fastest The degree of response to keyboard input. However, because windows messages are in the form of queues, there will be a lot of delays when the messages are delivered, and sometimes more than a dozen messages are delivered per second. This speed does not meet the requirements of the game. DirectInput bypasses the windows message and directly deals with the keyboard driver. Of course, the efficiency is improved a lot. Therefore, there will be no response to such a program whether PostMessage or keybd_event is used, because these functions are at a higher level. For such a program, I had to use the method of directly reading and writing the keyboard port to simulate the hardware event. To use this method to simulate the keyboard, you need to understand the relevant knowledge of keyboard programming.

 


Let ’s start with the simplest simulation operation. There are many ways to simulate keyboard and mouse. I roughly divide them into message simulation, API simulation, and driver simulation.
For web pages, you can also use JavaScript simulation, although this is not in the scope of this tutorial.

One message simulation

Everyone who learns Windows programming knows that Windows programs will respond to window messages, so we send a message by ourselves. In the past, the program would think that it was a human operating and responding.

Look at the API to be used

// 发送消息到指定窗口,不用等待消息处理就返回,参数和窗口过程里的一样

BOOL WINAPI PostMessage(

  _In_opt_ HWND   hWnd,

  _In_     UINT   Msg,

  _In_     WPARAM wParam,

  _In_     LPARAM lParam

);

// 获取窗口句柄,参数是窗口类名和窗口标题,其中一个可以传入NULL表示通配

HWND FindWindow(

LPCTSTR lpClassName,

LPCTSTR lpWindowName

);

// 用来获取子窗口句柄

HWND WINAPI FindWindowEx(

_In_opt_ HWND hwndParent,

_In_opt_ HWND hwndChildAfter,

_In_opt_ LPCTSTR lpszClass,

_In_opt_ LPCTSTR lpszWindow

);

Take notepad as an example

First of all, we must know the window class name of Notepad

Open Notepad, open VS2013, find spy++ in the tool

Find the search window in the toolbar, drag the finder tool to the notepad window, and get the class name "Notepad" of the notepad window

In the same way, you can know that the class name of the edit box is Edit

We write a program to simulate pressing A in the edit box

HWND notepadWnd = FindWindow(_T("Notepad"), NULL); // 记事本窗口句柄

if (notepadWnd == NULL)

{

printf("没有找到记事本窗口\n");

return 0;

}

HWND editWnd = FindWindowEx(notepadWnd, NULL, _T("Edit"), NULL); // 编辑框窗口句柄


const BYTE vk = 'A'; // 虚拟键码

//UINT scanCode = MapVirtualKey(vk, MAPVK_VK_TO_VSC); // 扫描码

PostMessage(editWnd, WM_KEYDOWN, vk, 1 /*| scanCode << 16*/);

Sleep(100);

PostMessage(editWnd, WM_KEYUP, vk, 1 /*| scanCode << 16*/ | 1 << 30 | 1 << 31);

Run the program to see if there is more a in the notepad

 

Write another program to simulate clicking the right mouse button

New API used


// 取坐标处窗口句柄

HWND WINAPI WindowFromPoint(

_In_ POINT Point

);

// 取鼠标坐标

BOOL WINAPI GetCursorPos(

_Out_ LPPOINT lpPoint

);

// 把屏幕坐标转为相对于窗口客户区的坐标

BOOL ScreenToClient(

_In_ HWND hWnd,

LPPOINT lpPoint

);

Program that simulates right-click of the mouse

 
Sleep(3000); // 等待3秒把鼠标移到指定窗口


POINT pos; // 鼠标坐标

GetCursorPos(&pos);

HWND wnd = WindowFromPoint(pos); // 鼠标指向的窗口的句柄

ScreenToClient(wnd, &pos); // 把pos转成相对于窗口客户区的坐标

LPARAM lParam = MAKELPARAM(pos.x, pos.y);


PostMessage(wnd, WM_RBUTTONDOWN, 0, lParam);

Sleep(100);

PostMessage(wnd, WM_RBUTTONUP, 0, lParam);

After running, move the mouse to Notepad, a menu will pop up

 

The advantage of the method of sending a message to simulate input is that it can be simulated even if the window is minimized, but the disadvantage is that not all programs will process window messages, for example, most games use DInput to input

Two API simulation (reference one and two )

API simulation is to use the API provided by Windows to simulate input, such as keybd_event, mouse_event, SendInput, but Microsoft recommends using SendInput instead of the other two. Then I will only talk about how to use SendInput.

API used

UINT WINAPI SendInput(

_In_ UINT nInputs,

_In_ LPINPUT pInputs,

_In_ int cbSize

);


typedef struct tagINPUT {

  DWORD type;

  union {

    MOUSEINPUT    mi;

    KEYBDINPUT    ki;

    HARDWAREINPUT hi;

  };

} INPUT, *PINPUT;
This API can simulate events such as keyboard press, mouse movement, mouse click, etc. The parameters are the number of INPUT structures, the pointer of the INPUT array, and the size of the INPUT structure

The value of type in INPUT is INPUT_MOUSE, INPUT_KEYBOARD, INPUT_HARDWARE, which respectively indicate the use of mi, ki, and hi structures

For detailed instructions, please see MSDN:
MOUSEINPUT
KEYBDINPUT

Simulate mouse movement to the middle of the screen and right click

INPUT input[3];

ZeroMemory(&input, sizeof(input));

// 鼠标移动到屏幕中间,也可以用SetCursorPos(x, y)

input[0].type = INPUT_MOUSE;

input[0].mi.dx = 65535 / 2; // 坐标取值范围是0-65535

input[0].mi.dy = 65535 / 2;

input[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;

// 点击鼠标右键

input[1].type = INPUT_MOUSE;

input[1].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;

input[2].type = INPUT_MOUSE;

input[2].mi.dwFlags = MOUSEEVENTF_RIGHTUP;


SendInput(_countof(input), input, sizeof(INPUT));

Simulate pressing the A key:

INPUT input[2];

ZeroMemory(&input, sizeof(input));

input[0].type = INPUT_KEYBOARD;

input[0].ki.wVk = 'A';

// 也可以不加这句但是对DInput输入的程序会没用

input[0].ki.wScan = MapVirtualKey(input[0].ki.wVk, MAPVK_VK_TO_VSC);

input[1].type = INPUT_KEYBOARD;

input[1].ki.wVk = input[0].ki.wVk;

input[1].ki.wScan = input[0].ki.wScan;

input[1].ki.dwFlags = KEYEVENTF_KEYUP;


SendInput(_countof(input), input, sizeof(INPUT));

Let’s take an advanced example: Dongfang Huayingzuka’s Z-key hitting

If you want to send a flick in the Oriental Huaying Mound, you have to keep pressing the Z button, which is very laborious, so I want to realize the function of automatically sending flicks by holding down the C button (just like the Great War of the Fairies)

This program uses MFC, if you don’t understand it, I suggest you learn MFC programming

// 开启

void CC2ZDlg::OnBnClickedButton1()

{

m_enableButton.EnableWindow(FALSE);

m_disableButton.EnableWindow(TRUE);

SetTimer(1, 200, timerProc); // 每0.2s检测C键是否按下,并模拟Z键

}


// 关闭

void CC2ZDlg::OnBnClickedButton2()

{

m_enableButton.EnableWindow(TRUE);

m_disableButton.EnableWindow(FALSE);

KillTimer(1);

}


//定时模拟按下Z

void CALLBACK CC2ZDlg::timerProc(HWND hWnd, UINT nMsg, UINT nTimerid, DWORD dwTime)

{

if ((GetKeyState('C') & (1 << 15)) != 0) // C键按下

{

INPUT input;

ZeroMemory(&input, sizeof(input));

input.type = INPUT_KEYBOARD;

input.ki.wVk = 'Z';

input.ki.wScan = MapVirtualKey(input.ki.wVk, MAPVK_VK_TO_VSC);

SendInput(1, &input, sizeof(INPUT)); // 按下Z键

Sleep(100); // 可能东方是在处理逻辑时检测一下Z键是否按下才发弹幕,如果这时Z键刚好弹起就没有反应,所以要延迟一下

input.ki.dwFlags = KEYEVENTF_KEYUP;

SendInput(1, &input, sizeof(INPUT)); // 弹起Z键

}

}

Complete source code

In this way, the input of most games can be simulated, but some games will be protected, so drive simulation is used

Three drive simulation

Drive simulation is to write your own driver program, operate the I/O port in the system kernel, and send instructions to the integrated circuit connected to the keyboard (usually 8042 chip), so that it generates a button press message, so that your analog input is for all The program is sent from a real device, and it can bypass many protections
(you can learn about the WinIo library if you need to operate the I/O port ),
but I don’t write this kind of driver _(:з」∠)_, And loading the driver in the x64 system needs to have a trusted digital signature, otherwise it will be more troublesome, and also know the underlying knowledge of the 8042 chip...
So I found a library written by some people to implement Git that drives the Interception API on the
official website of
Interception. Library

Its driver has a digital signature and has been tested on platforms from XP to win10.
It can also intercept and modify input (including CTRL+ALT+DELETE), but here I only talk about analog input, so let’s study it myself...
(It seems Its analog input is not the operation port but the kernel version of SendInput?)

installation method:

(You can go to my network disk ) Download Interception.zip, unzip it and run install-interception.exe

Environment setup: If the target system is 64-bit, you must first add the x64 configuration in the configuration manager

Find the VC++ directory in your project properties, including the directory plus Interception\library, the library directory is based on whether the target system is 64-bit or 32-bit plus library\x64 or library\x86 to
find the linker-input, additional dependencies plus interception .lib
Then put the interception.dll in library\x64 or library\x86 in the same directory of your program and
finally #include <interception.h> in your source code

Simulate the mouse moving to the middle of the screen and right-click:

 
InterceptionContext context = interception_create_context();


InterceptionMouseStroke mouseStroke[3];

ZeroMemory(mouseStroke, sizeof(mouseStroke));

// 鼠标移动到屏幕中间

mouseStroke[0].flags = INTERCEPTION_MOUSE_MOVE_ABSOLUTE;

mouseStroke[0].x = 65535 / 2; // 坐标取值范围是0-65535

mouseStroke[0].y = 65535 / 2;

// 点击鼠标右键

mouseStroke[1].state = INTERCEPTION_MOUSE_RIGHT_BUTTON_DOWN;

mouseStroke[2].state = INTERCEPTION_MOUSE_RIGHT_BUTTON_UP;

interception_send(context, INTERCEPTION_MOUSE(0), (InterceptionStroke*)mouseStroke, _countof(mouseStroke));


interception_destroy_context(context);

Simulate pressing the A key:

InterceptionContext context = interception_create_context();


InterceptionKeyStroke keyStroke[2];

ZeroMemory(keyStroke, sizeof(keyStroke));

keyStroke[0].code = MapVirtualKey('A', MAPVK_VK_TO_VSC);

keyStroke[0].state = INTERCEPTION_KEY_DOWN;

keyStroke[1].code = keyStroke[0].code;

keyStroke[1].state = INTERCEPTION_KEY_UP;

interception_send(context, INTERCEPTION_KEYBOARD(0), (InterceptionStroke*)keyStroke, _countof(keyStroke));


interception_destroy_context(context);

 

The driver simulation is very powerful, but it is more troublesome, and generally does not need _(:з」∠)_

 

 

Reference: https://www.cnblogs.com/Jnshushi99/archive/2011/09/03/2164617.html

Guess you like

Origin blog.csdn.net/THMAIL/article/details/113812698