Le but de ce scanner d'image n'est pas de lutter contre les images anti-spam, qui sont de plus en plus bruitée avec des écritures limites illisibles, mais simplement d'expliquer comment utiliser la librairie PHP : GD. Son utilité ? Etre capable justement de coder une image anti-spam ! Pour comprendre le principe nous allons également nous amuser à coder un (très) petit OCR en PHP.


L'installation de la bibliothèque GD sur un serveur WAMPP (Windows Apache MySQL PhP) il suffit de modifier le fichier php.ini comme suit (rechercher le texte _gd pour vous aider à trouver la ligne) :


 ;extension=php_gd2.dll 
 
 -en-

 extension=php_gd2.dll 

Si vous avez installer un serveur Apache à la main et qu'il n'y a pas cette ligne dans votre fichier, il faut alors l'ajouter et télécharger l'extension à l'adresse suivante : http://w.its.free.fr/dl/pub/Extension_Apache_GD.zip . Vous devrez ensuite relancer le serveur Apache.

Pour vérifier si le module est bien chargé, dans "PHP Info" accessible comme ceci:


<? phpinfo(); ?> 

Avec LAMPP, c'est à dire un équivalent d'EasyPHP pour Linux, (également disponible pour Windows) la library est déjà installée (libgdm.so de /opt/lampp/lib)





0x02. GENERATION D'IMAGE


La génération d'une image à partir d'un script PHP, permet de créer une image antispam, mais on peut imaginer que cela serve à développer une interface d'un jeu Web ou alors d'un challenge. Vous avez très probablement vu la génération de ce genre d'image.


<?php
 
// On initialise le mot de passe
$devine_mot_de_passe = "";
 
// Longueur du mot de passe personnalisable
$longueur_passe = 7;
 
// On alterne consonne/voyelle pour que ce soit lisible
$voyelles = "aeiouy";
$consonnes = "bcdfghjklmnpqrstvwxz";
 
for($i=0;$i<$longueur_passe;$i++)
	if($i % 2 == 0)
		$devine_mot_de_passe .= $consonnes{rand(1,strlen($consonnes)-1)};
	else
		$devine_mot_de_passe .= $voyelles{rand(1,strlen($voyelles)-1)};
 
 
// Déclare que le type de fichier chargé est une image
header ("Content-type: image/png");
 
// Création du "handle" de l'image
$im = @imagecreate (300, 200) or die ("Impossible d'initialiser la librairie GD");
 
 
$background_color = imagecolorallocate ($im, 0, 0, 0);
$text_color = imagecolorallocate ($im, 255, 255, 255);
imagefontheight($text_color) ;
 
// Crée une chaine de caractère :
// sur le handle $im
// la taille de la police est 10
// la position sur la largeur varie de 5 à 200
// la position sur la longeuur varie de 5 à 100
imagestring ($im, 10, rand(5,200), rand(5,100),  $devine_mot_de_passe, $text_color); 
 
// Charge et traite l'image au format PNG
imagepng ($im);
 
?> 




0x03. RECONNAISSANCE


Maintenant que nous avons donc générer une image, nous voudrions récupérer le texte qui est dedans. Pour ça il y a plusieurs méthodes :

- Utiliser un programme tel que GOCR (GNU/Optical Caracter Recognition)
- Utiliser une classe déjà existante
- Programmer notre propre OCR en PHP

Dans notre cas, nous utiliserons la dernière option qui consiste donc à se coder son propre OCR.

Nous allons pour cela utiliser devoir récupérer quelques informations sur l'image, surtout sa résolution. Le but de notre programme sera de scanner de long en large l'image, c'est à dire balayer l'image comme un tube cathodique fait sur un écran, à chaque caractère nous associerons un motif qui permettra d'identifier un caractère de manière unique, prenons le cas du caractère 't':


 1 3 5 7                            000        ...
+-------> x          R#            11         ##   
|                    ##          0011000    ..##...
|2                 ######        1111110    ######.
|                    ##          0011000    ..##...
|4                   ##                            
|                    ##  ##                        
V                     ####    

y                    R est le repère, R = ( 0 , 0 )
 



0x04. PIXEL PAR PIXEL


