Publi

Esteganografía: Inserción en el LSB en PHP (Solucionando el Challenge 17 en #tuentiContest)

La esteganografía es el arte de introducir una información dentro de otra de forma oculta, ya sea texto dentro de imagen, audio dentro de imagen, imagen dentro de audio, imagen dentro de imagen, lo que nos dé la imaginación.

Hace unos meses, en Código para llevar leí un par de posts interesantes sobre este tema, donde se proponía una implementación en Python ( I y II )

En este caso, se trata de descifrar un texto oculto dentro de una imagen PNG. ¿ Por qué PNG ? Sobre todo porque es un estándar bastante extendido de compresión de imágenes sin pérdidas, podría ser también un JPEG LS, aunque se ve menos.
La técnica utilizada para “ocultar” el mensaje se basa en modificar el bit menos significativo de cada componente de cada pixel de la imagen, y es que para el ojo humano, en una gama de 256 niveles de rojo, por ejemplo, le resulta muy pero que muy difícil (por no decir imposible), distinguir entre dos niveles muy próximos, así que nos aprovechamos de eso, utilizaremos el bit menos significativo (LSB) de cada componente para escribir nuestro mensaje, y a efectos prácticos no nos daremos cuenta visualmente de esto. Además, podremos encerrar un carácter ASCII por cada 2.66 pixels (3componentes por pixel, 8 bits por carácter).

En el reto, el mensaje estaba encerrado sólo en la primera línea de la imagen, por lo que sólo leeremos esa línea, es fácil extender el código a varias líneas o columnas, incluso.

Vamos a por un poco de código de cómo descifrar el mensaje:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
// Esta función cogerá las componentes RGB del pixel situado en
// $x, $y
function getcolor($img, $x, $y)
{
  $rgb = imagecolorat($img, $x, $y);
  $r = ($rgb >> 16) & 0xFF;
  $g = ($rgb >> 8) & 0xFF;
  $b = $rgb & 0xFF;
 
  return array($r, $g, $b);
}

// Convierte una cadena de unos y ceros a ASCII, cogiendo de 8 en 8
function _bin2asc($str)
{
  $len = strlen($str);
 
  for ($i=0;$i<$len;$i+=8)
  {
    $ch=bindec(substr($str,$i,8));
    $data[]=$ch;
  }
  return $data;
}

// Extrae los datos de los LSB de cada componente
function decod($i)
{
  $tx=imagesx($i);
  $ty=imagesy($i);
  // Eliminar la línea de abajo para extraer información de toda la imagen
  $ty=1;

  $data='';
  for ($y=0; $y<$ty; $y++)
    {
      for ($x=0; $x<$tx; $x++)
    {
      $cdat=getcolor($i, $x, $y);

      $data.=($cdat[0]&1).($cdat[1]&1).($cdat[2]&1);
    }
    }
  return $data;
}

$img=imagecreatefrompng($tnam);

$d=decod ($img);
echo $d;
?>

Es cierto que esto habría que optimizarlo mucho, ya que estamos almacenando valores binarios en una cadena, ocuparía mucho y tardaríamos tiempo en la reconstrucción ya que _bin2asc() trabaja haciendo contantemente substr(). Podríamos por ejemplo reconstruir el mensaje a medida que vamos obteniendo bits, almacenando en un buffer hasta 8 bits. Aunque yo creo que este ejemplo se entiende mejor, y por otra parte, dada la naturaleza del concurso había que investigar muchas posibilidades, por lo que era mejor hacer un código menos óptimo y más rápido de hacer.

Por otra parte, para solucionar el reto del #tuentiContest, era importante colocar los bits en orden, es decir, los bits extraídos no están es este orden, teníamos que investigar y hacer pruebas, en lugar de estar en RGB (como en el ejemplo superior), están en BGR.

A continuación, aunque no era necesario para el reto, incluyo mi solución, un programa que extrae la información de la entrada estándar, la decodifica (base64_decode), la pasa a un archivo PNG y abrimos ese archivo desde PHP que leeremos. A continuación teníamos que devolver un fragmento del código extraído, por lo tanto delimitamos el texto y devolvemos. Aunque sólo bastaba con hacer un programa que escribiera el código.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?php
// Convierte una cadena de unos y ceros a ASCII, cogiendo de 8 en 8
function _bin2asc($str)
{
  $len = strlen($str);
 
  for ($i=0;$i<$len;$i+=8){
    $ch=bindec(substr($str,$i,8));

      $data[]=$ch;

  }
  return $data;
}

function decod($i)
{
  $tx=imagesx($i);
  $ty=imagesy($i);

  $ty=1;

  $data='';
  for ($y=0; $y<$ty; $y++)
    {
      for ($x=0; $x<$tx; $x++)
    {
      $cdat=getcolor($i, $x, $y);

      $data.=($cdat[2]&1).($cdat[1]&1).($cdat[0]&1);
    }
    }
  return $data;
}

// Esta función cogerá las componentes RGB del pixel situado en
// $x, $y
function getcolor($img, $x, $y)
{
  $rgb = imagecolorat($img, $x, $y);
  $r = ($rgb >> 16) & 0xFF;
  $g = ($rgb >> 8) & 0xFF;
  $b = $rgb & 0xFF;
 
  return array($r, $g, $b);
}

$fp=fopen("php://stdin","r");

$line='';
while(!feof($fp))
  {
    $line.=fread($fp,5192);
  }

$tnam=tempnam("/tmp", "tuenticontest_");
file_put_contents($tnam, base64_decode($line));
$img=imagecreatefrompng($tnam);

$d=decod ($img);

$b=_bin2asc($d);

$frase='';
for ($i=0; $i<count($b); $i++)
  $frase.=chr($b[$i]);
//echo $frase;
$pre='following code: ';
$code=substr($frase, strpos($frase,$pre)+strlen($pre));
$clean=substr($code, 0, strpos($code, '.'));
echo $clean;

?>

También podría interesarte...

There are 2 comments left Ir a comentario

  1. Pingback: Bitacoras.com /

  2. Pingback: Poesía binaria » Recopilación de soluciones para los retos de #tuentiContest . Challenge #17 /

Leave a Reply