Эмуляция устройств ввода
Рассмотрим технику эмуляции устройств ввода. Этот подход применяется для обхода защит от кликеров, которые проверяют состояние клавиатуры. Алгоритм работы таких защит подробно разобран во второй главе.
Когда мы используем вместо клавиатуры или мыши эмулятор, у ОС нет возможности обнаружить подмену. Симулируемые эмулятором события (например, нажатия клавиш) будут обрабатываться ОС точно так же, как и для настоящей клавиатуры. Поэтому защите игрового приложения будет намного сложнее различать действия бота и игрока.
Инструменты для разработки
Прежде всего нам следует выбрать устройство, которое будет выполнять роль эмулятора. Рассмотрим основные требования к нему:
Невысокая цена.
Средства разработки (IDE и компилятор) должны быть бесплатны.
Среда разработки должна предоставлять библиотеки для эмуляции устройств ввода.
Должна быть доступная подробная документация.
Плата Arduino удовлетворяет всем перечисленным требованиям. Кроме того, Arduino — это одна из лучших аппаратных платформ, чтобы познакомиться с разработкой программ для встраиваемых систем.
Следующий вопрос, который следует решить: какую версию платы Arduino выбрать? Чтобы ответить на него, изучим возможности средств разработки. Arduino IDE предоставляет библиотеки для эмуляции клавиатуры и мыши. Согласно документации, некоторые версии плат их не поддерживают. Следовательно, нам они не подойдут. Нас устроят следующие модели: Leonardo, Micro и Due.
Мы выбрали аппаратную платформу. Теперь самое время установить средства разработки для неё. Компания производитель плат Arduino предоставляет бесплатную IDE с интегрированным C++ компилятором и библиотеками для поддержки периферии. Скачайте её с официального сайта и установите.
Теперь установим драйвер для работы с платой Arduino. Для этого нужна программа установки из каталога Arduino IDE. Её путь по умолчанию: C:\Program Files (x86)\Arduino\drivers
. В каталоге drivers
есть две программы: dpinst-amd64.exe
для 64-разрядной версии Windows и dpinst-x86.exe
для 32-разрядной. Выберите подходящую вам и перед её запуском подключите плату к компьютеру с помощью USB кабеля.
После установки драйвера выполните заключительные шаги конфигурации в Arduino IDE:
Прочитайте модель вашей платы. Для этого в главном меню выберите пункт "Tools" ➤ "Get Board Info" ("Инструменты" ➤ "Информация о плате"). Проверьте, что в пункте меню "Tools" ➤ "Board:..." ("Инструменты" ➤ "Плата:...") модель указана правильно.
Укажите порт подключения платы в пункте главного меню "Tools"➤"Port:..." ("Инструменты" ➤ "Порт:...").
Теперь Arduino IDE настроена и готова к работе.
Самой по себе платы Arduino недостаточно для эмуляции устройств ввода. Мы должны написать для неё программу, которая посылала бы ОС события о симулируемых действиях. Со стороны компьютера этой программой будет управлять бот-кликер, написанный на языке AutoIt. Для такого взаимодействия понадобится набор AutoIt скриптов CommAPI.
Эмуляция клавиатуры
Есть два варианта реализации бота, использующего эмулятор устройства ввода.
В первом случае все алгоритмы бота реализованы в программе, работающей на плате Arduino. После её загрузки на устройство, всё готово к работе. Бот запускается автоматически, как только вы подключите плату к компьютеру через USB. Такая архитектура лучше всего подходит для "слепых" ботов, которые нажимают кнопки, не проверяя состояние игровых объектов. К сожалению, программа, запущенная на Arduino не имеет доступа к WinAPI-интерфейсу. Следовательно, она не сможет прочитать данные из процесса игрового приложения или устройства вывода.
Если ваш бот должен реагировать на игровые события, следует выбрать второй вариант реализации. В этом случае его алгоритмы запускаются и работают на компьютере. Программа платы Arduino отвечает только за симуляцию событий устройства ввода. В такой схеме бот имеет полный доступ к WinAPI и может читать состояние игровых объектов. После принятия решения, он отправляет плате Arduino команду на симуляцию нужного действия.
Мы рассмотрим пример второго варианта реализации бота. Он более надёжен и универсален.
Интерфейс взаимодействия платы и бота может быть любым: Ethernet, UART, I2C, SPI. Предлагаю остановиться на самом простом варианте, не требующем дополнительного оборудования кроме самой платы и USB провода. Речь идёт об интерфейсе UART (Universal Asynchronous Receiver-Transmitter).
Листинг 5-1 демонстрирует программу keyboard.ino
для платы Arduino. Она симулирует события клавиатуры. При этом из UART-интерфейса читается код клавиши, которую требуется нажать.
Листинг 5-1. Программа keyboard.ino
В этой программе мы используем библиотеку Keyboard, которую предоставляет Arduino IDE. Она позволяет генерировать события нажатия клавиш. Подключенный по USB компьютер получает их через интерфейс HID (Human Interface Device). Он является современным стандартом взаимодействия с устройствами ввода.
Оба интерфейса HID и UART способны работать одновременно по одному USB-кабелю, соединяющему плату Arduino и компьютер.
В первой строке программы мы включаем заголовок Keyboard.h
. В нём создаётся глобальный объект Keyboard
класса Keyboard_
. Все возможности библиотеки доступны через его методы.
В нашей программе всего две функции: setup
и loop
. Возможно, вы помните, что в любом C++ приложении обязательно должна быть ещё функция main
. Она генерируется IDE во времени компиляции. В ней выполняется два действия: однократный вызов setup
и цикличный вызов loop
. Прототипы обеих этих функций предопределены, и поменять их нельзя.
Кроме Keyboard
мы используем глобальный объект Serial
. Он предоставляет доступ к интерфейсу UART. Для инициализации обоих объектов в функции setup
вызываются методы begin
. Для Serial
этот метод принимает входным параметром скорость передачи данных между компьютером и платой, которая в нашем случае равна 9600 бит/c. У метода begin
объекта Keyboard
нет входных параметров. Сразу после его вызова плата начинает эмулировать клавиатуру.
После выполнения функции setup
Arduino плата готова принимать команды по UART интерфейсу, и симулировать нажатия соответствующих клавиш. За это отвечает код функции loop
. Её алгоритм состоит из трёх шагов:
С помощью метода
available
объектаSerial
проверить, были ли получены данные по UART интерфейсу. Они сохраняются во входном буфере платы, размер которого 64 байта. Метод возвращает количество принятых байт. Если передачи не было, вернётся значение ноль.Прочитать один байт из входного буфера UART с помощью метода
read
объектаSerial
. Байт интерпретируется как ASCII код клавиши, нажатие которой следует симулировать.Симулировать нажатие клавиши через HID-интерфейс с помощью метода
write
объектаKeyboard
. Подключённый по USB компьютер обработает его как событие обычной клавиатуры.
Чтобы скомпилировать программу keyboard.ino
и загрузить её на плату, откройте её в Arduino IDE и нажмите комбинацию клавиш Ctrl+U.
Мы подготовили плату. Теперь разработаем AutoIt скрипт, который будет ею управлять. Он должен посылать через UART интерфейс ASCII коды клавиш. Функции работы с UART предоставляет ОС через WinAPI. Доступ к ним из языка AutoIt могут значительно упростить обёртки CommAPI. Скачайте и скопируйте их в каталог вашего скрипта. Проверьте, что все необходимые файлы на месте:
CommAPI.au3
CommAPIConstants.au3
CommAPIHelper.au3
CommInterface.au3
CommUtilities.au3
Листинг 5-2 демонстрирует использование обёрток CommAPI. Приведённый в нём скрипт печатает строку "Hello world!" в окне Notepad. Для симуляции нажатий клавиш он использует плату Arduino с загруженной на неё программой из листинга 5-1.
Листинг 5-2. Скрипт ControlKeyboard.au3
Общий алгоритм скрипта состоит из следующих шагов:
Переключиться на окно Notepad с помощью AutoIt функции
WinActivate
.Установить последовательное соединение (serial communication) по интерфейсу UART с платой Arduino, используя функцию
OpenPort
.Отправить команду набора строки "Hello world!" на плату с помощью функции
SendArduino
.Закрыть последовательное соединение функцией
ClosePort
.
Рассмотрим подробнее работу функций OpenPort
, SendArduino
и ClosePort
.
Функция OpenPort
устанавливает соединение и подготавливает плату Arduino к взаимодействию. Она возвращает дескриптор соединения. В ней происходят следующие вызовы CommAPI:
_CommAPI_OpenCOMPort
устанавливает последовательное соединение с указанными параметрами. Из нихiParity
,iByteSize
иiStopBits
одинаковы для Arduino плат всех моделей. ПараметрiBaud
задаёт скорость передачи данных. Она должна соответствовать скорости, переданной в методbegin
объектаSerial
в программе платы. ПараметрiPort
определяет номер последовательного порта (COM порта), через который плата подключена к компьютеру. На самом деле подключение происходит по USB, а COM порт эмулируется. Уточнить номер порта можно в пункте меню "Tools" ➤ "Port:..." ("Инструменты" ➤ "Порт:...") Arduino IDE. Например, если там указан COM7, параметрiPort
должен быть равен 7._CommAPI_ClearCommError
возвращает код ошибки при передаче данных. Через второй необязательный параметр функции возвращается текущее состояние подключённого устройства. В нашем случае он не используется. Функция вызывается для сброса флага ошибки на стороне платы. Это действие очень важно, поскольку передача данных будет заблокирована до тех пор, пока флаг ошибки взведён._CommAPI_PurgeComm
отменяет все текущие операции по передаче данных, а также очищает входной и выходной буферы подключённого устройства. После завершения работы этой функции Arduino готова принимать команды по UART.
Функция SendArduino
представляет собой обёртку над вызовом _CommAPI_TransmitString
, который передаёт указанную строку по UART интерфейсу.
Функция ClosePort
закрывает соединение по переданному в неё дескриптору.
Вспомогательная функция ShowError
нужна для отладки. Она выводит сообщение с кодом ошибки, которая может произойти на любом этапе установки соединения.
Чтобы протестировать скрипт, выполните следующие действия:
Подключите Arduino плату с загруженной на неё программой
keyboard.ino
к компьютеру с помощью USB кабеля.Запустите приложение Notepad.
Запустите скрипт
ControlKeyboard.au3
.
В результате в окне Notepad будет набран текст "Hello world!".
Сочетание клавиш
Разработанная нами программа keyboard.ino
успешно справляется с симуляцией нажатия одной клавиши за раз. Однако в некоторых играх может понадобится симулировать сочетание клавиш, например Ctrl+Z. В этом случае одного байта для передачи команды будет недостаточно. Кроме кода основной клавиши нужно отправлять код клавиши-модификатора. Таким образом, программа должна уметь читать два байта из входного буфера UART интерфейса.
Рассмотрим методы объекта Serial
. Раньше мы использовали read
, но с его помощью можно прочитать только один байт из входного буфера UART. Есть альтернативный метод readBytes
, который читает последовательность байт указанной длины. Первым параметром в него передаётся массив, в который будут сохранены данные. Вторым – его размер. Метод возвращает количество прочитанных байтов. Оно может отличаться от значения второго параметра, если буфер содержит меньше данных.
Задумаемся над вопросом: достаточно ли будет передавать только коды модификатора и клавиши? На самом деле, если по какой-то причине приём данных на плате начнётся с середины команды, возникнут серьёзные сложности. Второй байт этой команды будет интерпретирован как первый. Первый же байт следующей команды – как второй. В результате будет симулировано нажатие не той клавиши, которую ожидает управляющий скрипт. Из-за возникшего сдвига все последующие команды также выполнятся неверно.
Возможна ли ситуация, когда плата получает очередную команду не с начала? Если мы подключаем устройство до запуска управляющего скрипта, это маловероятно. Однако такая ситуация возможна, если плата перезагрузится например из-за отошедшего USB разъема или ошибки драйвера Windows.
Проблему можно решить с помощью преамбулы. Преамбула – это предопределённое значение, которое сигнализирует о начале команды. Для неё мы выделим первый байт сообщения. Теперь мы легко отличим начало передачи. Если программа Arduino получила первый байт, и он отличается от преамбулы, значит команда читается со сдвигом и её лучше проигнорировать.
По сути мы разработали простейший протокол для передачи команд эмулятору по UART интерфейсу. В таблице 5-1 приведены значения каждого байта в сообщении.
Таблица 5-1. Формат команды
Номер байта | Значение |
1 | Преамбула. |
2 | Код клавиши-модификатора. |
3 | Код основной клавиши. |
Рассмотрим пример команды для симуляции нажатия Alt+Tab. В этом случае управляющий скрипт отправляет три байта:
Первый из них (0xDC) – это преамбула. Дальше идёт код клавиши-модификатора 0x82, который соответствует Alt. Последний байт 0xB3 – это код клавиши Tab.
Листинг 5-3 демонстрирует Arduino программу, поддерживающую наш протокол.
Листинг 5-3. Программа keyboard-combo.ino
В программе появилась новая функция pressKey
. Кроме этого, алгоритм loop
стал сложнее. Мы читаем принятую команду из входного буфера UART с помощью метод readBytes
объекта Serial
. Для проверки её корректности используем операторы if
. Первый из них сравнивает длину команды с ожидаемой. Второй — соответствие её первого байта и преамбулы. Если любая из проверок не проходит, обработка команды прекращается.
Симуляция нажатия сочетания клавиш происходит в функции pressKey
. У неё два входных параметра: код модификатора и клавиши. Чтобы нажать и удерживать модификатор, используется метод press
объекта Keyboard
. Затем симулируется нажатие основной клавиши с помощью метода write
. После этого модификатор отпускается вызовом release
.
Управляющий AutoIt скрипт также должен поддерживать новый протокол передачи команд. Его исправленная версия приведена в листинге 5-4.
Листинг 5-4. Скрипт ControlKeyboardCombo.au3
Единственное отличие здесь от скрипта ControlKeyboard.au3
в функции SendArduino
. Теперь вместо строки символов, которые передаются последовательно, она передаёт команду из трёх байтов: преамбула, модификатор и клавиша. Для отправки данных используется та же CommAPI функция _CommAPI_TransmitString
. Сложность заключается в том, что она ожидает входным параметром строку. Команда же представляет собой байтовый массив. Его можно преобразовать в строку с помощью стандартной функции AutoIt StringFromASCIIArray
.
Для тестирования Arduino программы и скрипта выполните следующие шаги:
Загрузите программу
keyboard-combo.ino
на Arduino плату.Откройте несколько окон на компьютере.
Запустите скрипт
ControlKeyboardCombo.au3
.
Скрипт будет симулировать нажатие сочетания клавиш Alt+Tab и переключаться между открытыми окнами.
Эмуляция мыши
С помощью платы Arduino можно эмулировать не только клавиатуру, но и мышь.
Все библиотеки Arduino IDE рассчитаны на разработку устройств на основе платы. Например, уже знакомая нам библиотека Keyboard. С её помощью мы могли бы собрать и запрограммировать свою собственную клавиатуру. Но вместо этого мы использовали её для эмуляции настоящего устройства. Keyboard отлично подошла для решения этой задачи.
У Arduino IDE есть библиотека Mouse. Она аналогична Keyboard, но служит для разработки сходных с мышью устройств (например трекболы или джойстики). Mouse хорошо справляется со своей основной целью, но для эмуляции мыши её использовать неудобно.
Проблема в том, что библиотека оперирует относительными координатами курсора. Чем продиктовано такое решение? Представьте, что вы разрабатываете свою мышь на основе платы Arduino. Её перемещения по столу читаются с помощью светодиода-сенсора. Этот сенсор может сообщить на сколько единиц расстояния произошел сдвиг относительно прошлого положения устройства. Значение сдвига посылается на компьютер через HID интерфейс, и ОС отрисовывает курсор в новой позиции экрана. Абсолютные координаты в эту схему не укладываются, поскольку светодиод-сенсор не способен установить расположение мыши относительно какой-либо точки стола.
Для нашей цели эмуляции устройства абсолютные координаты были бы удобнее. По ним управляющий AutoIt скрипт читает пиксели экрана. Он знает, в какой именно точке нужно совершить щелчок мыши. Поэтому было бы естественно для скрипта указывать именно абсолютные координаты экрана.
У этой проблемы есть два возможных решения:
На стороне управляющего скрипта – реализовать алгоритм для расчёта относительных координат целевой точки.
На стороне программы платы – исправить библиотеку Mouse так, чтобы она работала с абсолютными координатами.
Сообщество пользователей Arduino уже решило задачу модификации библиотеки Mouse. Необходимые для этого изменения описаны в статье. К сожалению, это решение подходит только для Arduino IDE старой версий 1.0. В ней библиотеки Keyboard и Mouse были объединены в одну под название HID.
Чтобы исправить библиотеку Mouse в новых версиях IDE, выполните следующие действия:
Скачайте файл
Mouse.cpp
из архива примеров к этой книге.Скопируйте его с заменой в каталог Arduino IDE. Путь по умолчанию должен быть
C:\Program Files (x86)\Arduino\libraries\Mouse\src
.
Также вы можете исправить файл Mouse.cpp
самостоятельно. Для этого объявите макрос ABSOLUTE_MOUSE_MODE
и измените часть массива _hidReportDescriptor
следующим образом:
В массиве _hidReportDescriptor
перечислены данные, которые плата может отправить и получить от компьютера. Другими словами в нём описан протокол передачи данных. Благодаря ему компьютер может взаимодействовать со всем HID устройствами единообразно.
Если макрос ABSOLUTE_MOUSE_MODE
объявлен, протокол будет изменён в двух местах:
Значение байта
LOGICAL_MINIMUM
с ID равным 0x15 изменено с -127 (0x81 в шестнадцатеричной системе) на 1. Таким образом мы задали минимально допустимое значение координаты курсора. Для относительной координаты оно может быть отрицательным, но не абсолютной.Значение байта
INPUT
с ID равным 0x81 изменено с 0x06 на 0x02. Это означает, что теперь будут передаваться абсолютные координаты, а не относительные.
Чтобы переключиться обратно в режим относительных координат, просто удалите или закомментируйте объявление макроса ABSOLUTE_MOUSE_MODE
:
Программа mouse.ino
из листинга 5-5 симулирует нажатие кнопки мыши в указанной точке экрана.
Листинг 5-5. Программа mouse.ino
Алгоритмы программ mouse.ino
и keyboard-combo.ino
из листинга 5-3 очень похожи. Теперь мы получаем от управляющего AutoIt скрипта команду, состоящую не из трёх байт, а из четырёх. Её формат приведён в таблице 5-2.
Таблица 5-2. Формат команды
Номер байта | Значение |
1 | Преамбула. |
2 | Координата X-точки, в которой следует симулировать нажатие кнопки. |
3 | Координата Y-точки. |
4 | Код кнопки мыши, которая будет нажата. |
Получив команду по UART интерфейсу, мы проверяем её длину и корректность первого байта преамбулы. Если оба условия выполнены, вызываем функцию click
. Для симуляции действий мыши используется глобальный объект Mouse
. Он инициализируется с помощью метода begin
точно так же, как и Keyboard
. Перед тем как нажать кнопку, необходимо переместить курсор в заданную координату. Для этого вызываем метод move
объекта Mouse
, в который передаём координаты X и Y целевой точки. Затем с помощью метода click
симулируем нажатие в текущей позиции курсора.
Внимательный читатель заметит, что максимально допустимые значения координат X и Y ограничены числом 127. В шестнадцатеричном виде оно равно 0x7F. Это максимальное целое положительное число со знаком, которое может быть передано в одном байте. Это ограничение продиктовано протоколом HID. Обратите внимание на значение байта LOGICAL_MAXIMUM
в массиве _hidReportDescriptor
:
Получается, что максимальные координаты, на которые может переместить курсор Arduino плата, равны 127×127. Однако разрешение современных мониторов значительно превышает эти числа. Перекладка координат HID устройства в координаты монитора происходит на уровне ОС. Придётся повторить её в нашем управляющем AutoIt скрипте, чтобы правильно спозиционировать курсор.
Итак, скрипт знает абсолютные координаты точки экрана, в которой следует симулировать нажатие кнопки мыши. Задача заключается в том, чтобы перевести эти координаты в шкалу Arduino платы.
Формулы перевода координат выглядят следующим образом:
Значения переменных приведены в таблице 5-3.
Таблица 5-3. Переменные в формулах перевода координат
Переменные | Значение |
Xa, Ya | Координаты X и Y в шкале Arduino. |
X, Y | Координаты X и Y в шкале экрана. |
Xres, Yres | Разрешение экрана в пикселях. |
Рассмотрим пример перевода координат с помощью формул. Предположим, что разрешение нашего экрана 1366×768. Управляющий скрипт симулирует нажатие кнопки мыши в точке с координатами экрана X = 250 и Y = 300. Тогда ему надо отправить плате Arduino такие координаты:
Координата X = 23 в шестнадцатеричном виде равна 0x17, а Y = 49 равна 0x31. Команда целиком будет выглядеть следующим образом:
Листинг 5-6 демонстрирует управляющий скрипт для программы mouse.ino
.
Листинг 5-6. Скрипт ControlMouse.au3
Этот скрипт очень похож на ControlKeyboardCombo.au3
из листинга 5-4. Теперь в функцию SendArduino
передаются четыре параметра: дескриптор порта, координаты курсора X и Y, код кнопки для нажатия. Кроме этого появились две новые функции: GetX
и GetY
. Они переводят соответствующие координаты из шкалы экрана в шкалу Arduino платы.
В функциях GetX
и GetY
используется текущее разрешение экрана. В нашем примере оно равно 1366×768. Не забудьте поменять его на актуальное значение для вашего монитора.
GetX
и GetY
используется текущее разрешение экрана. В нашем примере оно равно 1366×768. Не забудьте поменять его на актуальное значение для вашего монитора.Для тестирования эмулятора мыши выполните следующие шаги:
Загрузите программу
mouse.ino
на Arduino плату.Запустите приложение Paint. Переключитесь в нём на инструмент Brush (кисть).
Запустите скрипт
ControlMouse.au3
.
Скрипт симулирует щелчок левой кнопки мыши в точке с абсолютными координатами 250×300 в окне Paint. В ней должна появиться чёрная точка.
Эмуляция клавиатуры и мыши
Мы разработали программы для Arduino платы, чтобы эмулировать клавиатуру и мышь по отдельности. Такое решение хорошо работает, если для управления персонажем в игре требуется только одно из устройств ввода. Если же нужны оба, вам придётся купить две платы, запрограммировать их и сделать так, чтобы бот управлял обоими. Это неудобно. Намного лучше будет совместить функции эмуляции клавиатуры и мыши в одном устройстве. HID интерфейс это позволяет. Единственная сложность заключается в протоколе передачи данных по UART. Нам потребуется его расширить.
Прежде всего программа платы должна понять, какое именно действие требует выполнить управляющий AutoIt-скрипт. Назначим каждому из возможных действий код. Например, как предложено в таблице 5-4.
Таблица 5-4. Коды симулируемых действий
Код | Действие |
0x1 | Нажатие клавиши без модификатора. |
0x2 | Нажатие клавиши с модификатором. |
0x3 | Щелчок мыши. |
В команде код действия должен идти сразу после байта преамбулы. Благодаря этому программа сможет правильно интерпретировать оставшиеся данные. Если код равен 0x1 или 0x2, применяется алгоритм симуляции нажатия клавиши из программы keyboard-combo.ino
(листинг 5-3). В случае кода 0x3, отрабатывает алгоритм программы mouse.ino
(листинг 5-5).
Листинг 5-7 демонстрирует программу для платы, которая поддерживает новый формат команд.
Листинг 5-7. Программа keyboard-mouse.ino
Для выбора симулируемого действия в зависимости от полученного кода, мы используем оператор switch
в функции loop
. Этот оператор проверяет значение второго байта команды. Он определяет, какая из функций будет вызвана для обработки оставшихся байт. Для удобства в операторе switch
мы используем константы с кодами команд: KEYBOARD_COMMAND
(0x1), KEYBOARD_MODIFIER_COMMAND
(0x2) и MOUSE_COMMAND
(0x3).
Возможно, вы заметили, что в случае команды на нажатие клавиши управляющий скрипт передаёт лишние данные. Метод readBytes
объекта Serial
всегда читает пять байтов (это константа BUFFER_SIZE
) из входного буфера UART. Но используются из них только три в случае нажатия без модификатора или четыре – с модификатором. Можно ли оптимизировать эти накладные расходы и не передавать лишние данные? Предположим, что мы исправили управляющий скрипт. В результате этого длина команды зависит от кода действия, указанного во втором байте. Проблема в том, что мы должны передать в метод readBytes
число байт для чтения из входного буфера UART. Но на момент его вызова, эта информация неизвестна. Поэтому нам придётся воспользоваться другим методом объекта Serial
.
Метод readBytesUntil
позволяет читать байты из входного буфера до тех пор, пока не встретится символ ограничитель или терминатор. Ограничитель – это предопределённое значение, которое сигнализирует об окончании передачи. Такой подход выглядит перспективным. Единственный вопрос, на который осталось ответить: какой ограничитель выбрать? Если вы задумаетесь над ним, то придёте к выводу, что однозначного ответа нет. Ограничитель, как и преамбула, – это один байт. Его значение не должно встречаться в данных команды. То есть отпадают все значения, которые могут принять координаты позиций курсора мыши (от 0x00 до 0x7F) и коды клавиш (от 0x00 до 0xFF). К сожалению, код клавиши может быть любым из диапазона значений, помещающихся в один байт. Поэтому нельзя гарантировать уникальность ограничителя. Мы могли бы увеличить его длину до двух байт. Это бы решило проблему, но тогда мы ничего не выиграем от команд переменной длины. Нам придётся передавать столько же байт, а иногда и больше, как и в случае с командами одинаковой длины.
Объект Serial
предоставляет ещё один метод – read
. Он читает все байты, находящиеся во входном буфере UART. С его помощью можно было бы решить нашу проблему, но только в том случае, если управляющий скрипт будет делать задержки между командами. Длительности каждой задержки должно быть достаточно, чтобы программа Arduino успела прочитать буфер. В противном случае в буфер будут попадать несколько команд за раз и различить их окажется проблематично. Этот подход ненадёжен, поскольку скрипт может генерировать запросы к плате очень часто.
В результате мы приходим к выводу, что накладные расходы, связанные с одинаковой длиной команд, приемлемы. Ими мы расплачиваемся за надёжную передачи данных.
Листинг 5-8 демонстрирует управляющий скрипт для программы keyboard-mouse.ino
.
Листинг 5-8. Скрипт ControlKeyboardMouse.au3
В этом скрипте мы реализовали две отдельные функции для симуляции действий клавиатуры и мыши. SendArduinoKeyboard
отправляет на плату команду для нажатия клавиши. Её алгоритм почти такой же, как у функции SendArduino
из скрипта ControlKeyboardCombo.au3
(листинг 5-4). Отличие в формате команды: появился второй байт с кодом действия. Также мы дополняем байтовый массив на выдачу до необходимой длины в пять байтов с помощью константного значения 0xFF. Если нажатие симулируется без модификатора, то третий байт сообщения также заменяется на 0xFF.
Функция SendArduinoMouse
отправляет команду для симуляции щелчка мыши. Единственное её отличие от аналога из скрипта ControlMouse.au3
(листинг 5-6) – добавлен код действия во втором байте.
Чтобы протестировать скрипт ControlKeyboardMouse.au3
, выполните следующие действия:
Загрузите программу
keyboard-mouse.ino
на Arduino-плату.Запустите приложение Paint.
Запустите приложение Notepad.
Запустите скрипт.
Скрипт последовательно выполнит три действия:
Щелчок левой кнопкой мыши в окне Paint.
Набор строки "Test" в окне Notepad.
Переключение между открытыми окнами с помощью комбинации клавиш Alt+Tab.
Может возникнуть вопрос: почему мы использовали константное значение 0xFF для дополнения команд до нужной длины? Разумнее было бы подставлять 0x00. Это решение продиктовано особенностью AutoIt-функции StringFromASCIIArray
, с помощью которой мы конвертируем массив в строку. Она обрабатывает значение 0x00 как ограничитель строки. Другими словами, результирующая строка будет обрезана до этого символа. Эта особенность означает, что все наши команды не должны содержать нулевых байтов. Следовательно, мы не сможем симулировать нажатие клавиши с кодом 0x00.
Выводы
Мы рассмотрели технику эмуляции клавиатуры и мыши с помощью платы Arduino. AutoIt скрипт, в котором реализована вся логика бота, может управлять ею через UART интерфейс. Таким образом совмещаются возможности анализа изображения на экране и симуляции действий устройств ввода. Благодаря этому вашего кликера будет невозможно обнаружить с помощью защит, основанных на проверке состояния клавиатуры и мыши.
Last updated