UBI file system reolink

Nella terza parte abbiamo perlustrato i dispositivi hardware di Reolink RLC810-A, soffermandoci sulla memoria NAND. Continuiamo con la quarta parte dell’esplorazione di un firmware di una telecamera IP, introducendo il concetto di file system. Andremo ad esplorare le ragioni tecniche per la scelta di UbiFS, un file system utilizzato specialmente per una categoria di memorie di messa e andremo a scompattare la parte di ubifs utilizzando il tool ubi-extract.

Introduzione ai File System

Una volta introdotta la fase di booting, dobbiamo capire quali componenti rilevati da Binwalk possono essere utili alla nostra indagine e quali no. Dalla prima parte, abbiamo visto che Binwalk mostra una serie di risultati. In particolar modo, siamo interessati all’immagine del kernel e l’immagine del file system.

Empiricamente, abbiamo potuto osservare che all’interno dell’immagine che Reolink dispone ai suoi clienti c’è una parte dedicata all’UBI file system. È interessante comprendere le peculiarità di questo file system per andare a fondo sulle scelte progettuali.

Incominciamo spiegando che cosa è un file system. Tutte le informazioni elaborate da un dispositivo elettronico possono essere divisi in due categorie: le informazioni volatili (allo spegnimento del dispositivo, vengono perse) e le informazioni non volatili. A seconda della memoria di massa utilizzata, le informazioni non volatili (che rimangono anche in assenza di corrente elettrica) possono essere memorizzate in maniera sequenziale, ad accesso casuale; tramite laser oppure testine. Schede SD, hard disk, dischi magnetici, nastri, CD, DVD. Insomma, esistono moltissimi molteplici tecnologie per rendere un dato fisso.

Ad alto livello, però troviamo una serie di astrazioni comuni tra tutti i tipi di memoria: il concetto di file, le operazioni di scrittura, lettura, creazione di nuovi file e molte altre operazioni. Tra questa serie di astrazioni e i controllori di basso livello che si occupano solo di fare operazioni di input/output, c’è un particolare strato architetturale chiamato file system che organizza le informazioni in modo logico, strutturandole in file e cartelle.

Dal punto di vista concettuale, tra tutti i dispositivi, non c’è distinzione tra file. Un file all’interno di un computer ha la stessa struttura logica di un altro file posizionato però all’interno di un cellulare. Questo vale sempre se entrambi i dispositivi sfruttano lo stesso file system. Non importa però dove i dispositivi scrivano il contenuto dei file: se su due supporti diversi (computer su hard-disk e cellulare su microSD), oppure sullo stesso tipo. L’astrazione che fornisce un file system è molto potente, dato che nessun tipo di processo dovrà mai interfacciarsi con i controllori di basso livello.

I compiti di un file system sono innumerevoli e vanno dalla gestione dello spazio fisico (come alloco lo spazio per un file: se in modo contiguo o frammentato…) alla gestione dei metadati (nome del file, dimensione, permessi e attributi vari). Il mantenimento dell’integrità del contenuto, la sicurezza dei file, le operazioni più banali di scrittura e lettura: tutto passa dal file system.

Scegliere un file system influisce parecchio sulle prestazioni del sistema operativo e sulla gestione dello spazio libero. È molto importante azzeccare la scelta del file system rispetto alle esigenze (tipo di supporto in cui si sta scrivendo, limiti progettuali) perché altrimenti si rischia di avere un sistema operativo poco performante o addirittura di danneggiare il supporto di memorizzazione.

Per esempio, basti pensare alla caratteristica unica dell’APFS (Apple File System) che, quando copia una cartella, non effettua l’operazione sul contenuto totale. Piuttosto crea un collegamento alla cartella originale e memorizza sul supporto solo le differenze che ci sono tra la cartella originale e quella nuova copiata. Tale tecnica è chiamata Delta extents e permette di risparmiare parecchio spazio!

MicroSD e memoria NAND flash

Un altro chiaro esempio in cui la scelta di un file system è critica, è dato da questo caso: abbiamo una telecamera IP che esegue un sistema operativo da una microSD. Evidenziamone le caratteristiche tecnologiche. Rispetto a supporti come dischi magnetici, le microSD si basano sulla tecnologia NAND FLASH, un supporto di memoria elettornica che può essere cancellato e riprogrammato (utilizzando a basso livello porte NAND che mantengono le informazioni).

Per capire meglio però i vantaggi di file system, dobbiamo introdurre un paio di concetti relativi al funzionamento delle memoria flash.

