Tipps zur Forensik nach Durcheinander auf Plattenbereichen

Dann und wann geschieht es: der lvm verliert seine Konfiguration oder durch andere traurige Umstände hat man lediglich noch einen Haufen „extents“ bekannter Länge vorliegen, die einmal eine Platte waren.
Dem Autor passierte dies beim versehentlichen Löschen einer falschen virtual Disk im IBM SVC („SAN Volume Controller“).
Bekannt war hier noch, dass ein Solaris UFS mit knapp 1TB Grösse verloren gegangen war.
Schaut man sich dann in Ruhe ein paar Grundlagen zu UFS-Datenstrukturen auf disk an, etwa in
http://www.informit.com/articles/article.aspx?p=605371&seqNum=2, und besorgt sich einige „magic“ Konstanten aus den guten alten .h-files in einem alten Solaris, kann man sich ein kleines C-Programm wie dieses

findb.c:
 #define _LARGEFILE64_SOURCE
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>

/*
#define FS_MAGIC 0x011954
#define CG_MAGIC 0x090255
*/

#define FS_MAGIC 0x54190100
#define FSMOFF 343
#define CG_MAGIC 0x55020900

#define NEXT 2113
#define ESIZE 512*1024*1024
static char edata[ESIZE];
#define HEADL 32768*2048
#define SBDIST 6495
#define SBONE 2

long *edata32;

main()
{
long rb;
int fd;
unsigned int extno,boff,isbi,x,cgno;
off64_t offs, offo;
 extno=0;
 edata32=&edata;
 fd=open("/dev/sdb",O_RDONLY);
 if (fd<0) { printf("open errorn"); exit(99); }

 while(extno < NEXT) {
 printf("reading extent number %dn",extno);
 rb=read(fd,edata,ESIZE);
 if(rb != ESIZE) { printf("short readn"); exit(98); }
/*  rb=read(fd,edata,HEADL);
 if(rb != HEADL) { printf("short readn"); exit(98); }
 offs=ESIZE-HEADL;
 offo=lseek64(fd,offs,SEEK_CUR);
 if(offo == (off64_t) -1 ) {printf("seek errorn"); exit(97);} */
/*  printf(" sb %08x cg %08x n",edata32[2050],edata32[4097]); */
 boff=0;
 while((boff*4) <ESIZE) {
/*  while((boff*4) <HEADL) { */
/*  if(edata32[343+boff]==FS_MAGIC) { printf("fs magic found, ext=%d off=%d sblkno= %08xn ",extno,boff/2048,edata32[2+boff]); */
/*  isb=SBONE;
 while(isb < NEXT*65536) {
 if((isb & 0x0ffff) == boff/2048) break;
 isb=isb+SBDIST;
 }
 printf("isb=%d, ext=%dn",isb,isb/65536);
 } */
 if(edata32[1+boff]==CG_MAGIC) {
 x=edata32[3+boff];
 cgno= (x>>24) |
 ((x<<8) & 0x00FF0000) |
 ((x>>8) & 0x0000FF00) |
 (x<<24);
 printf("CG_MAGIC match, ext=%d off=%d cgx=%d time=%08x cyls=%dn",extno,boff/2048,cgno,edata32[2+boff],edata32[4+boff]>>24); }
 boff=boff+2048;
 }
 extno=extno+1;
 }
}

schreiben, was in der hier wiedergegebenen Form für alle Cylindergroup-Strukturen den Wert deren offset „cgx“ ausgibt, eben den laufenden index der jeweiligen cyclindergroup Struktur. Beachte die Zweckmässigkeit des Programms (nur ganz notwendige Fehlerabfragen) und die Tricks, um Solaris/Sparc Zahlen (BigEndian) in Linux/X86 Zahlen (LittleEndian) umzurechnen (Konstanten wurden per Hand konvertiert).

Durch Sortieren der extents (bekannter Grösse) kann man so mit etwas Glück die originale Reihenfolge der extents rekonstruieren. Dabei hat dieses script gute Dienste geleistet:

fsbchain.sh:
#!/bin/sh -vx
SBONE=0
SB=$SBONE

sbf=xfb64ec.out

while true ; do
l1=`grep "cgx=$SB$" $sbf`
if [ $? -ne 0 ]; then
echo end of chain
exit
fi
echo $l1
set - $l1
SBEXT=$1
echo `echo $SBEXT | cut -d = -f 2`>>extlist
ln=`grep "$SBEXT " $sbf | tail -1`
echo $ln
SBL=`echo $ln | cut -d = -f 3`
SB=`expr $SBL + 1`
echo $SB
#read
done

Mit ein paar weiteren shell scripten, die sicherstellen, dass nur valide CG-Blocks gezählt werden (deswegen werden Grösse und Datum mit ausgegeben) und einem anderen kleinen Hilfsprogramm

copyb.c:
#define _LARGEFILE64_SOURCE
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <limits.h>

#define ESIZE 512*1024*1024
static char edata[ESIZE];

main(argc,argv)
int argc;
char *argv[];
{
long rb;
int infd, outfd;
unsigned int extno,boff,isbi,x,cgno;
off64_t ioffs, ooffs, offo;
int ein,eout;

 ein=atoi(argv[1]);
 eout=atoi(argv[2]);
 printf("copying extent %d to extent %dn",ein,eout);

 infd=open("/dev/sdb",O_RDONLY);
 if (infd<0) { printf("input open errorn"); exit(99); }
 outfd=open("/dev/sdc",O_RDWR);
 if (outfd<0) { printf("output open errorn"); exit(99); }

 ioffs=(off64_t) ein * ESIZE;
 ooffs=(off64_t) eout * ESIZE;
 printf("input seek to %llu output seek to %llun",ioffs,ooffs);
 offo=lseek64(infd,ioffs,SEEK_SET);
 if(offo < 0 ) {printf("input seek errorn"); exit(97);}
 offo=lseek64(outfd,ooffs,SEEK_SET);
 if(offo < 0 ) {printf("output seek errorn"); exit(97);}
 rb=read(infd,edata,ESIZE);
 if(rb != ESIZE) { printf("short readn"); exit(98); }
 rb=write(outfd,edata,ESIZE);
 if(rb != ESIZE) { printf("short writen"); exit(98); }

}

zum effektiven Zusammensetzen des disk /dev/sdc aus den Daten der /dev/sdb nach einer eingelesenen Steuerdatei der Form

[root@nisad-pc03 ~]# head -10 extlist
12
307
21
407
268
304
305
19
314
357
[root@nisad-pc03 ~]#

Ob ähnliches Vorgehen bei ext2/ext3 Filesystemen gelingen würde dort gibt es sogenannte block groups, die wohl den cylinder groups im ufs entsprechen, mit  s_block_group_nr (block group # of this superblock) wird sich bei Gelegenheit zeigen (müssen).

2 Gedanken zu „Tipps zur Forensik nach Durcheinander auf Plattenbereichen“

  1. Hallo Herr Kalle,
    uns ist etwas ähnliches mit einem VMware filesystem passiert, was soweit mir bekannt ist, ein ext3 filesystem ist. Haben Sie mittlerweile herausgefunden, wie/ob man diese extents auch wiederherstellen kann?

    Vielen Dank

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert