10 septiembre 2012

Forense de SQLite VII - Ejercicio resuelto

He aquí mis respuestas a las preguntas propuestas por Alejandro en el post de "Práctica Forense de SQLite".

Software utilizado: HxD Hex Editor & Disk Editor.

  1. ¿Qué tamaño tienen las páginas de la base de datos?El tamaño de página se define en la cabecera del fichero, tal y como explica Alejandro en "Forense de SQLite II - Cabecera", se encuentra en el offset 16 y tiene un tamaño de 2 bytes.

    Tamaño de las páginas.
    (04 00)h = 1024d → 1024 bytes.
  2. ¿Qué versión del motor de base de datos se ha usado?La versión de la base de datos usada se encuentra almacenada en el offset 96 y tiene un tamaño de 4 bytes.

    Versión del motor de la base de datos.
    (00 2D E2 18)h = 3007000d → Versión 3.7.0
  3. ¿Cuántas páginas libres tiene?El número de páginas libres se almacena, esta vez, en el offset 36, usando 4 bytes de espacio.

    Número de páginas libres.
    Esta página libre es, en este caso, la llamada página troncal libre y la podemos localizar dentro del archivo gracias a un par de parejas más de bytes de la cabecera del archivo, concretamente los inmediatamente a continuación del offset 32,

    Posición de la página libre.
    y a la fórmula siguiente: (NºdePágina - 1) x TamañoDePágina → (4 - 1) x 1024 = 3072.
    Así pues la página libre comenzará en la posición 3072.

    Comienzo de la página troncal libre.
    Indicándonos, además, mediante sus 4 primeros bytes (que son los que han de apuntar a otras páginas libres, que no existen más, algo obvio puesto que como hemos visto al principio de la pregunta sólo existe 1 página libre.
  4. ¿Cuáles son las hojas de páginas de tabla b-tree?Las páginas de tablas b-tree son especificadas por un valor de tamaño 1 byte en la posición primera de la página (offset relativo 0) y pueden ser de los siguientes tipos:
    • Página interior de índice. ( 2d → 02h  ).
    • Página interior de tabla. ( 5d → 05h  ).
    • Hoja de página de un índice. ( 10d → 0Ah  ).
    • Hoja de página de una tabla. ( 13d → 0Dh  ).

    La primera página b-tree empieza en la posición 100, donde vemos un 0D, indicándonos que es una hoja de página de una tabla. Existe una hoja más de este tipo como podemos apreciar en la posición 1024.

    Páginas B-Tree.
    Así pues tenemos 4 páginas en el documento:
    • Página 1: de 0 a 1023.
      Contiene la cabecera del archivo así como la primera página de tabla b-tree (a partir del offset 100).
    • Página 2: de 1024 a 2047.
      Segunda página b-tree.
    • Página 3: de 2048 a 3071.
      Página de overflow o desbordamiento.
    • Página 4: de 3072 a 4095 .
      Página libre troncal.

  5. ¿Hay hojas overflow?Las hojas overflow, como ya ha sido explicado, se crean al sobrepasar el tamaño de página especificado en la cabecera (en este caso, 1024 bytes). Si se necesitan más páginas overflow para almacenar la información, éstas tendrán en sus 4 primeros bytes (offset relativo 0) la posición de las siguientes hasta llegar a la última (que contendrá en esos 4 bytes el valor 00 00 00 00).

    Por tanto, tan sólo tenemos una hoja de desbordamiento (que va desde 2048 a 3071) como podemos ver a continuación:

    Página de desbordamiento.
    Y generada a causa del contenido de un registro demasiado largo:

    Continuación de los datos en la página de desbordamiento.
    Se nos señala que continuará en la página 3 (de desbordamiento).
  6. ¿Qué registro se ha borrado?Cuando hay celdas/registros borrados se producen bloques libres. Por tanto, analizaremos la cabecera de la página b-tree, que es donde se nos indica si existen ( que existirán ;) ), la cual comienza en 1024.

    Descripción de cabecera b-tree.
    • 0D: Indica que es una hoja de tabla.
    • 00 FC: Señala que existen bloques libres y que empiezan por la dirección relativa a la tabla 252 (00 FC → 252) ó, lo que es lo mismo, la dirección absoluta 1276.
    • 00 06: Especifica que existen 6 registros en la página.
    • 00 8E: La posición(142, relativa a la tabla) de la primera celda.
    • 00: No existen bytes fragmentados.
    • 03 E0 .. 00 8E: Las posiciones de cada uno de los 6 registros que existen ( 992, 810, 755, 700, 663 y 142 ó 2016, 1834, 1779, 1724, 1687 y 1166 respectivamente). Como vemos están de mayor a menos, esto es debido a que los datos se van almacenando desde el final de la página hacia arriba, tomando así los valores más antiguos las direcciones más altas (más abajo de la página) y viceversa para los valores más actuales.
    • 00 8E y 00 20: Puesto que ya hemos encontrado los 6 registros que indicaba el encabezado, el hecho que haya 2 grupos de 2 bytes más es indicativo de que se han borrado 2 registros.
  7. ¿Qué otros datos han sido modificados?Si examinamos los 6 registros encontrados vemos que hay un gran espacio disponible entre el final del primer registro y el principio del siguiente. Este espacio va desde el offset 1276 (tal y como hemos visto en la pregunta anterior) hasta el 1686 y podemos ver algo curioso.

    Registros similares.
    Vemos que hay dos registros similares. La única diferencia apreciable es que el final cambia.
    Así pues, parece que existía un registro muy similar al que empieza en la posición 1687, por tanto, podemos deducir que al registro inicial se le hizo una modificación mediante una sentencia UPDATE.
  8. ¿Cuánto espacio libre total hay en el fichero?Tenemos:
    • Por un lado, los 411 bytes del bloque libre que hemos comentado en la pregunta anterior.
    • Por otro, 66 bytes, que son los bytes sobrantes de la página de desbordamiento (página 3).

    Bytes no utilizados de la página de desbordamiento.

    • Y por último, los bytes sobrantes de la página libre troncal (página 4), que son todos menos los 8 primeros, los cuales servirían para señalar a otras posibles páginas troncales (primeros 4 bytes: 00 00 00 00) y para indicar el número de elementos restantes de la hoja actual (siguientes 4 bytes: también 00 00 00 00).

     Total: 411 + 66 + (1024-8) = 1493 bytes libres.


  9. Comentarios adicionales.Linkeo las sentencias SQL que usó para crear el fichero y que me mandó Alejandro una vez que estaba prácticamente terminado el artículo, por si queréis trastear un reto con ellas.

    Muchas gracias a Alejandro por los consejos y felicidades a todos por un blog tan exitoso.
