Random IRC quote :      <ggonzalez> esque los que quemamos iglesias estamos marginados

En ocasiones… veo patrones…

Un día mientras analizaba un firmware me puse a hacer un pequeño script en python para convertir un archivo cualquiera a una imagen y así poder analizar “visualmente” un fichero para ver la entropía del mismo así como las diferentes partes de un archivo. Por ejemplo, visualizando un firmware, se puede ver fácilmente donde está el código del bootloader y donde está el código comprimido y/o cifrado, como en la siguiente imagen:

Firmware de un router Zyxel sin desempacar

En esta imagen podemos ver como en la parte inicial donde se ven más “rayitas” y “coloritos” está el código del bootloader, una pequeña sección de datos con mucha entropía (código comprimido),  una sección llena de carácteres FFh (la parte en negro) y el código real del firmware, comprimido (la mayor parte del archivo).

En el script lo único que hago es leer cada byte del archivo dado como entrada y convertir grupos de 3 bytes en colores RGB, es decir, el primer byte será el código para el “Red”, el segundo byte para el “Green”, el tercer byte para el “Blue” y así sucesivamente.

También es útil, por ejemplo, para ver ejecutables y sus secciones de recursos, como en la siguiente imagen del programa IceSword donde se ve perfectamente la espada:

Imagen del IceSword

Posibles usos

