когда мы используем импорт, нам нужно точно указать имя модуля и имя функции.и мы не можем использовать сложные алгоритмы.Также для exe не существует широко известного псевдонима, который мы можем использовать вместо точно exe name.для сравнения: в случае получения GetModuleHandle
мы можем использовать NULL
для получения дескриптора файла, используемого для создания вызывающего процесса (файл .exe).но в случае LoadLibraryExW
мы не можем использовать 0 или пустую строку (L""
) или какой-либо другой псевдоним, скажем - мы хотим дескриптор exe .когда загрузчик загружает наш модуль - он читает имя dll из IMAGE_IMPORT_DESCRIPTOR
и пытается найти или загрузить модуль с этим именем сначала по низкому уровню, частный, ядро LoadLibraryExW
.здесь нужно именно имя.или загрузка не удалась.В результате используйте импорт - здесь не решение, если мы не знаем имя exe во время сборки
возможный вариант - разрешите функции-указатели самостоятельно во время выполнения.здесь мы можем получить exe HMODULE
на GetModuleHandle(0)
.Также при необходимости мы можем искать функцию не только в 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__
name.так же, как и в первом варианте, когда мы объявляем указатель самостоятельно.Единственная разница в синтаксисе и ... имени символа.это будет выглядеть как __imp_?fn2@@YAXH@Z
.проблема здесь в том, что __imp_?fn2@@YAXH@Z
недопустимое имя для c / c ++ - мы не можем напрямую присвоить ему значение из c / c ++ .даже если мы объявим функцию с extern "C"
- имя функции будет содержать символ @
(недопустимо для c ++ ) для функций __stdcall
и __fastcall
, для x86 .также имя будет отличаться для разных платформ ( x86 , x64 и т. д.).для доступа к таким именам - нужно или использовать внешний asm файл (для символов asm ?
и @
, допустимых в имени) или использовать опцию /alternatename
linker - для установки псевдонима для такого имени и символа доступа черезЭто.скажем, как
__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
).для задержки загрузки нам нужно внедрить
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);
для получения базы ех .«unique_exe_alias» (который компоновщик получил из библиотеки импорта) здесь играют роль не реального exe имени, которое неизвестно, но уникального тега (псевдонима) для exe