Ogni memoria FLASH utilizza un processo chiamato “Programma/Cancella” (program/erase) per archiviare i dati. Senza entrare troppo nell’elettronica, le limitazioni di questa memoria sono tre principali:

  • cancellazione a blocchi: posso agire con operazioni di lettura e scrittura in modo quasi chirurgico, tuttavia per cancellare un intero file però sono obbligato a cancellare l’intero blocco. Se all’interno di un blocco ci fossero N file, devo fare molteplici operazioni per cancellare anche un solo file.
  • limitati cicli di scrittura: ogni memoria flash ha un numero limitato di cicli di scrittura, di solito questo valore si avvicina a 100.000. Sopra questo valore medio, c’è il rischio che le operazioni di scrittura non siano così efficaci come dovrebbero essere. I dati che sto scrivendo, seppur corretti in un primo momento, possono diventare errati nel momento della scrittura sulla memoria.
  • disturbo dei dati contigui: il metodo per leggere la memoria flash NAND può far sì che celle vicine al blocco che sto leggendo cambino stato (da 0 a 1 o viceversa). Per evitare questo disturbo (anche noto come disturbo della lettura), è possibile adottare alcune azioni lato file system, come tenere traccia di quante letture ho effettuato.

Questi problemi sono comuni a tutti i supporti che utilizzano la tecnologia delle memoria FLASH. In aggiunta, dal punto di vista fisico, una microSD ha dimensioni molto piccole (15×11×1 millimetri). Ciò vuol dire che un cortocircuito o una temperatura elevata potrebbe avere conseguenze disastrose dato che si sta agendo su una superficie di contatto molto piccola, rispetto ad un SSD.

UBI File system

A causa delle peculiarità della tecnologia flash, è meglio preferire un file system specifico che possa dialogare con il controllore per la correzione degli errori e per il livellamento dell’usura. Il file system UBI è un perfetto esempio di filesystem che gestisce con eleganza tutti i problemi sopracitati.

Sviluppato da Nokia e dall’università di Szeged agli inizi del 2008, UBIFS è un file system progettato specificatamente per dispositivi di memoria flash non gestiti. Ha principalmente due scopi:

  • tracciare i blocchi danneggiati della memoria flash in modo da non poter più riscrivere in e/o leggere da un blocco danneggiato ed evitare così la propagazione degli errori;

  • fornire il livellamento dell’usura, non concentrando tutte le operazioni in una sola parte di memoria fisica, bensì distribuendo le cancellazioni e le scritture sull’intero dispositivo flash.

Flash Translation Layer

Per essere più corretti tecnicamente, non tutti i file system agiscono allo stesso livello. Tra la parte più vicina all’hardware e la parte ad alto livello, esistono una serie di file system secondari che gestiscono compiti specifici come l’organizzazione dei blocchi, la suddivisione dei blocchi all’interno della memoria. UBIFS utilizza e si basa su UBI (Unsorted Block Images), tecnologia che si rapporta con la memoria in modo nudo e crudo.

Alcuni dispositivi basati su schede FLASH hanno una sorta di livello di memoria virtuale chiamata “Flash Translation Layer” che consente di nascondere i dettagli dell’I/O in modo che un file system di livello superiore non debba preoccuparsene. Originariamente è stato inventato con lo scopo di mantenere la stessa interfaccia per consentire la sostituzione di qualsiasi disco rigido con un disco a stato solido.

Nel caso dell’UBIFS, le ottimizzazioni vengono vanificate dal Flash Translation Layer che riorganizza in modo autonomo tutte le informazioni da scrivere e leggere. Per questo non tutte le schede microSD sono compatibili con UbiFS. Ed ecco anche il perché UbiFS è considerato un file system unico nel suo genere.

Caratteristiche principali di UbiFS

Rispetto ad altri file system, UBIFS si differenzia per:

  • la velocità del mouting del dispositivo: UBIfs riesce a istanziare una panoramica del file system senza eseguire la scansione di tutto il supporto. Tempo di mouting consiste in qualche millisecondo e non dipende dalla dimensione della memoria flash.

  • la scalabilità: il consumo di memoria e la velocità delle letture/scritture non dipendono dalla dimensione della memoria flash: tempo costante per ogni operazione!;

  • supporto write/back: UbiFS utilizza un sistema di buffering e cache per scrivere poche volte e solo quando è necessario. Diminuisce il tasso di errori e permette di montare il file system molto velocemente.

  • integrità: UbiFS controlla l’instestazione dei file attraverso un checksum sui metadata, opzionalmente si può abilitare questa caratteristica anche sul contenuto del file;

Per i dispositivi embedded, UbiFS rappresenta un’ottima soluzione che consente di avere il giusto compromesso tra il reale utilizzo delle microSD e lo sfruttamento del sistema di buffering. Perfetto sia per durabilità che per efficienza.

Un’altra caratteristica di UbiFS che piace molto agli sviluppatori e progettisti di dispositivi embedded è la tolleranza alle interruzioni di corrente. Se consideriamo il contesto in cui i dispositivi embedded sono posizionati (industriali ma anche domestiche), capita che i dispositivi possano avere delle interruzioni di corrente. Le interruzioni di corrente sono abbastanza critiche da gestire, perché se un’operazione viene interrotta, potrebbe corrompere le strutture dati del file system, danneggiando una parte dei file. Dal momento che le operazioni devono essere il più atomiche possibili, gli sviluppatori di UbiFS hanno sviluppato alcuni test per trovare eventuali problemi sulle operazioni che se interrotte possono dare problemi.

