Hackeando un sistema de seguridad intercom para abrir la puerta con un teléfono

Cómo comenzó…

La oficina que actualmente trabajo en el se encuentra en un edificio que tiene un sistema de interfon. Cuando cualquiera de nosotros quiere entrar, basta con pulsar un botón y esperar que alguien responda y presione un botón en el auricular para abrir la puerta.

El problema se presenta cuando no hay nadie dentro (o esta en el baño).

Así surgió la idea de crear un servicio para abrir la puerta con el teléfono. ¿Cómo lo hacemos?

El algoritmo es algo como esto:
1) averiguar cómo funciona el botón de auricular
2) crear un circuito para hackear la señal del auricular
3) Codificar un servicio para activar el circuito
4) Hacer una aplicación de teléfono para comunicarse con el servicio

Así que vamos!

Descubra cómo funciona el auricular

Afortunadamente, en el trabajo contamos con gente muy hábil. Así que le pedí uno de ellos, Javier, echarle un vistazo al aparato. Resulta que la puerta funciona con un dispositivo magnético. El botón interrumpe el poder que mantiene el imán energizado. Lo hace tocando 2 pequeñas placas. No podía ser más sencillo.

Crear un circuito para hackear la señal

Usando un protoboard, Javier monto un circuito que consistió en circuit1un relé y un arduino (junto con un ethernet shield). La idea era conectar algunos cables en las placas, y usar el relé para cerrar el circuito. Para activar el relé, tendríamos que enviar que una instrucción al Arduino que estaria conectado al módem. Así que todo lo que tenemos que hacer es conectarnos a la red inalámbrica y enviar un comando a la dirección de ip de Arduino.

Codificar un servicio para activar el circuito

Ya que no tengo ninguna experiencia significativa codificando en un Arduino (aunque había codificado algo para un microcontrolador rabbit antes) me base en uno de los demos para un servidor web. Después de algunos ajustes y pruebas, termine con algo como:

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0x60, 0xA5, 0xEA, 0x0E, 0x07, 0x10
};
IPAddress ip(200, 200, 200, 200);

EthernetServer server(80);

void setup() {

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(7,OUTPUT);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}


void loop() {
 
 // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    
// an http request ends with a blank line
    boolean currentLineIsBlank = true;
    String currentLine = "";
    String command = "";
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        currentLine = currentLine + c;
        
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        
        if (c == '\n' && currentLineIsBlank && command == "some command") {

          digitalWrite(7, HIGH);  
          delay(1000);              
          digitalWrite(7, LOW);    
          //delay(1000);  
          
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");            
          client.println(command);
          client.println("</html>");
          command = "";
          break;
        }
        
        // you're starting a new line 
        if (c == '\n') {                                 
          currentLineIsBlank = true;
          Serial.println("current line = " + currentLine);
          currentLine = "";
        } 
       // you've gotten a character on the current line 
       else if (c != '\r') {                          
          currentLineIsBlank = false;
        } 

       //get the command
       else if (c == '\r' and currentLine.startsWith("GET")){ 
          int start = currentLine.indexOf('/')+1;
          int endAt = currentLine.indexOf(' ',start);
          command = currentLine.substring(start, endAt);         
        }
      }
    }

    // give the web browser time to receive the data
    delay(1);

    // close the connection:
    client.stop();
    Serial.println("client disconnected");
    Ethernet.maintain();
  }
}

La idea era proporcionar una API web que respondiera la petición a una URL concreta. A continuación extraemos la última parte de la url solicitada y utilizamos esto como comando para activar el relé y por lo tanto, abrir la puerta. Cualquier persona con la dirección ip y la URL correcta puede activar el relé, siempre y cuando sea la misma subred que el Arduino.

Código de una aplicación de teléfono para comunicarse con el servicio

Por lo tanto, Josue (otro compañero de trabajo) sugirió que puesto que había creado la web también podría crear una aplicación de teléfono para consumirla. Ya que la mayoría de las personas de la oficina tienen un teléfono android, pensé que sería una buena oportunidad para probar la plataforma. Tengo cero conocimiento del modelo de programación Android, asi que Josue sugirió que le echara un vistazo a Xamarin. Resulta ser bastante sencilla. Todavía tengo que aprender más de la plataforma…

Aquí está el código:

[Activity(Label = "Jarvis", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        private string url = "http://200.200.200.200/";

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            Button button = FindViewById<Button>(Resource.Id.MyButton);
            TextView text = FindViewById<TextView>(Resource.Id.textView1);
            
            button.Click += async(sender,e) =>
            {
                try
                {
                    await send(jarvisCommands.openDoor);
                    text.Text += "Get in! \n\r";
                }
                catch (Exception ex)
                {
                    text.Text += ex.Message + "\n\r" + Resources.GetString(Resource.String.Error) + "\n\r";
                }

            };
        }

        private async Task send(jarvisCommands command)
        {
                HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create(new Uri(url + command.ToString()));
                request.ContentType = "application/text";
                request.Method = "GET";
                await request.GetResponseAsync();
         
        }

        enum jarvisCommands { openDoor}

Cosas aprendidas:
1) la necesidad es la madre de la inventiva
2) puedes crear cosas interesantes cuando estás rodeado de gente interesante
3) si sabes Java, C++, C# o cualquier otro idioma entre llaves, ya eres capaz de escribir algo para el Arduino
4) conceptualmente el codigo en la plataforma Android es sólo una implementación de un bus de mensajes

Creo que esto es todo. Hasta el próximo post! (Voy a escribir a menudo, en serio)