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

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

问题描述

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



我应该如何检查一个DLL如果一个函数存在?我想在之前检查 我加载它(使用 LoadLibrary ),但是我想没有必要加载它来执行此检查



更新



我已经接受了David的回答,我会发布我的最终代码来显示整个过程。我把它变成一个返回Bool的函数,无论它是否成功,清理了一些代码,并在底部添加了另一个功能,使用这个功能逐个检查每个名称。



我决定使用这种方法,而不是阅读 GetProcAddress ,因为它会帮助我在未来与其他东西。

 键入
PIMAGE_NT_HEADERS = ^ IMAGE_NT_HEADERS;
PIMAGE_EXPORT_DIRECTORY = ^ IMAGE_EXPORT_DIRECTORY;

函数ImageNtHeader(Base:Pointer):PIMAGE_NT_HEADERS;标准
external'dbghelp.dll';
function ImageRvaToVa(NtHeaders:Pointer; Base:Pointer; Rva:ULONG;
LastRvaSection:Pointer):Pointer;标准外部'dbghelp.dll';

函数ExportedFunctionNames(const ImageName:string; NamesList:TStrings):Bool;
var
i:整数;
FileHandle:THandle;
ImageHandle:THandle;
ImagePointer:指针
标题:PIMAGE_NT_HEADERS;
ExportTable:PIMAGE_EXPORT_DIRECTORY;
NamesPointer:指针;
名称:PAnsiChar;
NamesDataLeft:Integer;
begin
结果:= False;
NamesList.Clear;
FileHandle:= CreateFile(PChar(ImageName),GENERIC_READ,FILE_SHARE_READ,
nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
如果FileHandle = INVALID_HANDLE_VALUE则退出;
try
ImageHandle:= CreateFileMapping(FileHandle,nil,PAGE_READONLY,0,0,nil);
如果ImageHandle = 0,则退出;
try
ImagePointer:= MapViewOfFile(ImageHandle,FILE_MAP_READ,0,0,0);
如果没有分配(ImagePointer)然后退出;
try
标题:= ImageNtHeader(ImagePointer);
如果没有分配(标题)然后退出;
如果Header.Signature<> $ 00004550然后退出; //PE\0\0作为DWORD。
ExportTable:= ImageRvaToVa(Header,ImagePointer,
Header.OptionalHeader.DataDirectory [0] .VirtualAddress,nil);
如果没有分配(ExportTable)然后退出;
NamesPointer:= ImageRvaToVa(Header,ImagePointer,
Cardinal(ExportTable.AddressOfNames),nil);
如果没有分配(NamesPointer)然后退出;
名称:= ImageRvaToVa(Header,ImagePointer,Cardinal(NamesPointer ^),nil);
如果没有分配(名称)然后退出;
NamesDataLeft:= Header.OptionalHeader.DataDirectory [0] .Size;
for i:= 0 to ExportTable.NumberOfNames - 1 do begin
NamesList.Add(Names);
while(Names ^<> chr(0))和(NamesDataLeft> 0)do begin
Inc(Names);
Dec(NamesDataLeft);
结束
Inc(Names);
结束
结果:= True;
finally
UnmapViewOfFile(ImagePointer);
结束
finally
CloseHandle(ImageHandle);
结束
finally
CloseHandle(FileHandle);
结束
结束

函数IsMyDLL(const Filename:String):Bool;
var
H:THandle;
L:TStringList;
函数InList(const Func:String):Bool;
begin
结果:= L.IndexOf(Func)> = 0;
结束
begin
结果:= False;
L:= TStringList.Create;
尝试
如果ExportedFunctionNames(Filename,L)然后开始
结果:= //需要存在的函数的名称
InList('GetName')和
InList ('GetDescription')和
InList('GetVersion')和
InList('Start')和
InList('Stop');
结束
finally
L.Free;
结束
结束


解决方案

如果您在控制DLL,不想加载它们以检查功能,那么您可以使用版本资源来指示功能。这将需要主机应用程序了解每个可选DLL功能的最低支持版本是多少。您可以在不加载DLL的情况下便宜地阅读版本资源。



完全可以,而且很简单,获取DLL导出的函数列表,将其加载到你的进程与LoadLibrary。 dbghelp.dll系统库提供了这样的服务。但是,我怀疑你的情况是过度的。



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






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

 键入
PIMAGE_NT_HEADERS = ^ IMAGE_NT_HEADERS;
PIMAGE_EXPORT_DIRECTORY = ^ IMAGE_EXPORT_DIRECTORY;

函数ImageNtHeader(Base:Pointer):PIMAGE_NT_HEADERS;标准外部'dbghelp.dll';
function ImageRvaToVa(NtHeaders:Pointer; Base:Pointer; Rva:ULONG; LastRvaSection:Pointer):Pointer;标准外部'dbghelp.dll';

procedure ImageExportedFunctionNames(const ImageName:string; NamesList:TStrings);
var
i:整数;
FileHandle:THandle;
ImageHandle:THandle;
ImagePointer:指针;
标题:PIMAGE_NT_HEADERS;
ExportTable:PIMAGE_EXPORT_DIRECTORY;
NamesPointer:指针;
名称:PAnsiChar;
NamesDataLeft:Integer;
begin
//注意:我们在此过程中的策略是退出任何失败并返回空列表

NamesList.Clear;

FileHandle:= CreateFile(
PChar(ImageName),
GENERIC_READ,
FILE_SHARE_READ,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0
);
如果FileHandle = INVALID_HANDLE_VALUE然后开始
exit;
结束
尝试
ImageHandle:= CreateFileMapping(FileHandle,nil,PAGE_READONLY,0,0,nil);
如果ImageHandle = 0,则开始
exit;
结束
尝试
ImagePointer:= MapViewOfFile(ImageHandle,FILE_MAP_READ,0,0,0);
如果没有分配(ImagePointer)然后开始
exit;
结束

尝试
标题:= ImageNtHeader(ImagePointer);
如果没有分配(标题)然后开始
退出;
结束
如果Header.Signature<> $ 00004550然后开始//PE\0\0作为DWORD。
退出;
结束

ExportTable:= ImageRvaToVa(Header,ImagePointer,Header.OptionalHeader.DataDirectory [0] .VirtualAddress,nil);
如果没有分配(ExportTable)然后开始
exit;
结束

NamesPointer:= ImageRvaToVa(Header,ImagePointer,Cardinal(ExportTable.AddressOfNames),nil);
如果没有分配(NamesPointer)然后开始
退出;
结束
名称:= ImageRvaToVa(Header,ImagePointer,Cardinal(NamesPointer ^),nil);
如果没有分配(名称)然后开始
退出;
结束

NamesDataLeft:= Header.OptionalHeader.DataDirectory [0] .Size;
for i:= 0 to ExportTable.NumberOfNames-1 do begin
NamesList.Add(Names);
//找到下一个名字
while(Names ^<> chr(0))和(NamesDataLeft> 0)开始
inc(Names);
dec(NamesDataLeft);
结束
inc(Names);
结束
最后
UnmapViewOfFile(ImagePointer); //忽略错误,因为我们无法做到这一点。
结束;
最后
CloseHandle(ImageHandle);
结束;
最后
CloseHandle(FileHandle);
结束;
结束


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.

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.

UPDATE

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.

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\0\0" 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;

解决方案

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.

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.

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.


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\0\0" 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天全站免登陆