Twitter Stalker. Script para obtener datos de una cuenta de Twitter

Mitad por entretenimiento y mitad por motivos personales, me vi desarrollando una aplicación en Bash que me permitiera obtener los datos de una cuenta de Twitter, a partir del nombre de usuario del que quisiera obtener dichos datos.

Hace algunos dias la terminé, y ayer la traducí a PHP, puesta hoy pública (aunque todavia no el código) para detectar posibles fallos y ser reportado de algún tipo de bug o comentario, es por eso por lo que animo a que cualquiera que tenga un rato la pruebe: http://old.delanover.com/projects/twitter.

Quisiera relatar un poco la experiencia que tuve en este post. Como dije, al principio la escribí en Bash. Viene muy bien saber comandos de linux para esto, y en el proceso de creación del script descubrí algunas cosas interesantes de Bash.

El comando paste, nos permite juntar dos ficheros desde la consola. Pero no de la misma forma que hariamos con cat archivo1 archivo2. De esta última forma, concatenariamos archivos. Entenderemos este comando mejor con un ejemplo:
Archivo 1
Hola
Adios
Archivo 2
Mundo
Mundo

Realizamos: paste archivo1 archivo2 > final y obtenemos un fichero final cuyo contenido es:
Hola Mundo
Adios Mundo
(Palabras separadas por un tabulador)

Parece que no, pero puede ser útil este comando, y con lo sencillo que es, me parecia raro no haber oido hablar de él.

También tuve que lidiar con la búsqueda de expresiones regulares de forma un-greedy (o non-greedy). ¿Qué es esto? Lo veremos también claro con un ejemplo. Imaginemos que tenemos la siguiente string: inicio intermedio fin basura basura fin y queremos obtener lo que hay entre inicio y fin (el primer fin, no el último). Usando expresiones regulares, podriamos pensar en algo así:

