2

我想制作一个应用程序,列出 DLL 中所有导出和导入的函数。

我已经通过使用ImageDirectoryEntryToData功能完成了导出部分:

 type
 TDLLExportCallback = function (const name: String; ordinal: Integer;
     address: Pointer): Boolean of Object;

Function ListDLLExports( const filename : String ; callback : TDLLExportCallback ;Var        ErrMsg : String) : Boolean; 
Var 

  imageinfo: LoadedImage;
  pExportDirectory: PImageExportDirectory;
  dirsize: Cardinal; 
Begin { ListDLLExports }
  Result :=False ;
  Assert( Assigned( callback ));  // assert(expression) --> raise an exception if expr is eval to false;
If not FileExists( filename ) Then
Begin
  ErrMsg := Format(eDLLnotFound, [filename]);
  Result := False;
  Exit;
End;
If MapAndLoad( PChar( filename ), nil, @imageinfo, true, true ) Then
 try
    pExportDirectory := ImageDirectoryEntryToData(imageinfo.MappedAddress, False,
                        IMAGE_DIRECTORY_ENTRY_EXPORT, dirsize );
    If pExportDirectory = Nil Then
    begin
      ErrMsg := SysErrorMessage(GetLastError);
      Result := False;
      Exit;
    end
    Else
      EnumExports( pExportDirectory^, imageinfo, callback );

  finally 
    UnMapAndLoad( @imageinfo );
  end 
Else
Begin
  ErrMsg := SysErrorMessage(GetLastError);
  Result := False;
  Exit;
  //RaiseLastWin32Error;
End;

 End; { ListDLLExports }


Procedure EnumExports( const ExportDirectory : TImageExportDirectory ;
                   const image : LoadedImage ;
                   callback : TDLLExportCallback ) ; 
 Type 
    TDWordArray = Array [0..$FFFFF] of DWORD; 
 Var 
    i: Cardinal; 
   pNameRVAs, pFunctionRVas: ^TDWordArray; 
   pOrdinals: ^TWordArray; 
   name: String;
   base : Pointer;
   address: Pointer;
   ordinal: Word;
 Begin { EnumExports }

   pNameRVAs := RVAToPointer( DWORD(ExportDirectory.AddressOfNames), image );
   pFunctionRVAs := RVAToPointer( DWORD(ExportDirectory.AddressOfFunctions), image );
   pOrdinals := RVAToPointer( DWORD(ExportDirectory.AddressOfNameOrdinals), image );
   For i:= 0 to Pred( ExportDirectory.NumberOfNames ) Do
   Begin
     name := RVAToPChar( pNameRVAs^[i], image );
     ordinal := pOrdinals^[i];
     address := Pointer( pFunctionRVAs^[ ordinal ] );

     If not callback( name, ordinal+ExportDirectory.Base, address ) Then
        Exit; 
    End; { For }
  End; { EnumExports }



Function RVAToPointer( rva : DWORD ; const Image : LoadedImage ) : Pointer; 
 var 
   pDummy: PImageSectionHeader; 
 Begin { RVAToPchar } 
     pDummy := nil; 
     Result := ImageRvaToVa( Image.FileHeader, Image.MappedAddress, rva, pDummy ); 
     If Result = Nil Then 
          RaiseLastWin32Error; 
 End; { RVAToPointer } 

  Function RVAToPchar( rva : DWORD ; const Image : LoadedImage ) : PChar ; 
  Begin { RVAToPchar } 
       Result := RVAToPointer( rva, image ); 
  End; { RVAToPchar } 

最后我使用了以下回调函数:

 Function TFrmDLLViewer.MyImportCallBack(const Name : String ; Ord1 : Integer ; Addr1 :     Pointer) : Boolean;
Begin
  Result := False;
  Form1.Memo1.Lines.Add(Name + '  ' + IntToStr(Ord1) + '=' +a ) ;
  Result := True;

end;

此导出目录代码有效。

我想知道如何适当地修改它以获取导入目录表。

提前致谢 。

4

1 回答 1

0

回调原型:

type
  TDLLImportCallback = function(const module, name: string): Boolean of object;

典型用法:

function TMainForm.ImportCallback(const module, name: string): Boolean;
begin
  Memo1.Lines.Add(Format('   Module: <%-30s>  Name  : <%-30s>', [module, name]));
  //
  Result := True;
end;  

procedure TMainForm.DisplayHeader(const AHeader: string);
begin
  if Memo1.Lines.Count<>0 then
    Memo1.Lines.Add('');
  Memo1.Lines.Add(AHeader);
  Memo1.Lines.Add('');
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  Caption := 'DllExportImport - ['+FFileName+']';

  Memo1.Clear;

  //DisplayHeader('Exports:');
  //ListDLLExports(FFileName, ExportCallback, FErrorMSG);

  DisplayHeader('Imports:');
  ListDLLImports(FFileName, ImportCallback, FErrorMSG); // <<<
end;

棘手的EnumImports实现:

procedure EnumImports(const ImportDirectory: TImageImportDirectory;
  const Image: LoadedImage; callback: TDLLImportCallback);
var
  pImportDirectory: PImageImportDirectory;
  pThunk : PImageThunkData32;
  modulename: string;
  name: string;
begin
  pImportDirectory := PImageImportDirectory(@ImportDirectory);

  while pImportDirectory.Name <> 0 do
  begin
    modulename := RVAToPChar(DWord(pImportDirectory.Name), image); // <<<

    pThunk := RVAToPointer(DWord(pImportDirectory.OriginalFirstThunk), Image);

    if pThunk <> nil then
    begin
      while pThunk^.Function_ <> nil do
      begin
        name := RVAToPChar(DWORD(@pThunk^.AddressOfData^.Name), image); // <<<
        //
        if not callback(modulename, name) then
          Exit;
        //
        Inc(pThunk);
      end;
      //
      Inc(pImportDirectory);
    end;
  end;
end;

最困难的事情是找到在 Delphi 中根本不可用的所需的适当结构。

为方便起见,保留了与EnumExports类似的签名:将第一个参数作为 PImageImportDirectory 而不是 TImageImportDirectory 更合适。


直接ListDLLImports实施:

function ListDLLImports(const filename: string; callback: TDLLImportCallback;
  var ErrMsg: string): Boolean;
var
  imageinfo: LoadedImage;
  pImportDirectory: PImageImportDirectory;
  dirsize: Cardinal;
begin
  Result := False;

  Assert(Assigned(callback));

  if not FileExists(filename) then
  begin
    ErrMsg := Format('%s not found.', [filename]);
    Result := False;
    //
    Exit;
  end;

  if MapAndLoad(PAnsiChar(filename), nil, @imageinfo, True, True) then
    try
      pImportDirectory := ImageDirectoryEntryToData(imageinfo.MappedAddress,
        False, IMAGE_DIRECTORY_ENTRY_IMPORT, dirsize);

      if pImportDirectory = nil then
      begin
        ErrMsg := SysErrorMessage(GetLastError);
        Result := False;
        //
        Exit;
      end
      else
        EnumImports(pImportDirectory^, imageinfo, callback);
    finally
      UnMapAndLoad(@imageinfo);
    end
  else
  begin
    ErrMsg := SysErrorMessage(GetLastError);
    Result := False;
  end;
end;
于 2012-04-20T08:51:23.723 回答