On teste vers le haut (-1) pour vérifier qu'il ne s'agit pas du caractère 'f'. La fonction de test de caractère prendra 4 paramètres: le handle de l'image $img , afin de se positionner aux coordonnées, donc $x et $y , puis la variable de destination $text qui contiendra le resultat


 
function test_caractere($img, $x, $y, $text)
{
   // t //////////////////////////////////
   if(
        imagecolorat($img, $x, $y)     == 1 // La barre verticale
   &&   imagecolorat($img, $x+1, $y  ) == 1 
   &&   imagecolorat($img, $x-2, $y+1) == 0   
   &&   imagecolorat($img, $x-1, $y+1) == 0 
   &&   imagecolorat($img, $x  , $y+1) == 1 // La barre verticale
   &&   imagecolorat($img, $x+1, $y+1) == 1 
   &&   imagecolorat($img, $x+2, $y+1) == 0 
   &&   imagecolorat($img, $x+3, $y+1) == 0 
   &&   imagecolorat($img, $x-2, $y+2) == 1 // La barre horizontale   
   &&   imagecolorat($img, $x-1, $y+2) == 1 
   &&   imagecolorat($img, $x  , $y+2) == 1 
   &&   imagecolorat($img, $x+1, $y+2) == 1 
   &&   imagecolorat($img, $x+2, $y+2) == 1 
   &&   imagecolorat($img, $x+3, $y+2) == 1 
   &&   imagecolorat($img, $x-2, $y+3) == 0   
   &&   imagecolorat($img, $x-1, $y+3) == 0 
   &&   imagecolorat($img, $x  , $y+3) == 1 // La barre verticale 
   &&   imagecolorat($img, $x+1, $y+3) == 1 
   &&   imagecolorat($img, $x+2, $y+3) == 0 
   &&   imagecolorat($img, $x+3, $y+3) == 0 
   &&   imagecolorat($img, $x+1, $y-1) == 0 // Le haut du caractère
   &&   imagecolorat($img, $x+2, $y-1) == 0 
   &&   imagecolorat($img, $x+3, $y-1) == 0 
     )   $text.= "t";
 
   return($text);
}
 

Le parcours de l'image est fait, tout simplement avec deux boucles imbriquées, afin de parcourir l'image :


<?
 
function ocr($img, $largeur, $hauteur)
{
   // Le résultat sera stocké ici
   $text = "";
 
   // On parcours tout l'image
   for( $x=0 ; $x<$largeur ; $x++ )
   {
      for( $y=0 ; $y<$hauteur ; $y++ )
      {
         $rgb = imagecolorat($img, $x, $y);
 
         // Si le caractère n'est pas noir
         // à adapter selon l'image
         if( $rgb )	
         {
	    test_caractere($img, $x, $y, $text);
         }
      }
   }
   return($text);
}
 
?> 

Après il faut refaire se raisonnement autant qu'il y a de caractères à tester... a -> z, A -> Z, 0 -> 9 ... ça fait 62 caractères, ce n'est pas bien compliqué, mais ça devient vite fastidieux, nous allons donc cherche une méthode plus simple.





0x05. AVEC UN LOGICIEL


Avec un logiciel initialement programmé pour Linux, nommé GOCR, il est possible de faire de la reconnaissance par ligne de commande, ce qui siginie même ce programme peut être appelé par Apache via la fonction PHP exec( $cmd ) :


<?
 
 $fichier_image = "img_tmp_ocr.png";
 $fichier_texte = "txt_tmp_ocr.txt";
 
 $cmd_1 = "wget http://127.0.0.1/%5B%20hack%20%5D/challenges/level%2010%20-%20ocr.php -O " . $fichier_image ;
 $cmd_2 = "gocr -i img_tmp_ocr.png -o " . $fichier_texte ;
 
 exec($cmd_1);
 exec($cmd_2);
 
 $fh = fopen( $fichier_texte , "rb" );
    $text = fgets( $fh );
 fclose( $fh );
 
 echo "Texte extrait de l'image: " . $text;
 
 ?> 

C'est plus court, plus efficace, plus simple mais fait par quelqu'un d'autre :)





0x06. CAPTCHA


CAPTCHA signifie : Completely Automated Public Turing to tell Computers from Humans Apart" et permet la validation d'un code de sorte à ce que cela ne puisse pas être validé par autre chose que l'humain qui est derrière la machine. Il y a des algorithmes plus poussé que celui présenté ci-dessous, qui applique une déformation sur les caractères jusqu'à un certain degré de lisibilité du point de vue de l'homme et au contraire pousse au maximum à l'isibilité du point de vue de la machine.


