Jugando con la esteganografía: El comienzo (parte 1)


No recuerdo cómo ni por qué llegué a un termino llamado “esteganografía“. Lo que sí recuerdo es que me resultó interesante y comencé a curiosear del tema. Leí un poco de aquí y de allá sobre esta parte de la criptología y me recorrió una no tan extraña sensación por el cuerpo que me invitaba a indagar, probar, investigar o como os dé la gana llamarlo. La cuestión es que pensé en hacer mi propio programa que de una forma básica pusiera en práctica la esteganografía para, después, poco a poco, ir haciendo pruebas y sacar conclusiones. Pero ¿qué es la esteganografía? Según un extracto de la wikipedia:

La esteganografía (del griego στεγανος (steganos):cubierto u oculto, y γραφος (graphos): escritura), es la parte de la criptología en la que se estudian y aplican técnicas que permiten el ocultamiento de mensajes u objetos, dentro de otros, llamados portadores, de modo que no se perciba su existencia. (http://es.wikipedia.org/wiki/Esteganograf%C3%ADa)

Fácil, ¿no?

Bien, para mi pequeño programa he utilizado la esteganografía en imagenes, es decir, ocultar “cosas” en una imagen sin que se perciba. Antes de explicar cómo lo he hecho y mostrar los resultados, os dejo el código del programa que podéis encontrar en https://github.com/NachE/stegapy (donde además lo iré actualizando). Lo he llamado Stegapy:

Nota: He borrado los comentarios del código, haz click aquí para ver la versión con comentarios (en inglés)

#!/usr/bin/env python
# Stegapy
# Copyright 2013 J.A. Nache
# See LICENSE for details.

#python-PIL
import Image, sys, os

class UnestegFile:

	def __init__(self, pathOrigImage, pathDestFile):

		self.pathOrigImage = pathOrigImage
		self.pathDestFile = pathDestFile

		self.file = open(self.pathDestFile, 'wb')
		self.imageObj = Image.open(self.pathOrigImage)
		self.imgPix = self.imageObj.load()

	def readImgInBit(self):
		for x in range(0, self.imageObj.size[0]):
			for y in range(0, self.imageObj.size[1]):
				RGB = self.imgPix[x,y]
				for color in RGB:
					yield bin(color)[-1:]

	def unHide(self):
		bitcount = 0
		bytecount = 0
		byte = str()
		size = str()
		maxeof = 4294967295 # The largest file size in bytes we can hide
		bytes = self.readImgInBit()

		print("\nUnhidding file, wait...\n")
		for bit in bytes:
			byte += bit
			bitcount = bitcount + 1

			if bitcount == 8:
				bytecount = bytecount + 1

				size +=byte
				if bytecount == 4:
					print('-> Size of hidden file: '+
							str(int( size ,2))+' bytes')
					print('\tAn extra 4 bytes (32bits) at header are skipped')
					print('\tThese extra 4 bytes are used to save the original file size')
					print('\tso its not part of file.')
					maxeof = int(size ,2)+4 

				if bytecount > 4:
					self.file.write( chr( int( byte ,2)) )

				byte = str()
				bitcount = 0

				if bytecount == maxeof:
					break
		print ('\nDone')
		print ('==================================')
		print (str(bytecount - 4)+' bytes writed on file '+self.pathDestFile)

	def closeFile(self):
		self.file.close()

class EstegFile:

	def __init__(self, pathOrigFile, pathOrigImage, pathDestImage):

		self.pathOrigFile = pathOrigFile
		self.pathOrigImage = pathOrigImage
		self.pathDestImage = pathDestImage

		self.imageObj = Image.open(self.pathOrigImage)
		self.imgPix = self.imageObj.load()

		self.file = open(self.pathOrigFile, 'rb')

		self.imageObjNew = Image.new("RGB", self.imageObj.size, "white")
		self.imgPixNew = self.imageObjNew.load()

	def saveDest(self):
		self.imageObjNew.save(self.pathDestImage,"PNG")

	def readFileInBit(self):
		fsize = os.fstat(self.file.fileno()).st_size
		for bit in bin(fsize)[2:].zfill(32):
			yield bit

		while True:
			character = self.file.read(1)
			if not character:
				break
			else:
				byte = bin( ord( character ) )[2:].zfill(8)
				for bit in byte:
					yield bit

	def buildNew(self):
		inbytes = self.readFileInBit()
		bitscount = 0;

		print("\nHidding file, wait...")
		for x in range(0, self.imageObj.size[0]):
			for y in range(0, self.imageObj.size[1]):

				RGB = self.imgPix[x,y]	
				RGBnew = ()

				for color in RGB:
					try:
						RGBnew = RGBnew + ( int( bin(color)[:-1]+inbytes.next() ,2), )
						bitscount = bitscount + 1

					except StopIteration:
						RGBnew = RGBnew + (color, )

				self.imgPixNew[x,y] = RGBnew

		print ('\nDone')
		print ('==================================')
		print ('Orig File: '+self.pathOrigFile)
		print ('Hided with image: '+self.pathOrigImage)
		print ('Result File: '+self.pathDestImage)
		print ('==================================')
		print ('Hidded '+str(bitscount)+' bits | '
				+str(bitscount*0.125)+' bytes | '
				+str(bitscount*0.000122070312)+' kilobytes')
		print ('Extra 32 bits are used (represent the file size)')

def show_about():
	print('\n   Stegapy v0.1')
	print('   Copyright 2013 J.A. Nache under GPL v3\n')

def show_help():
	show_about()
	print ('\n   Usage')
	print ('   =========')
	print ('   Hide file:')
	print ('   '+sys.argv[0]+' h <File To Hide> <Source Image> <New Image File>')
	print ('\n   Unhide file:')
	print ('   '+sys.argv[0]+' u <Image with hidden data> <Dest File>\n')

try:
	if sys.argv[1] == 'h':
		show_about()
		sg = EstegFile(sys.argv[2], sys.argv[3], sys.argv[4])
		sg.buildNew()
		sg.saveDest()

	elif sys.argv[1] == 'u':
		show_about()
		sg = UnestegFile(sys.argv[2], sys.argv[3])
		sg.unHide()
		sg.closeFile()
	else:
		show_help()

except IndexError:
	show_help()

El código de Stegapy aquí arriba es capaz de ocultar cualquier archivo en una imagen (por el momento imagen PNG), además del acto contrario: extraer el archivo oculto. Esto nos lleva al siguiente punto:

¿Cómo funciona la esteganografía en imágenes? (En Stegapy, más bien)

Para ser más precisos, esta es la forma en la que Stegapy hace uso de la “inserción en el bit menos significativo“. Lo primero que debemos saber es que cada pixel de una imágen se puede descomponer en tres valores: Rojo, Verde y Azul, o más comúnmente RGB por sus siglas en inglés, Red Green Blue (sí, en la viña del señor hay de todo, pero me he centrado en el RGB). El rango de valores de cada color va desde el 0 hasta el 255 o dicho en binario, desde el 00000000 hasta el 11111111. De este modo, un pixel completamente blanco de una imagen tendrá un valor RGB de R=11111111 G=11111111 B=11111111. Un pixel completamente negro tendrá un valor RGB de R=00000000 G=00000000 B=00000000, y, como es lógico, un pixel completamente rojo tendrá un valor RGB de R=11111111 G=00000000 B=00000000.

La idea principal de la inserción en el bit menos significativo es utilizar el mencionado bit menos significativo de cada color (el situado más a la derecha) y sustituirlo por los diferentes valores bit a bit del archivo a ocultar. Digamos por ejemplo que queremos ocultar la letra A mayúscula, que tiene un valor binario de 01000001 dentro de una imagen. Para ello necesitaríamos ocho colores (son ocho bits). Dado que cada pixel contiene tres colores (R, G y B), vamos a necesitar casi tres pixels. Digamos que los tres primeros pixels de nuestra imagen tienen unos valores tal que:

Pixel 1: R=01010011 G=00011001 B=11011010
Pixel 2: R=01010101 G=10101010 B=11111100
Pixel 3: R=11111111 G=00000000 B=11111111

Ahora queremos ocultar nuestra letra A mayúscula con valor binario 010000001 en estos pixels, por lo que usando nuestro bit menos significativo, quedaría tal que:

Pixel 1: R=01010010 G=00011001 B=11011010
Pixel 2: R=01010100 G=10101010 B=11111100
Pixel 3: R=11111110 G=00000001 B=11111111

El resultado final serán tres pixeles ligeramente modificados, pero lo suficientemente inapreciable como para que el conjunto no sufra un cambio brusco.

Una vez que sabemos cómo funciona Stegapy, llega el momento de hacer pruebas y comprobar los resultados, lo que nos lleva al siguiente punto:

Obteniendo los resultados de la inserción en el bit menos significativo (Esteganografía)

Para las siguientes pruebas voy a utilizar el texto de la licencia GPLv3 de Stegapy que se puede encontrar en: https://github.com/NachE/stegapy/blob/master/LICENSE. Decir tiene que Stegapy recorre los pixels de la imagen de arriba a abajo, por lo que la información oculta irá formando columnas de izquierda a derecha.

Para las pruebas voy a utilizar una imagen cualquiera, digamos…, esta:

Yo raro sin datos ocultos

Imagen para pruebas con Stegapy

Ahora, vamos a insertar el texto propuesto, el texto que queremos ocultar, dentro de esta imagen y comparar el resultado:

Imagen original

Imagen original

Imagen con datos ocultos

Imagen con datos ocultos

¿Alguna diferencia? A simple vista parece que la imagen no ha sufrido ningún cambio, ¿pero qué tal si echamos un vistazo con un editor gráfico a la imagen generada? Vamos allá:

Modificando los valores de nivel parece que se aprecia algo, pero levemente

Modificando los valores de nivel parece que se aprecia algo, pero levemente

Según los datos obtenidos y las pruebas realizadas, parece que a simple vista no se aprecia cambio alguno, no obstante modificando los niveles en los colores se intuye una columna de datos que el ojo más hábil será capaz de ver. Para tratar de forzar el florecimiento de esta columna, vamos a hacer un último experimento, coloquemos unas franjas horizontales de color blanco (con pixeles de valor 11111111) en la imagen original y volvamos a esconder nuestro texto en esta nueva imagen para después tratarla con nuestro editor gráfico:

Imagen con franjas y pasada por editor gráfico

Imagen con franjas y pasada por editor gráfico

En esta ocasión, tras someter la imagen con datos ocultos a nuestro editor gráfico, observamos como aparece una columna de datos en las franjas blancas, lo que nos indica que cuanto más ruido tiene nuestra imagen, menos perceptible es al ojo humano.

Y este es el fin de la primera parte. Para las siguientes partes de la saga investigaré técnicas de detección de datos ocultos mediante esteganografía y experimentaré con nuevas formas de ocultación para dificultar en mayor medida la detección de nuestros datos.

Hala, hasta la próxima.

Anuncios

Convertir pantalla de portátil en monitor de PC (LVDS to VGA)


Resultado final

Hace tiempo que venía pensando qué hacer con un viejo portátil que funcionaba a veces si, a veces no. Era un portátil relativamente antiguo, hacía poco ruido y lo quería transformar en un router pero…, el resultado no fue fructífero. La placa base estaba hecha un asco y cuando arreglaba una cosa, se rompía otra. La cuestión es que viendo que la placa base estaba para el arrastre, decidí aprovechar el resto de piezas y, la primera de ellas, fue la pantalla. Así que:

.

.

Cómo convertir una pantalla de portátil en monitor de PC. Fácil, sencillo y para toda la familia.

Modelo de mi pantalla

Modelo de mi pantalla

El primer paso a realizar es identificar el modelo de nuestra pantalla, en mi caso era la LP150X08 de LG PHILIPS, un LCD usado por muchos fabricantes de la época. Una vez detectado el modelo de nuestra pantalla, es hora de buscar el datasheet para comprobar la forma en la que recibe la imágen. Nada más fácil que buscar “LP150X08 datasheet” en google. El mio lo encontré en este enlace. Pude comprobar entonces que mi pantalla tiene un receptor LVDS. Busqué esquemas de circuitos conversores de LVDS a VGA o de LVDS a DVI y a excepción de un extraño circuito en un foro francés, no encontré nada medianamente convincente.

Mi decisión final: comprar una controladora de LCD. La encontré en ebay, justo aquí, en la sección “LCD controller board kits DIY“. Concretamente este. Es una controladora sencilla. Conector VGA y botonera para brillo, contraste, encendido, etc. El menú viene con diferentes idiomas, entre ellos el español. El adaptador de corriente se vende por separado (así que atención a eso).

Antes de realizar el pedido, no está de más comprobar si la controladora soporta nuestra pantalla. En la misma página del producto podemos encontrar toda la información necesaria.  Si finalmente decidís comprar una controladora en el mismo sitio que yo, tendréis que comunicarle al vendedor el modelo de nuestra pantalla para que nos envíe el inversor y conector adecuado. Otra opción sería comunicarles directamente el número de lámparas y el tipo de conector que necesitamos. Yo opté por comunicarles el modelo además del número de lámparas y conector, que en mi caso es una lámpara y un conector LVDS de 30 pines.

Aviso para despistados: Si vais a comprar el adaptador de corriente, aseguraos de comprar el adecuado, yo casi compro el de enchufe UK. Menos mal que el vendedor me avisó…

En fin, tras una prudencial espera, llegó mi controladora. Así que me puse manos a la obra:

Transformación ilustrada de una pantalla de portátil a pantalla de PC.

Modelo de la pantalla en una pegatina

Modelo de la pantalla en el circuito impreso

La controladora junto con el conversor de corriente en la cajita, recién llegado de china.

Todo fuera de la caja

El circuito principal. La controladora de LCD

La botonera. Menú, + -, enter, power…

El conector LVDS de 30 pines

El nuevo inversor

La pantalla con el conector y el inversor antiguo.

La pantalla con la nueva controladora y el inversor nuevo.

Detalle del transformador

Primera toma de contacto. Probando la pantalla en modo texto.

Segunda prueba, ahora con gráficos más avanzados.

Llegados a este punto, se me plantea un problema. Todo funciona correctamente, la pantalla funciona, maneja bien la señal. Tiene incluso un ajuste automático que funciona realmente bien. El menú OSD va de lujo. Si, todo bien, pero… ¿Cómo carajo la pongo en pie? Pues eso, amigos míos, queda a cargo de vuestra imaginación. Yo opté por entrar en un bazar chino cerca de mi casa y dejar volar la imaginación. Esto fue lo que sucedió:

Alzando una pantalla de portátil para transformarla en una pantalla de PC. (Bricolaje informático)

Estos son los ingredientes. Tarrina de cds (y dos cds de plastico), brocha de madera, alambre, pistola de silicona, bridas y tornillería sobrante de otros proyectos.

Enganchando la brocha de una bisagra a otra.

El enganche se hace con un “punto de alambre”, ancestral arte aprendido en las bajas tierras de España (Concretamente en los invernaderos de El Ejido)

La tarrina de cds ya preparada para alojar la circuitería también se sujeta al palo de la brocha con unos cuantos puntos de alambre.

La pantalla con el soporte enganchado y sin circuitería (vista de lejos)

Parte trasera de la pantalla con el soporte y la circuitería

Plano abierto de la pantalla por su parte frontal, se puede apreciar la botonera ajustada en el palo de brocha

Detalle de la botonera (atornillada): Los ingredientes son cutres, pero en la ejecución me he esforzado :)

Parte trasera, la controladora sujeta a la esponja y el inversor al palo. Todo con bridas.

Resultado final. Puede servir para ver una peli mientras twitt…, err.., trabajas…. O puede servir para…

…monitorizar alguna que otra cosa (conky is your friend)

Y esto es todo. Para el próximo proyecto tengo pensado comprar una controladora de LCD con TDT y mando para transformar la pantalla de un portátil que se muere en una televisión. Eso, o transformar un trackpad en un ratón de PC por puerto USB o PS/2, según me de.

Ale, a pasarlo bien.