Steganografia in C++

28 December 2008 da Francesco

Su Mind Unpacked abbiamo gia’ parlato di steganografia digitale in due precedenti articoli (Introduzione alla steganografia digitale e Algoritmi steganografici), oggi percio’ discuteremo di un’implementazione in C++ dell’algoritmo LSB. L’algoritmo e’ gia’ stato trattato dal punto di vista teorico nei precedenti articoli, quindi non mi soffermero’ piu’ di tanto su di esso e passero’ quasi subito alla spiegazione del codice.

L’algoritmo LSB (least significat bit) mira a nascondere un messaggio in un contenitore (immagine, file audiom file video) modificando i bit meno significativi. Nel nostro caso creeremo un programma per nascondere file di testo all’interno di immagini bitmap. Ogni pixel della nostra immagine e’ rappresentato da una terna di numeri che indicano le tre componenti RGB. Modificheremo il bit meno significativo di ogni componente di ogni pixel e lo adatteremo ai bit del nostro messaggio di testo. Il cambiamento che otteremo modificando solo il bit meno significativo sara’ al massimo di una unita’ e, di conseguenza, produrra’ immagini all’apparenza identiche, poiche’ un tale cambiamento non e’ percepibile ad occhio nudo.

Il nostro programma e’ sostanzialmente diviso in due parti: quella per nascondere il testo nell’immagine e quella per recuperarlo.

Steganografare un testo

La prima parte del programma si occupera’ di fare le seguenti operazioni:

  1. Leggere il messaggio da un file di testo
  2. Calcolare il numero di pixels necessari ad occultare il messaggio nell’immagine ed accertarsi che l’immagine sia abbastanza grande da poterlo fare. Salvare in forma binaria il valore di ogni componente di questi pixels.
  3. Modificare i bit meno significativi per adattarli a quelli del messaggio
  4. Scrivere i nuovi bit su una nuova immagine apparentemente uguale alla prima, ma contenente il testo.

Saltando il passo 1 (che spero chiunque sia capace di fare), passiamo al due:

numbits = message.size()*8;  // La stringa message contiene il messaggio letto dal file
numpixels = (numbits%3 == 0 ? numbits/3 : (int)numbits/3 + 1);
numrows = (int)numpixels/img.TellWidth() + 1;
if (numrows > img.TellHeight()) {
	cout << "Errore: il messaggio e' troppo lungo per essere contenuto in questa immagine." << endl;
	exit(0);
}
for (int i = 0; i < numrows; i++) {
	for (int j = 0; j < img.TellWidth(); j++) {
		bits.push_back(bitset<8>((int)img(j, i)->Red));
		bits.push_back(bitset<8>((int)img(j, i)->Green));
		bits.push_back(bitset<8>((int)img(j, i)->Blue));
	}
}

Il programma calcola il numero di bit e il numero di pixel necessari in base alla lunghezza del messaggio. Visto che ogni pixel puo’ nascondere 3 bit, il numero di pixel viene calcolato semplicemente dividendo il numero di bit per 3 e arrotondando per eccesso nel caso di resto. numrows contiene invece il numero di righe dell’immagine che bisogna copiare in memoria. Se il numero di righe e’ maggiore dell’altezza del’immagine il programma genera un errore poiche’ l’immagine e’ troppo piccola per contenere il messaggio. Dopo aver fatto cio’, salviamo i tutti i valori RGB dei pixel che ci servono in un array di bitset. bits e’ un vettore (classe vector delle STL) di oggetti bitset, una classe della STL creata appositamente per gestire dati in forma binaria. La notazione bitset<8>(…) crea un oggetto bitset con 8 bit che assumono il valore dell’argomento che gli si passa tra parentesi. Visto che i valori RGB di un’immagine sono compresi tra 0 e 255 8 bit sono sufficienti.

count = 0;
for (int i = 0; i < message.size(); i++) {
	bitset<8> temp = bitset<8>(message[i]);
	for (int b = 7; b >= 0; b--) {
		bits[count].set(0, temp[b]); // 0 e' l'indice del bit meno significativo; 		temp[b] e' il b-esimo bit della lettera che stiamo esaminando
		count++;
	}
}

Dopo aver salvato i valori in memoria, dobbiamo modificare il bit meno significativo di ogni elemento salvato per renderlo uguale a quello del nostro messaggio. Il codice qui sopra svolge questo compito: scorre ogni lettera del messaggio e crea un oggetto bitset contenente il valore di quella lettera in binario. Il ciclo innestato scorre tutti i bit della lettera e modifica i bit meno significativi dell’immagine in modo appropriato.

