miércoles, 11 de febrero de 2015

Como leer Numeros Magicos con C (file signatures)

En esta ocasión aprenderemos a leer los números mágicos de un archivo,esto funciona para saber el tipo de archivo de un archivo,si así como suena ya que algunas ocasiones podemos deducir el tipo de archivo solo por su extension,por ejemplo podemos tener una imagen con este nombre:

imagen.png

Pero que tal si es formato .jpg o incluso un vídeo o audio,esto no es muy importante,para el usuario,pero a la hora de programar algo de bajo nivel o incluso de nivel usuario,por ejemplo una GUI si no seleccionamos el archivo correcto tendríamos un error,por ejemplo un reproductor de música o vídeo que le mandemos una imagen como ruta,esto causaría el cierre de la aplicación o en sistemas débiles el congelamiento de todo el sistema.

Bueno también en ocasiones tenemos archivos sin extensión,con el nombre por ejemplo:

archivo

En Unix si tiene permisos de ejecución lo mas seguro es que sea un programa,pero en ocasiones son archivos multimedia,como dije el usuario no se debe preocupar por esto ya que si tienen un explorador potente como por ejemplo Dolphin de KDE el lee los Magic Numbers y si tenemos una imagen sin extensión la detecta:



Como vemos detecta los archivos,pero si quisiéramos crear un explorador necesitaríamos usar números mágicos y no filtro por extensión.

En esta ocasión vamos a hacer este trabajo usando C,ya que lo vamos a usar en el sistema,si lo quisiéramos usar por ejemplo en Android lo podemos hacer en Java o usando el NDK.

Bien vamos a leer el archivo en modo binario y vamos a leer los primeros 8 bytes que serian 8 char,los primeros 8 char del archivo,luego los vamos a imprimir en pantalla,hay caracteres Hex que no se pueden imprimir asi que solo imprimiremos los que se puedan.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <ctype.h>

#define BYTES 8 // Numero de bytes

void readFile(char *file); // Funcion que leera el archivo,le pasamos el nombre

int main(int argv,char *argc[])
{
    if (argv>1)
        readFile(argc[1]); // Le pasamos el nombre del archivo que indiquemos en el argumento
    return 0;
}

void readFile(char *file)
{
    char nums[BYTES]; // Definimos un array de n bytes
    int i;
    FILE *fp = fopen(file,"rb"); // Abrimos el archivo en modo binario y lectura
    fread(nums,1,BYTES,fp); // Lemos los primeros n bytes
    for (i=0;i<BYTES;i++) // Recorremos el array
    {
        if (isgraph(nums[i]))  // Si se puede imprimir
            printf("Nums %c\n",nums[i]); // Lo imprimimos
    }

    fclose(fp);
    return;
}


Ahora vamos a usarlo:


Como vemos en el primer archivo use una imagen png con extensión jpg y no hubo problema al detectar el tipo de archivo,en el segundo use un .pdf y efectivamente es .pdf.

Algunos archivos no simplemente van a decir PNG o PDF,por ejemplo el numero magico de un mp3 es ID3:


Algunos números mágicos los podemos encontrar aquí:

https://en.wikipedia.org/wiki/List_of_file_signatures

Y eso nos sirve para definir un array de comprobacion,por ejemplo para el mp3,seria en HEX:

49 44 33

Que solo serian 3 bytes,3 char:

Veamos:


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <stdio.h>
#include <ctype.h>

#define BYTES 8 // Numero de bytes

typedef enum _boolean {FALSE,TRUE} boolean; // Tipo boolean

void readFile(char *file); // Funcion que leera el archivo,le pasamos el nombre

boolean isMp3(char *file); // Funcion para saber si es mp3

int main(int argv,char *argc[])
{
    if (argv>1){

        readFile(argc[1]); // Le pasamos el nombre del archivo que indiquemos en el argumento
        if(isMp3(argc[1])) // Vemos si es mp3
            printf("%s es mp3\n",argc[1]);
        else
            printf("%s no es mp3\n",argc[1]);
    }
    
    return 0;
}

boolean isMp3(char *file)
{
    const int lenNum = 3; // Numero de Bytes
    char nums[lenNum]; // array que contendra los numero magicos
    char id[] = {0x49,0x44,0x33}; // Numeros magicos del mp3
    int i;
    boolean flag = TRUE; // Este sera el valor de retorno
    FILE *fp = fopen(file,"rb"); // Abrimos el archivo
    fread(nums,1,lenNum,fp); // Lemos los primeros 3 bytes
    for (i=0;i<lenNum;i++) // Recorremos el array
    {
        if(nums[i] != id[i]) // Si no son iguales 
        {
             flag = FALSE; // Cambiamos el valor de retorno
             break; // rompemos el bucle
        }
    }
    fclose(fp); // Cerramos el archivo
    return flag; // Regresamos el valor
}