grep 'inicio.*fin' -o archivoCualquiera (el -o es para que nos muestre todo lo que abarca esa expresión regular. Es decir, que nosotros queremos: “inicio intermedio fin basura basura fin“, pero en lugar de eso, nos devuelve “inicio intermedio fin basura basura fin“. ¿Pero por qué? Porque primero busca “inicio”, después busca todos los carácteres posibles (todos los que simbolizan el punto) y cuando ya los tiene todos, se pone a buscar hacia atrás (de izquierda a derecha) “fin”. En otras palabras se podría interpretar con que empieza a buscar “inicio” por la izquierda y “fin” por la derecha, y muestra todo lo que hay de por medio.

El caso es que yo no quería esto. Yo quería que empezase a buscar por la derecha, y siguiese buscando desde ese momento hacia la derecha. De esta forma obtendríamos el “inicio intermedio fin basura basura fin“. Pues bien, este método de buscar se llama non-greedy o un-greedy, y nativamente, bash no lo permite y digo nativamente, porque grep tiene un parámetro que nos permite usar expresiones regulares de Perl y este sí que permite hacer lo que buscamos.

Solución final: grep -P 'inicio.*?fin' -o archivoCualquiera

Simplemente añadiendo un símbolo de interrogración tras el asterisco.

Volviendo al tema del script, mencionar que he usado la API de twitter que podeis encontrar aquí: https://dev.twitter.com/docs/api
Un apunte: Twitter permite como máximo 150 peticiones por hora por IP. Parecen suficientes o incluso muchas, pero innumerables veces me ha saltado el error de que he excedido el límite.

Por otra parte, al re-programarlo en PHP y al tratarse de que las respuestas vienen dadas en formato JSON, éste tiene una función que ayuda mucho: json_decode. El resultado no se trata como una matriz cualquiera, así que hablaré un poco. Por un lado, miraremos la estructura de esto. Para acceder a las propiedades del primer nivel sería tan fácil como:

$objeto = json_decode($cadena_json);
echo $objeto->{'created_at'}; //Ejemplo de acceso a un primer nivel
echo $objeto->{'user'}->{'name'}; //Ejemplo de acceso a un segundo nivel

De esta forma podemos acceder individualmente. Para ver todo el contenido de la petición es todavía más sencillo:

$objeto = json_decode($cadena_json);
var_dump($objeto);

Insisto en que lo probeis y me comentéis algo: http://old.delanover.com/twitter

Un saludo, lipman

Usando la API de Virustotal

Índice:
1.-Analizando un archivo
2.-Realizando comentarios
3.-Analizando una URL

Introducción

Quien no conoce Virustotal, el servicio online en el que podemos subir un archivo y nos lo escanea con hasta 42 antivirus para tener varios resultados.

Hace ya tiempo, los que se dedicaban al desarrollo de Malware usaban el KIMS (que recuerdos me trae), que era un software que se descargaba varios antivirus (creo que hasta 20) y los analizaba localmente en tu propio ordenador. Ahora con el avance de los tiempos, tenemos servicios online como este del que voy a hablar hoy, o de NoVirusThanks, el cual tuvo su auge en cuanto los desarrolladores supieron que la plataforma Virustotal distribuia lo que subian (o eso se dijo), por lo que si subian virus que estaban desarrollando, no les traia cuenta que estos ficheros fueran dados a las marcas de Antivirus para que los analizasen y detectasen posteriormente.

Una de las razones que me ha llevado a interesarme por esta API, es su increible simpleza y flexibilidad, como podremos ver a continuación en un script. Antes de nada, decir que este script, y todo lo que pondré a continuación está sacado de aquí. La razón por la cual quiera hacer un post de esto, es para dar a conocer más esta API y que todos vean lo simple que es.

Antes de dar paso al uso de la API, quisiera agradecer a Julio Canto y Emiliano Martínez (del staff de VirusTotal) por su total ayuda (incluso en un domingo =P) a través de twitter y por correo electrónico. Sin ellos no podría haber terminado este post, ya que tuve dudas importantes con el uso de la API analizando webs.

A continuación, vamos a hacer lo más simple, y es analizar un fichero y obtener el reporte de los antivirus. Para ello lo primero que tenemos que hacer, es registrarnos en Virustotal.com, y obtener la API Key, que se halla en My Account -> Inbox -> Public API.

1.-Analizando un archivo

Ahora os dejo con el código, comentado y explicado, de cómo analizar un fichero virus.exe y obtener el resultado:

0: fecha (-2 horas española)
--->1: Array
------>Nombre del antivirus: Resultado
------| Si no detecta nada, el resultado está vacio
------| Hay 42 antivirus
*/

echo "
"; if($retrieve){ foreach($retrieve['report'][1] as $clave => $valor) echo $clave . ": " . $valor . "
"; //Esto nos devuelve todos los antivirus, seguido del resultado que nos dan echo "
"; } function virustotal_scanfile($filepath, $key){ //Editada y comentada por lipman // Author: Peter Bailey // Website: Bailey-Projects.com $post = array('key' => $key, 'file' => '@'.$filepath); //preparamos lo que vamos a pasar por POST $ch = curl_init(); //Iniciamos CURL curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); curl_setopt($ch, CURLOPT_URL, 'http://www.virustotal.com/api/scan_file.json'); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($ch); curl_close($ch); //Cerramos CURL } function virustotal_getreport($resource, $key){ $url = 'https://www.virustotal.com/api/get_file_report.json'; $fields = array('resource'=>$resource, 'key'=>$key); foreach($fields as $key=>$value) $fields_string .= $key.'='.$value.'&'; $fields_string = rtrim($fields_string,'&'); //Preparamos la variable que enviaremos por el método POST $ch = curl_init(); //Iniciamos CURL curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_POST,count($fields)); curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($ch); curl_close($ch); //Cerramos CURL $result = json_decode($result, true); //Decodeamos usando la función de json el resultado if(isset($result['report'][0])) return $result; //Si todo sale bien, lo devolvemos a la función else return false; } ?>

Resultado (seguro que podeis adiviniar que virus testeé):

2.-Realizando comentarios

En los reportes que se generan online tras el analizado de un fichero, se pueden realizar comentarios, incluso puntuar si estos son buenos o malos. Para realizar comentarios también disponemos de una función que he explicado en los comentarios del script:

$key,'comment'=>$comment);
	 if(preg_match('/^https?:\/\/.*/',$fileorurl)){
	    $fields['url']=$fileorurl;
	 } else {
	    $fields['file']=$fileorurl;
	 }
	 //Esto detecta si lo que pasamos es la URL o el hash del archivo, pero como
	 //dije anteriormente, a mi pasando la URL no me funciona, asi que podriamos
	 //deshacer este if-else
	
    $fields_string='';
	foreach($fields as $key=>$value)
	   $fields_string .= $key.'='.$value.'&';
	
	$fields_string=rtrim($fields_string,'&');
	//Creamos lo que vamos a pasar por POST-JSON
	
        $ch = curl_init();
	//Iniciamos CURL
	curl_setopt($ch,CURLOPT_URL,$url);
	curl_setopt($ch,CURLOPT_POST,count($fields));
	curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

	$result = curl_exec($ch);

	curl_close($ch);
	//Cerramos CURL
	$result = json_decode($result, true);
	//Decodeamos el resultado para saber si ha sido exitoso o no el proceso

	 if($result['result']=="1"){
	 	return true;
	 } else {
	 	return false;
	 }
}

Analizando una URL