<?php
 
 
class CAPTCHA
{
    var $long;     
    var $lx;       
    var $ly;       
    var $filename; 
    var $font_file="comic";
    var $text;
 
 
   // Le constructeur initialise les propriétés
   function CAPTCHA()
   {
      $this->long=7;
      $this->lx=150;
      $this->ly=50;
      $this->filename=$this->GetFilename();
   }
 
 
   // Génère un nom de ficheir aléaoire
    function GetFilename()
   { 
      $filename = rand(10000000,99999999);
      if (!is_dir("tmp"))    mkdir("tmp"); 
      return "tmp/".$filename.".png";
    }
 
 
    // display a CAPTCHA picture with private text and return the public text
    function Make_CAPTCHA()
   {
      $image = imagecreatetruecolor($this->lx,$this->ly);
      $back  = ImageColorAllocate($image,intval(rand(224,255)),intval(rand(224,255)),intval(rand(224,255)));
      ImageFilledRectangle($image,0,0,$this->lx,$this->ly,$back);
 
      // Génère des lignes sur la largeur...
        for ($i=0;$i<$this->lx;$i+=10)
      {
         $color=imagecolorallocate($image,intval(rand(160,224)),intval(rand(160,224)),intval(rand(160,224)));
         imageline($image,$i,0,$i,$this->ly,$color);
        }
      // Génère des lignes sur la hauteur pour former une grille de couleurs aléatoires
        for ($i=0;$i<$this->ly;$i+=10)
      {
         $color=imagecolorallocate($image,intval(rand(160,224)),intval(rand(160,224)),intval(rand(160,224)));
         imageline($image,0,$i,$this->lx,$i,$color);
        }
 
      for( $i=0,$x=10 ; $i<$this->long ; $i++ )
      {
         // Génère des couleurs aléatoires
         $r = intval(rand(0,128));
         $g = intval(rand(0,128));
         $b = intval(rand(0,128));
 
         // Alloue les couleurs générées
         $color = ImageColorAllocate($image, $r,$g,$b);
         $shadow= ImageColorAllocate($image, $r+128, $g+128, $b+128);
 
         // Génère une taille et un angle aléatoire
         $size  = intval(rand(12,17));
         $angle = intval(rand(-30,30));
 
         // Génère un caractère aléatoire
         $text = rand(65,90);
         if(rand(1,3)==1) $text = rand(65,90);   // A - Z
         if(rand(1,3)==2) $text = rand(97,122);   // a - z
         if(rand(1,3)==3) $text = rand(49,56);   // 1 - 9
 
         // On collecte les caractères dessinés pour permettre de vérifier 
         // si le texte saisi est le bon
         $this->text .= chr($text);
         $text = chr($text);
 
         // Génère l'image en fonction des couleurs et du texte
         ImageTTFText($image,$size,$angle,$x  ,30,$color ,$this->font_file,$text);
 
         // Permet le décalage entre deux caractères
         $x = $x + $size + 2;
      }
 
      // Créé un fichier PNG en fonction de la ressource (handle) de l'image générée
      imagepng($image, $this->filename);
      ImageDestroy($image);
    }
 
 
   function Display_CAPTCHA()
   {
      $this->Make_CAPTCHA();
      $res     = "<img src='".$this->filename."' alt='$this->text' border='0'>\\n";
      return $res;
   }
}
 
?> 

La classe est utilisable via l'exemple suivant :


<?
 
  require_once("./captcha.php");
 
  $p=new CAPTCHA();
 
  echo "
<u> L'image: </u>
";
  echo $p->display_captcha(true);
 
  echo "
 
<u> Son texte: </u>
";
  echo $p->text;
 
?> 




0x07. CONCLUSION


La libraire GD permet bien des choses et les images CAPTCHA sont de plus en plus perfectionnée, et cela inspire les spammeurs, effectivement s'il n'est pas possible de lire le texte, ou du moins difficilement, c'est qu'un mail contenant ce genre d'image peut outre passer les filtre anti-spam. Il faut donc utiliser d'autre technique. Que ce soit sur un forum, ou lors d'une inscription n'hésitez pas à en mettre un peu partout, les lammers sont bien présents, soyez vigilents.




   =>   Écrit par : Nicolas, le 10 décembre 2008


 
Mots clés :  
  php 
  
  sql 
  
  web 
    >   Articles connexes :

Troll The Lamer



Se protéger des injections SQL



Backdoor via injection SQL


Nous allons voir comme effectuer une injection SQL en contournant la protection par addslashes().

HTTP Server, tell me who you are ?


Discuter avec un serveur web *apparement* muet ? Voici comment faire...

TLD et Indexes téléphoniques



8260457