Esplorare il Sistema Operativo di Reolink RLC-810A
Pubblicato il – 15 minuti di lettura – 3148 parole

Nella quarta parte ci siamo concentrati sul file system e, prima di concludere, siamo riusciti ad estrapolare i file da due immagini UBIFS contenute all’interno del firmware. Nella quinta parte cercheremo di capire meglio la struttura del file system di root, introducendo alcuni concetti fondamentali del sistema operativo utilizzato da Reolink RLC 810A, ovvero Linux.
Perché Linux?
Linux è uno dei sistemi operativi maggiormente utilizzati al mondo, se non il primo per diffusione. Potreste pensare che sistemi operativi commerciali come Windows o MacOS siano i più utilizzati, ma d’altra parte un intero esercito di dispositivi embedded (come la telecamera di questa serie) ha basato i suoi firmware su Linux.
Creato all’inizio degli anni 90 da Linus Torvalds, il kernel Linux è un kernel monolitico, modulare e multitasking principalmente sviluppato in risposta alla commercializzazione del sistema operativo UNIX, mantenuto da AT&T. Il kernel Linux e tutto l’ecosistema che sta sopra di esso è ampiamente open-source: ogni persona del pianeta è libero di poter consultare i sorgenti e modificarli.
La scelta di Linux come base per i firmware dei dispositivi embedded deriva da una serie caratteristiche che sono essenziali per lo sviluppo. Il primo tra tutti è la flessibilità di Linux: essendo modulare, è possibile caricare a tempo d’esecuzione componenti aggiuntivi che arricchiscono Linux di nuove funzionalità. Il secondo è dato dalla stabilità del Kernel, grazie a molteplici sviluppatori che dedicano notte e giorno e la grande diffusione del kernel che ha permesso di avere più occhi sullo sviluppo del codice. Il terzo motivo è forse uno dei più importanti: è gratis. Quando si commercializza un dispositivo, ci sono una serie di licenze che l’azienda deve pagare (hardware, dispositivi vari). Per le aziende che utilizzano Linux nei loro prodotti, non c’è alcun costo.
Prima di cominciare ad analizzare le varie cartelle è bene dire che la quasi totalità dei sistemi operativi adoperati per dispositivi embedded è una distribuzione basata sul Linux kernel. La caratteristica di essere open source, di avere un ampio supporto online e di essere un kernel abbastanza stabile hanno reso l’intera famiglia di sistemi operativi basati su Linux/Unix il sistema operativo ideale per questo tipo di dispositivi.
Le Cartelle del File System
Proseguiamo la nostra analisi, esplorando in dettaglio la struttura del sistema operativo. Per prima cosa, cambiamo la directory su rootfs
. Eseguiamo ls
per avere una panoramica più completa delle cartelle.
bin dev etc init lib linuxrc mnt proc root sbin sys tmp usr var
Come potevamo aspettarci, si tratta della caratteristica FileSystem Hierarchy Standard utilizzata per i file system dei sistemi operativi Unix-like. Lo standard infatti definisce quali cartelle e file system virtuali devono essere montati all’avvio dal sistema operativo e che cosa ogni cartella contiene. Questo standard viene condiviso tra tutti i sistemi operativi unix-like per garantire la compatibilità tra programmi su piattaforme diverse.
Esploriamo più in dettaglio ogni cartella e file:
bin
: contiene file binari condivise da tutti gli utenti. In questa cartella troviamo ad esempio: la shell, cat, ls, cp e molti altri.dev
: contiene file per le periferiche (dispositivi). La maggior parte dei sistemi operativi Unix-like adotta l’approccio di “Everything as a file”: ogni periferica viene descritta tramite alcuni file speciali che permettono di manipolare lo stato di un dispositivo. Contiene anche altri dispositivi pseudo-virtuali come/dev/null
che non produce output oppure/dev/random
che genera numeri pseudocasuali.etc
: contiene file di configurazione del sistema. Cartella molto interessante per capire come il produttore abbia configurato la camera.init
: l’eseguibile che Uboot carica ed esegue all’inizio del booting;lib
: contiene librerie essenziali per i binari in /bin/ e /sbin/;linuxrc
: file che viene eseguito da init per iniziare a popolare e montare alcuni file system virtuali (come dev, proc);mnt
: destinazione utilizzata per montare file system aggiuntivi (temporaneamente);proc
: file system virtuale che contiene informazioni sui processi attivi (principalmente leggibili come file di testo);root
: la cartella principale per l’utente “root” o “supervisor”;sbin
: cartella simile a sbin, si differenzia solo per i binari contenuti: in questo caso sono binari che devono essere eseguiti da root per l’amministrazione del sistema;sys
: file system che consente di interfacciarsi direttamente con i dispositivi hardware;tmp
: cartella temporanea. I file all’interno sono generalmente eliminati quando si riavvia il sistema.usr
: contiene le applicazioni e i file utilizzati dagli utenti. Applicazioni quali browser, messaggistica istantanea e tutto ciò che non è utilizzato dal sistema viene messo in questa cartella.var
: file con dati variabili. In particolare abbiamo/var/log
che contiene i log prodotti dai vari componenti del sistema e applicativi;
Ora che abbiamo introdotto le diverse cartelle possiamo incominciare ad esplorarne il contenuto una per una. Per i prossimi articoli ci occuperemo di andare a fondo di alcune cartelle per capirne meglio il loro significato.
Cartella Bin
La cartella bin contiene i file binari essenziali per il sistema operativo. All’interno troviamo eseguibili come ls
(per ottenere una lista dei file), cp
(per copiare un file), echo
(per stampare una stringa a video) e molti altri ancora.
Nel nostro caso, possiamo procedere a vedere più in dettaglio i vari binari utilizzando ls
.
ash chmod date echo fgrep hostname linux64 mkdir netstat ping rc_profile setarch su umount zcat
busybox chown dd ed fsync hush ln mknod nice ping6 rev setserial sync uname
cat conspy df egrep getopt iostat login more nvtrtspd pq_video_rtsp rm sh tar uncompress
catv cp dmesg false grep ipcalc ls mount nvtrtspd_2ch printenv rmdir sleep test_vos usleep
chattr cpio dnsdomainname fatattr gunzip kill lsattr mpstat nvtrtspd_ipc ps scriptreplay stat touch vi
chgrp cttyhack dumpkmap fdflush gzip linux32 lzop mv pidof pwd sed stty true watch
Molti di questi sono comandi che troviamo abitualmente in qualsiasi sistema operativo. mkdir
serve per creare cartelle, date
per stampare la data attuale, more
per leggere un file. Destano però subito all’occhio alcuni file binari particolari che non sono abitualmente contenuti in distribuzioni unix-like, come ad esempio nvtrtspd
oppure pq_video_rtsp
. Ci ritorneremo sopra perché sembrano essere binari interessanti.
Procediamo quindi ad identificare la shell. La shell è un’interfaccia a riga di comando utilizzata per interfacciarsi con il sistema operativo: è possibile eseguire comandi, richiedere l’avvio di programmi, modificare impostazioni. Insieme al kernel, è un componente fondamentale per un sistema operativo.
Sono diverse le shell testuali più diffuse: la più famosa, Bash, utilizzata per sistemi GNU/Linux; ma anche cmd.exe per Windows, Z Shell per MacOS. Per Reolink RLC 810A, la shell è contenuta all’interno di un file binario chiamato busybox
. A seconda di come è stato configurato l’applicativo, può esserci sia la bash sia la zshell.
BusyBox
BusyBox è uno strumento che permette di incapsulare molte applicazioni Unix standard in un unico piccolo eseguibile da poter rilasciare all’interno del firmware senza troppe complicazioni. Il principale vantaggio è una ridotta dimensione del sistema operativo dal momento che è necessario solo un binario per poter eseguire molteplici comandi. La chiamata ai diversi comandi con un singolo eseguibile funziona grazie a collegamenti simbolici che richiamano il singolo binario.
Considerato il coltellino svizzero del Linux Embedded, Busybox è principalmente scritto in linguaggio C e attualmente viene utilizzato da un enorme varietà di distribuzione per dispositivi embedded, incluso BuildRoot e OpenWRT.
Ogni comando del sistema è collegato simbolicamente al binario di busybox. Quando un qualsiasi comando viene eseguito, busybot legge da argv[0]
il nome del comando da eseguire e in argv[i]
tutti i parametri di quel comando. Il collegamento tra nome del comando e binario su busybox viene confermato da un semplice file ls
nella cartella /bin
.
ls: symbolic link to busybox
Per esclusione, troviamo gli unici file che sono binari custom, non propri di BusyBox: nvtrtspd
, nvtrtspd_2ch
, nvtrtspd_ipc
, pq_video_rtsp
, rc_profile
, test_vos
. Possiamo spostare alcuni binari in una cartella ad-hoc per il futuro, momento in cui approfondiremo tramite reverse engineering le funzionalità e le caratteristiche di ogni programma.
I File di Configurazione (etc)
Una volta che abbiamo concluso la parte dei binari, un po’ noiosa, possiamo addentrarci in un’altra cartella che risulterà molto interessante: etc. La cartella etc
contiene tutti i file di configurazione per gli applicativi di sistema; tali file di configurazioni sono condivise tra tutti gli utenti che accedono al sistema.
In origine la cartella etc
doveva contenere tutti i file che non appartenessero ad alcuna categoria particolare (da qui l’utilizzo dell’“et cetera” dal Latino), ma diventò subito la cartella della configurazione dei file de facto. Alcune moderne interpretazioni rimandano “etc” a “Editable Text Configuration” oppure “Extended Tool Chest.”
Riprendendo con la nostra analisi, proseguiamo, focalizzandoci su ogni file trovato. Per ragioni di semplicità e di vastità, ho preferito includere tutti i file rilevanti all’interno della cartella /etc/
in ordine alfabetico.
Device Tree dei Sensori (application.dtb)
Il primo file che analizziamo all’interno della cartella /etc/
si chiama application.dtb
ed è un device tree, un file che permette di descrivere una particolare configurazione hardware. Eseguiamo il comando utilizzato durante la terza parte per visualizzarne il contenuto:
dtc -I dtb -O dts -o - application.dtb
ed ecco il risultato:
/dts-v1/;
{
sensor@1 {
dev_name = "nvt_sen_imx291";
cfg = "sen_imx291_cfg1\0sen_imx291_cfg2";
sie = <0x00 0x01>;
};
sensor@2 {
dev_name = "nvt_sen_imx323";
cfg = "sen_imx323_cfg1";
sie = <0x03>;
};
};
Il device tree serve per descrivere due sensori ottici, apparentemente non utilizzati in questa videocamera. Viene utilizzato da un programma di testing incluso nel firmware, ma quasi mai utilizzato dall’utente finale.
bootchartd.conf
Il file bootchard.conf
permette di impartire alcune impostazioni al famoso strumento bootchart
che serve per misurare le performance dell’avvio di Linux. In contesti normali, che sia un computer o un dispositivo cellulare non abbiamo molto interesse a spendere qualche secondo in più durante l’avvio.
Quando però si tratta di dispositivi embedded, ogni secondo è cruciale perché non esiste per l’utente finale il concetto di “aspettare”. Ad esempio per iniziare a registrare un qualsiasi video, ci basta accendere una videocamera e premere un pulsante. L’utente non sa che in quel frangente di tempo una serie di operazioni viene eseguita e pertanto si aspetta di poter utilizzare lo strumento subito!
Lo strumento è utile anche in contesti embedded per cercare di individuare pericolosi colli di bottiglia. Nel caso di Reolink sono due le impostazioni date al kernel: sample_period
(ovvero l’intervallo di tempo per misurare le performance) e process_accounting
che permette di tracciare tutti i processi eseguiti all’avvio.
firmware.info
Il file firmware.info contiene qualche informazione utile riguardo il firmware e il Software Development Kit utilizzato per generare la distribuzione embedded. Non è granché utile ai fini del sistema operativo, ma consente di estrapolare un paio di informazioni in più.
SDK_VER="NVT_NT96660_Linux_V0.4.8"
BUILDDATE="Tue Mar 1 18:25:28 CST 2016"
fstab, group e hostname
Il file fstab indica quali file system virtuali montare, in che posizione ed eventualmente altri parametri. Il file fstab viene letto dal comando mount che avviene automaticamente all’avvio per determinare la struttura complessiva del file system.
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
tmpfs /tmp tmpfs defaults,noatime,nosuid,nodev,exec,mode=1777,size=5M 0 0
tmpfs /var/run tmpfs defaults,rw,nosuid,mode=0755 0 0
tmpfs /mnt/tmp tmpfs defaults,noatime,nosuid,nodev,exec,mode=1777,size=20M 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
Il file group
invece riporta la suddivisione degli utenti in differenti gruppi. Di per sé non è molto interessante per la nostra indagine. root:x:0:
Il file hostname
riporta il nome della macchina: BAICHUAN
, un chiaro riferimento all’azienda dietro Reolink.
Inetd.conf
Il file inetd.conf
è il file di configurazione per il daemon inetd
ed ogni riga rappresenta un servizio che viene gestito da questo demone. Il demone inetd
che permette di controllare i servizi Internet che vengono resi disponibili dalla macchina. Il demone avvia i servizi in base al file di configurazione. In particolare troviamo: ftpd che viene avviato sulla porta 21 da root e rende disponibile upload/downloading di file da sd, e tftpd sulla porta 69.
[..redatto..]
21 stream tcp nowait root ftpd ftpd -w /mnt/sd
69 dgram udp nowait root tftpd tftpd -l -c
Inittab
Il file inittab
specifica la configurazione iniziale che il processo init
deve eseguire per busybox. È composto da entry che seguono il formato:
Identificatore:Livello:Azione:Comando
dove:
- identificatore: una stringa che univocamente identifica un entry;
- livello: livello associato all’entry, rappresentato da un numero (da 0 a 9). Ogni entry viene eseguita nel momento in cui il sistema è nel livello uguale all’entry;
- azione: specifica al processo
init
come trattare il processo; - comando: il comando da eseguire;
::sysinit:sh /etc/init.d/rcS
# Stuff to do when restarting the init process
::restart:/sbin/init
# Stuff to do before rebooting
::ctrlaltdel:/sbin/reboot
::shutdown:/etc/init.d/rcK
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
Le seguenti azioni sono specificate dall’inittab:
- all’avvio del sistema, avvia gli script iniziali (
/etc/init.d/rcS
); - se il sistema viene riavviato, allora riavvia anche il processo init;
- quando viene registrata una qualsiasi azione tra riavvio o spegnimento, allora esegue lo script
/etc/init.d/rcK
e smonta il filesystem.
mdev.conf
Mdev è lo strumento sviluppato ad-hoc da Busybox per sostituire udev
. Udev è il gestore dei dispositivi per i sistemi operativi Linux-like e viene eseguito come daemon. A differenza di sistemi operativi basati su Unix che si basano solamente sulla gestione dei dispositivi tramite di file di testo statici su /dev
, udev permette di gestire in modo dinamico la creazione o rimozione di questi file speciali.
La sintassi che segue mdev.conf è abbastanza arzigolata:
[-]devicename_regex user:group mode [=path]|[>path]|[!] [@|$|*cmd args...]
Nel nostro caso mmcblk[0-2] root:root 660 */etc/mdev-script/autosd.sh
, si traduce in “se trovi dispositivi che sono mmcblk allora esegui lo script autosd.sh”. Mmcblk è il nome del sottosistema che i dispositivi SD/MMC utilizzano quando sono collegati alla board. Quando viene rilevata una nuova micro SD, lo script autosd.sh
verifica una serie di parametri a livello hardware (nome della microSD, tipo) e se corrisponde, allora provede a creare un nuovo hard link al contenuto della microSD. Dal momento che viene fatto un controllo molto accurato sul tipo di scheda microSD, si spiega perché alcune microSD potrebbero non funzionare.
passwd
Il file passwd serve per configurare quali utenti hanno accesso al sistema di login e con quale combinazione di username e password possono entrare. La sintassi per comprendere un file passwd è la seguente:
username:password:userid:groupid:useridinfo:homedirectory:shell
dove:
- username: il nome utente che viene utilizzato durante l’accesso;
- password: se c’è un solo carattere
x
, allora la password è crittografata e il suo contenuto è disponibile nel file/etc/shadow
- userid: è l’ID dell’utente
- groupid: è il gruppo di cui l’utente fa parte
- useridinfo: campo aggiuntivo che permette di memorizzare informazioni aggiuntive riguardo l’utente
- homedirectory: percorso assoluto in cui si troverà l’utente una volta effettuato l’accesso
- shell: percorso assoluto della shell da avviare
Per Reolink RLC 810A, in particolare troviamo questo file:
root:XF4sg5T82tV4k:0:0:root:/root:/bin/sh
Significa che l’utente di default per la webcam è il seguente username: root
e la password hashata: XF4sg5T82tV4k
. Attraverso hashcat, possiamo scoprire che dietro all’hash c’è bc2020
, la password originale. Questa password non consente di entrare in interfacce esposte sulla Rete come quella tramite browser. Piuttosto, se attaccassimo la board attraverso UART ad un terminale, potremo entrare all’interno del sistema utilizzando quella determinata combinazione di username/password.
Includere un file passwd
e lasciare la password visibile all’interno di un firmware non pone alcun rischio aggiuntivo, se il login richiede l’accesso fisico e locale alla board. Non posso però pronunciarmi rispetto alla sicurezza di questo login dato che non ho trovato alcuna informazione riguardo questa password. Se qualcuno scopre qualcosa di più, mi può scrivere una e-mail.
profile e profile_prjcfg
Il file profile serve per inizializzare la shell di default a livello di sistema. Quando la shell infatti è avviata come una shell di tipo interattiva, legge il file /etc/profile
, esegue i comandi al suo interno e legge un altro file ~/.bash_profile
che è l’equivalente del file profile però specifico di un utente.
# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).
# source profile_prjcfg on /etc/init.d/rcS (init script cycle) and /etc/profile (after startup cycle)
source /etc/profile_prjcfg
export PATH="/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/bin:/sbin"
export LD_LIBRARY_PATH="/lib:/usr/local/lib:/usr/lib:/mnt/app"
export TERMINFO=/usr/share/terminfo
#export LD_PRELOAD="libnvtlibc.so"
if [ -f /etc/hostname ]; then
/bin/hostname -F /etc/hostname
fi
# coredump setting
echo 1 > /proc/sys/kernel/core_uses_pid
ulimit -c unlimited
echo "/var/log/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
export HOSTNAME=`/bin/hostname`
export PS1='[\u@\h:\w]\$ '
echo "$HOSTNAME Linux shell..."
Alcuni comandi interessanti di questo file sono le seguenti:
source /etc/profile_prjcfg
: esegue il fileprofile_prjcfg
, impostando alcune variabili di ambiente;echo 1 > /proc/sys/kernel/core_uses_pid
eecho "/var/log/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
: queste due opzioni sono utilizzate per configurare lo strumentocore
che permette di generare core dump nel momento in cui un’applicazione fallisce.ulimit -c unlimited
: non c’è alcun limite di memoria per la shell; il comandoulimit
permette di impostare un limite di memoria che la shell o uno dei programmi avviati da shell deve rispettare, pena la “decapitazione” (kill) da parte del sistema operativo. L’utilizzo di questo comando pone seri problemi dato che un processo è libero di poter allocare tanta memoria quanto ne richiede. Talvolta si rende necessario aumentare la memoria disponibile per un processo (e quindi bypassare il limite) quando un programma ha qualche strano memory leak. Il workaround però dovrebbe essere temporaneo, non permanente in produzione! Questo suggerisce una pratica di sviluppo di bassa qualità.
Per quanto riguarda il file profile_prjcfg
, si tratta principalmente di una serie di variabili di ambiente che vengono impostate. Per ora il loro significato è sconosciuto dato che la maggior parte dei nomi delle variabili di ambiente è basato sul NovaTek SDK. Cercheremo di trovare qualche indizio che possa aiutarci a chiarire le idee.
export MODEL=/home/zfj/520_project/sdk/v2.02/NT9852x_linux_sdk_v2.02.000/na51055_linux_sdk_bc/configs/Linux/cfg_IPCAM1_523_EVB/ModelConfig.mk
export BOARD_DRAM_ADDR="0x00000000"
export BOARD_DRAM_SIZE="0x08000000"
export BOARD_FDT_ADDR="0x00100000"
export BOARD_FDT_SIZE="0x00100000"
export BOARD_SHMEM_ADDR="0x00200000"
export BOARD_SHMEM_SIZE="0x00100000"
[.... continua ....]
Anche in questo caso possiamo vedere come la fase di building di un firmware lasci alcuni metadata. Lo sviluppatore che ha costruito la distribuzione embedded ha incluso all’interno del file in produzione alcuni riferimenti, ovviamente inesistenti. Non è la prima volta che possiamo notare questo e durante il corso della serie ne troveremo ben altre.
Altri file di configurazione
All’interno della cartella /etc
ci sono altri file di configurazione come services
, udhcpdw.conf
, wifiap_wpa2.conf
e wpa_supplicant.conf
. Molto interessante notare come alcuni servizi (ad esempio i file di configurazione per il Wi-fi) che non sono minimamente utilizzati da parte di questa videocamera sono comunque presenti all’interno del firmware. La videocamera RLC-810A infatti ha un unico modo per poter essere connessa ad Internet: Ethernet che utilizza anche come risorsa elettrica grazie a PoE (Power Over Ethernet).
Qui si conclude la nostra analisi dei file di configurazione e dei file binari. È stato interessante capire come ogni file di configurazione potesse darci alcuni indizi sui servizi abilitati della macchina e su come funzionasse. Quello che ci concentreremo sul prossimo articolo riguarderà la cartella /lib
e le successive cartelle da analizzare.
Reolink Serie
- Parte 1 – Introduzione all'Analisi del Firmware di una Telecamera IP ReoLink
- Parte 2 – Avvio di un OS Embedded: la Fase di Booting e U-Boot
- Parte 3 – Esplorare Hardware di ReoLink RLC-810A
- Parte 4 – Approfondire UBI FileSystem nei Dispositivi Embedded
- Parte 5 – Esplorare il Sistema Operativo di Reolink RLC-810A
- Parte 6 – Tecniche per Impostare le Periferiche via DMA e PIO
- Parte 7 – Reverse Engineering del driver Omnivision OS12D40