Virustotal también nos permite analizar una URL. A priori, al analizar una URL, los motores de análisis tales como Google Safebrowsing y el de Firefox, nos indican la maliciosidad de la web (devuelto en un array, al igual que los resultados de cuando analizábamos un archivo). Pero además de esto, si la URL que indicábamos corresponde a un archivo, también nos devuelve un identificador que posteriormente podemos usar para ver si ese archivo es detectado o no.

Aquí el código, explicado:

$key = 'API_KEY';
//Api que se obtiene en el perfil
$file = "http://i476.photobucket.com/albums/rr125/lipmandj/asd.jpg";
//URL a analizar

$identificador = virustotal_scanurl($file,$key);

$retrieve = virustotal_geturlreport($key,$identificador);


if($retrieve){
	print_r($retrieve); // Array que contiene el reporte
}

function virustotal_scanurl($url, $key){

	// Esta función envia la URL

	
    // Author: Kenny Lyons aka ih8censorship
	// Website: http://pasture.sourceforge.net
    // Editado por lipman
	
    //URL a la que enviaremos los datos
    $url = 'http://www.virustotal.com/api/scan_url.json';
    //Preparamos los campos de lo que vamos a enviar
	$fields = array('url'=>$url, 'key'=>$key);
    $fields_string='';
	foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
	$fields_string=rtrim($fields_string,'&');

    //Procedemos a enviarlo mediante cURL
    $ch = curl_init();

	curl_setopt($ch,CURLOPT_URL,$url);
	curl_setopt($process, CURLOPT_HTTPHEADER, array("Content-Type: text/xml","SOAPAction: \"/soap/action/query\"", "Content-length: ".strlen($fields_string))); 
	curl_setopt($ch,CURLOPT_POST,count($fields));
	curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	
	$result = curl_exec($ch);

	curl_close($ch);
	//Tras enviarlo, cerramos la conexión y decodeamos el resultado devuelto
	$result = json_decode($result, true);
	
	
	if($result['result']=="1"){ //Si todo va bien, devolvemos la ID del scan
		return $result['scan_id'];
	} else {
		return false;
	}
	
}

function virustotal_geturlreport($key,$resource,$scan=false){

	// Nos devuelve el reporte
	
    // Author: Kenny Lyons aka ih8censorship
	// Website: http://pasture.sourceforge.net
    //Editado por lipman	

    //URL a donde enviaremos los datos
    $url = 'http://www.virustotal.com/api/get_url_report.json';
    //Preparamos los campos
	$fields = array('resource'=>$resource, 'key'=>$key,'scan'=>$scan);
    $fields_string='';
	foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
	$fields_string=rtrim($fields_string,'&');
    //Finalmente, usamos cURL para enviarlo y recoger el resultado
    $ch = curl_init();

	curl_setopt($ch,CURLOPT_URL,$url);
	curl_setopt($ch,CURLOPT_POST,count($fields));
	curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	
	$result = curl_exec($ch);

	curl_close($ch);
	
	$result = json_decode($result, true);
	
	 if($scan && $result['result']=="0"){
	 	return $result['scan_id'];
	 } else {
	 	return $result;
	 }
	
}

Saludos, lipman

Trazando la ruta geográfica de nuestra víctima

Índice:
1.-Introducción
2.-Preparando el documento
3.-Analizando el documento usando PHP
4.-Caso real: recopilando, analizando y arreglando

Actualmente no funciona, debido a que al parecer, Google tiene deshabilitada la respuesta JSON de cuando enviamos una petición para geolocalizar

1.-Introducción

Últimamente no paro de hablar de la geolocalización a través de las MAC gracias a Google, y no es para menos. El hecho de que sabiendo una MAC de un router obtengamos la localización geográfica muy exacta (igual falla en unos metros) puede ser benificioso y a la vez peligroso.

Programas como Prey, hacen de esta característica que parezca buena y beneficiosa por si nos roban un dispositivo capacitado para acceder a la red, como un móvil, tablet o notebook.

Sin embargo, todo tiene su lado negativo, y realmente esto de cierta manera atenta contra la privacidad de las personas, en mayor o en menor medida: imaginemos que disponemos de un programa para movil que va captando las MACs y a raiz de eso, se geolocaliza y obtiene la dirección. Tendriamos el móvil totalmente controlado. ¿Y si instalásemos este programa en un dispositivo de una persona ajena? Podriamos saber en todo momento por dónde ha pasado.

De hecho, existe algo muy parecido, y se llama Creepy. Este programa, a partir de una cuenta de twitter, puede situar en el mapa los puntos en los que hay tweets de un usuario en donde se haya geolocalizado a la hora de escribirlos.
Sin embargo, esto no es intrusivo, ya que el usuario es quien decide si poner o no su localización.

