Использование Free Pascal\Lazarus для анализа большого двоичного файла на наличие определенных значений

Мне нужно проанализировать дамп ОЗУ на наличие записей MFT (из Файловая система NTFS).

В прошлом я занимался программированием в отношении чтения заголовков нескольких файлов (используя класс FileSearcher и т. д.), но я не совсем уверен, как начать чтение с начала большого файла, прочитать его и когда найдено определенное значение, мне нужно прочитать 1024 байта с точки, в которой найдено магическое значение (FILE0, в случае записей MFT), и «делать что-то» со значениями между этим и концом 1024 байта диапазон. Затем необходимо продолжить поиск следующей FILE0 записи.

Пока у меня есть следующее: мое намерение состоит в том, чтобы он читал исходный файл (который является TFileStream) в поисках «FILE0». Когда он найдет его, на данном этапе я просто хочу, чтобы он сообщил, что он нашел запись, и вывел позицию, но со временем мне нужно, чтобы он затем прочитал серию байтов с точки, в которой был найден FILE0:

type
    MFTRecordsStore = packed record
    FILE0MagicMarker: array[0..4] of byte;
    // Lots more follow....
end;

var
    MFTHeaderArray : MFTRecordsStore;
    FILE0Present : string;
    i : integer;

begin
    SourceFile.Position := 0;
    while (SourceFile.Position < SourceFile.Size) do
        begin
            SourceFile.ReadBuffer(MFTHeaderArray, SizeOf(MFTHeaderArray));
            for i := 0 to 4 do
                FILE0Present := FILE0Present + IntToHex(MFTHeaderArray.FILE0MagicMarker[i], 2);
                if FILE0Present = 'FILE0' then
                    begin
                        Memo1.Lines.Add('FILE0 Entry found at '+ IntToStr(SourceFile.Position));
                    end;
        end;
  end;

Этот код компилируется и запускается (начинает анализировать файл), но после нескольких минут интенсивной загрузки процессора программа вылетает и сообщает, что не может прочитать поток. У меня такое ощущение, что это как-то связано с тем, что файл дошел до конца, и не осталось полного «куска» для чтения, поэтому он вылетает?

Каково решение?


person Gizmo_the_Great    schedule 30.03.2012    source источник


Ответы (2)


Я публикую пример того, как я буду писать и читать файл записей с использованием потоков и искать в нем определенный текст ANSI. Вы также можете проверить commented version этого сообщения.

Вот определение записи, используемое в этом примере:

type
  TFileRecord = packed record
    Marker: array [0..4] of Byte;
    Width: Integer;
    Height: Integer;
    Useful: Boolean;
  end;

Вот как создать такой файл записей (то, что у вас уже есть :)

procedure TForm1.Button1Click(Sender: TObject);
var
  FileStream: TFileStream;
  FileRecord: TFileRecord;
const
  RecordSize = SizeOf(TFileRecord);

  procedure FillFileRecord(const AMarker: string; const AWidth: Integer;
    const AHeight: Integer; const AUseful: Boolean);
  begin
    FillChar(FileRecord, RecordSize, 0);
    Move(AMarker[1], FileRecord.Marker, Length(FileRecord.Marker));
    FileRecord.Width := AWidth;
    FileRecord.Height := AHeight;
    FileRecord.Useful := AUseful;
  end;

begin
  FileStream := TFileStream.Create('File.dat', fmCreate);
  try
    FillFileRecord('FILE1', 111, 112, False);
    FileStream.Write(FileRecord, RecordSize);
    FillFileRecord('FILE2', 211, 212, False);
    FileStream.Write(FileRecord, RecordSize);
    FillFileRecord('FILE3', 311, 312, False);
    FileStream.Write(FileRecord, RecordSize);
    FillFileRecord('FILE4', 411, 412, False);
    FileStream.Write(FileRecord, RecordSize);
    FillFileRecord('FILE0', 666, 777, True);
    FileStream.Write(FileRecord, RecordSize);
    FillFileRecord('FILE5', 511, 512, False);
    FileStream.Write(FileRecord, RecordSize);
    FillFileRecord('FILE0', 11111, 22222, True);
    FileStream.Write(FileRecord, RecordSize);
    FillFileRecord('FILE6', 611, 612, False);
    FileStream.Write(FileRecord, RecordSize);
  finally
    FileStream.Free;
  end;
end;

А вот как прочитать такой файл:

procedure TForm1.Button2Click(Sender: TObject);
var
  FileStream: TFileStream;
  FileRecord: TFileRecord;
const
  HeaderSeq = 'FILE0';
  HeaderLen = Length(HeaderSeq);
  RecordSize = SizeOf(TFileRecord);
begin
  FileStream := TFileStream.Create('File.dat', fmOpenRead);
  try
    while FileStream.Read(FileRecord, RecordSize) = RecordSize do
    begin
      if CompareMem(@HeaderSeq[1], @FileRecord.Marker[0], HeaderLen) then
      begin
        Memo1.Lines.Add('FILE0 entry found at '+
          IntToStr(FileStream.Position - RecordSize));
        Memo1.Lines.Add('FileRecord.Width = ' +
          IntToStr(FileRecord.Width));
        Memo1.Lines.Add('FileRecord.Height = ' +
          IntToStr(FileRecord.Height));
        Memo1.Lines.Add('FileRecord.Useful = ' +
          BoolToStr(FileRecord.Useful, True));
      end;
    end;
  finally
    FileStream.Free;
  end;
end;   
person TLama    schedule 31.03.2012
comment
В мире есть люди, которых нужно благодарить за их время и усилия. Ты в их числе. Очень хорошо, что вы потратили много времени на подготовку! Я посмотрю на его разработку и использую его полностью или частично и посмотрю, как я справлюсь. Еще раз благодарю вас за вашу помощь. - person Gizmo_the_Great; 31.03.2012
comment
См. commented version. ИМХО здесь больше нечего оптимизировать или упрощать. Если вы замените, например. Read с ReadBuffer единственное, что вы получите, это сообщение об ошибке, возникающее при блокировке записи вашего файла поврежден (по какой-то неожиданной причине). - person TLama; 31.03.2012
comment
Эй, TLama... Должен признаться, что я немного боролся с этим и попытался сократить путь (согласно моему другому вопросу), но я уже понимаю, что для этой задачи я должен приложить усилия в начале и тогда все остальное встанет на свои места позже. Итак, сегодня вечером я провел время, читая вашу версию с комментариями, и собираюсь реализовать ее на пасхальных каникулах. Спасибо. - person Gizmo_the_Great; 05.04.2012
comment
В Lazarus\FreePascal нет TFileRecord. Я не уверен, что эквивалент может быть. - person Gizmo_the_Great; 06.04.2012
comment
Это определение записи, которое я использовал в примере. У меня TFileRecord, у вас MFTRecordsStore ;-) Существует негласное соглашение называть типы с начальным символом T, поэтому в вашем случае это будет TMFTRecordsStore. - person TLama; 06.04.2012
comment
Тлама... почему я когда-либо сомневался в твоем решении! Посидев некоторое время, чтобы попытаться понять все это, и получив ваше объяснение выше, оно работает! Я использовал только небольшой образец всего кода, но то, что у меня есть сейчас, делает то, чего нет в других примерах, а именно: а) возвращает находку FILE0 и б) это смещение в исходном файле (в отличие от того, что из буфер) и c) выводит эту информацию! (единственная загвоздка в том, что он надежно находит записи, но сообщаемая позиция всегда на 54 байта дальше, чем на самом деле? Тем не менее, еще раз спасибо за вашу помощь! - person Gizmo_the_Great; 06.04.2012
comment
TLama - разобрался (думаю). Позиция всегда будет в конце размера буфера. Таким образом, если размер буфера составляет 54 байта, и запись была найдена в этом сегменте буфера, он сообщает, что нашел ее в конце буфера. На данный момент я только что сделал SourceFilePositionLessBufferSize := SourceFile.Position - ArraySize; и это надежно теперь работает каждый раз! Я не уверен, что это обман? - person Gizmo_the_Great; 06.04.2012
comment
Чтение потока начинается с позиции 0 (то, что установлено после TFileStream.Create) и каждый последующий вызов TFileStream.Read будет читать (из моего примера) RecordSize байт и увеличивать позицию на значение RecordSize. Так что да, вы правы, TFileStream.Position может сообщить о конце файла в моем примере. Так что использовать что-то вроде RealPosition := SourceFile.Position - ArraySize; однозначно правильно (это у меня баг в этом посте, исправлю). Спасибо за указание на это и извините за введение в заблуждение :-) - person TLama; 06.04.2012
comment
TLama - это меняет то, что я помогаю что-то заметить! Еще один комментарий: код читает исходный файл очень маленькими сегментами по 5 байт, что может быть довольно медленным. Как я могу увеличить размер буфера, скажем, до 1 Мб без потери точности\эффективности кода? Извините, что спрашиваю, но это все еще очень ново для меня. - person Gizmo_the_Great; 09.04.2012
comment
Всем привет, насчет медленного чтения вы правы, эффективнее будет читать большой блок и работать с ним из памяти. Всего один вопрос; ваши файлы всегда будут состоять только из записей, выровненных по одному размеру? Я спрашиваю, потому что думаю о массиве этих записей. - person TLama; 09.04.2012
comment
ваши файлы всегда будут состоять только из записей, выровненных по одному размеру? - Записи MFT обычно (и обычно) имеют общий размер 1024 байта, но иногда они могут быть больше. Однако магический маркер «FILE0» всегда появляется в первых 5 байтах такой записи. Итак, что я собираюсь сделать, это проанализировать файл дампа, который может быть размером в Гб в буферах, скажем, 10 Мб, найти записи «FILE0» и для каждой записи прочитать 1019 байтов с этой точки. Это долгосрочная цель! - person Gizmo_the_Great; 09.04.2012
comment
Не могли бы вы присоединиться к this room для дальнейшего обсуждения? - person TLama; 09.04.2012

Если вы действительно подозреваете чтение после EOF, попробуйте:

while (SourceFile.Position + SizeOf(MFTHeaderArray) <= SourceFile.Size) do

person user323094    schedule 30.03.2012
comment
Привет. Да, это остановило ошибку, спасибо. Код по-прежнему ничего не выводит, но я думаю, что это связано с ошибкой в ​​другом месте, и мой вопрос заключался в том, чтобы найти решение ошибки потока, которая теперь решена благодаря вашему предложению. - person Gizmo_the_Great; 31.03.2012
comment
О, я готовил весь пример (он уже размещен здесь для обзора в удаленном состоянии). Так что только одно замечание: будьте осторожны, когда вы используете ReadBuffer, вы можете получить ошибку, когда блок для чтения не содержит достаточно данных. Я сделал это для нетипизированных файлов, что означает, что заголовок и пакет могут быть повсюду в файле, поэтому данные не нужно будет выравнивать по пакетам. - person TLama; 31.03.2012
comment
Пожалуйста, опубликуйте свой пример, если вы это сделали! Это обязательно будет лучше, чем мои усилия!! В вашем комментарии нет ссылки? Так что, если бы вы могли добавить это, я был бы обязан. - person Gizmo_the_Great; 31.03.2012