【问题标题】:GetProcAddress vs __declspec( dllimport )GetProcAddress 与 __declspec(dllimport)
【发布时间】:2018-09-15 04:56:23
【问题描述】:

两者有什么区别?

即用于查找 Nt___ 或 Zw___ 等函数

【问题讨论】:

  • 真的__declspec(dllimport) 只影响 CL 声明指向符号(函数)的指针并通过此指针调用函数(或访问数据)。全部。实际效果在构建过程中使用导入库

标签: c++ windows winapi ntdll


【解决方案1】:

在 MS Windos 中,隐式链接和显式链接是有区别的。

隐式链接

可执行文件链接到提供从 DLL 导出的符号的随附库(.lib 文件)。 (用于导入的函数用__declspec(dllimport) 标注。)隐式链接的DLL 加载了可执行文件。

显式链接

程序加载一个显式调用LoadLibrary() 的DLL。要调用 DLL 的函数,它们的地址必须使用GetProcAddress() 来确定。

不过,GetProcAddress() 也可用于来自隐式链接 DLL 的函数。如果意外地在多个 DLL 中使用了相同的符号(例如,如果使用了链接到不同运行时 DLL 的 DLL),这可能会有所帮助。

有时,提供的 DLL 没有导入库。一个众所周知的例子是 OpenGL,MS 在 1.2 版中停止了对它的支持。但是,如果有足够的硬件和最新的驱动程序,当前 OpenGL 版本的所有功能都可能可用(并且可以在运行时使用GetProcAdress() 加载)。

一段OpenGL绑定示例代码MyGL.cc:

  // version 2.0
  glAttachShader
    = (PFNGLATTACHSHADERPROC)wglGetProcAddress(
      "glAttachShader");
  glCompileShader
    = (PFNGLCOMPILESHADERPROC)wglGetProcAddress(
      "glCompileShader");
  glCreateProgram
    = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress(
      "glCreateProgram");
  glCreateShader
    = (PFNGLCREATESHADERPROC)wglGetProcAddress(
      "glCreateShader");
  glDeleteProgram
    = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress(
      "glDeleteProgram");
  glDeleteShader
    = (PFNGLDELETESHADERPROC)wglGetProcAddress(
      "glDeleteShader");

MyGL.h:

// Version 2.0
extern MY_GL_API PFNGLATTACHSHADERPROC glAttachShader;
extern MY_GL_API PFNGLCOMPILESHADERPROC glCompileShader;
extern MY_GL_API PFNGLCREATEPROGRAMPROC glCreateProgram;
extern MY_GL_API PFNGLCREATESHADERPROC glCreateShader;
extern MY_GL_API PFNGLDELETEPROGRAMPROC glDeleteProgram;
extern MY_GL_API PFNGLDELETESHADERPROC glDeleteShader;

其中MY_GL_API 在编译MyGL.dll 时定义为__declspec(dllexport),否则定义为__declspec(dllimport)。 (所以,实际上 __declspec(dllimport) 和 GetProcAddress() 而不是 vs. 作为函数指针本身是 dllexported 但在运行时初始化 GetProcAddress()。)

(PFNGL 宏扩展为具有适当签名的函数指针类型。它们包含在kronos.org 提供的标头中。)

GetProcAddress() 的另一个重要用途是用于在某个版本的 Windows 之前可能不存在的函数(或其他可能在 DLL 中可用或不可用的函数)。因此,当GetProcAddress() 无法实现预期功能时,应用程序可以向后兼容地编写,从而提供备用回退。

MSDN 上为GetProcAddress() 提供的示例:

typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);

// Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.

   PGNSI pGNSI;
   SYSTEM_INFO si;

   ZeroMemory(&si, sizeof(SYSTEM_INFO));

   pGNSI = (PGNSI) GetProcAddress(
      GetModuleHandle(TEXT("kernel32.dll")), 
      "GetNativeSystemInfo");
   if (NULL != pGNSI) {
      pGNSI(&si);
   } else {
       GetSystemInfo(&si);
   }

延伸阅读:MSDN: Link an executable to a DLL

【讨论】:

  • 编译器如何通过隐式链接知道函数存储在名为 ntdll 的 DLL 中?为彻底的答案投票:)
  • @CryptographyMan 在链接的GetProcAddress() 页面中有一个 C++ 示例。
  • 我知道 GetProcAddress 是如何工作的,我目前正在使用它,它工作正常。 dllimport 看起来更容易,我想将它与 GetProcAddress 进行反分析比较
  • @CryptographyMan 如果隐式链接是一个选项,那么就使用它。 GetProcAddress() 适用于不能选择隐式链接的情况。 (我举了两个例子。)
  • @CryptographyMan 编译器怎么知道编译器不知道这个。它只是生成变量__imp_FuncName 并通过它调用函数。链接器搜索 __imp_FuncName 符号。如果在导入库中找到它 - 这里存在有关 dll 名称的信息,从哪个函数导入。他将__imp_FuncName 变量放入IAT 并在导入表中为它/dll 创建记录。加载器在加载 dll 时使用此记录为您解析函数地址
【解决方案2】:

GetProcAddress() 让您可以在运行时查找函数。它对于可选导入或可以动态加载的插件之类的东西很有用。它的另一个用途是用于导出但没有头文件或库文件的未记录函数。

使用__declspec( dllimport ) 将该函数添加到您的导入表中,以便它与您的可执行文件一起自动加载。

【讨论】:

  • 关于反分析,哪个更难检测?我假设 GetProcAddress 因为它是运行时的,但我个人不知道:/
  • 这是一个单独的问题,但它们都很简单。查看Dependency Walker。
  • 二进制中一个是静态的,另一个是有条件的,对吧?
  • 使用 __declspec( dllimport ) 将函数添加到您的导入表中 严格说不。它只是等于声明extern "C" { extern void* __imp_FuncName; } 并通过此指针调用函数。比我们需要以某种方式实现void* __imp_FuncName。可能并指导自己。但通常,当它在导入库中实现时 - 是的,链接器将此变量添加到 IAT 并在导入表中创建记录
  • 如果我们将 dll 标记为延迟加载 - 它将被添加到延迟加载描述符中
猜你喜欢
  • 1970-01-01
  • 2011-01-18
  • 2013-03-26
  • 1970-01-01
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 2011-05-28
相关资源
最近更新 更多