Volviendo al tema de antes, se me ocurrió diseñar un script que, a partir de un array con direcciones MACs, te geolocalice y trace una ruta en GMaps. De esta manera, si tuvieramos “algo” capaz de obtener las MACs que nos rodean durante un viaje, podriamos saber la ruta que ha seguido (siempre aproximadamente).

Como no sé programar para Android, al menos todavia, no sé muy bien si esto sería fácil o difícil de programar para un móvil/tablet, pero me he valido de la suite de aircrack (concretamente de airodump y aircrack) para poner a prueba el script que pondré a continuación para probar esto mismo.

Lo que hago es lo siguiente:
1.-Con el airodump capturo paquetes durante todo el viaje o trayecto.
2.-Una vez capturados, uso aircrack y redirecciono la salida para tener todas las MACs en un txt.
3.-Posteriormente lo filtro bien para tener un txt del tipo:
aa:aa:aa:aa:aa:aa
bb:bb:bb:bb:bb:bb
cc:cc:cc:cc:cc:cc
...

4.-Finalmente, leo mediante un script en PHP el txt generado y usando las APIs de GMaps y la de la geolocalización, establezco los puntos y trazo la ruta.

2.-Preparando el documento

Antes de todo, hacemos una captura con el airodump y nos vamos a dar un paseo:
$ airodump-ng mon0 -w captura --output-format pcap

Una vez tengamos todos los datos, nos generará un fichero llamado captura-01.cap. Vamos a abrirlo con el aircrack redireccionando su salida en un txt.
$ aircrack-ng captura-01.cap > documento.txt

Una vez hagamos esto, tenemos todos los datos guardados en el fichero documento.txt. Ahora, simplemente tenemos que filtrar el documento para obtener uno que quede como dije anteriormente:
$ cat documento.txt | grep -e '[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]' -o > macs.txt

Vamos a usar un poco el sentido común: si estamos en una ciudad y damos un paseo de cinco minutos, podemos acabar con varios centenares de direcciones MAC. Esto a la hora de pasarlo como argumento y usarlo para localizar puede que tarde mucho, e incluso es bastante inutil. ¿Para qué queremos 10 direcciones MACs que nos deberian de situar en el mismo sitio? Con una nos deberia bastar, ¿no?

Lo único que se me ocurre para filtrar un poco más, y obtener menos direcciones MAC, son dos cosas: obtener solo las MACs de routers que usan WPA (que son mucho menos que los de WEP, tristemente) o usar las direcciones MAC de redes en las que hemos capturado algún paquete (ya que si vamos andando, de muchas no capturaremos nada).

Para ayudarnos a la hora de crear un filtro para estos dos casos, miraremos la estructura de una captura pasada por aircrack:

Posteriormente, es fácil deducir que podriamos filtrarlos de la siguiente manera. En el primer caso (solo las WPA):
$ cat documento.txt | grep 'WPA (' | grep -e '[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]' -o > macs.tx

En el segundo caso (WEPs con datos)
$ cat documento.txt | grep 'WEP (' | grep -e '[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]:[0-F][0-F]' -o > macs.tx

3.-Analizando el documento usando PHP

No tengo mucho que decir aquí, ya que el código viene completamente comentado, así que haré un resumen de su funcionamiento.

Tengo una matriz con las MACs. Lo que hago es, a partir de esa matriz, obtener una matriz de localizaciones (en orden siempre) usando la Google Geolocation API. Posteriormente simplemente es usar la API de Google Maps de la que uso 3 funciones: establecer un mapa, establecer los puntos, y trazar una ruta entre los puntos.

Aquí va el código:




 

';



function trazar_ruta($matriz){
/*A partir de una matriz, trazaremos la ruta entre las direcciones MAC
  ya que tenemos las coordenadas. El contenido será del tipo
  matriz[0]=a,b;
  matriz[1]=c,d; Siendo a,b,c,d coordenadas */
  
echo '
  var flightPlanCoordinates = [
  ';
  //Ponemos todos los valores de los puntos a unir
  foreach($matriz as $clave => $valor)
     echo "new google.maps.LatLng(" . $valor . "),
";
  
//Configuramos las caracteristicas: color, opacidad y tamaño
	echo '
  ];
 
  var flightPath = new google.maps.Polyline({
    path: flightPlanCoordinates,
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 2
  });

 
  flightPath.setMap(map);

	';
	
}

function poner_punto($coordenadas,$titulo){
/* Esta función nos permite poner un punto en el mapa
   tenemos que darle las coordenadas, y el título que tendrá ese punto

*/
	
//Establecemos las coordenadas
echo "var coordenadas = new google.maps.LatLng(" . $coordenadas[0] . "," . $coordenadas[1] . ");
";

//Lo ponemos en el mapa
echo '  var marker = new google.maps.Marker({ //opciones
      position: coordenadas,
      map: map,
      //Nombre del mapa
      title:"' . substr(str_replace("-",":",$titulo), 0, -1) . '"
      //Titulo (visible cuando colocamos el ratón sobre el punto)
  });
