Disclaimer: Este post se ha realizado con motivos únicamente EDUCACIONALES, se incluye una prueba de concepto. El uso que le den a este código no es mi responsabilidad. OK
¿Por qué? El formulario de envío de mensajes de Telefónica tiene un captcha sumamente sencillo, que puede ser "roto" utilizando librerías abiertas de OCR. Para esta prueba de concepto voy a utilizar la herramienta Tesseract que es un programita escrito en C que sirve para realizar OCR de texto.
¿Qué tenemos que hacer para romper el captcha?
1-Iniciar una sesión con la página de envío de SMS de telefónica.
2-Descargar la imágen utilizando el Cookie de sesión enviado por el servidor.
3-Convertir la imágen a monocromático.
4-Enviar la imágen a una biblioteca de OCR.
5-Desplegar el resultado.
Habiendo dicho esto, les dejo el código y luego explico los detalles:
/*
* Created by SharpDevelop.
* Author: Mario Gomez
* Date: 23/12/2011
* Time: 08:27 p.m.
* Este código está protegido bajo la licencia GNU/GPL 2.0 o posterior.
*/
using System;
using System.Net;
using System.Web;
using System.IO;
using System.Media;
using System.Drawing;
using tessnet2;
using System.Collections;
using System.Collections.Generic;
namespace MensajitoCSharp
{
class Program
{
public static void Main(string[] args)
{
WebClient client = new WebClient();
client.Headers.Add("User-Agent"," Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1) Gecko/20100101 Firefox/8.0.1");
Console.WriteLine("Request headers:");
for(int i=0;i<client.Headers.Count;i++) {
Console.WriteLine(client.Headers.AllKeys[i]+":"+client.Headers[i]);
}
Console.WriteLine("URL: http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp");
TextReader reader = new StreamReader( client.OpenRead("http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp"));
reader.Close();
Console.WriteLine("Response headers:");
for(int i=0;i<client.ResponseHeaders.Count;i++) {
Console.WriteLine(client.ResponseHeaders.AllKeys[i]+":"+client.ResponseHeaders[i]);
}
client.Headers.Add("Cookie",client.ResponseHeaders["Set-Cookie"]);
Console.WriteLine("Request headers:");
for(int i=0;i<client.Headers.Count;i++) {
Console.WriteLine(client.Headers.AllKeys[i]+":"+client.Headers[i]);
}
Console.WriteLine("URL: http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp?vrClientId=form1:imageEx1");
client.DownloadFile("http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp?vrClientId=form1:imageEx1",@".\output.png");
Console.Read();
Bitmap img = new Bitmap(@".\output.png");
int x,y;
Color currentColor;
for(x=0;x<img.Width;x++) {
for(y=0;y<img.Height;y++) {
currentColor = img.GetPixel(x,y);
img.SetPixel(x,y,(currentColor.R<192 && currentColor.G<192 && currentColor.B<192) ? Color.Black : Color.White);
}
}
img.Save(@".\output_processed.png");
tessnet2.Tesseract ocr = new tessnet2.Tesseract();
ocr.SetVariable("tessedit_char_whitelist", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@/"); // If digit only
ocr.Init(@".\", "eng", false); // To use correct tessdata
List<tessnet2.Word> result = ocr.DoOCR(img, Rectangle.Empty);
foreach (tessnet2.Word word in result)
Console.WriteLine("{0} : {1}", word.Confidence, word.Text);
}
}
}
Explico detalladamente:
1-Vamos a usar una clase del .NET Framework que resulta utilísima, esta es el WebClient. El WebClient simula un micro-navegador que nos ahorra el trabajo de abrir las conexiones leer los datos y demás. Para crear este objeto basta con que escribamos la siguiente línea de código:
WebClient client = new WebClient();
2-Luego de inicializar nuestro WebClient, lo vamos a "disfrazar" de Firefox, para hacer esto tenemos que establecer en nuestro encabezado un User-Agent. Realmente deberíamos de establecer otros encabezados, pero para fines prácticos solo voy a establecer el user agent. En los logs del servidor nuestra aplicación aparecerá como un Firefox más, a pesar de ser un bot.
client.Headers.Add("User-Agent"," Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1) Gecko/20100101 Firefox/8.0.1");
3-Vamos a agregar un poco de información de debug para darnos una idea de que es lo que estamos enviando desde la consola:
Console.WriteLine("Request headers:");
for(int i=0;i<client.Headers.Count;i++) {
Console.WriteLine(client.Headers.AllKeys[i]+":"+client.Headers[i]);
}
Console.WriteLine("URL: http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp");
4-Lo siguiente es hacer el Request. Para ello vamos a utilizar la función OpenRead. Por ahora no estamos interesandos en el output (HTML) de la página, simplemente en los encabezados, podríamos guardar la respuesta en un string si quisieramos luego revisar si se ha establecido alguna variable en el código que pudieramos utilizar. Mientras tanto solo vamos a leer la respuesta y vamos a cerrar nuestro reader.
TextReader reader = new StreamReader( client.OpenRead("http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp"));
reader.Close();
5-Para los curiosos, agregamos más información de debug que nos muestra los encabezados de la respuesta del servidor:
Console.WriteLine("Response headers:");
for(int i=0;i<client.ResponseHeaders.Count;i++) {
Console.WriteLine(client.ResponseHeaders.AllKeys[i]+":"+client.ResponseHeaders[i]);
}
6-El siguiente paso es identificar los encabezados, estamos interesados en el encabezado "Set-Cookie", este encabezado le dice al navegador que cookies va a establecer. Recordemos que nuestro WebClient es un Micro-navegador, así que debemos especificarle que cookies son las que va a enviar para el siguiente Request.
client.Headers.Add("Cookie",client.ResponseHeaders["Set-Cookie"]);
7-Más información de debug, aquí deberían de ver la cookie que acabamos de establecer:
Console.WriteLine("Request headers:");
for(int i=0;i<client.Headers.Count;i++) {
Console.WriteLine(client.Headers.AllKeys[i]+":"+client.Headers[i]);
}
Console.WriteLine("URL: http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp?vrClientId=form1:imageEx1");
8-Ahora vamos a usar una función muy interesante, WebClient nos permite entre otras cosas descargar archivos. En esta ocasión vamos a descargar el PNG correspondiente al captcha generado para esa sesión, para descargar archivos vamos a usar la función DownloadFile, para utilizarla simplemente tenemos que especificar la URL y el archivo a donde queremos que se guarde. Para fines prácticos decidí utilizar "output.png"
client.DownloadFile("http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp?vrClientId=form1:imageEx1",@".\output.png");
9-Post proceso de la imagen. ¿Recuerdan que les dije que telefónica utiliza un captcha extremadamente sencillo. Esto es porque es una simple imágen con caractéres sobrepuesta a unas lineas grises. Así que para facilitar el trabajo del OCR, lo primero que vamos a hacer es... Eliminar las líneas grises.
¿Como hacemos esto? Vamos a hacer uso de la clase Bitmap, esta clase nos permite modificar los pixels de la imagen, lo que vamos a hacer es recorrer los pixels y todo lo que nos paresca gris lo vamos a hacer blanco, el resultado lo vamos a guardar en output_processed.png.
Bitmap img = new Bitmap(@".\output.png");
int x,y;
Color currentColor;
for(x=0;x<img.Width;x++) {
for(y=0;y<img.Height;y++) {
currentColor = img.GetPixel(x,y);
img.SetPixel(x,y,(currentColor.R<192 && currentColor.G<192 && currentColor.B<192) ? Color.Black : Color.White);
}
}
img.Save(@".\output_processed.png");
Imagen original:

Imagen procesada:

OJO: Ese IF que puse solo funciona para escala de grises y convierte nuestra imagen en una versión monocromática, también elimina el anti-alias facilitando el proceso de OCR.
10-Por último, una vez con nuestra imágen procesada, simplemente pasamos el texto a nuestra biblioteca de OCR, en este caso estoy utilizando Tesseract-OCR 2.0 + el wrapper Tessnet2 para .NET.
tessnet2.Tesseract ocr = new tessnet2.Tesseract();
ocr.SetVariable("tessedit_char_whitelist", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@/");
ocr.Init(@".\", "eng", false);
List<tessnet2.Word> result = ocr.DoOCR(img, Rectangle.Empty);
foreach (tessnet2.Word word in result)
Console.WriteLine("{0} : {1}", word.Confidence, word.Text);
11-Compile y ejecute
PS C:\Users\Modem\Documents\SharpDevelop Projects\MensajitoCsharp\MensajitoCsharp\bin\Debug> .\MensajitoCsharp.exe
Request headers:
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1) Gecko/20100101 Firefox/8.0.1
URL: http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp
Response headers:
Content-Language:en
Transfer-Encoding:chunked
Cache-Control:no-cache="set-cookie, set-cookie2"
Content-Type:text/html; charset=ISO-8859-1
Date:Sat, 24 Dec 2011 04:40:13 GMT
Expires:Thu, 01 Dec 1994 16:00:00 GMT
Set-Cookie:JSESSIONID=0000XaCFc9XxgmfG-CwHV75ZNYv:-1; Path=/
Server:WebSphere Application Server/6.1
Request headers:
Cookie:JSESSIONID=0000XaCFc9XxgmfG-CwHV75ZNYv:-1; Path=/
URL: http://corporativo.telefonica.com.sv/EnviarSMSSV/faces/EnviarSMS.jsp?vrClientId=form1:imageEx1
174 : pebR4
Screenshot:

Uploaded with
ImageShack.us+Info acerca de Tessnet2:
http://www.pixel-technology.com/freeware/tessnet2/Para compilar y ejecutar van a necesitar descargar los siguientes archivos:
http://www.pixel-technology.com/freeware/tessnet2/bin.ziphttp://code.google.com/p/tesseract-ocr/downloads/detail?name=tesseract-2.00.eng.tar.gz&can=2&q=Gracias a naruto por haberme puesto a recordar viejos tiempos

espero que esta info le sirva para su versión en PHP.
P.D.: OK realmente son menos de 75 líneas, el código necesario para realizar la decodificación del captcha solo son realmente como 15 líneas.. pero pues, hay que meter algo de info de debug tambien
