2012-07-17 2 views
2

작은 PE 파일 분석기를 작성 중이므로 PE 파일의 내용을 읽어야합니다. - 그것은 기적으로 발생Delphi XE2의 비동기 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 종종 지연 것을 눈치이다 : 아래 그림과 같이 나는 ReadFile 기능을 통해이 일을하고 있습니다. 그동안 UI는 얼어 붙어서 사용자에게 해를 끼치 지 않습니다. 사용자를 해고하려는 유혹을받을 것입니다. 스레딩을 고려해 봤지만 비동기 모드에서 ReadFile을 사용할 수있는 방법이 있는지 알고 싶습니다. 존재하지 않는다면 코드 내에서 수정해야 할 부분이 많을지라도 스레딩으로 넘어갈 것입니다.

미리 감사드립니다.

+1

호기심에서 벗어나서 휠을 재발 명할 이유가 있습니까? 2007 이전의 Delphi 버전에는 체육 정보를 덤프하는 데모가 있지만 (데모 이름이 없지만 나중에 게시 할 예정) [JEDI 코드 라이브러리] (http://delphi-jedi.org)에는 이 모든 작업을 수행 할 준비가 된 함수를 포함하는 전체 유닛 ('JclPEImage'). ('DependencyWalker'와 매우 유사한 방식으로 모든 것을 표시하는 PEViewer 데모도 있습니다.) 모든 코드는 UI 문제 또는 지연없이 매우 큰 실행 파일 (5MB +)에서도 잘 수행됩니다. –

+1

안녕하세요, @KenWhite, 구현에 대한 기본 설정이 없습니다. JEDI를 배제한 이유는 하나의 유닛이 다른 유닛과 연결되어 있기 때문입니다. 즉, IMHO를 사용하면 단일 프로젝트를 수행하는 것보다 전체 프로젝트를 유지하기가 어려워집니다. 이전 델파이 데모를 살펴 보겠습니다. 아직 델파이 7 디스크가 있습니다. –

+0

집에 도착하면 D7에서 데모 이름을 찾아서 여기에 의견을 게시합니다. 'JCL'은 유닛을 사용한 후에 'JVCL'보다 낫다. 왜냐하면 그것은 엄격한 코드이기 때문이다. 그것은 다른 것들을 추가하지만,이 경우에는 uses 절에'JclPEImage'를 포함하는 단일 유닛 이름을 추가하고 코드에서 간단한 함수를 호출합니다. 그것은 나쁘지 않습니다. (JVCL이 포함하고있는 다른 유닛들과도 귀찮게 공존 함). –

답변

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; 
+0

그게 최선의 방법이라고 생각합니다. 내 문제를 해결했지만 이것이 내 초기 질문에 가장 가까운 접근 방법 인 것 같습니다. 기술적 인 코멘트를 위해 특히 감사합니다, 전체 뭉치 :) –

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 
+0

아니요, UI가 관련되어있는 한 파일이 괜찮은 방식으로 처리되기를 기다리고 싶습니다. 사용자는 함수의 결과를 기다리는 것보다 많은 것을 할 필요가 없습니다. 메시지 처리는 아무 것도 변경하지 않습니다. 주요 병목 현상이 ReadFile 기능인 것처럼 보입니다. –

+2

'Application.ProcessMessages'를 사용하지 마십시오. 'SplashScreen.sLabel1.Update'를 사용하십시오. 이것은'ProcessMessages'의 부작용없이 똑같은 일을합니다 (새로운 텍스트를 보여줍니다). 'ProcessMessages'는 전염병처럼 피해야합니다. –

+0

Ken이 제안한대로 Application.ProcessMessages 또는 SplashScreen.sLabel1.Update를 사용하여 메시지를 처리하면 UI의 모양을 업데이트 할 수 있습니다. 나는 그것이 당신의 모든 시간을 감당하고 있다고 가정하기 때문에 반복 할 것을 제안했습니다. 그러나 SplashScreen.sLabel1.Caption을 변경할 때마다 할 수 있습니다. –

관련 문제