Русифицируем уточку. Как заставить BadUSB работать с разными раскладками клавиатуры
Эксплойт
BadUSB — один из самых интересных инструментов в арсенале хакера. Этот класс атак позволяет при помощи девайсов вроде Rubber Ducky захватить контроль над многими устройствами, у которых есть порт USB. Можно эмулировать любую периферию, но чаще всего подделывают клавиатуру. В этой статье я покажу, как решить одну из главных связанных с этим проблем — зависимость от текущей раскладки.
Существуют две основные категории устройств типа BadUSB: флешка с контроллером Phison 2251-03 (для него была разработана специальная прошивка, эмулирующая клавиатуру) и устройства на микроконтроллере ATmega (например, Rubber Ducky).
BadUSB придумали относительно недавно, в 2014 году. Поэтому еще есть шанс найти новые трюки и решить по дороге разные нетривиальные задачи. Для тех, кто живет в странах, где, помимо латинского алфавита, используется какой-то другой, одна из таких задач — это переключение ввода на нужную раскладку клавиатуры. Если в операционной системе выставлена русская раскладка, а твой скрипт скармливает ей нажатия на клавиши в английской, то из этого, конечно же, ничего не выйдет.

Как обойти эту проблему? Например, можно попытаться учесть изменение раскладки в скриптах, но для этого придется узнавать, какие именно языки используются в системе, сколько их, в каком порядке они стоят и каким сочетанием клавиш переключаются. Это явно не самый рациональный метод.
Я предлагаю использовать другой вариант — вводить все символы при помощи Alt-кодов. Этот способ в Windows иногда используется для ввода символов, которые отсутствуют на клавиатуре. Например, зажимаешь Alt, удерживая, набираешь на цифровом блоке 9731, и получается снеговик. Здорово? Нам это тоже очень поможет, поскольку таким образом можно вводить и обычные символы, причем независимо от текущей раскладки.