';
	
}



function obtener_coordenadas($mac, &$resultado){
/* Esta función nos permite guardar en "resultado" (pasado por referencia)
   las coordenadas de la mac pasada en el primer parámetro
*/ 
	
//Datos que enviaremos
$datos_completos = '{version:"1.1.0",request_address:true,wifi_towers:[{mac_address:"' . $mac . '"}]}';
 
//Iniciamos cURL
$ch = curl_init();
//Configuramos cURL
curl_setopt($ch, CURLOPT_POST, 1); 
curl_setopt($ch, CURLOPT_POSTFIELDS, $datos_completos);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 
curl_setopt($ch, CURLOPT_URL,'www.google.com/loc/json'); 
 
$respuesta = curl_exec($ch);
curl_close($ch);
 
//Obtenemos la respuesta y posteriormente la decodificamos
$respuesta = json_decode($respuesta, true);
 
//Para terminar, pasamos los resultados a los valores que posteriormente usaremos
$resultado[0]=$respuesta['location']['latitude'];
$resultado[1]=$respuesta['location']['longitude'];

}


?>



  

4.-Caso real: recopilando, analizando y arreglando

Esta mañana decidí poner a prueba lo que he escrito anteriormente: puse el portatil a escanear y me fui desde donde vivo actualmente hasta la universidad para posteriormente analizarlo y ver si consigo trazar la ruta correctamente.

Para empezar, antes de salir de casa, puse la tty3 (para poder cerrar el portatil sin que se suspenda o apague) de mi Fedora escaneando con airodump y recogiendo paquetes de datos. Al llegar a la uni, filtreé los datos recogidos como anteriormente puse, generando 3 resultados distintos:
-606 MACs en total
-33 MACs con datos WEP (de las 606)
-35 MACs con WPA (de las 606)

Al script de geolocalizar y trazar rutas se le pueden pasar todos los datos que queramos, como si le pasamos over 9000 MACs, lo que pasa que hay que recordar que tenemos un timeout por defecto puesto en 60, por lo que dependiendo de nuestra velocidad de conexión podremos tratar más o menos MACs. Por ejemplo, en la uni puedo tratar hasta 225 MACs, pero en casa, alrededor de 100.

Podemos cambiar el timeout yéndonos al fichero php.ini y cambiando los valores (que están en segundos) de max_execution_time y max_input_time. Recordad que si tenemos corriendo nuestro servidor, para que se hagan efectivos los cambios habría que reiniciarlo.

Los puntos verdes muestran el inicio y el fin desde donde recogí datos. La primera imagen refleja el resultado de usar las 33 MACs con datos WEP:

Esta segunda imagen, es la de las 35 MACs que usan WPA:

Como podemos ver, en algunos puntos se nos va un poco de la ruta, pero creo que eso es debido a que hay repetidores aquí por la ciudad para tener WiFi gratis (además de los propios de la uni) y eso desestabiliza un poco.

Vamos a tratar ahora de solucionar el problema del límite de MACs que anteriormente dijimos ya que, esta captura la he realizado en 15 minutos.. Si fueran horas, tendriamos decenas de miles de direcciones. Para ello, lo que hacemos es pasar la matriz por una función cuyo algoritmo determine qué direcciones MAC deberiamos de eliminar para no pasar el límite.

Por ejemplo, si tenemos 5 MACs y queremos buscar dos, pues obviamente cogeriamos la primera y la última. Si tuvieramos 20 MACs con un límite de 4, podriamos coger 1 cada 5, y así sucesivamente.

El siguiente código no es el más eficaz ni el más perfecto, ya que le dediqué 5 minutos en hacerlo, pero funciona, y nos permite parsear una matriz y establecer otra a partir de un límite:

function reducir(&$matriz, &$resultado, $limite=100){
	$total=count($matriz);
	if($total>$limite) {
	  $division = $total/$limite;
	  $division = explode(".",$division);
	  $division = $division[0];
	
	  for($a=0;$a<$limite;$a++)
	    $resultado[$a]=$matriz[$a*$division];
	  unset($matriz);
	 //$matriz = $nueva;
	}
	//En caso contrario no hace falta modificar
	echo "

"; }

Un saludo, lipman

Usando la Geolocalización MAC en nuestro favor. Parte II

Anteriores entradas relacionadas:
[Análisis] Prey, software antirrobo de Código Abierto
Usando la Geolocalización MAC en nuestro favor. Parte I

Actualmente no funciona, debido a que al parecer, Google tiene deshabilitada la respuesta JSON de cuando enviamos una petición para geolocalizar

Índice:
1.-Geolocalización con un script en PHP mediante file_get_contents
2.-Geolocalización con un script en PHP mediante cURL
3.-Geolocalización a través de la shell (también usando cURL)
4.-Geolocation API Specification (W3C)

Siguiendo el hilo argumentativo del análisis que hice a Prey, hice una primera parte que quedó un poco escueta, sobre la que hablé de Geolocalización MAC. Pudimos usar un software para tratar de geolocalizar MACs, e incluso desde la página del MapXSS de Samy. Pero nos quedó pendiente una cosa: geolocalizar MACs por nuestros medios.

En esta entrada voy a tratar 4 formas distintas de geolocalizar una MAC, tres de ellas (las primeras) usando la API de Google. Dos de estas formas serán a través de scripts en PHP: uno de ellos usando cURL, y otro mediante file_get_contains. La otra forma, un tanto curiosa, será a través de la shell (en la que también necesitaremos tener cURL instalado). Con respecto a la forma que no usa la API de Google, usaremos la especificación de la W3C para geolocalizar, que está disponible en algunos navegadores.

1.-Geolocalización con un script en PHP mediante file_get_contents

Los dos primeros métodos funcionan internamente igual: hacemos una enviamos una petición JSON mediante POST, esperando recibir una respuesta que deberiamos de recibir, y posteriormente cuando la tengamos, la decodificamos y obtenemos un array con todos los valores y datos que deseamos, nuestra preciada dirección.

Antes de poner el código, una pequeña aclaración: si probamos este código en un servidor web remoto de algún hosting, es muy probable (casi 100%) de que no funcione. Esto tendriamos que probarlo en nuestro propio servidor PHP. Esto es debido a que los hostings deshabilitan (o deberian) la función file_get_contents por una vulnerabilidad que se encontró en la misma.

Aquí dejo el código explicado:

array(
    'method' => "POST",
    'header'  => 'Content-type: application/x-www-form-urlencoded',
    'content' => $datos
  )
);

//Utilizamos file_get_contents para hacer la petición a la API de Google
$respuesta = file_get_contents(
    'http://www.google.com/loc/json',
    false,
    stream_context_create($datos_completos)
);

//Decodificamos lo que nos devuelve
$respuesta = json_decode($respuesta, true);

//Obtenemos los resultados
echo "Direccion Geografica aproximada:
"; foreach($respuesta['location']['address'] as $clave => $valor) echo $clave . ": " . $valor . "
"; echo "Coordenadas aproximadas:
"; echo "Latitud: " . $respuesta['location']['latitude'] . "
Longitud: " . $respuesta['location']['longitude']; ?>

A continuación escribiré una estructura de tipo árbol de los datos que nos devuelve Google tras este envio, ya que nos devuelve una matriz multidimensional, y viene bien saber el contenido de esta:


$respuesta (Array)

>access_token
>location: Array
|-->latitude
|-->longitude
|-->accuracy
|-->address: Array
|---|-->country
|---|-->country_code
|---|-->region
|---|-->country
|---|-->city
|---|-->street
|---|-->postal_code

2.-Geolocalización con un script en PHP mediante cURL

Aquí si ponemos el mismo código que enviamos en el script anterior, no nos funciona, así que tendremos que modificar la petición como veremos a continuación de una manera todavía más sencilla. Además de eso, tendriamos que cambiar la parte de file_get_contents por el cURL. Este código si que deberia de funcionarnos en un servidor remoto, y digo deberia porque yo no lo he conseguido, solo he conseguido que me aparezca el robot de Google tan mono advirtiéndome de un error “temporal” que no tengo ni idea de porqué puede ser… Pero vamos, que en nuestro servidor casero si que funciona.

Aquí el código:


//Datos que enviaremos
$datos_completos = '{version:"1.1.0",request_address:true,wifi_towers:[{mac_address:"xx-xx-xx-xx-xx-xx"}]}';

//Iniciamos cURL
$ch = curl_init();
//Configuramos cURL
curl_setopt($ch, CURLOPT_POST, 1); 
curl_setopt($ch, CURLOPT_POSTFIELDS, $datos_completos);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); 
curl_setopt($ch, CURLOPT_URL,'www.google.com/loc/json'); 

$respuesta = curl_exec($ch);
curl_close($ch);

//Obtenemos la respuesta y posteriormente la decodificamos
$respuesta = json_decode($respuesta, true);

//Para terminar, printeamos los resultados
echo "Direccion Geografica aproximada:
"; foreach($respuesta['location']['address'] as $clave => $valor) echo $clave . ": " . $valor . "
"; echo "Coordenadas aproximadas:
"; echo "Latitud: " . $respuesta['location']['latitude'] . "
Longitud: " . $respuesta['location']['longitude'];

