让 dll 从其调用 .exe 导入符号
Have dll import symbols from its calling .exe
相关但不等同于DLL Get Symbols From Its Parent (Loader)
有没有办法说服 Windows 加载程序从加载可执行文件或中间 dll 中解析 A.dll 引用的特定符号,而无需指定文件来解析 [=] 中的符号25=]?
如果加载的 .exe 具有已知名称,则很明显如何执行此操作,但如果不是...
以下是您真正想要这样做的一个很好的理由:https://www.gnu.org/software/libc/manual/html_node/Replacing-malloc.html
如果这可以做到,一个好的答案会说明如何以某种方式做到这一点。
我半期待答案是这不可能完成。在那种情况下,一个好的答案将说明为什么这是不可能的。 "The build tools don't support this." 是一个错误的答案。
当我们使用import时,我们需要准确指出模块名称和函数名称。而且我们不能使用复杂的算法。同样对于 exe 不存在众所周知的别名,我们可以准确地使用 exe 名称。进行比较:万一得到GetModuleHandle
we can use NULL
for get handle to the file used to create the calling process (.exe file). but in case LoadLibraryExW
we can not use 0 or empty string (L""
) or some another alias for say - we want handle to exe. when loader load our module - he read dll name from IMAGE_IMPORT_DESCRIPTOR
and try found or load module with this name first by low level, private, core of LoadLibraryExW
。这里需要确切的名字。或加载失败。结果使用导入 - 如果我们在构建时不知道 exe 名称,这里不是解决方案
可能的变体 - 在运行时自行解析函数指针。这里我们可以通过GetModuleHandle(0)
得到exeHMODULE
。此外,如果需要,我们不仅可以在 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。不同平台的名称也会不同(x86、x64 等)。要访问此类名称 - 需要或使用外部 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
where dllname
- exactly name from import lib. sense that this dllname
will(can) be not match to exe - all what need - it must be unique. and we need yourself handle delayload (called when we first time call fn
). for implement delayload we need implement
extern "C" FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
我们可以自己实现,或者添加delayimp.lib
到我们的项目中。此处(在 delayimp.lib
中)delayLoadHelper2
并实施。但是我们必须自定义这个过程(默认实现(查看 /include/DelayHlp.cpp
)将使用 LoadLibraryExA
和 dllname
,这在我们的例子中不例外 - 否则我们可以简单地按原样使用导入) .所以我们需要强制执行 __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
的唯一标记(别名)
相关但不等同于DLL Get Symbols From Its Parent (Loader)
有没有办法说服 Windows 加载程序从加载可执行文件或中间 dll 中解析 A.dll 引用的特定符号,而无需指定文件来解析 [=] 中的符号25=]?
如果加载的 .exe 具有已知名称,则很明显如何执行此操作,但如果不是...
以下是您真正想要这样做的一个很好的理由:https://www.gnu.org/software/libc/manual/html_node/Replacing-malloc.html
如果这可以做到,一个好的答案会说明如何以某种方式做到这一点。
我半期待答案是这不可能完成。在那种情况下,一个好的答案将说明为什么这是不可能的。 "The build tools don't support this." 是一个错误的答案。
当我们使用import时,我们需要准确指出模块名称和函数名称。而且我们不能使用复杂的算法。同样对于 exe 不存在众所周知的别名,我们可以准确地使用 exe 名称。进行比较:万一得到GetModuleHandle
we can use NULL
for get handle to the file used to create the calling process (.exe file). but in case LoadLibraryExW
we can not use 0 or empty string (L""
) or some another alias for say - we want handle to exe. when loader load our module - he read dll name from IMAGE_IMPORT_DESCRIPTOR
and try found or load module with this name first by low level, private, core of LoadLibraryExW
。这里需要确切的名字。或加载失败。结果使用导入 - 如果我们在构建时不知道 exe 名称,这里不是解决方案
可能的变体 - 在运行时自行解析函数指针。这里我们可以通过GetModuleHandle(0)
得到exeHMODULE
。此外,如果需要,我们不仅可以在 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。不同平台的名称也会不同(x86、x64 等)。要访问此类名称 - 需要或使用外部 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
where dllname
- exactly name from import lib. sense that this dllname
will(can) be not match to exe - all what need - it must be unique. and we need yourself handle delayload (called when we first time call fn
). for implement delayload we need implement
extern "C" FARPROC WINAPI __delayLoadHelper2(
PCImgDelayDescr pidd,
FARPROC * ppfnIATEntry
);
我们可以自己实现,或者添加delayimp.lib
到我们的项目中。此处(在 delayimp.lib
中)delayLoadHelper2
并实施。但是我们必须自定义这个过程(默认实现(查看 /include/DelayHlp.cpp
)将使用 LoadLibraryExA
和 dllname
,这在我们的例子中不例外 - 否则我们可以简单地按原样使用导入) .所以我们需要强制执行 __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