Uno de los usos interesantes, además de para ver ficheros y buscar patrones como si fuéramos enfermos mentales (soy un polvo borracho según mi madre :( ) puede ser, por ejemplo, para codificar un fichero en una imagen, es decir, para esteganografía. El contenido del fichero, al completo, se codifica como una imagen ocultando (de un modo un poco txapuzas) la verdadera naturaleza del fichero a envíar.

Otro uso más, para el que ya se ha utilizado esto es para comprobar si un generador de números aleatorios es realmente aleatorio o no buscando patrones en la imagen que salga. Es decir, se pone a funcionar el generador de números aleatorios indicándole que nos devuelva “0″ o “1″ y nosotros dibujamos un pixel negro o un pixel blanco en función de que nos ha devuelto. Un ejemplo de uso real se muestra aquí, en el que se demuestra la debilidad de la función Rand() de PHP de versiones ya bastante viejas:

Imagen de un pseudo generador de números aleatorios que funciona bien

Imagen de un PRNG que es un "Fail!" como una catedral

El código de este pequeño script en Python es el siguiente:

#!/usr/bin/python

import os
import sys

from math import sqrt
from PIL import Image

def main(bin, filename):
    buf = open(bin, "rb").read()
    size = int(sqrt(len(buf))*0.5)

    img = Image.new("RGB", (size, size), "red")
    putpixel = img.putpixel
    i = 0

    for y in range(int(size)):
        for x in range(int(size)):
            if i > len(buf) or len(buf)-i <= 3:
                break

            value = (ord(buf[i:i+1]), ord(buf[i+1:i+2]), ord(buf[i+3:i+4]))
            i += 3
            putpixel((x, y), value)

    img.save(filename)
    os.system("start " + filename)

def usage():
    print "Usage:", sys.argv[0], " "

if __name__ == "__main__":
    if len(sys.argv) != 3:
        usage()
    else:
        main(sys.argv[1], sys.argv[2])
 

17 Comentarios para “En ocasiones… veo patrones…”

  1. NCR
    Comment por NCR | 06/30/10 at 10:20 am

    Muy bueno! me gusto! Una vuelta de tuerca interesante!

  2. Comment por vierito5 | 06/30/10 at 10:35 am

    Muy bueno! Un PRNG malo te desmonta por completo un sistema criptográfico.

    Es una pifia parecida a usar un el moco ECB en los cifrados. No lleva realimentación de los resultados entre bloques y en un análisis como este se podrían ver perfectamente los diferentes bloques.

    Hoy he visto este enlace y me ha parecido curioso:
    http://smokedchicken.org/2010/06/ida-entropy-plugin.html

  3. Comment por Dreg | 06/30/10 at 1:14 pm

    Ostia, molan estas movidas :-)

  4. Comment por Ruben | 06/30/10 at 3:36 pm

    Muy chulo!

    El visualizar este tipo de datos ayuda mucho, no recuerdo ahora quien era el que lo hizo pero habia una herramienta para visualizar el comportamiento del heap de aplicaciones concretas para desarrollo de exploits.

    El de http://www.dumpanalysis.org/ también tiene una cosas del estilo para visualizar dumpeados de memoria.

  5. Comment por neofito | 06/30/10 at 3:40 pm

    Muy interesante, y gracias por compartir el script.

    Saludos

    PD: Por cierto Ruben, te refieres a esto, ¿verdad?

  6. miK
    Comment por miK | 06/30/10 at 3:57 pm

    Qué curioso :-O

  7. Comment por matalaz | 06/30/10 at 4:04 pm

    @Ruben

    Dices esta herramienta: http://www.coresecurity.com/content/open-source-projects#HeapDraw

    Es de Gerardo Richarte, como no :)

  8. Comment por tmeto | 07/01/10 at 2:57 am

    Pues esta util el tema este……pero como te de por mirar mucho rato una cosa de estas te llaman al telefono y 7 dias despues mueres que lo he visto yo en una peli…

    Gracias por el script!

    pd: soy el unico que en la primera imagen ve a cristo!?!?!?!

  9. Comment por Ruben | 07/01/10 at 7:25 am

    xDDDDDDDDDDDDDDDDDDDDDDDD
    haztelo mirar “tmeto”

  10. Comment por Newlog | 07/01/10 at 7:55 am

    Muy bueno lo de los números aleatorios!

  11. Comment por kerrigan29a | 07/01/10 at 11:50 am

    Una cosa que he visto en el código. Porque –> size = int(sqrt(len(buf)) * 0.5) size += 10 size = int(sqrt(len(buf) / 3)) <–
    Es decir, 166128/3 (porque agrupas los bytes en tripletas) y luego la raíz cuadrada para conseguir el lado del cuadrado

    Como he dicho, puede que no lo haya entendido yo. Si es así perdón por las molestias ( agradecería que me lo explicases).

  12. Comment por matalaz | 07/02/10 at 2:21 am

    @kerrigan29a

    Sí, tienes razón, el código no estaba bien. Es lo que tiene escribirlo al vuelo :)
    Ya está corregido, muchas gracias!

  13. Comment por pancake | 07/02/10 at 7:34 am

    Esto radare lo hace dsd hace 2 anyos polomenos y en interactivo con el zoom mode (Vz) o con “!rsc Display” (tira de imagemagick).

    Este tipo de analisis tb es util para detectar tamaños de claves ciclicas de cifrado, alineamientos en strides en ficheros de video (mjpeg) etc.. lo usé para recuperar un video de una camara de seguridad con un disco jodido hace tiempo.

  14. Comment por cristhian | 07/05/10 at 5:52 am

    Yo siempre he tenido la idea de que esto de los patrones se podía utlilizar como algoritmo de compresión.
    Os imagináis poner a un programa a buscar patrones, no ya bidimensionales, como en las imagenes, sino n-dimensionales, y que la compresión resultante fuese un número pequeño de funciones a partir de las cuales se pudiera obtener el archivo original?
    Os imagináis poder comprimir un blu-ray en un archivo de 10 megas? Estaría guay eh!

  15. Comment por ps-el | 05/08/11 at 1:47 am

    Después de leer este post me dió por utilizar el script para generar ficheros como imagenes y luego hacer otro que hiciera el trabajo inverso, desde la imagen recuperará el fichero original. Haciendo esto me di cuenta de que el script tiene un pequeño fallo.

    > ord(buf[i:i+1]), ord(buf[i+1:i+2]), ord(buf[i+3:i+4])

    debería ser

    > ord(buf[i:i+1]), ord(buf[i+1:i+2]), ord(buf[i+2:i+3])

    Se esta saltando 1 de cada 3 bytes.

  16. Comment por ps -el | 05/20/11 at 9:11 am

    Os adjunto el código que convierte de binario a imagen (que es prácitamente calcado al del post) y el que convierte de imagen a binario otra vez. No son lo mejor del mundo pero funciona con bmp y png

    bin2jpg.py

    #!/usr/bin/python
    import sys

    from math import sqrt
    from PIL import Image

    def main(bin, filename):
    buf = open(bin, “rb”).read()
    size = int(sqrt(len(buf)/3))
    leakInSize = len(buf) – (size * size * 3)

    leakPoints = (leakInSize/ 3)
    if leakInSize % 3 != 0: leakPoints += 1

    size_y = 0
    if leakPoints % size != 0: size_y = (leakPoints / size) + 1
    if leakPoints % size == 0: size_y = (leakPoints / size)
    size_y += size

    img = Image.new(“RGB”, (size, size_y), “red”)
    putpixel = img.putpixel
    i = 0

    for y in range(int(size_y)):
    for x in range(int(size)):
    if i >= len(buf):
    break
    if len(buf) -i < 2 :
    value = (ord(buf[i:i+1]), 3 ,3)
    elif len(buf) -i < 3 :
    value = (ord(buf[i:i+1]), ord(buf[i+1:i+2]),3)
    else:
    value = (ord(buf[i:i+1]), ord(buf[i+1:i+2]), ord(buf[i+2:i+3]))
    i += 3
    putpixel((x, y), value)
    img.save(filename)

    def usage():
    print "Usage:", sys.argv[0], " file_origen file_destination"

    if __name__ == "__main__":
    if len(sys.argv) != 3:
    usage()
    else:
    main(sys.argv[1], sys.argv[2])

    jpg2bin

    #!/usr/bin/python

    import os
    import sys

    from PIL import Image
    import binascii

    def main(jpg, bin):
    img = Image.open(jpg)
    (size_x, size_y) = img.size

    file = open(bin, “wb”)
    res = “”
    num_reds = 0
    for y in range (size_y):
    for x in range (size_x):
    pixel = img.getpixel((x,y))
    if pixel == (255,0,0) and (y == size_y -1):
    num_reds+=1
    else:
    num_reds = 0
    if num_reds > 2: break
    value = ‘%02x%02x%02x’%( pixel[0],pixel[1],pixel[2])
    res += value
    binaryData = res[:-12]
    if binaryData[-2:] == “03″: binaryData = binaryData[:-2]
    if binaryData[-2:] == “03″: binaryData = binaryData[:-2]
    file.write (binascii.a2b_hex(binaryData))
    file.close()

    def usage():
    print “Usage:”, sys.argv[0], ” file_origen file_destination”

    if __name__ == “__main__”:
    if len(sys.argv) != 3:
    usage()
    else:
    main(sys.argv[1], sys.argv[2])

  17. Comment por ps -el | 05/20/11 at 9:26 am

    En la vista previa estaba bien tabulado, :(

Se han cerrado los comentarios