Esto nos devolverá exáctamente el mismo resultado que el anterior script.

3.-Geolocalización a través de la shell (también usando cURL)

Ya por último, acabaremos esta entrada geolocalizando de la forma más sencilla, a través de la shell, en la que solo tenemos que escribirlo y lo recibimos inmediatamente, como veremos en la siguiente imagen.

Usaremos el comando de cURL, por lo que propiamente es hacer lo mismo que en el segundo apartado, pero con la shell:


curl -d '{version:1.1.0,request_address:true,wifi_towers:[{mac_address:xx-xx-xx-xx-xx-xx}]}' www.google.com/loc/json

4.-Geolocation API Specification (W3C)

Para los que usamos twitter, esta especificación no nos deberia de parecer nueva, ya que desde hace tiempo tenemos la opción de añadir desde donde twitteamos usando este método.

A continuación, el código que nos permite esto:











Tras ejecutarlo en Firefox por ejemplo, nos saldrá un aviso de si queremos compartir nuestra localización, que igual a alguno le suena haber visto anteriormente:

Y nos saldrá esto:

A veces no me funciona, igual es que tiene algún límite de peticiones por tiempo (como algunas APIs). Lo curioso (y bueno) del asunto, es que si analizamos con Wireshark los datos, podemos ver que pasan por SSL.

Para terminar con este apartado, decir que en el siguiente enlace disponemos más detalladamente datos relativos al uso de esta Geolocation API.

Saludos, lipman

Usando Google Maps API versión 3

Índice:
1.-Breve Introducción
2.-Inicializando un mapa
3.-Estableciendo puntos
4.-Eventos
5.-Eventos (pasando valores)
6.-Polígonos
7.-Líneas

1.-Breve Introducción

Mucha de la documentación que se encuentra en Internet con respecto a la API de Google Maps es sobre las versiones viejas, por lo que he decidido intentar hacer un conjunto de posts exclusivamente para la última versión.

Para empezar, oficialmente las versiones 1 y 2 se consideran obsoletas, por lo que viene bien aprender un poco acerca de la última versión. Por otro lado, una de las implementaciones nuevas de esta versión, es la adición de Google Street View.

Con las versiones viejas, primero de todo habia que obtener la clave API dependiendo del sitio web en donde quisieramos usar el código. Pero con esta nueva versión no es necesario, es todo mucho más sencillo.

2.-Inicializando un mapa

Lo primero de todo vamos a tratar de entender cómo funciona el “hola mundo de la api de GMaps”:





(1): Como podemos ver, la URL que cargamos es la siguiente: http://maps.google.com/maps/api/js?sensor=false. Aquí simplemente quería explicar qué significa el parámetro sensor, que puede tomar valores de true y false. Este sensor se pone en true cuando el dispositivo sobre el cual se espera que se cargue esta página dispone de un localizador (como un GPS) encargado de determinar la localización del usuario. Como estamos haciendo pruebas sobre un ordenador (que no dispone de GPS) lo tengo puesto en false.

(2): Aquí simplemente decir que creamos una variable llamada coordenadas en la que establecemos el punto céntrico en donde queremos que se centre nuestro mapa. Para obtener las coordenadas, simplemente nos movemos con el Google Maps a un punto, y le damos al botón de enlazar para obtener las coordenadas en los parámetros que se pasan en la URL, como en la siguiente imagen.

(3): Este valor es la cantidad de zoom que queremos aplicar a nuestro mapa. Normalmente va desde el valor 0 al 19. Este valor depende de la zona y del tipo de mapa empleado.

(4): Establecemos el centro, que es la variable coordenadas anteriormente definida.

(5): Aquí elegimos el tipo de mapa que queremos usar:

  • ROADMAP: mapa callejero con el nombre de las calles y demás.
  • SATELLITE: mapa satélite con ningún nombre puesto.
  • HYBRID: mapa híbrido, satélite con los nombres
  • TERRAIN: mapa físico con algunos nombres

(6): Creamos el mapa. Nos tenemos que dar cuenta de que usamos el identificador mapa para luego posteriormente usarlo.

Ahora nos vamos con la parte HTML del asunto:


  

Como podemos ver, creamos un div con el identificador anteriormente usado, y establecemos mediante CSS que ocupe toda la pantalla del navegador.

Ahora, juntamos todo el código fuente, y nos deberia quedar algo así:









  

3.-Estableciendo puntos