Per dettagli relativi invece al funzionamento di UbiFS, consiglio di leggere il paper "Abstract Specification of the UBIFS File System for Flash Memory" che riassume ad alto livello come funziona il file system.

Estrarre l’immagine UBI

Ora che abbiamo introdotto la tecnologia di UbiFS, possiamo incominciare la parte più pratica. Binwalk utilizza uno strumento chiamato ubi_image_extract per estrarre i files da immagini UBI o UBIFS. Ubi_image_extract fa parte di un insieme di strumenti sviluppati in Python per manipolare e visualizzare il contenuto di immagini UbiFS.

Dal punto di vista del funzionamento, ubi_image_extractnon non fa altro che montare virtualmente l’immagine, verificando tutti i blocchi del file. Una volta che ha montato l’immagine, copia il contenuto in una cartella a scelta.

È richiesta l’installazione di tutti i componenti di UbiReader per proseguire con l’analisi del firmware. UbiReader è una serie di script realizzati con Python per poter leggere e ricavare informazioni da un immagine UBI o UBIFS.

Per installarlo è sufficiente avere come prerequisito i pacchetti liblzo2-dev e python-lzo. Successivamente:

git clone https://github.com/jrspruitt/ubi_reader
cd ubi_reader
python setup.py install

Il comando per estrarre il contenuto dell’UBI image è il seguente:

binwalk -e firmware_rlc_810_a.pak --run-as=root

La flag --run-as=root serve a binwalk per eseguire ubi_image_extract come root: questo è stato implementato perché alcuni file di ubi sono speciali e richiedono di essere creati tramite root.

Binwalk creerà una nuova cartella chiamata _firmware_rlc_810_a.pak.extracted che conterrà il contenuto estratto dall’immagine analizzata. All’interno della cartella la maggior parte delle volte ci saranno molti tipi di file, quello che però a noi interessa sono il contenuto dei file system. Abitualmente, questi file sono inseriti in una cartella con il pattern nomefilesystem-root dove nomefilesystem è il nome abbreviato del file system. In caso di dubbi, potete sempre provare ad aprire tutte le cartelle e vedere dove sono situati i file di interesse.

/ubifs-root# ls
261434259  468222262

Due cartelle nominate con dei numeri? Per capirci meglio, possiamo utilizzare un altro comodo strumento presente all’interno di UbiReader chiamato ubireader_display_info che stampa le informazioni relative ad un immagine Ubi.

/# ubireader_display_info image.ubi 

UBI File
---------------------
        Min I/O: 2048
        LEB Size: 126976
        PEB Size: 131072
        Total Block Count: 316
        Data Block Count: 312
        Layout Block Count: 4
        Internal Volume Block Count: 0
        Unknown Block Count: 0
        First UBI PEB Number: 0

        Image: 468222262
        ---------------------
                Image Sequence Num: 468222262
                Volume Name: rootfs
                PEB Range: 0 - 198

                Volume: rootfs
                ---------------------
                        Vol ID: 0
                        Name: rootfs
                        Block Count: 197

        Image: 261434259
        ---------------------
                Image Sequence Num: 261434259
                Volume Name: app
                PEB Range: 199 - 315

                Volume: app
                ---------------------
                        Vol ID: 0
                        Name: app
                        Block Count: 115

Ottimo! Le prime informazioni confermano le ipotesi nell’articolo riguardo lo schema della memoria flash di 128 Mb. All’interno dell’immagine Ubi analizzata troviamo due sotto-immagini, una per il rootfs (contenente il sistema operativo) e app (contenente i file applicativi, realizzati ad-hoc per Reolink). Suppongo che la divisione sia stata eseguita per rendere più modulare il sistema operativo e per non cambiare tutto il sistema operativo ad ogni aggiornamento, se possibile. Brillante idea! In questo caso, possiamo ipotizzare che sia il componente applicativo che il file system root avessero bisogno di un aggiornamento.

Lo strumento risponde anche alla domanda che ci eravamo posti. I numeri delle cartelle non indicano altro che il numero della sequenza dell’immagine (Image Sequence Number). Binwalk non utilizza i nomi dei volumi come nomi per le cartelle, ma questi particolari numeri. Scopriamo però anche che all’interno di ogni cartella c’è una sottocartella con il nome del volume. [fine del rant]

/reolink/_firmware_rlc_810_a.pak.extracted/ubifs-root# tree -L 2
.
├── 261434259
│   └── app
└── 468222262
    └── rootfs

Dando una rapida occhiata alle cartelle, possiamo confermare quello che ci aspettavamo prima. Due volumi, uno chiamato “app” contenente i file applicativi e un altro “rootfs” contenente il file system di root (quello che viene montato all’avvio del sistema operativo.) Non resta che concludere qui questo articolo e proseguire con la parte quinta dove andiamo ad esplorare maggiormente le diverse cartelle.