void readFile(char *file)
{
    char nums[BYTES]; // Definimos un array de n bytes
    int i;
    FILE *fp = fopen(file,"rb"); // Abrimos el archivo en modo binario y lectura
    fread(nums,1,BYTES,fp); // Lemos los primeros n bytes
    for (i=0;i<BYTES;i++) // Recorremos el array
    {
        if (isgraph(nums[i]))  // Si se puede imprimir
            printf("Nums %c\n",nums[i]); // Lo imprimimos
    }

    fclose(fp);
    return;
}

Con esa funcion que definimos logramos saber si un archivo es mp3,igual lo podemos hacer con un mp4 solo que wikipedia nos dice que hay dos posibilidades,veamos:


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <stdio.h>
#include <ctype.h>

#define BYTES 8 // Numero de bytes

typedef enum _boolean {FALSE,TRUE} boolean; // Tipo boolean

void readFile(char *file); // Funcion que leera el archivo,le pasamos el nombre

boolean isMp3(char *file); // Funcion para saber si es mp3

boolean isMp4(char *file);

int main(int argv,char *argc[])
{
    if (argv>1){

        readFile(argc[1]); // Le pasamos el nombre del archivo que indiquemos en el argumento
        if(isMp4(argc[1])) // Vemos si es mp3
            printf("%s es mp4\n",argc[1]);
        else
            printf("%s no es mp4\n",argc[1]);
    }
    
    return 0;
}

boolean isMp4(char *file)
{
    const int lenNum = 4; // Numero de Bytes
    char nums[lenNum]; // array que contendra los numero magicos
    char id1[] = {0x22,0x67,0x70,0x35}; // Numeros magicos del mp4
    char id2[] = {0x66,0x74,0x79,0x70}; // Numeros magicos del mp4(otra posibilidad)
    int i;
    boolean flag = TRUE; // Este sera el valor de retorno
    FILE *fp = fopen(file,"rb"); // Abrimos el archivo
    fread(nums,1,lenNum,fp); // Lemos los primeros 4 bytes para probar el primer id
    for (i=0;i<lenNum;i++) // Recorremos el array
    {
        if(nums[i] != id1[i]) // Si no son iguales 
        {
             flag = FALSE; // Cambiamos el valor de retorno
             break; // rompemos el bucle
        }
    }
    if (flag == FALSE) // Si la bandera es FALSE probamos el siguiente id
    {
        flag = TRUE; // Cambiamos el flag a TRUE
        fseek( fp, 4, SEEK_SET ); // Nos movemos 4 bytes a la derecha ya que no nos sirven estos : 00 00 00 nn
        fread(nums,1,lenNum,fp); // Lemos los segundos 4 bytes
        for (i=0;i<lenNum;i++) // Recorremos el array
        {
            if(nums[i] != id2[i]) // Si no son iguales 
            {
                flag = FALSE; // Cambiamos el valor de retorno
                break; // rompemos el bucle
            }
        }
    }
    fclose(fp); // Cerramos el archivo
    return flag; // Regresamos el valor
}

boolean isMp3(char *file)
{
    const int lenNum = 3; // Numero de Bytes
    char nums[lenNum]; // array que contendra los numero magicos
    char id[] = {0x49,0x44,0x33}; // Numeros magicos del mp3
    int i;
    boolean flag = TRUE; // Este sera el valor de retorno
    FILE *fp = fopen(file,"rb"); // Abrimos el archivo
    fread(nums,1,lenNum,fp); // Lemos los primeros 3 bytes
    for (i=0;i<lenNum;i++) // Recorremos el array
    {
        if(nums[i] != id[i]) // Si no son iguales 
        {
             flag = FALSE; // Cambiamos el valor de retorno
             break; // rompemos el bucle
        }
    }
    fclose(fp); // Cerramos el archivo
    return flag; // Regresamos el valor
}

void readFile(char *file)
{
    char nums[BYTES]; // Definimos un array de n bytes
    int i;
    FILE *fp = fopen(file,"rb"); // Abrimos el archivo en modo binario y lectura
    fread(nums,1,BYTES,fp); // Lemos los primeros n bytes
    for (i=0;i<BYTES;i++) // Recorremos el array
    {
        if (isgraph(nums[i]))  // Si se puede imprimir
            printf("Nums %c\n",nums[i]); // Lo imprimimos
    }

    fclose(fp);
    return;
}

Así podemos crear un biblioteca con todas las extensiones para usarlas en un programa.

Eso es todo :D

No hay comentarios.:

Publicar un comentario

Los comentarios serán revisados antes de ser publicados.