加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – 如果函数存在,如何检查DLL?

发布时间:2020-12-15 09:49:54 所属栏目:大数据 来源:网络整理
导读:我正在研究动态加载特殊配方DLL的东西.在考虑使用此DLL之前,我需要能够检查DLL并确保所有预期的功能都存在.如果它缺少某些功能,我不应该尝试加载它.我知道我可以尝试调用其中一个函数并查看是否存在异常,但我会在调试模式下看到错误. 如果函数存在,我该如何
我正在研究动态加载特殊配方DLL的东西.在考虑使用此DLL之前,我需要能够检查DLL并确保所有预期的功能都存在.如果它缺少某些功能,我不应该尝试加载它.我知道我可以尝试调用其中一个函数并查看是否存在异常,但我会在调试模式下看到错误.

如果函数存在,我该如何检查DLL?我想在加载它之前检查它(使用LoadLibrary),但我想如果我必须加载它来执行此检查也没关系.

UPDATE

我在下面接受了David的回答,并且认为我会发布我的最终代码以显示整个过程.我把它变成了一个函数返回一个Bool,无论它是否成功,清理了一下代码,并在底部添加了另一个函数,使用这个函数逐个检查每个名字.

我决定使用这种方法而不是阅读GetProcAddress,因为它将来会帮助我解决其他问题.

type
? PIMAGE_NT_HEADERS = ^IMAGE_NT_HEADERS;
? PIMAGE_EXPORT_DIRECTORY = ^IMAGE_EXPORT_DIRECTORY;

function ImageNtHeader(Base: Pointer): PIMAGE_NT_HEADERS; stdcall; 
  external 'dbghelp.dll';
function ImageRvaToVa(NtHeaders: Pointer; Base: Pointer; Rva: ULONG; 
  LastRvaSection: Pointer): Pointer; stdcall; external 'dbghelp.dll';

function ExportedFunctionNames(const ImageName: string; NamesList: TStrings): Bool;
var
  i: Integer;
  FileHandle: THandle;
  ImageHandle: THandle;
  ImagePointer: Pointer;
  Header: PIMAGE_NT_HEADERS;
  ExportTable: PIMAGE_EXPORT_DIRECTORY;
  NamesPointer: Pointer;
  Names: PAnsiChar;
  NamesDataLeft: Integer;
