【问题标题】:Have dll import symbols from its calling .exe让 dll 从其调用 .exe 导入符号
【发布时间】:2018-09-16 18:30:15
【问题描述】:

DLL Get Symbols From Its Parent (Loader)相关但不等价

有没有办法说服 Windows 加载程序从加载的可执行文件或中间 dll 中解析 A​​.dll 引用的特定符号,而无需指定从 A.dll 中解析符号的文件?

如果加载的 .exe 具有已知名称,则很明显如何执行此操作,但如果不是...

这是您真正想要这样做的充分理由:https://www.gnu.org/software/libc/manual/html_node/Replacing-malloc.html

如果可以做到这一点,一个好的答案会说明如何以某种方式做到这一点。

我半信半疑的回答是它无法完成。在这种情况下,一个好的答案将说明为什么这是不可能的。 “构建工具不支持这一点。”是一个错误的答案。

【问题讨论】:

  • 如果你使用 import - 你需要直接设置 PE 名称和函数名称。确切地。如果您使用延迟加载导入或您自己获得了函数地址 - 您可以做任何事情
  • @RbMm:如果你能写出一个涉及延迟负载的答案,你可以获得便宜的代表。
  • 这取决于您用于构建 PE 的链接器(在您的情况下为 dll)。如果 link.exe 你需要自己实现extern "C" LPCVOID WINAPI __delayLoadHelper2(PImgDelayDescr , PImgThunkData ) 你可以自己实现任何解析符号策略
  • 您提到了构建工具。任何特定的(VStudioMinGW、...)?
  • @CristiFati:我已经从 RbMm 得到了一个与工具无关的答案,所以没有必要限制这个问题。

标签: windows dll dllimport dllexport


【解决方案1】:

当我们使用 import 时,我们需要准确地指出模块名称和函数名称。我们不能使用复杂的算法。对于 exe 也不存在众所周知的别名,我们可以在适当的位置使用 exe 名称。比较:如果得到GetModuleHandle,我们可以使用NULL 获取用于创建调用进程的文件(.exe 文件)的句柄。但是在LoadLibraryExW 的情况下,我们不能使用 0 或空字符串 (L"") 或其他别名 - 我们想要处理 exe。当加载器加载我们的模块时 - 他从IMAGE_IMPORT_DESCRIPTOR 读取 dll 名称并尝试首先通过LoadLibraryExW 的低级私有核心找到或加载具有此名称的模块。这里需要确切的名称。或加载失败。结果使用导入 - 如果我们在构建时不知道 exe 名称,这里不是解决方案

可能的变体 - 在运行时自行解析函数指针。在这里,我们可以通过GetModuleHandle(0) 获取exe HMODULE。如果需要,我们不仅可以在 exe 中搜索功能,还可以在其他地方搜索功能。可以实现任何搜索算法。

这里有几种方法。举个具体的例子,我们需要用签名获取指向函数的指针:

void WINAPI fn(int i);

我们可以声明指向这个函数的指针并在运行时解析它

void (WINAPI *fn)(int);

*(void**)&fn = GetProcAddress(GetModuleHandleW(0), "fn");

DLL_PROCESS_ATTACH

一个稍微不同的解决方案(尽管在二进制级别它是完全等效的)用__declspec(dllimport) 属性声明函数。这仅适用于 CL.EXE(也称为 MSVC)编译器。所以

__declspec(dllimport) void fn(int i);

在这种情况下,CL 自己生成指向函数名称为 __imp_ ## __FUNCDNAME__ 名称的指针。所以实际上与第一个变体相同,当我们自己声明指针时。唯一的区别是语法和.. 符号名称。它看起来像__imp_?fn2@@YAXH@Z。这里的问题是__imp_?fn2@@YAXH@Z 不是 c/c++ 的有效名称 - 我们不能直接从 c/c++ 为其赋值。即使我们用extern "C" 声明函数 - 对于__stdcall__fastcall 函数,函数名称将包含@ 符号(对于c++ 是非法的),对于x86 .对于不同的平台(x86x64 等),名称也会有所不同。访问此类名称 - 需要或使用外部 asm 文件(对于名称中有效的 asm ?@ 符号)或使用 /alternatename 链接器选项 - 为此类名称和访问符号设置别名通过它。说喜欢

__pragma(comment(linker, "/alternatename:__imp_?fn@@YAXH@Z=__imp_fn"))

并通过

初始化
*(void**)&__imp_fn = GetProcAddress(GetModuleHandle(0), "fn");

另一个选项在函数声明中使用__declspec(dllimport) + 添加导入库,其中定义了所有__imp___FUNCDNAME__(例如__imp_?fn2@@YAXH@Z)。 (即使我们没有这样的库,我们也可以轻松地自己创建它——所有需要的——正确的函数声明和空实现)。在我们将这样的导入库添加到链接器输入之后 - 添加/DELAYLOAD:dllname 其中dllname - 正是来自导入库的名称。感觉这个dllname 将(可以)与 exe 不匹配 - 所有需要 - 它必须是唯一的。我们需要自己处理延迟负载(当我们第一次调用fn 时调用)。为了实现延迟加载,我们需要implement

extern "C" FARPROC WINAPI __delayLoadHelper2(   
   PCImgDelayDescr pidd,  
   FARPROC * ppfnIATEntry  
); 

我们可以自己实现,或者将delayimp.lib添加到我们的项目中。在这里(在delayimp.libdelayLoadHelper2 并实施。但是我们必须自定义此过程(默认实现(查看/include/DelayHlp.cpp)将使用LoadLibraryExAdllname,这在我们的案例中不例外 - 否则我们可以简单地按原样使用导入)。所以我们需要强制实现__pfnDliNotifyHook2:

例如:

FARPROC WINAPI MyDliHook(
                             unsigned        dliNotify,
                             PDelayLoadInfo  pdli
                             )
{
    switch (dliNotify)
    {
    case dliNotePreLoadLibrary:
        if (!strcmp(pdli->szDll, "unique_exe_alias"))
        {
            return (FARPROC)GetModuleHandle(0);
        }
    }

    return 0;
}

const PfnDliHook  __pfnDliNotifyHook2 = MyDliHook;

我们可以寻找dliNotePreLoadLibrary 通知,而不是默认LoadLibraryEx(dli.szDll, NULL, 0); 使用GetModuleHandle(0); 获取exe 的基础。 此处的“unique_exe_alias”(链接器从导入库中获取)扮演的角色不是真正的 exe 名称,这是未知的,而是 exe 的唯一标签(别名)

【讨论】:

  • 在使用 MinGW 构建 node-api 插件时,我在链接 node.exe 的导入符号时遇到问题。您的回答非常有用 - 它给出了解决方法的想法。但我仍然无法隐式链接。你能看看stackoverflow.com/q/67648225/9363996吗?
猜你喜欢
  • 2012-12-20
  • 2020-08-27
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多