------------------------------------------------------------------------

Contribución gracias a @MaranonD

Todos las entradas de la serie:

3 comments :

Alejandro Ramos dijo...

Otra solución de palako muy completa y que está en el comentario de la entrada anterior:
https://docs.google.com/document/d/1SX3D-Ifflsm48hcairkxNTHtpZ4memSfmYWO0W-_otA/edit?pli=1

Marcos Agüero Rodríguez dijo...

Un detalle aleatorio sobre este documento. En el punto 6 dice:


'En la pagina 2 parece haber una celda borrada en el offset 0x20. Los primeros bytes (dedicados al tamaño del payload, identificador de fila y tamaño de la cabecera del payload estan a 0



Esto es debido a que cuando se borra un registro en sqlite ese trozo se marca como "fragmento". En el offset 7 de la cabecera de la pagina BTree tenemos el número de fragmentos (espacios libres de más de 4 bytes entre celdas). En cada fragmento, en los primeros 4 bytes se escribe el tamaño de ese fragmento en la página. Volviendo a la solucion:

$ xxd -s `echo -e "ibase=16\n400+20"|bc` -l 10 -p personas.sqlite0000006e171529199019



0x0000006e = 110, lo que significa que ese fragmento ocupa 110 bytes, por eso no tiene sentido al intentar interpretarlo como tamaño del payload.

palako dijo...

Gracias por aclararlo. Mientras escribia el doc sabia que tenia que irme a la spec a ver que era, porque obviamente se estaba redefiniendo el campo para algo mas, pero lo deje anotado como "no tiene sentido" y se me olvido.