Datei stückweise bzw. Sektorweise einlesen - Lazarus [SOLVED]

DKK007

PCGH-Community-Veteran(in)
Datei stückweise bzw. Sektorweise einlesen - Lazarus [SOLVED]

Hallo,

Ich arbeite derzeit an einem Programm, zum Auslesen von Datenträgerimages. Dabei sind die Rohdaten oft mehrere GB groß und könnten durchaus auch im TB Bereich landen.
Allerdings funktionieren die typischen TMemory/TStringStream nur bis 2 GB und die Komponente zum Anzeigen wird schon bei etwa 100 MB sehr träge.

Gibt es denn eine Möglichkeit bei einer großen Datei nur einen bestimmten Teil einzulesen? Dieser Teil würde mit Start- und End-Byte bzw. Sektor angegeben werden. Dabei würde es auch reichen, wenn man immer nur Bereiche aus ganzen Sektoren (512kB) auslesen kann.

Gibt es so eine Möglichkeit in Lazarus?

Ich möchte nämlich auch noch eine Funktion schreiben, mit der sich eine Hash-Liste von Sektoren erstellen lässt. Dabei soll es dann auch möglich sein bestimmte Hashes, von z.B. Sektoren, die nur aus FF o.ä. bestehen oder leer sind (alles 00) auszuschließen. Damit soll es möglich sein, Dateien die identisch mehrmals auf dem Datenträger vorhanden sind aufzuspüren. Bei größeren Dateien, die über mehrere Sektoren gehen, ließen sich somit auch vielleicht welche Retten, wenn man zusammen passende Fragmente findet.
(Vielleicht hat da ja auch noch jemand Tipps wegen Performance usw.)

Vielen Dank fürs Lesen. ;)

MFG. DKK007
 
Zuletzt bearbeitet:
AW: Datei stückweise bzw. Sektorweise einlesen - Lazarus

Normalerweise müsste es neben eines Textreader auch einen Binärreader geben, der nackte Bytes lesen kann. Damit kann man dann beispielsweise immer 512 Byte Blöcke (Chunks) lesen. Müsstest du vielleicht mal nach "read binary file" oder so suchen.
 
AW: Datei stückweise bzw. Sektorweise einlesen - Lazarus

Also zumindest in C ist es doch möglich, dass man an einem bestimmten Punkt einer Datei anfängt zu lesen. (weiss aber ned mehr wie die Funktion heisst)
Wenn du immer die letzte Position speicherst, kannst du ja dann bei Position+1 weiter machen und so die Datei in kleineren Teilen (100MB) verarbeiten.

edit: zumindest in C++ und Binärdateien:
datei.seekg(100) -> Hinter Byte 100 in der Datei gehen

edit2: sowas ^^
Seek
 
Zuletzt bearbeitet:
AW: Datei stückweise bzw. Sektorweise einlesen - Lazarus

Meine letzten Pascal-Programmierungen sind zwar mind. 20 Jahre her (Borland Pascal unter DOS), aber Google findet für Lazarus (und damit Free Pascal)
FileSeek

FileSeek(THandle, offset, origin)
FieRead(THandle, Buffer, Count)

So lange die Datei (oder das Device) als suchfähig ist, kannst Du damit beliebige Sektorweise im wahlfreien Zugriff lesen. Das macht aber nur auf SSDs "Spaß", sonst ist das lineare Lesen bedeutend schneller.

Aus Performancesicht ist das Lesen einzelner Sektoren aber das schlechteste, was man einem physikalischen Speichermedium antun kann. Sinnvoller ist es (falls die Daten sowieso benötigt werden und man von einem intakten Speichermedium ausgeht) Blöcke im MB-Bereich zu lesen und daraus selber die Sektoren zu extraieren. Bei welcher Blockgröße Du die beste Performance erreichst, kannst Du nur auf Deinem System (und der Programmiersprache) selber testen.

Und um die Performance dann noch zu steigern kann man für die so gelesenen Sektoren natürlich parallel Hashes berechnen lassen. Ob sich der Aufwand für die Parallelisierung lohnt, hängt von der Datenquelle ab. Im Backup-Modul meiner Bilddatenbank mache ich sowas (Datenquelle ist meist die interne SSD), im Prüfmodul für die archivierten Daten ist es nicht enthalten, da dies selbst auf einem uralten Atom Z520 mit einer USB2-Platte keinen Performancegewinn brachte.

Bei größeren Dateien, die über mehrere Sektoren gehen, ließen sich somit auch vielleicht welche Retten, wenn man zusammen passende Fragmente findet.
Die "Kunst" ist dabei aber, die korrekten Blöcke zu erkennen. falls man nicht das gesamte Speichermedium wieder herstellen möchte. Da geht es dann in die Analyse des Dateisystems.....

