How Runescape catches the bots, and why she didn't catch me



Automating player actions has always been a major problem in MMORPGs such as World of Warcraft and Runescape . This kind of hacking of games is very different from traditional cheats, for example, in shooters.



One weekend I decided to take a look at the detection systems Jagex uses to prevent Runescape from automating player actions.



Botaniculture



For the past few months, the account has been sch0u



playing around the clock on the world 67 server, performing such monotonous tasks as killing mobs and collecting resources. At first glance, this account looks like any other player, but there is one important difference: it is a bot .





I launched this bot back in October to test the capabilities of the bot recognition system. After trying to find information on how Jagex fights against bot bots, I only found videos of paid bots, whose developers boasted that their mouse movement systems were indistinguishable from human actions.



Therefore, the only thing I could understand was the importance of mouse movements , but is this really so?



Heuristics!



To confirm this theory, I started by examining the Runescape client and almost immediately noticed a global variable hhk



being set shortly after launch.



const auto module_handle = GetModuleHandleA(0);
hhk = SetWindowsHookExA(WH_MOUSE_LL, rs::mouse_hook_handler, module_handle, 0);
      
      





This code installs a low-level interceptor on the mouse that is attached to the system interceptor chain . This allows Windows applications to intercept all mouse events, even if they are not associated with a specific application. Low-level interceptors (hooks) are often used by keyloggers , but they can also be used for peaceful purposes, for example, in heuristics, like the aforementioned mouse interceptor.



In fact, Runescape's mouse handler is pretty simple (the pseudocode was tweaked for beauty by hand):



LRESULT __fastcall rs::mouse_hook_handler(int code, WPARAM wParam, LPARAM lParam)
{
  if ( rs::client::singleton )
  {
      // Call the internal logging handler
      rs::mouse_hook_handler_internal(rs::client::singleton->window_ctx, wParam, lParam);
  }
  // Pass the information to the next hook on the system
  return CallNextHookEx(hhk, code, wParam, lParam);
}
      
      





void __fastcall rs::mouse_hook_handler_internal(rs::window_ctx *window_ctx, __int64 wparam, _DWORD *lparam)
{
  // If the mouse event happens outside of the Runescape window, don't log it.
  if (!window_ctx->event_inside_of_window(lparam))
  {
    return;
  }

  switch (wparam)
  {
    case WM_MOUSEMOVE:
      rs::heuristics::log_movement(lparam);
      break;
    
    case WM_LBUTTONDOWN:
    case WM_LBUTTONDBLCLK:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONDBLCLK:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONDBLCLK:
      rs::heuristics::log_button(lparam);
      break;
  }
}
      
      





to reduce the load on the link, these functions rs::heuristics::log_*



use simple algorithms to skip event data that resembles previously recorded events.



Later, this event data is parsed by a function rs::heuristics::process



that is called every frame of the main render loop.



void __fastcall rs::heuristics::process(rs::heuristic_engine *heuristic_engine)
{
  // Don't process any data if the player is not in a world
  auto client = heuristic_engine->client;
  if (client->state != STATE_IN_GAME)
  {
    return;
  }

  // Make sure the connection object is properly initialised
  auto connection = client->network->connection;
  if (!connection || connection->server->mode != SERVER_INITIALISED)
  {
    return;
  }

  // The following functions parse and pack the event data, and is later sent
  // by a different component related to networking that has a queue system for
  // packets.

  // Process data gathered by internal handlers
  rs::heuristics::process_source(&heuristic_engine->event_client_source);

  // Process data gathered by the low level mouse hook
  rs::heuristics::process_source(&heuristic_engine->event_hook_source);
}
      
      





Away from keyboard?



During the reverse engineering process, I tried to figure out the importance to me of each function I learned, usually by creating hooks or patches for those functions. You can usually tell if a feature is relevant by making it useless and monitoring the state of the software. This research methodology led to an interesting observation.



Having forbidden the game to call the function rs::heuristics::process



, at first I did not notice anything, but after exactly five minutes the server logged me out. Obviously, Runescape determines player inactivity solely by the heuristics data transmitted by the client to the server, even if you can play quite normally. This raised a new question: if the server thinks that I am not playing, does it think that I am playing as a bot? ...



After that, I spent a few more days reverse-engineering the game's network layer, so that my bot can now do almost anything using network packets alone .



To prove my theory, I was bothered around the clock, seven days a week, without even moving my mouse. Having spent thousands of hours like this, I can confidently say that either the bot detection system uses only the data from the heuristic events sent to the server by the client, or only works when the player is not "afk". Any player who manages to play without moving the cursor should be immediately banned, that is, developers should pay attention to this flaw.






All Articles