Contenu

Comment on a (presque) caché un message invisible dans le drapeau de la micronation de Dirtybiology : Une introduction à la stéganographie et à la réprésentation des données par un ordinateur

note personnelle : pourrait être un titre de light novel : Dirtybiologyのミクロネーションフラグで目に見えないメッセージを隠す方法:データのステガノグラフィとコンピュータ表現の概要 , ça rend bien non ?

Introduction

Le 1er novembre 2021, Léo Grasset de la chaîne Dirtybiology a mis en place une expérience sociale lors d’une vidéo dans laquelle il met en lumière les bizarreries diplomatiques des micronations (cette vidéo n’est pas essentielle pour comprendre ce post, mais n’hésitez pas à aller la voir quand même).

Cette expérience consiste en la création d’une micronation pour et par les abonnés de sa chaîne youtube. Chaque “citoyen” se voit attribué un pixel sur un drapeau qu’il peut changer à volonté (pour moi le 181 en x et 77 en y). De plus tous les “citoyens” sont fortement incités à aller sur le serveur discord officiel de la micronation pour organiser entre eux les motifs à mettre sur le drapeau, mais aussi tout ce qui va autour de la micronation (constitution, journaux, langue…).

Dès que j’ai appris qu’on pouvait faire mumuse avec des pixels, j’ai tout de suite pensé à faire ce que l’on appelle de la stéganographie.

Selon notre bon ami wikipédia, “La stéganographie est l’art de la dissimulation : son objet est de faire passer inaperçu un message dans un autre message.”. En gros l’idée est de cacher un message, ici à travers une image (le drapeau).

La stéganographie est un sujet passionnant et très vaste mais dans ce petit post je ne vais en exposer qu’une petite facette : une technique qui utilise les “bits de poids faible”.

Explication de quelques bases

Avant d’expliquer la technique en elle même, il est important d’expliquer quelques bases de l’informatique.

Représentation numérique

D’abord, un ordinateur ne fait que manipuler des nombres. En effet on peut représenter énormément de choses (si ce n’est tout) par des nombres, des tweets aux films en passant par vos jeux vidéos favoris.

Cependant, au lieu de les représenter comme nous en base 10, il les représente en base 2, aussi appelée binaire. Il utilise seulement des 0 et des 1 (vous l’avez probablement déjà entendu).

Un fait un peu moins connu, c’est qu’on regroupe généralement ces bits dans des paquets de 8 qu’on appelle octets. (C’est de la que viennent les Gigaoctets de vos forfaits téléphoniques : ils représente la quantité de données (le nombre de 0 et de 1) que vous pouvez faire passer en 1 mois). Par exemple, l’octet 00000010 équivaut à 2 en base 10, et l’octet 11111111 à 255.

Dans la pratique, utiliser le binaire directement c’est assez pénible, imaginez le bazar avec les milliers de 0 et 1. C’est pour ça qu’on utilise souvent de l’hexadécimal. En fait on compte en base 16 : 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11 ….. Par exemple 2A correspond à 42. L’avantage de cette méthode, c’est qu’avec 2 “chiffres” on peut représenter jusqu’à 255 valeurs. Et oui comme pour les octets ! On représente donc les octets en hexadécimal la plupart du temps, pour des raisons pratiques.

Ici, vous l’avez compris, on va s’intéresser à comment l’ordinateur représente une image et du texte grâce à ces octets.

Représentation d’un texte

Depuis les années 60, les informaticiens ce se sont mis d’accords sur une manière de représenter des caractères grâce à des nombres. C’est ce qu’on appelle la table ASCII.

Elle est très simple : pour chaque lettre (ou plus précisément caractère, comme par exemple le retour à la ligne ou l’espace) on va associer un nombre (représentés ici à gauche en base 10,au mileux en binaire et à droite en hexadécimal).

Un nombre = une lettre

/fr/dirtybiology/table.jpg
La table ASCII

Cette table ASCII contient 128 caractères, mais il en existe des versions plus grandes de 256 caractères. Ce n’est pas un hasard puisqu’on peut représenter 256 nombres différents avec un octet. On représente donc chaque caractère par un octet. Par exemple le texte Hello correspond à ces 5 octets (n’hésitez pas à le vérifier avec la table) : 01001000 01100101 01101100 01101100 01101111

(Aujourd’hui cette table est la plupart du temps remplacée par un autre codage, nommé UTF-8, il est rétrocompatible avec la table ASCII, mais c’est un tout autre sujet)

Bien, maintenant on sait comment faire un texte, voyons les images.

Représentation d’une image

Une image numérique est en fait une matrice de pixels. Un quadrillage de petits carrés de couleurs, qui vus de loin forment une image.

Mais maintenant on doit se poser une autre question : comment on représente une couleur ?

Vous avez probablement vu en cours de physique (ou dans les vidéos de Dirtybiology 😉), qu’une couleur peut être représentée en synthèse additive par un mélange de rouge, vert et bleu.

Pour représenter une couleur numériquement, on utilise 3 octets, un pour la quantité de rouge, un pour la verte et un dernier pour la bleue.

On peut la représenter dans un tuple, par exemple le blanc correspond à (255, 255, 255). Le rouge est (255, 0, 0), le vert (0, 255, 0) et le bleu (0, 0, 255).

Avec ça on peut représenter 256x256x256 = 16 777 216 couleurs, ce que vous pouvez souvent voir sur les pubs des produits “rgb” de vrais g@mers : jusqu’à 16 millions de couleurs.

Et maintenant vous pouvez comprendre les codes hexadécimaux des couleurs : c’est 3 octets représentés sous forme hexadécimale qui forment une couleur ! FF correspond à 255, donc #FF-FF-FF correspond à (255, 255, 255), soit du blanc ! #FF0000 c’est du rouge, etc etc.

Une image peut donc être représentée par des groupes de 3 octets, un pour chaque pixel.

Voici par exemple une image de panda roux vue par nous :

/fr/dirtybiology/panda2.jpg
Beau panda roux

et vue par un ordinateur :

/fr/dirtybiology/panda.jpg
Panda roux moins beau (mais beau quand même)

(dans la pratique les images sont compressées pour prendre moins de données, mais pour des raisons de “c’est beaucoup trop compliqué pour des débutants” on l’ignorera dans cet article)

Petit résumé

Si vous retenez/comprenez ça, cet article n’aura pas été inutile :

Dans notre monde “numérique” tout est représenté par des nombres. Une vidéo/photo numérique, un tweet, un jeu vidéo, un document word, un projet photoshop, absolument tout. Ce qui importe c’est comment on interprète ces données pour les faire représenter ce que l’on veux (à l’aide d’ordinateurs et de programmes).

On représente la plupart du temps les nombres sous formes hexadécimale, mais il est aussi possible de les représenter sous forme décimale comme dans la vie quotidienne, ou en binaire.

Maintenant si vous avez compris, la suite vous permettra de mettre en pratique ces notions.

La technique

Ici notre but est de cacher du texte dans une image. On cherche donc à faire passer discrètement des octets (puisque comme on l’a vu des octets peuvent tout représenter, y compris du texte), sans altérer trop l’image initiale.

Tout est dans le trop, puisque on va utiliser le dernier bit de chaque composante de la couleur de chaque pixel.

Je m’explique : prenons par exemple la couleur turquoise de code hexadécimal #10EDB8. En décimal et en rgb elle correspond à (16, 237, 184). Si on écrit cela en binaire on obtient (00010000, 11101101, 10111000). Regardez ce qui se passe lorsqu’on change le dernier bit de chaque nombre : (00010001, 11101100, 10111000).

/fr/dirtybiology/comparison.png
Corporate needs you to find the differences between this picture and this picture.

Vous ne voyez pas ? C’est normal c’est indiscernable pour un humain. Pourtant un ordinateur peut faire la différence (vous pouvez vérifier à la pipette si vous voulez), on va exploiter cela pour faire passer toutes nos données dans ce dernier bit (que l’on appelle le bit de poids faible)

Il suffit maintenant de faire un programme pour prendre les pixels dans l’ordre, et pour chaque composante de la couleur mettre les derniers bits les uns à la suite des autres. On fait des paquets de 8 bits pour obtenir des octets et ta da, on récupère des données potentiellement exploitables. Si on modifie volontairement ces derniers bits, on peut stocker les données que l’on souhaite.

Cette image résume bien l’idée (ici on utilise les deux derniers bits et pas seulement le dernier mais le principe est exactement le même):

/fr/dirtybiology/example.jpg
via https://null-byte.wonderhowto.com/how-to/steganography-hide-secret-data-inside-image-audio-file-seconds-0180936/

Si l’on applique cette technique sur le drapeau on obtient (pour les 500 premiers pixels) : I$Il9$▒ 1$HØbI$$I$7$maÉ$I$I<É$Aä $òI$I$ìÎgm¶Ûm¶Ûm¶Òm¶Ûmn©-­»m¶Û$Ûm¶Ù+n$I$I$I$I$I$I&Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶½¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶Ûm¶í¶Ûm¶Ûm¶Ûm¶Ð¢$I$I-

Ça n’a aucun sens puisque nous n’avons encore rien modifié.

Ici on va encoder un texte. Il faut 8 bits pour 1 caractère, et 3 pixels contiennent 9 bits. Il faut donc un peu moins de 3 pixels par caractère. Il ne reste alors plus qu’à modifier légèrement chaque pixel du drapeau pour encoder un texte.

