2

小さな PE ファイル アナライザーを作成していて、PE ファイルの内容を読み取る必要があります。ReadFile以下に示すように、関数を介してこれを行っています。

function TMainForm.GetPEData(var filename: string) : boolean;
var
  hFile:   DWORD;
  IDH:     TImageDosHeader;
  INH:     TImageNtHeaders;
  ISH:     TImageSectionHeader;
  dwRead: DWORD;
  szBuff: array[0..7] of Char;
  i:      WORD;
  PE: TPEFile;
begin
  Result := False;
  PE := TPeFile.Create;
  if PE.LoadFromFile (filename) then  
    Form2.edEntryPoint.Text := IntToHex(PE.RvaToFileOffset(PE.AddressOfEntryPoint), 8);
  SplashScreen.sLabel1.Caption := 'PE File Loaded';
  hFile := CreateFile(PChar(filename), GENERIC_READ, 
                      FILE_SHARE_WRITE, nil, 
                      OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    SetFilePointer(hFile, 0, nil, FILE_BEGIN);
    SplashScreen.sLabel1.Caption := 'Reading DOS File Headers...';
    ReadFile(hFile, IDH, 64, dwRead, nil);
    if IDH.e_magic = IMAGE_DOS_SIGNATURE then
    begin
      SetFilePointer(hFile, IDH._lfanew, nil, FILE_BEGIN);
      SplashScreen.sLabel1.Caption := 'Reading NT File Headers...';
      //Here is where the UI freezes while the file is read...
      ReadFile(hFile, INH, 248, dwRead, nil);
      if INH.Signature = IMAGE_NT_SIGNATURE then
      begin
        Form2.edImageBase.Text := IntToHex(INH.OptionalHeader.ImageBase, 8);
        Form2.edSizeOfImage.Text := IntToHex(INH.OptionalHeader.SizeOfImage, 8);
        Form2.edLinkerVersion.Text := IntToStr(INH.OptionalHeader.MajorLinkerVersion) + '.' + 
              IntToStr(INH.OptionalHeader.MinorLinkerVersion);
        Form2.edFileAlignment.Text := IntToHex(INH.OptionalHeader.FileAlignment, 8);
        Form2.edSectionAlignment.Text := IntToHex(INH.OptionalHeader.SectionAlignment, 8);
        Form2.edSubSystem.Text := IntToHex(INH.OptionalHeader.Subsystem, 4);
        Form2.edEPFilestamp.Text := IntToStr(INH.FileHeader.TimeDateStamp);
        Form2.edFileType.Text := GetPEFileType(PE.ImageNtHeaders.Signature);

        for i := 0 to INH.FileHeader.NumberOfSections - 1 do
        begin
          SetFilePointer(hFile, IDH._lfanew + 248 + i * 40, nil, FILE_BEGIN);
          ReadFile(hFile, ISH, 40, dwRead, nil);
          CopyMemory(@szBuff[0], @ISH.Name[0], 8);

          with Form2.sListView1.Items.Add do
          begin
            Caption := ShortString(szBuff);
            SubItems.Add(IntToHex(ISH.VirtualAddress, 8));
            SubItems.Add(IntToHex(ISH.Misc.VirtualSize, 8));
            SubItems.Add(IntToHex(ISH.PointerToRawData, 8));
            SubItems.Add(IntToHex(ISH.SizeOfRawData, 8));
            SubItems.Add(IntToHex(ISH.Characteristics, 8));
          end;
        end;
      end;
    end;
    CloseHandle(hFile);
    Result := True;
  end;
end;

悪いことに、ファイルのサイズによっては、ReadFile遅延が発生することが多く、同期的に発生することに気付きました。その間、UI はフリーズし、ユーザーにはひどく間違ったように見えます。ユーザーは UI を終了させたくなるでしょう。ReadFileスレッド化を検討しましたが、非同期モードで使用できる方法があるかどうかを確認したいだけです。そうでない場合は、コードを変更する必要がたくさんある場合でも、スレッド化にジャンプします。

前もって感謝します。

4

2 に答える 2

1

ReadFile関数は、ファイルからデータを読み取り、ファイルポインターが示す位置から開始します。この関数は、同期操作と非同期操作の両方に使用できます。

ReadFileを非同期で使用することは可能ですが、UIによっては、これが最善の解決策ではない場合があります。PEファイルがロードされるのを待っている間にユーザーに何かをしてもらいたいですか?

ユーザーを待たせても、プログラムがフリーズしないことを確信できる場合は、プログレスバーを追加するか、SplashScreenを更新するだけです。

for i := 0 to INH.FileHeader.NumberOfSections - 1 do
begin
   SplashScreen.sLabel1.Caption := 'Reading section ' + IntToStr(i) + ' of ' + IntToStr(INH.FileHeader.NumberOfSections);
   SplashScreen.sLabel1.Update; // see Ken Whites comment
   // Application.ProcessMessages;
   ...
end
于 2012-07-17T19:53:46.227 に答える
1

この場合、常にファイル全体をメモリに読み込みます。また、操作を簡単にするために TFileStream クラスを使用します。

ファイル全体をメモリに格納する方が簡単で、PE ファイルは通常小さいです。

  type
    TSections = array [0..0] of TImageSectionHeader;
    PSections = ^TSections;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  FS : TFileStream;
  fisier : PImageDosHeader;
  INH : PImageNtHeaders;
  ISH : PSections;
  i   : Word;
begin
  FS := TFileStream.Create('fisierul_tau.exe',fmOpenRead);
  GetMem(fisier,FS.size); //Aloci memorie pentru fisier
  FS.Read(fisier^,FS.Size); // Il citesti;
  FS.Free;
  INH := PImageNtHeaders(DWORD(fisier) + DWORD(fisier^._lfanew));
  ISH := PSections(DWORD(INH) + SizeOf(TImageNtHeaders));
  for i := 0 to INH^.FileHeader.NumberOfSections - 1 do
  begin
      ShowMessage(PAnsiChar(@ISH[i].Name[0]));
  end;
end;
于 2012-07-18T12:03:11.603 に答える