Таблица Alt-кодов
Для своих экспериментов я использовал Rubber Ducky на основе ATmega32U4. В первую очередь нужно научить ее воспроизводить команды — это делается на специальном языке, который называется Ducky Script. Однако, изучив материалы, я не нашел никаких сведений о возможности эмуляции клавиш цифрового блока. Поэтому сначала нужно было модифицировать прошивку BadUSB.
Новая прошивка BadUSB
За основу берем библиотеку keyboard. У меня получился вот такой код.
#include "Keyboard.h"
#include <SD.h>
#include <string.h>
#include <SPI.h>
File script;
boolean first = true;
String DEFAULT_FILE_NAME = "script.txt"; void setup() { if (!SD.begin(4)) { return; } script = SD.open(DEFAULT_FILE_NAME); if (script) { Keyboard.begin(); String line = ""; while (script.available()) { char m = script.read(); if (m == '\n'){ Line(line); line = ""; } else if((int) m != 13) { line += m; } } Line(line); script.close(); } else { } Keyboard.end();
} void Line(String l)
{ int space_1 = l.indexOf(" "); if (space_1 == -1) { Press(l); } else if (l.substring(0,space_1) == "STRING") { Keyboard.print(l.substring(space_1 + 1)); } else if (l.substring(0,space_1) == "DELAY") { int delaytime = l.substring(space_1 + 1).toInt(); delay(delaytime); } else if(l.substring(0,space_1) == "REM"){} else { String remain = l; while(remain.length() > 0) { int latest_space = remain.indexOf(" "); if (latest_space == -1) { Press(remain); remain = ""; } else { Press(remain.substring(0, latest_space)); remain = remain.substring(latest_space + 1); } delay(5); } } Keyboard.releaseAll();
} void Press(String b)
{ if(b.length() == 1) { char c = b[0]; Keyboard.press(c); } else if (b.equals("ENTER")) { Keyboard.press(KEY_RETURN); } else if (b.equals("CTRL")) { Keyboard.press(KEY_LEFT_CTRL); } else if (b.equals("SHIFT")) { Keyboard.press(KEY_LEFT_SHIFT); } else if (b.equals("ALT")) { Keyboard.press(KEY_LEFT_ALT); } else if (b.equals("GUI")) { Keyboard.press(KEY_LEFT_GUI); } else if (b.equals("NUMLOCK")) { Keyboard.press(219); } else if (b.equals("UP") || b.equals("UPARROW")) { Keyboard.press(KEY_UP_ARROW); } else if (b.equals("DOWN") || b.equals("DOWNARROW")) { Keyboard.press(KEY_DOWN_ARROW); } else if (b.equals("LEFT") || b.equals("LEFTARROW")) { Keyboard.press(KEY_LEFT_ARROW); } else if (b.equals("RIGHT") || b.equals("RIGHTARROW")) { Keyboard.press(KEY_RIGHT_ARROW); } else if (b.equals("DELETE")) { Keyboard.press(KEY_DELETE); } else if (b.equals("PAGEUP")) { Keyboard.press(KEY_PAGE_UP); } else if (b.equals("PAGEDOWN")) { Keyboard.press(KEY_PAGE_DOWN); } else if (b.equals("HOME")) { Keyboard.press(KEY_HOME); } else if (b.equals("ESC")) { Keyboard.press(KEY_ESC); } else if (b.equals("INSERT")) { Keyboard.press(KEY_INSERT); } else if (b.equals("TAB")) { Keyboard.press(KEY_TAB); } else if (b.equals("END")) { Keyboard.press(KEY_END); } else if (b.equals("CAPSLOCK")) { Keyboard.press(KEY_CAPS_LOCK); } else if (b.equals("F1")) { Keyboard.press(KEY_F1); } else if (b.equals("F2")) { Keyboard.press(KEY_F2); } else if (b.equals("F3")) { Keyboard.press(KEY_F3); } else if (b.equals("F4")) { Keyboard.press(KEY_F4); } else if (b.equals("F5")) { Keyboard.press(KEY_F5); } else if (b.equals("F6")) { Keyboard.press(KEY_F6); } else if (b.equals("F7")) { Keyboard.press(KEY_F7); } else if (b.equals("F8")) { Keyboard.press(KEY_F8); } else if (b.equals("F9")) { Keyboard.press(KEY_F9); } else if (b.equals("F10")) { Keyboard.press(KEY_F10); } else if (b.equals("F11")) { Keyboard.press(KEY_F11); } else if (b.equals("F12")) { Keyboard.press(KEY_F12); } else if (b.equals("N9")) { Keyboard.press (233); Keyboard.release (233); } else if (b.equals("N8")) { Keyboard.press (232); Keyboard.release (232); } else if (b.equals("N7")) { Keyboard.press (231); Keyboard.release (231); } else if (b.equals("N6")) { Keyboard.press (230); Keyboard.release (230); } else if (b.equals("N5")) { Keyboard.press (229); Keyboard.release (229); } else if (b.equals("N4")) { Keyboard.press (228); Keyboard.release (228); } else if (b.equals("N3")) { Keyboard.press (227); Keyboard.release (227); } else if (b.equals("N2")) { Keyboard.press (226); Keyboard.release (226); } else if (b.equals("N1")) { Keyboard.press (225); Keyboard.release (225); } else if (b.equals("N0")) { Keyboard.press (234); Keyboard.release (234); }
} void loop() {}
Работать микроконтроллер должен так же, как Rubber Duck, но с поддержкой цифрового блока. Все команды совпадают с Ducky Script, кроме одной. Если написать N и цифру (без разделителей), то будет эмулироваться нажатие клавиш на Numpad.
Конвертер строк
Теоретически уже на этом этапе можно начинать писать скрипты, но вручную вбивать альт-коды — не лучший способ потратить свое время. Чтобы облегчить себе задачу, напишем небольшой скрипт на Python 3, который будет преобразовывать текст на английском в нажатия комбинаций с Alt.
convert = { 'A': 'ALT N6 N5', 'B': 'ALT N6 N6', 'C': 'ALT N6 N7', 'D': 'ALT N6 N8', 'E': 'ALT N6 N9', 'F': 'ALT N7 N0', 'G': 'ALT N7 N1', 'H': 'ALT N7 N2', 'I': 'ALT N7 N3', 'J': 'ALT N7 N4', 'K': 'ALT N7 N5', 'L': 'ALT N7 N6', 'M': 'ALT N7 N7', 'N': 'ALT N7 N8', 'O': 'ALT N7 N9', 'P': 'ALT N8 N0', 'Q': 'ALT N8 N1', 'R': 'ALT N8 N2', 'S': 'ALT N8 N3', 'T': 'ALT N8 N4', 'U': 'ALT N8 N5', 'V': 'ALT N8 N6', 'W': 'ALT N8 N7', 'X': 'ALT N8 N8', 'Y': 'ALT N8 N9', 'Z': 'ALT N9 N0', 'a': 'ALT N9 N7', 'b': 'ALT N9 N8', 'c': 'ALT N9 N9', 'd': 'ALT N1 N0 N0', 'e': 'ALT N1 N0 N1', 'f': 'ALT N1 N0 N2', 'g': 'ALT N1 N0 N3', 'h': 'ALT N1 N0 N4', 'i': 'ALT N1 N0 N5', 'j': 'ALT N1 N0 N6', 'k': 'ALT N1 N0 N7', 'l': 'ALT N1 N0 N8', 'm': 'ALT N1 N0 N9', 'n': 'ALT N1 N1 N0', 'o': 'ALT N1 N1 N1', 'p': 'ALT N1 N1 N2', 'q': 'ALT N1 N1 N3', 'r': 'ALT N1 N1 N4', 's': 'ALT N1 N1 N5', 't': 'ALT N1 N1 N6', 'u': 'ALT N1 N1 N7', 'v': 'ALT N1 N1 N8', 'w': 'ALT N1 N1 N9', 'x': 'ALT N1 N2 N0', 'y': 'ALT N1 N2 N1', 'z': 'ALT N1 N2 N2', ',': 'ALT N4 N4', '.': 'ALT N4 N6', '/': 'ALT N4 N7', '<': 'ALT N6 N0', '>': 'ALT N6 N2', '?': 'ALT N6 N3', ';': 'ALT N5 N9', ':': 'ALT N5 N8', '"': 'ALT N3 N4', '[': 'ALT N9 N1', ']': 'ALT N9 N3', '{': 'ALT N1 N2 N3', '}': 'ALT N1 N2 N5',} stEng = input("Введите текст для конвертирования на английском: ")
i = 0
f = 0
otvet = []
for k in stEng: try: print(convert[stEng[i]]) i += 1 except KeyError: print('STRING',k) i += 1
Расширяем память
Обрати внимание, что этот скрипт преобразует только строки, но не весь код целиком. Кстати, из-за того, что каждый вводимый символ превращается в 12 символов, отдельные скрипты, которые раньше помещались в память микроконтроллера, теперь могут не поместиться, так что придется как-нибудь выкручиваться.
ATmega32U4 располагает 32 Кбайт флеш-памяти, из которых 4 Кбайт используется для хранения загрузчика. Также у нее 2,5 Кбайт ОЗУ и 1 Кбайт EEPROM.
Arduino — штука довольно гибкая, и ее можно улучшить, в первую очередь всякими дополнительными модулями. Если нужно увеличить память, то берем картридер microSD и подключаем его по интерфейсу SPI.