C’est la fin des explications techniques, La suite de l’article explique comment le projet de mettre un message dans le drapeau s’est déroulé. Si vous n’avez pas compris cette première partie, n’hésitez pas à venir me poser des questions par un des moyens de contact qui ont listés sur la page d’accueil de ce site (je suis aussi ouvert aux suggestions :) ).

Le déroulement de l’opération

J’ai eu l’idée de la stéganographie dès l’annonce de l’expérience, mais je n’ai pas pu l’organiser avant le week-end final par manque de temps durant la semaine. J’avais tout de même tenté de me faire entendre sur de nombreux serveurs mais personne ne semblait emballé par l’idée assez dure à expliquer 😢 (faire quelque chose qui ne se voit pas ? pourquoi faire ?)

J’ai quand même réussi à rassembler quelques personnes motivées (merci à eux : lunaticstraydog, Furycad, BTP, capsule_man entre autres). Et samedi, après n’avoir absolument pas fait le vote prévu à la base, on s’est rapidement mis à encoder.

Le but était de cacher “Cela aussi passera https://www.youtube.com/channel/UCtqICqGbPSbTN09K1_7VZ3Q". Le 20 novembre à 19h12, nous avions écrit “Cela aussi passera httpq://vww.yguDube.cnvOcèannel/UCvqICqGbD]fWM¶Ûm6Þ7v”. 170/200 pixels validés, tout allait pour le mieux.

/fr/dirtybiology/yes.png
C'est cool ça

C’était sans compter les évènements qui allaient suivre dans la nuit. À cause d’attaques répétées sur les motifs du centre historique, toutes les régions ou presque avaient mis en place une politique assez agressive. Des dizaines de personnes étaient à l’affût du moindre changement de pixel, même infime. En revenant le lendemain à 10 heures, seuls 6 pixels avaient tenu bon 😭

/fr/dirtybiology/sad.png
C'est beaucoup moins cool ça

On se doutait que ce problème pouvait survenir, on avait donc demandé gentiment au centre historique un peu plus de 24 heures avant la fin de l’expérience de nous inclure dans leur drapeau, ce à quoi on nous a répondu en gros “C’est trop tard, désolé mais on s’en fiche” (sachant qu’on demandait 200 pixels et qu’ils en ont changés des milliers en moins de 24 heures, un peu dommage mais bon pas grave). Certains organisateurs d’autres régions étaient prêt à nous aider (merci Albane !), mais malheureusement il n’y avait que 3 pixels sur leur zone 😅.

Dès qu’on a constaté la disparition de notre message initial, on a donc décidé en urgence de se mettre dans la toute dernière ligne du tableau, et d’utiliser 2 bits au lieu d’un seul pour changer moins de pixels. Malheureusement avec seulement une heure et demi restante, un bug dans un programme beaucoup plus compliqué qu’il n’aurait du l’être a tout fait rater. Encore aujourd’hui je ne vois pas où était le problème, le code était censé marcher. J’ai du tout réécrire en 25 minutes sous pression et j’avais enfin un code propre, mais il était déjà 11h50, trop tard.

“Cela aussi passera”, et ben lui il est passé vite, entre 19h50 et 23h30 le 20 novembre 2021.
Mais mon message était tout de même passé, dans les deux sens du terme, démontrant ciniquement l’éphémérité de notre existence. (c’était évidemment tout à fait calculé 🤫)

Pour autant est-ce un échec ? Personnellement j’en suis tout de même satisfait. J’ai fait découvrir de nouvelles choses à plusieurs personnes, j’ai réussi à en mobiliser certaines, j’ai appris à quel point il était dur de faire entendre sa voix dans un gros projet extérieur au sien. Même si le message n’a été que passager, cet article sera passager pendant un peu plus longtemps et, je l’espère, sera utile pour quelques personnes.
(Ah et si vous pouvez regarder dans la nuit du 19 au 20 novembre, vous pourrez peut-être tomber sur notre message de test, qui lui aussi est passé vite : https://youtu.be/dQw4w9WgXcQ)

2 petits liens que je sais pas où mettre :
Le tweet d’annonce pour l’article
Le wiki du projet

Merci de m’avoir lu

Pour aller plus loin

Si vous connaissez python et que voulez vous amuser, essayez d’extraire un message de cette archive du drapeau. Sinon vous pouvez utiliser cette image que j’ai créée au format bitmap :

/fr/dirtybiology/challenge.bmp
C'est beau

Je publierai une correction dans quelques jours :)
(Et en bonus le code que j’ai utilisé pour encoder et faire l’image)

Un conseil : l’ordre des pixels dans la liste est le même que l’ordre des pixels dans le drapeau, de gauche à droite ligne par ligne. Le drapeau fait 513 par 257 pixels.