for (int i = 0; i < numrows; i++) {
	for (int j = 0; j < img.TellWidth(); j++) {
		imgout(j, i)->Red = (int)bits[count++].to_ulong();
		imgout(j, i)->Green = (int)bits[count++].to_ulong();
		imgout(j, i)->Blue = (int)bits[count++].to_ulong();
	}
}
for (int i = numrows; i < img.TellHeight(); i++) {
	for (int j = 0; j < img.TellWidth(); j++) {
		imgout(j, i)->Red = img(j, i)->Red;
		imgout(j, i)->Green = img(j, i)->Green;
		imgout(j, i)->Blue = img(j, i)->Blue;
	}
}
imgout.WriteToFile("out.bmp");

Una volta che i bit sono stati modificati in maniera corretta, dobbiamo solo riscriverli su una nuova immagine. Il primo ciclo scrive i bit contenenti il messaggio (la funzione to_ulong() converte un bitset nel suo valore decimale), mentre il secondo riscrive semplicemente i valori dei pixel che non sono stati modificati riprendendoli dall’immagine originale.

Recuperare il testo steganografato

La seconda parte del programma e’ quella che, data in input un’immagine recupera il testo contenuto in essa. Un difetto del programma e’ quello di richiedere la lunghezza del messaggio per poterlo estrarre: in realta’ si potrebbe ovviare a questo problema in maniera piuttosto semplice (dopo vedremo come) ma per ora, bisognera’ accontentarsi di andare a tentativi se non si conosce la lunghezza del messaggio completo.

La struttura di questa seconda parte ricalca quella della prima: dopo aver preso in input la lunghezza del messaggio ed aver calcolato in base ad essa il numero di pixel che sono stati modificati, il programma prende dall’immagine i valori delle componenti di questi pixel e li mette in un array di bitset. Un secondo ciclo:

for (int i = 1; i < numbits; i++) {
	temp.set(count, bits[i-1][0]);
	count--;
	if (count == -1) {
		count = 7;
		cout << (char)temp.to_ulong();
	}
}

si occupa di prendere i bit meno significativi a gruppi di 8 e di stampare il corrispondente valore alfanumerico.

Possibili miglioramenti

Il programma in questa maniera e’ piuttosto rudimentale, ecco alcuni possibili miglioramenti che potreste allenarvi a mettere in pratica:

  1. Evitare di dover richiedere la lunghezza quando bisogna estrarre un messaggio. Una possibilita’ potrebbe essere quella di conservare i primi N bit meno significativi dell’immagine per salvare la lunghezza del messaggio e per estrarla al volo senza doverla richiedere all’utente. 10 bit permetterebbero, per esempio, di salvare un numero fino a 210 = 1024.
  2. Introdurre la possibilita’ di inserire una chiave. Una chiave potrebbe essere un numero intero N che indicherebbe al programma di modificare non tutti i pixel uno per uno ma di prendere 1 pixel ogni N. Questo ridurrebbe la capacita’ dell’immagine di contenere messaggi ma renderebbe piu’ difficile un’eventuale individuazione del messaggio.
  3. Introdurre la possibilita’ di crittografare un messaggio prima di steganografarlo per una maggiore sicurezza.

Ecco il codice del programma: Steganografia in C++. Fa uso delle librerie EasyBMP per la gestione delle immagini presenti nell’archivio stesso.

Ciao!

Condividi l'articolo!
  • Digg
  • del.icio.us
  • Facebook
  • Diggita
  • StumbleUpon
  • Technorati
  • Twitter

Tags: , ,
Pubblicato in Informatica | Commenti (4)

4 Risposte a “Steganografia in C++”

  1. Marco ha scritto:

    molto bene, davvero molto bene. ottimo questo articolo.

  2. Sonia ha scritto:

    Ho letto con molto interesse l’articolo, che ho trovato molto piacevole; anche se il sorgente finale m’è parso realizzato in modo grossolano. Comunque siete molto bravi, è sempre un piacere leggere il blog. Ciao

  3. Marco ha scritto:

    da quel che so il programma è stato codato in fretta, forse per questo risulta un po’ grossolano. in ogni caso grazie dei complimenti.

  4. Francesco ha scritto:

    E’ vero, il programma e’ abbastanza grossolano ma era giusto per dimostrazione.
    Fra qualche giorno penso di allegare allo stesso articolo una versione migliorata (con alcuni dei miglioramenti che avevo io stesso suggerito nell’articolo).
    Se hai idee per eventuali correzioni al programma, fammi pure sapere.
    Ciao e grazie comunque dei complimenti.

Leave a Reply