Далее нам нужно модифицировать скетч, который будет читать скрипт с SD-карты. Постоянно перезаливать скетч в Arduino не нужно, можно просто залить их на карту. Файл со скриптом обязательно должен называться script.txt.
Инструкция к использованию
Вот как будет выглядеть скрипт, открывающий блокнот и вводящий в него текст.
DELAY 500 GUI r STRING notepad ENTER DELAY 500 STRING Hello World!
Далее нужно преобразовать все строки: в данном примере она одна — STRING.
Копируем текст «Hello World!» и вставляем в нашу программу на Python.
Получившийся код выглядит так:
DELAY 500 GUI r STRING notepad ENTER DELAY 500 ALT N7 N2 ALT N1 N0 N1 ALT N1 N0 N8 ALT N1 N0 N8 ALT N1 N1 N1 STRING ALT N8 N7 ALT N1 N1 N1 ALT N1 N1 N4 ALT N1 N0 N8 ALT N1 N0 N0 STRING !
Есть небольшая вероятность, что на целевом устройстве может быть выключен Num Lock, поэтому стоит в конце кода написать NUMLOCK и продублировать программу.
Пример кода с дублированием:
DELAY 500 GUI r // Активируем команду «Выполнить» ALT N1 N1 N0 // Если NumLock включен, то следующие программы сработают ALT N1 N1 N1 ALT N1 N1 N6 ALT N1 N0 N1 ALT N1 N1 N2 ALT N9 N7 ALT N1 N0 N0 // Вводим notepad ENTER DELAY 500 // Ждем 0,5 с ALT N7 N2 ALT N1 N0 N1 ALT N1 N0 N8 ALT N1 N0 N8 ALT N1 N1 N1 STRING ALT N8 N7 ALT N1 N1 N1 ALT N1 N1 N4 ALT N1 N0 N8 ALT N1 N0 N0 STRING ! // Вводим в блокноте «Hello World!» NUMLOCK // Если Num Lock был выключен, то мы его включаем этой командой и снова вводим код ALT F4 GUI r // Активируем команду «Выполнить» ALT N1 N1 N0 ALT N1 N1 N1 ALT N1 N1 N6 ALT N1 N0 N1 ALT N1 N1 N2 ALT N9 N7 ALT N1 N0 N0 // Вводим «notepad» ENTER DELAY 500 // Ждем 0,5 с ALT N7 N2 ALT N1 N0 N1 ALT N1 N0 N8 ALT N1 N0 N8 ALT N1 N1 N1 STRING ALT N8 N7 ALT N1 N1 N1 ALT N1 N1 N4 ALT N1 N0 N8 ALT N1 N0 N0 STRING ! // Вводим в блокноте «Hello World!»
Теперь сохраняем этот скрипт в файл script.txt, записываем на SD-карту, ставим ее в картридер устройства, устройство — в порт и наслаждаемся результатом!