En este apartado aprenderemos como establecer puntos dentro de lo que es el mapa. Para este ejemplo, seguiremos usando el código anterior, por lo que, el siguiente código irá a continuación del código Javascript anterior (dentro de la función de iniciar)

  var coordenadas = new google.maps.LatLng(37.680000,-1.669407);
  //Establecemos las coordenadas del punto
  var marker = new google.maps.Marker({ //opciones
      position: coordenadas,
      //Decimos que la posición es la de la variable 'coordenadas'
      map: map,
      //Nombre del mapa
      title:"Hola Mundo!"
      //Titulo (visible cuando colocamos el ratón sobre el punto)
  });

¿Sencillo verdad? Y el resultado es el siguiente:

4.-Eventos

Los eventos son acciones que se toman tras la realización de otra. Con el siguiente ejemplo lo veremos muy claro. Trataremos dos eventos: el cambio de zoom, y el “click” en una marca.

var map;
function iniciar() {
  var coordenadas= new google.maps.LatLng(-25.363882,131.044922);
  var myOptions = {
    zoom: 4,
    center: coordenadas,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }
  map = new google.maps.Map(document.getElementById("mapa"), myOptions);
  //Hasta aquí es igual que antes

  google.maps.event.addListener(map, 'zoom_changed', function() {
    setTimeout(moveToDarwin, 3000);
  });
  //(1) Primer evento
  
  var marker = new google.maps.Marker({
      position: myLatlng, 
      map: map,
      title:"Hello World!"
  }); //Esto ya lo vimos antes, creamos una marca o punto
  
  google.maps.event.addListener(marker, 'click', function() {
    map.setZoom(8);
  });
  //(2) Segundo evento
  
}
  
function moveToDarwin() {
  var darwin = new google.maps.LatLng(-12.461334, 130.841904);
  map.setCenter(darwin);
}
//Función que se encarga de centrar el mapa en esas coordenadas

Antes de entrar en cada evento particular, podemos destacar las características comunes. Un evento tiene 3 parámetros: el primero hace referencia al objeto que estamos tratando (un mapa o una marca en estos ejemplos). El segundo hace referencia al nombre interno del evento que tiene que ocurrir (que cambie el zoom o que se clickee). El último parámetro simplemente es el accionamiento de una función.

(1): El primer evento se acciona cuando cambiamos el zoom (ya sea para acercar o disminuir), y cuando esto ocurre, al cabo de 3000 ms se acciona la función moveToDarwin que establece el centro en otro sitio.

(2): Este evento se acciona cuando se clickea en la marca, y una vez esto, se establece que el zoom deba de ser 8.

5.-Eventos (pasando valores)

A veces, a la hora de esperar a que se realice un evento, este evento realizado puede transmitir información que nos sea útil. En los ejemplos anteriores no nos valia de nada transmitir información. En uno de los eventos anteriores, simplemente al hacer click en un sitio, estableciamos de zoom 8, y en el otro, simplemente nos moviamos a unas coordenadas predeterminadas.

En el siguiente ejemplo vamos a hacer que cada vez que pinchemos en el mapa, establezcamos una marca. Para esto, a la hora de pinchar, tenemos que transmitir la información de cuando pinchamos (las coordenadas) a la función que se encargará de crear esa marca.


(1): Como podemos ver aquí, transmitimos las coordenadas. event.latLng hace referencia a las coordenadas que queremos transmitir y que posteriormente usaremos para crear la marca.

6.-Polígonos

Simplemente pondré un script de cómo se hacen polígonos. Es realmente fácil y muy intuitivo. El siguiente polígono da como resultado la traza del triángulo de las bermudas:


 
 

7.-Líneas

Funcionan casi igual que los polígonos.. solo que tienen algunas opciones distintas y el nombre de la función (como es obvio..) también cambia. Código de ejemplo:

function initialize() {
  var myLatLng = new google.maps.LatLng(0, -180);
  var myOptions = {
    zoom: 3,
    center: myLatLng,
    mapTypeId: google.maps.MapTypeId.TERRAIN
  };

  var map = new google.maps.Map(document.getElementById("map_canvas"),
      myOptions);
  var flightPlanCoordinates = [
    new google.maps.LatLng(37.772323, -122.214897),
    new google.maps.LatLng(21.291982, -157.821856),
    new google.maps.LatLng(-18.142599, 178.431),
    new google.maps.LatLng(-27.46758, 153.027892)
  ];
  var flightPath = new google.maps.Polyline({
    path: flightPlanCoordinates,
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 2
  });

  flightPath.setMap(map);
}

Para la gente que se quiera adentrar y profundizar en este mundillo, teneis una referencia justo aquí de los métodos, valores que devuelven, tipos y propiedades, publicado por Google. Viene una cantidad ingente de información de la cual es imposible resumir, pero al venir en completo castellano, será fácil encontrar cosas que queramos adaptar a nuestro uso por parte de la API de Google Maps.

Saludos, lipman