Geschickt angestelt landet man dann bei der Deduplikation (warumm sollte man identische Blöcke mehrmals speichern?), was aber wiederum das Dateisystem unterstützen muss.

P.S. warum bastelt die Forensoftware sosnt immer den Quellserver für einen Link dazu, hier aber nicht? Dann wäre mir aufgefallen, das der Link von taks zu FreePascal und nicht zu einer C++ Doku geht.
 
Zuletzt bearbeitet:
AW: Datei stückweise bzw. Sektorweise einlesen - Lazarus

Ich werde wohl immer 500-2000 MiB in den RAM laden. Würde das dann so machen, das man das in den Einstellungen anpassen kann. Für den Hexview würde dann einfach auf ein 100 MiB "Fenster" zugegriffen werden, das dann um 25, 50 oder ganze 100 MB (ebenfalls Einstellbar) verschoben wird, wenn man weiter scrollt. Im Hintergrund werden dann die nächsten 100 MB gelesen und der "obere" Teil aus dem RAM geschmissen.

Da das ganze ja als forensisches Tool eh ReadOnly ist, sollte es ja reichen vom Hexview über einen Pointer auf den belegten RAM zuzugreifen, anstatt da etwas rein zu kopieren. Wäre ja auf jeden Fall deutlich schneller und ressourcensparender, muss ich nur mal schauen, ob das klappt.

Programmierung und Nutzung des Tools erfolgt derzeit auf dem Desktop mit i5, da ist also auch recht viel RAM da.


Das Image selbst sollte natürlich auf einem intakten Datenträger liegen und wird entweder mit OSForensics oder (bei defekten Datenträgern gar nicht anders möglich) mit ddrescue gezogen.
Wobei das auch ewig dauern kann. 500 GB Platte, 5000 Byte/s (avg.) => ~ 3,2 Jahre :devil:

Da kann man dann schon Wetten abschließen, was eher stirbt - die Quellplatte mit reihenweise Defekten Sektoren, die 2 TB Zielplatte, die Dockingstation oder mein Laptop. :D
Bisher hat sie aber schon ne komplette Woche durchgehalten und durfte sich jetzt über Weihnachten 2 Wochen ausruhen. Siehe: Kopiergeschwindigkeit dd bzw. ddrescue


Edit (09.01.2017):
Mein bisheriger Code zum testen. Musste erst mal schauen, wie es läuft. Bei ner Blocksize von 20 MB gibt es Probleme mit dem ByteArray.
Ansonsten ist es wichtig die Streamposition zurückzusetzen.

Code:
procedure TForm1.Button2Click(Sender: TObject);
const Blocksize = 10 * 1024 *1024; // 10 MB   | 25 * 512 * 1024; // 12.5 MB;
const Blocknr = 1;
var F : File;
    Buffer: TStream;
    filesizevalue: Int64;
    //dataarray: array[0..511] of byte; //array[0..524287] of byte; //Array of TBytes; //TByteArray;
    dataarray: array[0..(Blocksize-1)] of byte;
    Stream: TMemoryStream;
    Result: Int64;
begin

  if OpenDialog1.Execute
    then
      begin
        KHexEditor1.LoadFromFile(OpenDialog1.FileName);


        Filesizevalue := FileSize(OpenDialog1.FileName);


        AssignFile(F, OpenDialog1.FileName);
        //Reset(F,524288);    Reset(F,512);
        Reset(F,Blocksize);


        Seek(F,Blocknr);


        BlockRead(F,DataArray,1,Result);


        ShowMessage('Result: '+IntToStr(result));


        ShowMessage(IntToHex(DataArray[0],2)+' '+IntToHex(DataArray[1],2)+'  ... '+IntToHex(DataArray[Length(DataArray)-2],2)+' '+IntToHex(DataArray[Length(DataArray)-1],2));


        Stream := TMemoryStream.Create;
        if Filesizevalue < ((Blocknr)*Blocksize)       // Wenn Block außerhalb der Datei
          then
            begin
              Stream.WriteBuffer(DataArray[0], 0);
            end
          else
            begin
              if Filesizevalue < ((Blocknr+1)*Blocksize)     // Wenn nur ein Teil der Datei im Block ist
                then
                  begin
                    Stream.WriteBuffer(DataArray[0], Filesizevalue - (Blocknr*Blocksize));
                  end
                else
                  Stream.WriteBuffer(DataArray[0], Length(DataArray));
            end;


        Stream.Position:=0;


        KHexEditor2.AddressOffset:=Blocknr * Blocksize;
        KHexEditor2.LoadFromStream(Stream);


        Stream.Free;
        CloseFile(F);
      end;                    

   
end;
 
Zuletzt bearbeitet:
Zurück