Se si deve acquisire un hard disk in maniera forense, ossia con tutti i crismi necessari al fine di garantire che la copia sia identica all'originale, a tutti quelli che operano nel settore viene subito in mente l'uso di DD, DCFLDD o DC3DD (nel mondo Open Source GNU/Linux), con le classice opzioni e parametri.
Gli approcci sono due:
1) Ottimistico (consideriamo il disco sano e tutti i settori sani)
2) Pessimistico (consideriamo che il disco abbia qualche settore danneggiato)
Nel caso in cui l'approccio ottimistico sia sconfessato, perchè durante l'acquisizione ci si ritrova con degli errori di lettura, allora si tenderà a porsi nell'ottica pessimistica, a volte anche ricominciando tutto d'accapo.
Esaminiamo gli scenari:
dd if=/dev/sdb of=/media/sdc1/disco.dd conv=noerror,sync bs=32K
Il suscritto comando leggerà blocchi da 32Kb dal disco /dev/sdb e scriverà sul disco destinazione /media/sdc1 (montato in scrittura) il file disco.dd, l'opzione conv=noerror,sync serve a due cose:
1) Noerror - indica a dd di non fermarsi di fronte ad eventuali errori di lettura, ignorando i blocchi illeggibili, ma questo, senza il sync, cambierebbe l'indirizzamento del disco, poichè l'immagine (disco.dd) del disco avrebbe una dimensione finale errata, inferiore a quella del disco originale.
2) Sync - Il flag sync forza dd a scrivere i blocchi della dimensione prescelta (es. BS=32K) e se non ci sono abbastanza dati per riempire il blocco, quest'ultimo sarà riempito di zeri (0s padding). Questo crea un problema, il file immagine di destinazione avrà una dimensione multipla del BS prescelto. Es. se il disco origine è di 6Kb ed il BS=4K, il file immagine avrà come dimensione 8Kb.
Inoltre, con un BS=32K e conv=noerror,sync, se dovessimo incontrare dei settori danneggiati, che però appartengono al buffer di lettura di 32Kb, il dd così configurato andrebbe a riempire ben 32Kb di zeri, sacrificando anche i settori sani che potrebbero esserci in quei 32Kb. Se ci sono solo due di settori danneggiati 512 bytes * 2 =1024 bytes = 1Kb, significa che i rimanenti 31Kb saranno azzerati, perchè dd ha letto un blocco da 32Kb ha incontrato degli errori ed ha fatto il riempimento di zeri su tutto il blocco letto.
Quindi la soluzione sarebbe quella di impostare dd con un BS=512, che è la misura minima di un settore, così da leggere il disco, settore per settore e nel caso uno di essi fosse illeggibile, sarebbe riempito di zeri (sync), mentre il settore successivo (leggibile) sarebbe letto correttamente, preservandone il suo contenuto.
dd if=/dev/sdb of=/media/sdc1/disco.dd conv=noerror,sync bs=512
Il problema di questa configurazione è che stressa di più gli hard disk, costringendoli a moltissime letture (sector by sector) e rende l'operazione più lenta.
Come risolvere?
Ci sono dei tools alternativi come ddrescue o dc3dd, che permettono di leggere blocchi di dimensioni maggiori di 512 bytes, ma nel momento in cui si trovano di fronte ad un errore, questi useranno il block size di dimensione minima, 512 bytes appunto e lo riempiranno di zeri.
DC3DD ha il default blocksize di 32768 bytes (32Kb), al fine di aumentarne la performance.
Il file immagine finale è calibrato sulla dimensione del disco sorgente indipendentemente dalla dimensione del blocco, così la dimensione del file immagine è esattamente la stessa, come se avessimo usato un BS=512.
Sector Error Recovery
Quando si incontra un errore ed il block size è più grande del settore del device sorgente, e il parametro conv=sync,noerror è impostato, dc3dd cerca dalla fine all'inizio del blocco e prova a leggere ciascun settore indvidualmente, così i settori buoni saranno acquisiti e quelli cattivi saranno rimpiazzati dagli zero. Questa caratteristica permette di acquisire le aree non danneggiate del disco a velocità maggiori, (dato che il BS=32K), senza perdere i blocchi di dati che circondano un bad sector
Per attivare questa modalità è richiesto il direct I/O mode abilitato (iflag=direct su Linux, /dev/rdisk* su Mac OS X).
dc3dd if=/dev/sdb of=/media/sdc1/disco.dd conv=noerror,sync bs=32k iflag=direct
oppure con ddrescue
ddrescue -d -r3 /dev/sdb /media/sdc1/disco.dd log.txt
In sostanza il parametro iflag=direct o -d per ddrescue, serve a bypassare la Kernel Cache (pagecache)* e accedere direttamente al disco (direct I/O), considerando così la dimensione minima del settore.
Chi volesse usare DCFLDD con direct access non può utilizzare il parametro iflag=direct, dato che dcfldd è un fork di dd, quindi non segue lo stesso sviluppo di quest'ultimo, ma può accedere al device sorgente in direct I/O tramite il /dev/raw (per questo documentarsi sul device raw in Gnu/Linux).
E' chiaro che per uso forense si tende ad usare di più uno strumento come DC3DD dato che ha anche caratteristiche di multiple hashing automatico, verifica, ecc.
Es.: dc3dd if=/dev/sdb of=/media/sdc1/disco.dd conv=noerror,sync bs=32k iflag=direct progress=on hash=md5,sha1 log=log.txt vf=/dev/sdb verifylog=log_verifica.txt
*La page cache è il luogo in cui il kernel mantiene in RAM una copia dei dati, per migliorare le prestazioni evitando l'I/O del disco, quando i dati che devono essere letti sono già in RAM.
DC3DD è basato su DD, quindi iflag=direct funziona anche con DD.