游戏安全实验室 首页 技术入门 查看内容

 阅读目录

模拟按键类外挂实现方式

发布于: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_KEYUPWM_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(hwndnMsgIdwParamlParam); //模拟发送

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(hwndnScanCodenSleepTime);

void SimSendInput(HWND hwndint nScanCodeint 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, &stInputsizeof(INPUT)); //模拟按下

    printf("%d press event sent\n"nCnt);

    stInput.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;

    stInput.ki.time = GetTickCount();

    Sleep(nSleepTime);

    nCnt = SendInput(1, &stInputsizeof(INPUT)); //模拟松开

    printf("%d up event sent\n"nCnt);

}

调用窗口消息函数

有些时候我们可以把代码注入到目标进程内,为了避免模拟按键相关的函数受到干扰,就可以直接去调用目标窗口的窗口消息处理回调函数。

利用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);

    }

}

然后直接调用,就可以直接模拟相关的消息。


*转载请注明来自游戏安全实验室(GSLAB.QQ.COM)

分享到:
踩0 赞2

收藏

上一篇:游戏透视示例解析:通过D3D hook实现鬼泣4怪物透视

下一篇:[鬼泣4 游戏分析]HP,MP,部分道具相关逻辑分析

最新评论
B Color Image Link Quote Code Smilies

发表评论