begin
  Result:= False;
  NamesList.Clear;
  FileHandle:= CreateFile(PChar(ImageName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  if FileHandle = INVALID_HANDLE_VALUE then Exit;
  try
    ImageHandle:= CreateFileMapping(FileHandle,PAGE_READONLY,nil);
    if ImageHandle = 0 then Exit;
    try
      ImagePointer:= MapViewOfFile(ImageHandle,FILE_MAP_READ,0);
      if not Assigned(ImagePointer) then Exit;
      try
        Header:= ImageNtHeader(ImagePointer);
        if not Assigned(Header) then Exit;
        if Header.Signature <> $00004550 then Exit; // "PE" as a DWORD.
        ExportTable:= ImageRvaToVa(Header,ImagePointer,Header.OptionalHeader.DataDirectory[0].VirtualAddress,nil);
        if not Assigned(ExportTable) then Exit;
        NamesPointer:= ImageRvaToVa(Header,Cardinal(ExportTable.AddressOfNames),nil);
        if not Assigned(NamesPointer) then Exit;
        Names:= ImageRvaToVa(Header,Cardinal(NamesPointer^),nil);
        if not Assigned(Names) then Exit;
        NamesDataLeft:= Header.OptionalHeader.DataDirectory[0].Size;
        for i:= 0 to ExportTable.NumberOfNames - 1 do begin
          NamesList.Add(Names);
          while (Names^ <> chr(0)) and (NamesDataLeft > 0) do begin
            Inc(Names);
            Dec(NamesDataLeft);
          end;
          Inc(Names);
        end;
        Result:= True;
      finally
        UnmapViewOfFile(ImagePointer);
      end;
    finally
      CloseHandle(ImageHandle);
    end;
  finally
    CloseHandle(FileHandle);
  end;
end;

function IsMyDLL(const Filename: String): Bool;
var
  H: THandle;
  L: TStringList;
  function InList(const Func: String): Bool;
  begin
    Result:= L.IndexOf(Func) >= 0;
  end;
begin
  Result:= False;
  L:= TStringList.Create;
  try
    if ExportedFunctionNames(Filename,L) then begin
      Result:=//Names of functions which need to exist
        InList('GetName') and
        InList('GetDescription') and
        InList('GetVersion') and
        InList('Start') and
        InList('Stop');
    end;
  finally
    L.Free;
  end;
end;

解决方法

如果您控制DLL并且您不想加载它们以检查功能,那么您可以使用版本资源来指示功能.这将要求主机应用程序知道每个可选DLL功能的最低支持版本.您可以廉价地读取版本资源而无需加载DLL.

获取DLL导出的函数列表并使用LoadLibrary将其加载到进程中是完全可能的,而且非常简单. dbghelp.dll系统库提供了执行此操作的服务.但是,我怀疑这对你的情况来说太过分了.

如果加载和卸载DLL不是问题,那么GetProcAddress可能是首选的解决方案.如果有一些原因需要避免加载DLL以检查功能,请使用版本资源来推断功能.如果需要对没有有意义版本资源的旧DLL执行此操作,请使用dbghelp.dll查找导出的函数.

为了完整起见,这里有一些代码可以从DLL中读取所有导出的符号,而无需使用LoadLibrary加载它.

type
  PIMAGE_NT_HEADERS = ^IMAGE_NT_HEADERS;
  PIMAGE_EXPORT_DIRECTORY = ^IMAGE_EXPORT_DIRECTORY;

function ImageNtHeader(Base: Pointer): PIMAGE_NT_HEADERS; stdcall; external 'dbghelp.dll';
function ImageRvaToVa(NtHeaders: Pointer; Base: Pointer; Rva: ULONG; LastRvaSection: Pointer): Pointer; stdcall; external 'dbghelp.dll';

procedure ImageExportedFunctionNames(const ImageName: string; NamesList: TStrings);
var
  i: Integer;
  FileHandle: THandle;
  ImageHandle: THandle;
  ImagePointer: Pointer;
  Header: PIMAGE_NT_HEADERS;
  ExportTable: PIMAGE_EXPORT_DIRECTORY;
  NamesPointer: Pointer;
  Names: PAnsiChar;
  NamesDataLeft: Integer;
begin
  //NOTE: our policy in this procedure is to exit upon any failure and return an empty list

  NamesList.Clear;

  FileHandle := CreateFile(
    PChar(ImageName),0
  );
  if FileHandle=INVALID_HANDLE_VALUE then begin
    exit;
  end;
  Try
    ImageHandle := CreateFileMapping(FileHandle,nil);
    if ImageHandle=0 then begin
      exit;
    end;
    Try
      ImagePointer := MapViewOfFile(ImageHandle,0);
      if not Assigned(ImagePointer) then begin
        exit;
      end;

      Try
        Header := ImageNtHeader(ImagePointer);
        if not Assigned(Header) then begin
          exit;
        end;
        if Header.Signature<>$00004550 then begin // "PE" as a DWORD.
          exit;
        end;

        ExportTable := ImageRvaToVa(Header,nil);
        if not Assigned(ExportTable) then begin
          exit;
        end;

        NamesPointer := ImageRvaToVa(Header,nil);
        if not Assigned(NamesPointer) then begin
          exit;
        end;
        Names := ImageRvaToVa(Header,nil);
        if not Assigned(Names) then begin
          exit;
        end;

        NamesDataLeft := Header.OptionalHeader.DataDirectory[0].Size;
        for i := 0 to ExportTable.NumberOfNames-1 do begin
          NamesList.Add(Names);
          // Locate the next name
          while (Names^<>chr(0)) and (NamesDataLeft>0) do begin
            inc(Names);
            dec(NamesDataLeft);
          end;
          inc(Names);
        end;
      Finally
        UnmapViewOfFile(ImagePointer); // Ignore error as there is not much we could do.
      End;
    Finally
      CloseHandle(ImageHandle);
    End;
  Finally
    CloseHandle(FileHandle);
  End;
end;

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读