Attention, il n'est pas question ici d'extraire cette information, car il existe une infinité de façon de cacher les données dans le LSB :
- Un bit sur deux, un bit sur trois...
- Choix des bits de certaines composantes de l'image uniquement (que dans la composante verte, rouge, bleue, ou toutes les combinaisons possibles)
- Ordre des bits en lisant l'image ligne par ligne, colonne par colonne, une ligne sur 2....
- Lecture des bits à l'envers ou non
- Utilisation des bits de poids le plus faible, ou de poids un peu plus important (avec comme conséquence une altération de l'image plus importante)
- ...
Le point important est que toutes les méthodes stéganographiques à base de LSB altèrent le contenu de l'image et que même si cette légère modification n'est pas visible à l'œil nu, elle peut quand même être décelée si on utilise les bonnes techniques.
L'idée est la suivante, même si les bits de poids faibles sont porteurs de très peu d'information (comme vu dans mon article sur le LSB), ils ne sont tout de même pas distribués aléatoirement dans l'image. Porteur de très peu d'information, ne veut pas dire porteur d'aucune information. Dans une zone ou il n'y a qu'une couleur présente par exemple, il y a peu de chance qu'en plein milieu il y ait un pixel différent de tous ses voisins... possible, mais peu probable. Les bits de poids faibles respectent en règle générale l'apparence de l'image et les pixels sont cohérents entre eux.
Le but va donc être de visualiser cette cohérence entre les bits de poids faibles. Pour cela on va recréer l'image en "vidant" chaque bit des informations inutiles (c'est à dire les bits de poids fort). Seuls les bits susceptibles de cacher de l'information vont donc être gardés. Comme ce sont des bits de poids faibles, ils ont un impact faible sur le rendu de l'image et il sera donc très difficile de distinguer quoique soit. Il suffit pour cela d'augmenter le poids du bit en faisant un simple décalage binaire vers la gauche.
// On enlève les informations inutiles composante = composante & 1; // On augmente le poids du bit composante = composante << 7;
Exemple :
Composante (décimale) | 255 | 0 | 111 | 56 |
---|---|---|---|---|
Composante (binaire) | 11111111 | 00000000 | 01101111 | 00111000 |
Devient (binaire) | 10000000 | 00000000 | 10000000 | 00000000 |
Devient (décimale) | 128 | 0 | 128 | 0 |
# ./lsb.rb -h lsb.rb [options] -i input_image -o output_image Version 1.1 --colors|-c r|g|b: couleur que l'on veut garder --bit|-b bit: bit que l'on va utiliser (par defaut 0) --help|-h : affiche cette aide --version|-v : affiche le numero de version
Utilisons ce petit programme sur cette image qui ne cache aucune information à l'intérieur.
# ./lsb.rb tux.png -o tux_lsb.png
Cachons maintenant des informations dans notre image de départ et regardons le résultat :
# ./lsb_hide.rb -f hide.txt tux.png -o tux_hidden.png # ./lsb.rb tux_hidden.png -o tux_hidden_lsb.pngOn voit très clairement ici que quelque chose s'est passé sur le haut de l'image, puisque cette partie n'est plus du tout cohérente. La raison est qu'un message a été caché, et celui-ci étant court, toute l'image n'a pas été nécessaire pour le cacher.
Cette technique ne fonctionnera pas dans tous les cas, à plus forte raison si l'image est bruitée ou par exemple si elle avait été préalablement compressée en jpg. Cependant, je la trouve suffisamment intéressante pour être citée. De plus elle m'a rendu de précieux services lors de certains challenges.