Expert Witness Compression
Inhalt
Allgemeines
Header
Sektionen
Die Sektion "header"
Die Sektion "volume" bzw. "disk"
Die Sektion "table"
Die Sektion "error2"
Die Sektion "hash"
Beispiel zur Sektionsaufteilung
Allgemeines
Das Expert Witness Compression Dateiformat dient zur komprimierten Speicherung von Daten, auf die auch ohne vollständige Dekomprimierung zugegriffen werden kann. Dies wird dadurch erreicht, dass jeweils nur einzelne Datenblöcke in der Größe von 32 Kilobyte komprimiert und in die Datei geschrieben werden. Damit eignet sich dieses Format besonders zur Speicherung von Datenträgerimages, da gezielt und schnell auf bestimmte Datenbereiche zugegriffen werden kann. Weiters kann ein Image auf mehrere Dateien (Segmente) aufgeteilt werden.
Die Firma Guidance nutzt für das Programm EnCase eine erweiterte Variante des Expert Witness Compression Formats. Diese Erweiterung besteht hauptsächlich in der Verwendung einer zusätzlichen Sektion, die den MD5-Hash der gesicherten (vom Originaldatenträger eingelesenen) Daten enthält.
Diese Beschreibung bezieht sich ausschließlich auf die von Guidance EnCase verwendete Version.
Header
Jede Datei beginnt mit einem 13 Byte langen Header, der sich wie folgt aufbaut:
| Offset | Länge | Bedeutung |
| 0 | 8 Bytes | Magic-Bytes (0x45 56 46 09 0d 0a ff 00) |
| 8 | 1 Byte | Immer 0x01 |
| 9 | 1 Word | Segmentnummer |
| 11 | 1 Word | Immer 0x0000 |
Sektionen
Im Anschluss an den Header folgen die einzelnen Sektionen. Jede Sektion beginnt mit einem Header, der aus 76 Byte besteht:
| Offset | Länge | Bedeutung |
| 0 | 16 Bytes | Sektionstyp (String) |
| 16 | 1 QWord | Offset der nächsten Sektion |
| 24 | 1 Word | Länge der Sektion |
| 32 | 40 Bytes | Füllzeichen (0x00...) |
| 72 | 1 DWord | CRC-Wert der vorangegangenen Header-Daten (72 Bytes) |
Als Sektionstyp können folgende Strings eingetragen sein:
- header
- Hier sind Informationen zum Image eingetragen, wie z.B. die Fall-Nummer oder das Erstellungsdatum.
- volume / disk
- In dieser Sektion sind Partitionsdaten enthalten, falls es sich um eine einzelne Partition oder um eine Diskette bzw. CD handelt. In diesem Fall trägt die Sektion den Namen "volume". Wenn der Inhalt aus einer Festplatte besteht, sind in dieser Sektion die Datenträgerinformationen eingetragen und die Sektion heißt "disk". Diese Sektion existiert nur im ersten Segment (= der ersten Datei) des Image.
- table
- Diese Sektion enthält die Offsets der Datenblöcke. Es können mehrere table-Sektionen pro Segment (Datei) existieren.
- sectors oder data
- In dieser Sektion sind die Daten enthalten. Es können mehrere sectors-Sektionen pro Segment (Datei) existieren.
- error2
- In der error2-Sektion werden die fehlerhaften Bereiche gespeichert, die nicht vom Originaldatenträger gelesen werden konnten.
- hash
- In der hash-Sektion wird der MD5-Hash gespeichert. Diese Sektion ist nur im letzten Segment (Datei) des Images vorhanden.
- next bzw. done
- Am Ende einer Datei existiert entweder eine next-Sektion oder eine done-Sektion. Diese Sektionen enthalten keine Daten und der Offset-Pointer verweist auf die Sektion selbst. Handelt es sich bei der Datei um die letzte Datei eines Images, wird "done" verwendet, anderenfalls "next".
Endet ein Sektionsname mit "2", also etwa "header2" oder "table2", handelt es sich um eine Kopie der vorhergehenden, gleichnamigen Sektion. Eine Ausnahme bildet nur die Sektion "error2".
Der CRC-Wert wird mit folgender Funktion berechnet:
Quellcode (Object Pascal)
function Expert_Witness_Compression_CRC(const sString: String; iPrevKey: DWord = 1): DWord;
var
Buf: String;
b, d: DWord;
i: Integer;
begin
Buf := sString;
b := iPrevKey and $ffff;
d := (iPrevKey shr 16) and $ffff;
for i := 1 to Length(sString) do begin
Inc(b, Ord(Buf[i]));
Inc(d, b);
if ( (i <> 0) and ( (i mod $15b0 = 0) or (i = Length(sString)) ) ) then begin
b := b mod $fff1;
d := d mod $fff1;
end;
end;
Result := ((d shl 16) or b);
end;
Quellcode (C++)
uint Expert_Witness_Compression_CRC(void *buffer, int buffersize, uint prevkey)
{
unsigned char *cbuffer = (unsigned char *)buffer;
unsigned int b = prevkey & 0xffff;
unsigned int d = (prevkey >> 16) & 0xffff;
for(int i=0; i < buffersize; i++)
{
b += cbuffer[i];
d += b;
if ( i != 0 && ( (i % 0x15b0 == 0) || (i == buffersize - 1) ) )
{
b = b % 0xfff1;
d = d % 0xfff1;
}
}
return (d << 16) | b;
}
Beim ersten Aufruf der Funktion muss "prevkey" der Wert "1" übergeben werden, damit ein neuer CRC-Wert errechnet wird.
Die Sektion "header"
Die gesamte Sektion (ab Offset 76) enthält mit "zLib" komprimierte Daten. Der Inhalt ist ein mehrzeiliger Text, wovon in der ersten Zeile nur die Zahl "1" und in der zweiten Zeile der Text "main" enthalten ist. Die dritte Zeile enthält Kürzel, die mit Tabulatoren voneinander getrennt sind und sich auf den Inhalt der Tabulatorenfelder in der vierten Zeile bezieht, die die eigentlichen Informationen enthält.
Beispiel
1
main
c n a ...
Case Number Evidence Number Unique Description ...
Folgende Abkürzungen sind möglich:
| Abkürzung | Bedeutung |
| c | Aktenzahl |
| n | Eindeutige Nummer des Beweismittels |
| a | Eindeutige Beschreibung |
| e | Name des Erstellers |
| t | Notizen |
| m | Datum und Zeit der Erstellung Das Format ist: "yyyy m d h n s", z.B.: "2006 10 26 14 08 32", was dem 26.10.2006, 14:08:32 Uhr entspricht. |
| u | Systemzeit Die Systemzeit hat das gleiche Format wie die Erstellungszeit. |
| p | "pwhash" (sollte immer "0" sein) |
| r | Art der Komprimierung
Mögliche Werte sind:
"b" für beste Komprimierung
"f" für schnelle Komprimierung (Standard)
"n" für keine Komprimierung |
| av | Versionsnummer von EnCase |
| ov | Name und Version des Betriebssystems |
Die Sektion "volume" bzw. "disk"
| Offset | Länge | Bedeutung |
| 76 | 1 DWord | Reserviert |
| 80 | 1 DWord | Anzahl der Cluster |
| 84 | 1 DWord | Sektoren pro Cluster |
| 88 | 1 DWord | Bytes per Sektor |
| 92 | 1 DWord | Anzahl der Sektoren |
| 96 | 20 Bytes | Reserviert (0x00...) |
| 116 | | Füllzeichen (0x00...) |
| Ende-4 | 1 DWord | CRC-Wert der vorangegangenen Header-Daten (72 Bytes) |
Die Sektion "table"
| Offset | Länge | Bedeutung |
| 76 | 1 DWord | Anzahl der Einträge (cnt) in dieser Tabelle |
| 80 | 16 Bytes | Füllzeichen (0x00...) |
| 96 | 1 DWord | CRC-Hash der vorangegangenen Daten, beginnend bei Offset 76 |
| 100 | cnt * 4 Bytes | Offset-Array |
| Ende - 4 | 1 DWord | CRC-Hash der vorangegangenen Daten, beginnend bei Offset 100 |
Der Offset-Array besteht aus DWord-Werten, die die Positionen der einzelnen Datenblöcke in der sectors-Sektion angeben. Diese Positionsangaben beziehen sich jedoch auf den Anfang der Datei und nicht auf den Beginn einer sectors-Sektion.
Der Offset-Array kann bis zu 16.375 Einträge haben, jedoch können mehrere table-Sektionen in einer Datei existieren.
Die Einschränkung auf 16.375 Einträgen ergibt sich aus dem Umstand, dass das höherwertigste Bit des DWord-Wertes angibt, ob die Daten komprimiert sind oder nicht. Hier ein Codebeispiel dazu:
Code-Beispiel (Obejct Pascal)
if ((Offset and $80000000) = $80000000) then begin
Offset := Offset - $80000000;
Decompress := True;
end else begin
Decompress := False;
end;
Die Sektion "error2"
| Offset | Länge | Bedeutung |
| 76 | 1 DWord | Anzahl der Einträge (cnt) in der Tabelle |
| 80 | 512 Bytes | Füllzeichen (0x00...) |
| 592 | 1 DWord | CRC-Hash der vorangegangenen Daten, beginnend bei Offset 76 |
| 596 | cnt * 8 Bytes | Fehler-Array |
| Ende - 4 | 1 DWord | CRC-Hash der vorangegangenen Daten, beginnend bei Offset 596 |
Die Einträge im Fehler-Array bestehen aus zwei DWord-Werten, wobei der erste die Sektornummer und der zweite die Sektoranzahl des fehlerhaften Bereichs angibt.
Die Sektion "hash"
| Offset | Länge | Bedeutung |
| 76 | 16 Bytes | MD5-Hash |
| 92 | 1 DWord | CRC-Hash der vorangegangenen Daten, beginnend bei Offset 76 |
Der MD5-Hash wird mit den dekomprimierten Daten aus den sectors-Sektionen (in Verbindung mit den table-Sektionen) aller Segmente (Dateien) errechnet.
Beispiel zur Sektionsaufteilung
Im folgenden Beispiel sind nur die Sektionsnamen in ihrer Reihenfolge aufgeführt. Es wird angenommen, dass das Image aus zwei Segmenten (Dateien) besteht.
Segment-Header 1
header2
header
volume
sectors
table
table2
sectors
table
table2
next
Segment-Header 2
sectors
table
table2
sectors
table
table2
error2
hash
done