【问题标题】:Using Free Pascal\Lazarus to parse a large binary file for specific values使用 Free Pascal\Lazarus 解析大型二进制文件的特定值
【发布时间】:2012-03-30 14:06:55
【问题描述】:

我需要为MFT 记录(来自NTFS 文件系统)解析RAM 转储。

我过去做过一些关于读取多个文件的标题的编程(使用 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;

此代码编译并运行(它开始解析文件),但在 CPU 使用几分钟后,程序崩溃并报告它无法读取流。我感觉这与到达文件末尾有关,并且没有完整的“块”可供读取,所以它崩溃了?

解决办法是什么?

【问题讨论】:

    标签: while-loop eof freepascal lazarus


    【解决方案1】:

    我正在发布一个示例,说明如何使用流写入和读取记录文件并在其中寻找特定的 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;   
    

    【讨论】:

    • 世界上有些人需要感谢他们的时间和努力。你是他们中的一员。你能花我想象的很多时间来准备,真是太好了!我会考虑解决它并全部或部分使用它,然后看看我是怎么做的。再次感谢您的帮助。
    • commented version。恕我直言,这里没有什么可以优化或简化的了。如果你更换例如ReadReadBuffer 你唯一会得到的是当你的文件记录块损坏时引发的错误消息(出于某种意外原因)。
    • Hey TLama ....我必须承认我在这方面有点挣扎并尝试了一条捷径(根据我的另一个问题),但我已经意识到对于这项任务,我必须一开始就付出努力,然后其他一切都会到位。所以我今晚又花时间阅读了你的评论版本,并将在复活节假期期间实施它。谢谢。
    • Lazarus\FreePascal 中没有 TFileRecord。我不确定等价物可能是什么。
    • TLama...为什么我曾经怀疑过您的解决方案!坐了一会儿试图理解这一切,并在上面收到了你的解释,它有效!我只使用了总代码的一小部分示例,但我现在所做的是其他示例没有的,那就是 a) 返回 FILE0 的查找和 b) 它在原始文件中的偏移量(与缓冲区)和c)输出该信息! (唯一的障碍是它可以可靠地找到条目,但报告的位置总是比实际位置多 54 个字节?无论如何,再次感谢您的帮助!
    【解决方案2】:

    如果您真的怀疑阅读过 EOF,请尝试:

    while (SourceFile.Position + SizeOf(MFTHeaderArray) &lt;= SourceFile.Size) do

    【讨论】:

    • 嗨。是的,这已经停止了错误,谢谢。该代码仍然没有输出任何内容,但我认为这是由于其他地方的错误,我的问题是找到解决流错误的方法,由于您的建议,现在已经解决了。
    • D'oh,我正在准备整个示例(已在此处发布以在已删除状态下查看)。所以请注意,当您使用ReadBuffer 时要小心,当要读取的块不包含足够的数据时,您可能会引发错误。我对无类型文件执行此操作,这意味着标题和 packet 可能在文件中的任何位置,因此数据不需要对齐数据包。
    • 如果您已经完成,请发布您的示例!一定比我的努力好!!您的评论中没有链接?因此,如果您可以添加它,我将有义务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-13
    相关资源
    最近更新 更多