1

I have been building a gaming-related program that needs to send simulated input to the game (which is the top window on the screen and runs in fullscreen mode). After some struggling, I finally got mouse movements (cursor drag) and keyboard input working, but for some reason, the game will not respond to simulated mouse clicks.

I have tried the following:

#if TRUE // SendInput works for keyboard simulation and mouse drag, but not clicks:
    INPUT mouse = {0};
    mouse.type = INPUT_MOUSE;
    mouse.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    SendInput(1, &mouse, sizeof(INPUT));

    Sleep(100);

    ZeroMemory(&mouse, sizeof INPUT);
    mouse.type = INPUT_MOUSE;
    mouse.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    SendInput(1, &mouse, sizeof(INPUT));
#else // tried this, but it did not work:
    mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
    Sleep(75);
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
#endif

The first dilemma I faced was sending the messages in general, but I realized this issue was caused by game process privileges > sending application privileges. After this, I encountered another problem with keypresses only registering on certain game windows/screens, but after some searching, I was able to use scancodes to overcome this. For example:

void SendSpacePress(bool bHardwareLevel){
    INPUT space = {0};
    space.type = INPUT_KEYBOARD;
    space.ki.time = 0;
    space.ki.dwExtraInfo = 0;

    if(!bHardwareLevel){
        space.ki.wVk = VK_SPACE;
    }else{
        space.ki.wScan = 0x39; // physical keyboard scan code
    }

    space.ki.dwFlags = bHardwareLevel ? KEYEVENTF_SCANCODE : 0;
    SendInput(1, &space, sizeof(INPUT));

    Sleep(rand()%25 + 25);

    space.ki.dwFlags = bHardwareLevel ? KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP : KEYEVENTF_KEYUP;
    SendInput(1, &space, sizeof(INPUT));
}

I have also been able to do mouse movement using INPUT like below:

void PanMouse(){
    INPUT mouse = {0};
    mouse.type = INPUT_MOUSE;
    mouse.mi.time = 0;
    mouse.mi.mouseData = 0;
    mouse.mi.dwExtraInfo = 0;
    mouse.mi.dwFlags = MOUSEEVENTF_MOVE;
    mouse.mi.dx = rand()%10 -5;
    mouse.mi.dy = rand()%10 -5;
    SendInput(1, &mouse, sizeof(INPUT));
}

Now the big problem is the game refuses to register my simulated mouse clicks. I would preferably like a way to send the mouse inputs without having to dive into hooking. (The game has multiple client-side anti-cheat mechanisms, so I would venture to guess that any kind of foreign process hooking or DLL injection would trigger the protection.) From what I have read, I might need to write a custom driver for a simulated hardware mouse so the input comes at the kernel level. Not a preferable option, but if need be, sobeit. (And if need be, can anyone provide helpful information for this? I have never messed around with writing drivers, but there's a first time for everything I suppose.)

TL;DR: What do I need to do to get simulated mouse clicks to register in a game that seems to ignore non-hardware input? (I.e., how can I trick the game into thinking mouse clicks are legitimate similarly to using KEYEVENTF_SCANCODE when simulating keyboard input?)

genpfault
  • 51,148
  • 11
  • 85
  • 139
Spencer D
  • 3,376
  • 2
  • 27
  • 43
  • Why is the game running as admin in the first place? – Chuck Walbourn Feb 14 '17 at 06:18
  • @ChuckWalbourn, just how the game runs. Probably in an attempt to prevent other processes from modifying it. Unfortunately, it cannot run at a lower privilege level. – Spencer D Feb 14 '17 at 06:21
  • There's nothing about running as admin that protects you from other processes running as admin. I ask because MSFT has spent approximately the past 12 years telling game developers to not require admin rights to run their games. Admin to install, sure. Admin to run, not so much. See [User Account Control for Game Developers](https://msdn.microsoft.com/en-us/library/windows/desktop/ee419001.aspx) – Chuck Walbourn Feb 14 '17 at 06:25
  • @ChuckWalbourn, I'm not arguing there. I didn't write the game, and I certainly cannot speak for the game's developers. I am just a third party that needs to interact with the game in a way to replicate a user. Whatever their reasons are for requiring admin privileges (*perhaps as a means of monitoring other processes for suspicious activity?*) are more than likely negligible. – Spencer D Feb 14 '17 at 06:54

1 Answers1

-1

Instead of sleeping between inputs, you need to inject a pair of mouse inputs together with the time member appropriately set to indicate "how long".

Here's something close to what you want. This will click the current cursor position with the left mouse simulated for 100ms.

INPUT inputs[2] = {};

inputs[0].type = INPUT_MOUSE;
inputs[0].mi.time = 0;
inputs[0].mi.dx = 0;
inputs[0].mi.dy = 0;
inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;

inputs[1].type = INPUT_MOUSE;
inputs[1].mi.time = 100;
inputs[1].mi.dx = 0;
inputs[1].mi.dy = 0;
inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;

SendInput(2, inputs, sizeof(INPUT));

The mouse event won't happen immediately. It gets "queued" to happen shortly afterwards.

You should use the same technique for your keyboard events. Don't call "Sleep".

selbie
  • 100,020
  • 15
  • 103
  • 173
  • Thanks for the input and advice. I'll give that a try. – Spencer D Feb 14 '17 at 05:29
  • Hmm. I just gave that a try, but it's still not registering the click in the game process (or at least not responding to it as a mouse click). I know it's not a process escalation issue because because I left in a regular keyboard click, and that's still working fine, so it seems that it has to be an issue with the process ignoring the fake `MOUSEEVENTF`. – Spencer D Feb 14 '17 at 05:41