阅读目录
模拟按键类外挂实现方式
发布于:2016-6-28 17:15 | 160416次阅读 作者: 管理员 | 原作者: TP | 来自: 原创
在游戏中,我们经常会遇到一些需要重复进行的、但又没什么技术含量的操作,比如刷金币、采集材料、喊话等等,也有时候我们需要根据当前场景做出一些简单的应对,比如在MOBA游戏中通过移动来躲避一个指向性技能等等。这时候我们就会考虑,能不能让电脑来帮助我去做这些事情呢? 这时就需要用到模拟按键了。模拟按键指的是一类功能:不通过实体键盘、鼠标的操作,就完成原本需要键鼠操作才能完成的行为。 目前的Windows系统中,主要有5种模拟按键的实现方式: 1. 利用消息机制(发送消息、直接call消息处理函数等) 2. SendInput模拟输入 3. 调用KeyBoardClassServiceCallBack/MouseClassServiceCallback 4. 写IO端口 5. 通过虚拟键盘/鼠标设备输入 几种方式作用的位置大致如上图所示,其中前两种在用户层实现,后三种在内核层实现,本书仅讨论用户层的实现方法。 1 发送窗口消息 消息循环是操作系统和程序处理键鼠输入的最终步骤,使用窗口消息模拟输入的优点是使用简单、稳定性好,缺点在于基于DirectInput的程序不使用Windows的消息循环处理输入,所以这种方法也就没有效果。 SendMessage和PostMessage函数的作用都是向目标窗口发送消息,但是区别也比较大,如果: · 目标窗口与发送窗口处于同一个线程时,SendMessage不会将消息插入消息队列,而是直接调用目标窗口的消息处理回调函数,并返回消息处理回调函数的返回值;PostMessage将消息插入消息队列然后直接返回。 · 目标窗口与发送窗口不在同一个线程时,SendMessage将消息插入目标线程的消息队列,并等待其返回;PostMessage将消息插入消息队列然后直接返回。 和按键相关的窗口消息主要有WM_KEYDOWN、WM_KEYUP、WM_LBUTTONDOWN等等。在发送时必须指定要发送到的目标窗口句柄。 HWND hwnd = FindWindow(0, ppszArg[2]); //根据窗口名字查找窗口 if (hwnd == NULL) { printf("Can't find window: %s\n", ppszArg[2]); return; } int nMsgId = atol(ppszArg[3]); int wParam = atol(ppszArg[4]); int lParam = atol(ppszArg[5]); int nRet = SendMessage(hwnd, nMsgId, wParam, lParam); //模拟发送 printf("Message sent, return: %d", nRet); 上述代码可以根据指定的窗口名字,向该窗口发送对应的消息。PostMessage与此类似。 2 SendInput SendInput函数会从内核中构造一个输入信息结构,并传回给ring3。 由于是直接模拟的键盘/鼠标输入,类似于直接在键盘上按下了按键,所以使用时需要确保欲发送消息的窗口是激活的。 SendInput使用时需要填写如下结构: typedef struct tagINPUT { DWORD type; union { MOUSEINPUT mi; KEYBDINPUT ki; HARDWAREINPUT hi; }; } INPUT, *PINPUT; 包含一个union结构,本次依然对键盘进行模拟,需要填写如下结构: typedef struct tagKEYBDINPUT { WORD wVk; WORD wScan; DWORD dwFlags; DWORD time; ULONG_PTR dwExtraInfo; } KEYBDINPUT, *PKEYBDINPUT; 示例代码: HWND hwnd = FindWindow(0, ppszArg[2]); if (hwnd == NULL) { printf("Can't find window: %s\n", ppszArg[2]); return 0; } int nScanCode = atol(ppszArg[3]); //按键扫描码 int nSleepTime = atol(ppszArg[4]); //按下和松开的间隔 SimSendInput(hwnd, nScanCode, nSleepTime); … void SimSendInput(HWND hwnd, int nScanCode, int nSleepTime) { INPUT stInput; stInput.type = INPUT_KEYBOARD; //类型为键盘 stInput.ki.wVk = 0; stInput.ki.wScan = (WORD)nScanCode; stInput.ki.dwFlags = KEYEVENTF_SCANCODE; //只使用ScanCode构造 stInput.ki.time = GetTickCount(); stInput.ki.dwExtraInfo = GetMessageExtraInfo(); SetForegroundWindow(hwnd); //将窗口提到前台 SetFocus(hwnd); //激活窗口 Sleep(1000); int nCnt = SendInput(1, &stInput, sizeof(INPUT)); //模拟按下 printf("%d press event sent\n", nCnt); stInput.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP; stInput.ki.time = GetTickCount(); Sleep(nSleepTime); nCnt = SendInput(1, &stInput, sizeof(INPUT)); //模拟松开 printf("%d up event sent\n", nCnt); } 3 调用窗口消息函数 有些时候我们可以把代码注入到目标进程内,为了避免模拟按键相关的函数受到干扰,就可以直接去调用目标窗口的窗口消息处理回调函数。 利用GetWindowLong函数取得目标窗口的消息函数: void CallMessage(HWND hwnd, int nMsgId, int wParam, int lParam) { WNDPROC fWndProc = (WNDPROC)GetWindowLongW(hwnd, GWL_WNDPROC); //获取窗口wndproc if (fWndProc != NULL) { fWndProc(hwnd, nMsgId, wParam, lParam); } } 然后直接调用,就可以直接模拟相关的消息。 |

最新评论
发表评论