如果函数存在,如何检查DLL? [英] How to check a DLL if a function exists?

查看:36
本文介绍了如果函数存在,如何检查DLL?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究动态加载专门制定的 DLL 的东西.在考虑使用这个 DLL 之前,我需要能够检查 DLL 并确保所有预期的函数都存在.如果它缺少某些功能,我不应该尝试加载它.我知道我可以尝试调用其中一个函数并查看是否有异常,但我会在调试模式下看到错误.

I'm working on something which dynamically loads specially formulated DLL's. I need to be able to check the DLL and make sure all the expected functions exist before I consider using this DLL. If it's missing some certain functions, I should not try to load it. I know I could attempt to call one of the functions and see if there's an exception, but I would see errors in debug mode.

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

How should I go about checking a DLL if a function exists? I'd like to check it before I load it (using LoadLibrary) but I guess it's OK if I have to load it to perform this check too.

更新

我在下面接受了大卫的回答,并认为我会发布我的最终代码来展示整个过程.我把它变成了一个返回一个Bool的函数,不管成功与否,稍微清理了一下代码,在底部添加了另一个函数,用这个函数一个一个地检查每个名字.

I've accepted David's answer below, and thought I'd post my final code to show the whole process. I turned it into a function returning a Bool, whether it succeeded or not, cleaned the code a bit, and added another function at the bottom which uses this one to check each name one by one.

我决定使用这种方法而不是阅读GetProcAddress,因为它将在未来帮助我处理其他事情.

I decided to use this method instead of reading GetProcAddress because it will help me in the future with other things.

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, nil, PAGE_READONLY, 0, 0, nil);
    if ImageHandle = 0 then Exit;
    try
      ImagePointer:= MapViewOfFile(ImageHandle, FILE_MAP_READ, 0, 0, 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, ImagePointer,
          Cardinal(ExportTable.AddressOfNames), nil);
        if not Assigned(NamesPointer) then Exit;
        Names:= ImageRvaToVa(Header, ImagePointer, 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 的情况下廉价地读取版本资源.

If you are in control of the DLLs and you don't want to load them in order to check capability, then you could use the version resource to indicate capability. This would require the host app to have knowledge of what was the minimum supported version for each optional DLL feature. You can read the version resource cheaply without loading the DLL.

通过使用 LoadLibrary 将 DLL 加载到您的进程中,获取 DLL 导出的函数列表是完全可能的,而且相当简单.dbghelp.dll 系统库提供了服务来做到这一点.但是,我怀疑这对您的情况来说太过分了.

It is perfectly possible, and rather simple, to obtain the list of functions exported by a DLL with loading it into your process with LoadLibrary. The dbghelp.dll system library provides services to do that. However, I suspect that is overkill for your situation.

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

If it is not a problem to load and unload the DLL then GetProcAddress is probably the preferred solution. If there is some reason why you need to avoid loading the DLL in order to check capability, use the version resource to infer capability. If you need to do this with legacy DLLs that do not have a meaningful version resource then use dbghelp.dll to find the exported functions.

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

For the sake of completeness, here is some code to read all the exported symbols from a DLL, without loading it with 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),
    GENERIC_READ,
    FILE_SHARE_READ,
    nil,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    0
  );
  if FileHandle=INVALID_HANDLE_VALUE then begin
    exit;
  end;
  Try
    ImageHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
    if ImageHandle=0 then begin
      exit;
    end;
    Try
      ImagePointer := MapViewOfFile(ImageHandle, FILE_MAP_READ, 0, 0, 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, ImagePointer, Header.OptionalHeader.DataDirectory[0].VirtualAddress, nil);
        if not Assigned(ExportTable) then begin
          exit;
        end;

        NamesPointer := ImageRvaToVa(Header, ImagePointer, Cardinal(ExportTable.AddressOfNames), nil);
        if not Assigned(NamesPointer) then begin
          exit;
        end;
        Names := ImageRvaToVa(Header, ImagePointer, Cardinal(NamesPointer^), 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;

这篇关于如果函数存在,如何检查DLL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