从 dll 加载 dll?
从 dll 加载 dll 的最佳方法是什么?
我的问题是我无法在process_attach上加载dll,也无法从主程序加载dll,因为我无法控制主程序源.因此我也不能调用非 dllmain 函数.
解决方案在评论中进行了所有辩论之后,我认为最好以真实"的方式总结我的立场.回答.
首先,目前还不清楚为什么您需要使用 LoadLibrary 在 DllMain 中加载一个 dll.这绝对是一个坏主意,因为您的 DllMain 正在内部运行另一个对 LoadLibrary 的调用,它持有加载程序锁,正如 DllMain 的文档:
<块引用>在初始进程启动期间或调用 LoadLibrary 之后,系统会扫描进程的已加载 DLL 列表.对于尚未使用 DLL_PROCESS_ATTACH 值调用的每个 DLL,系统将调用 DLL 的入口点函数.这个调用是在导致进程地址空间改变的线程的上下文中进行的,例如进程的主线程或调用 LoadLibrary 的线程. 对入口点的访问由整个流程的系统.DllMain 中的线程持有加载器锁,因此无法动态加载或初始化额外的 DLL.<块引用>入口函数应该只执行简单的初始化或终止任务.它不得调用 LoadLibrary 或 LoadLibraryEx 函数(或调用这些函数的函数),因为这可能会在 DLL 加载顺序中创建依赖循环.这可能导致在系统执行其初始化代码之前使用 DLL.类似地,入口函数在进程终止期间不得调用 FreeLibrary 函数(或调用 FreeLibrary 的函数),因为这可能导致在系统执行其终止代码后使用 DLL.
(强调)
所以,这就是为什么它被禁止;有关清晰、更深入的解释,请参阅this 和 this,有关其他一些示例如果您在 DllMain 中不遵守这些规则,会发生什么情况另见 Raymond Chen 的博客.
现在,关于 Rakis 的回答.
正如我已经多次重复的那样,您认为的 DllMain,不是真正 dll 的 DllMain;相反,它只是一个由 dll 的真实入口点调用的函数.反过来,CRT 会自动使用这个来执行其额外的初始化/清理任务,其中包括全局对象和类的静态字段的构造(实际上所有这些从编译器的角度来看几乎是相同的)事物).在完成这些任务之后(或之前,为了清理),它会调用您的 DllMain.
事情是这样的(显然我没有写所有的错误检查逻辑,只是为了展示它是如何工作的):
/* 这实际上是链接器标记为 dll 入口点的函数 */BOOL WINAPI CRTllMain(__in HINSTANCE hinstDLL,__in DWORD fdwReason,__in LPVOID lpvReserved){BOOL ret=FALSE;开关(fdwReason){案例 DLL_PROCESS_ATTACH:/* 初始化全局 CRT 结构 */init_CRT();/* 构造全局对象和静态字段 */构造全局();/* 调用用户提供的 DllMain 并从中获取返回码 */ret = DllMain(hinstDLL, fdwReason, lpvReserved);休息;案例 DLL_PROCESS_DETACH:/* 调用用户提供的 DllMain 并从中获取返回码 */ret = DllMain(hinstDLL, fdwReason, lpvReserved);/* 销毁全局对象和静态字段 */destruct_globals();/* 销毁全局 CRT 结构 */cleanup_CRT();休息;案例 DLL_THREAD_ATTACH:/* 初始化 CRT 线程本地结构 */init_TLS_CRT();/* 与之前相同,但针对线程本地对象 */构造_TLS_globals();/* 调用用户提供的 DllMain 并从中获取返回码 */ret = DllMain(hinstDLL, fdwReason, lpvReserved);休息;案例 DLL_THREAD_DETACH:/* 调用用户提供的 DllMain 并从中获取返回码 */ret = DllMain(hinstDLL, fdwReason, lpvReserved);/* 销毁线程局部对象和静态字段 */destruct_TLS_globals();/* 销毁线程本地 CRT 结构 */cleanup_TLS_CRT();休息;默认:/* ?!?*//* 调用用户提供的 DllMain 并从中获取返回码 */ret = DllMain(hinstDLL, fdwReason, lpvReserved);}返回 ret;}
这没有什么特别之处:它也发生在普通的可执行文件中,您的 main 由真正的入口点调用,CRT 保留用于完全相同的目的.
现在,从这里可以清楚为什么 Rakis 的解决方案不起作用:全局对象的构造函数由真正的 DllMain(即 dll 的实际入口点,这是关于 MSDN 的入口点)调用DllMain 上的页面讨论),因此从那里调用 LoadLibrary 与从您的 fake-DllMain 调用它的效果完全相同.
因此,遵循该建议,您将获得与在 DllMain 中直接调用 LoadLibrary 相同的负面影响,并且您还将问题隐藏在一个看似无关的位置,这将使下一个维护者努力寻找在哪里定位此错误.
至于延迟加载:这可能是一个想法,但你必须非常小心,不要在你的 DllMain 中调用引用的 dll 的任何函数:事实上,如果你这样做了,你会触发对 LoadLibrary 的隐藏调用,这会直接调用它有同样的负面影响.
无论如何,在我看来,如果你需要引用 dll 中的某些函数,最好的选择是静态链接它的导入库,这样加载器会自动加载它而不会给你任何问题,它会自动解决任何可能出现的奇怪依赖链.
即使在这种情况下,您也不能在 DllMain 中调用此 dll 的任何函数,因为不能保证它已经被加载;实际上,在 DllMain 中,您只能依赖正在加载的 kernel32,并且也许在 dll 上,您绝对确定您的调用者在加载您的 dll 的 LoadLibrary 发布之前已经加载了(但您仍然应该不要依赖于此,因为您的 dll 也可能被与这些假设不匹配的应用程序加载,并且只想例如 无需调用代码即可加载 dll 资源).
正如我之前链接的文章所指出的,
<块引用>问题是,就您的二进制文件而言,DllMain 在真正独特的时刻被调用.到那时 OS 加载程序已经从磁盘找到、映射和绑定文件,但是 - 根据情况 - 从某种意义上说,您的二进制文件可能还没有完全诞生".事情可能很棘手.<块引用>简而言之,当调用 DllMain 时,OS 加载程序处于相当脆弱的状态.首先,它已对其结构应用了锁,以防止在该调用内部发生内部损坏,其次,您的某些依赖项可能未处于完全加载状态.在加载二进制文件之前,OS Loader 会查看其静态依赖项.如果这些需要额外的依赖项,它也会查看它们.作为这种分析的结果,它提出了一个序列,其中需要调用这些二进制文件的 DllMains.它对事物非常聪明,在大多数情况下,您甚至可以不遵守 MSDN 中描述的大多数规则 - 但并非总是.
问题是,加载顺序你不知道,但更重要的是,它是基于静态导入信息构建的.如果在 DLL_PROCESS_ATTACH 期间在您的 DllMain 中发生了一些动态加载,并且您正在进行出站调用,则所有赌注都将取消.不能保证该二进制文件的 DllMain 将被调用,因此如果您随后尝试将 GetProcAddress 导入该二进制文件中的函数,结果是完全不可预测的,因为全局变量可能尚未初始化.很可能你会得到一个 AV.
(再次强调)
顺便说一下,关于 Linux 与 Windows 的问题:我不是 Linux 系统编程专家,但我认为这方面的情况并没有太大不同.
仍然有一些 DllMain 的等价物(_init 和 _fini 函数),它们是 - 多么巧合!- 由 CRT 自动获取,CRT 反过来从 _init 调用全局对象的所有构造函数和用 __attribute__ 构造函数 标记的函数(在某种程度上相当于在 Win32 中提供给程序员的假"DllMain)._fini 中的析构函数也有类似的过程.
由于 _init 在 dll 加载过程中也被调用(dlopen 还没有返回),我认为你在你可以在那里做什么.尽管如此,在我看来,Linux 上的问题较少,因为 (1) 你必须明确选择一个类似 DllMain 的函数,所以你不会立即试图滥用它和 (2) Linux 应用程序,据我所知,倾向于使用较少的动态加载 dll.
简而言之
没有正确"的方法将允许您在 DllMain 中引用除 kernel32.dll 之外的任何 dll.因此,不要从 DllMain 中做任何重要的事情,既不要直接(即在 CRT 调用的您的"DllMain 中)也不要间接(在全局类/静态字段构造函数中),尤其是 不要加载其他 dll,同样,既不直接(通过 LoadLibrary)也不间接(调用延迟加载的 dll 中的函数,这会触发 LoadLibrary 调用).
将另一个 dll 作为依赖项加载的正确方法是 - 哦!- 将其标记为静态依赖项.只需链接它的静态导入库并至少引用它的一个函数:链接器会将它添加到可执行映像的依赖项表中,加载器将自动加载它(在调用 DllMain 之前或之后初始化它,您不需要知道它,因为你不能从 DllMain 调用它).
如果由于某种原因这不可行,那么仍然有延迟加载选项(有我之前说过的限制).
如果您仍然,出于某种未知的原因,莫名其妙地需要在 DllMain 中调用 LoadLibrary,那么,继续,用脚射击,这取决于您的能力.但别告诉我我没有警告你.
我忘记了:关于该主题的另一个基本信息来源是来自 Microsoft 的 [Best Practices for Creating DLLs][6] 文档,该文档实际上几乎只讨论了加载程序、DllMain、加载程序锁及其交互;查看它以获取有关该主题的其他信息.
附录
<块引用>不,这不是我问题的真正答案.它只是说:动态链接是不可能的,你必须静态链接",以及你不能从 dllmain 调用".这*是*你的问题的答案:在你强加的条件下,你不能做你想做的事.简而言之,从 DllMain 中您不能调用*除 kernel32 函数之外的任何内容*.时期.<块引用>虽然很详细,但我对它为什么不起作用并不是很感兴趣,
相反,你应该这样做,因为理解为什么以这种方式制定规则可以让你避免大错误.<块引用>事实是,加载程序没有正确解析依赖项,并且加载过程从 Microsoft 的部分被错误地线程化.
不,亲爱的,加载器正确地完成了它的工作,因为 * 在 * LoadLibrary 返回之后,所有的依赖项都被加载了,一切都可以使用了.加载程序尝试按依赖顺序调用 DllMain(以避免依赖于 DllMain 中的其他 dll 的损坏的 dll 出现问题),但在某些情况下这是根本不可能的.
例如,可能有两个相互依赖的 dll(比如 A.dll 和 B.dll):现在,首先调用谁的 DllMain?如果加载器首先初始化了 A.dll,并且在它的 DllMain 中调用了 B.dll 中的一个函数,那么任何事情都可能发生,因为 B.dll 还没有被初始化(它的 DllMain 还没有被调用).如果我们扭转局势,这同样适用.
在其他情况下可能会出现类似问题,所以简单的规则是:不要在 DllMain 中调用任何外部函数,DllMain 只是为了初始化你的 dll 的内部状态.
问题是除了在 dll_attach 上做这件事之外别无他法,所有关于不做任何事情的好话都是多余的,因为别无选择,至少在我的情况下没有.
这个讨论是这样进行的:你说我想在实域中解一个像 x^2+1=0 这样的方程".每个人都说你不可能;你说这不是答案,并责怪数学.
有人告诉你:嘿,你可以,这是一个技巧,解决方案就是+/-sqrt(-1);每个人都对这个答案投反对票(因为您的问题是错误的,我们超出了真正的领域),并且您指责谁投反对票.我向您解释为什么根据您的问题该解决方案不正确,以及为什么无法在实际领域中解决此问题.你说你不在乎为什么不能做,你只能在真正的领域做,又责怪数学.
现在,正如一百万次解释和重申的那样,在你的条件下你的答案没有解决方案,你能解释我们为什么你有"?做像在 DllMain 中加载 dll 这样愚蠢的事情?经常不可能"问题的出现是因为我们选择了一条奇怪的路线来解决另一个问题,这使我们陷入僵局.如果你解释了更大的图景,我们可以建议一个更好的解决方案,它不涉及在 DllMain 中加载 dll.
<块引用>PS:如果我将 DLL2(ole32.dll,Vista x64)与 DLL1(mydll)静态链接,在旧操作系统上链接器需要哪个版本的 dll?存在的那个(显然我假设你正在编译 32 位);如果您的应用程序所需的导出函数不存在于找到的 dll 中,则您的 dll 根本没有加载(LoadLibrary 失败).
附录 (2)
注入是肯定的,如果你想知道,可以使用 CreateRemoteThread.仅在 Linux 和 Mac 上,dll/共享库由加载程序加载.
将 dll 添加为静态依赖项(从一开始就建议这样做)使其完全像 Linux/Mac 一样由加载器加载,但问题仍然存在,因为正如我所解释的,在 DllMain 中你仍然不能依赖在 kernel32.dll 以外的任何东西上(即使加载程序通常足够智能以首先初始化依赖项).
问题仍然可以解决.使用 CreateRemoteThread 创建线程(实际上调用 LoadLibrary 来加载你的 dll);在 DllMain 中使用一些 IPC 方法(例如命名共享内存,其句柄将保存在某处以在 init 函数中关闭)将真实"的地址传递给注入程序.您的 dll 将提供的 init 函数.然后 DllMain 将退出而不做任何其他事情.相反,注入器应用程序将使用 CreateRemoteThread 提供的句柄使用 WaitForSingleObject 等待远程线程的结束.然后,在远程线程结束后(这样LoadLibrary就完成了,所有的依赖都会被初始化),注入器会从DllMain创建的命名共享内存中读取远程进程中init函数的地址,并启动它与 CreateRemoteThread.
问题:在 Windows 2000 上禁止使用来自 DllMain 的命名对象,因为
在 Windows 2000 中,命名对象由终端服务 DLL 提供.如果此 DLL 未初始化,则调用该 DLL 会导致进程崩溃.
所以,这个地址可能必须以另一种方式传递.一个非常干净的解决方案是在 dll 中创建一个共享数据段,将它加载到注入器应用程序和目标应用程序中,并将它放在这样的数据段中所需的地址.dll 显然必须首先在注入器中加载,然后在目标中加载,否则正确"的地址将被覆盖.
另一个真正有趣的方法是在另一个进程内存中写入一个小函数(直接在汇编中),该函数调用 LoadLibrary 并返回我们的 init 函数的地址;因为我们是在那里写的,所以我们也可以用 CreateRemoteThread 调用它,因为我们知道它在哪里.
在我看来,这是最好的方法,也是最简单的,因为代码已经在那里了,写在这个好文章.看看它,它很有趣,它可能会解决您的问题.
What's the best way for loading a dll from a dll ?
My problem is I can't load a dll on process_attach, and I cannot load the dll from the main program, because I don't control the main program source. And therefore I cannot call a non-dllmain function, too.
解决方案After all the debate that went on in the comments, I think that it's better to summarize my positions in a "real" answer.
First of all, it's still not clear why you need to load a dll in DllMain with LoadLibrary. This is definitely a bad idea, since your DllMain is running inside another call to LoadLibrary, which holds the loader lock, as explained by the documentation of DllMain:
During initial process startup or after a call to LoadLibrary, the system scans the list of loaded DLLs for the process. For each DLL that has not already been called with the DLL_PROCESS_ATTACH value, the system calls the DLL's entry-point function. This call is made in the context of the thread that caused the process address space to change, such as the primary thread of the process or the thread that called LoadLibrary. Access to the entry point is serialized by the system on a process-wide basis. Threads in DllMain hold the loader lock so no additional DLLs can be dynamically loaded or initialized.
The entry-point function should perform only simple initialization or termination tasks. It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions), because this may create dependency loops in the DLL load order. This can result in a DLL being used before the system has executed its initialization code. Similarly, the entry-point function must not call the FreeLibrary function (or a function that calls FreeLibrary) during process termination, because this can result in a DLL being used after the system has executed its termination code.
(emphasis added)
So, this on why it is forbidden; for a clear, more in-depth explanation, see this and this, for some other examples about what can happen if you don't stick to these rules in DllMain see also some posts in Raymond Chen's blog.
Now, on Rakis answer.
As I already repeated several times, what you think that is DllMain, isn't the real DllMain of the dll; instead, it's just a function that is called by the real entrypoint of the dll. This one, in turn, is automatically took by the CRT to perform its additional initialization/cleanup tasks, among which there is the construction of global objects and of the static fields of the classes (actually all these from the compiler's perspective are almost the same thing). After (or before, for the cleanup) it completes such tasks, it calls your DllMain.
It goes somehow like this (obviously I didn't write all the error checking logic, it's just to show how it works):
/* This is actually the function that the linker marks as entrypoint for the dll */
BOOL WINAPI CRTDllMain(
__in HINSTANCE hinstDLL,
__in DWORD fdwReason,
__in LPVOID lpvReserved
)
{
BOOL ret=FALSE;
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
/* Init the global CRT structures */
init_CRT();
/* Construct global objects and static fields */
construct_globals();
/* Call user-supplied DllMain and get from it the return code */
ret = DllMain(hinstDLL, fdwReason, lpvReserved);
break;
case DLL_PROCESS_DETACH:
/* Call user-supplied DllMain and get from it the return code */
ret = DllMain(hinstDLL, fdwReason, lpvReserved);
/* Destruct global objects and static fields */
destruct_globals();
/* Destruct the global CRT structures */
cleanup_CRT();
break;
case DLL_THREAD_ATTACH:
/* Init the CRT thread-local structures */
init_TLS_CRT();
/* The same as before, but for thread-local objects */
construct_TLS_globals();
/* Call user-supplied DllMain and get from it the return code */
ret = DllMain(hinstDLL, fdwReason, lpvReserved);
break;
case DLL_THREAD_DETACH:
/* Call user-supplied DllMain and get from it the return code */
ret = DllMain(hinstDLL, fdwReason, lpvReserved);
/* Destruct thread-local objects and static fields */
destruct_TLS_globals();
/* Destruct the thread-local CRT structures */
cleanup_TLS_CRT();
break;
default:
/* ?!? */
/* Call user-supplied DllMain and get from it the return code */
ret = DllMain(hinstDLL, fdwReason, lpvReserved);
}
return ret;
}
There isn't anything special about this: it also happens with normal executables, with your main being called by the real entrypoint, which is reserved by the CRT for the exact same purposes.
Now, from this it will be clear why the Rakis' solution isn't going to work: the constructors for global objects are called by the real DllMain (i.e. the actual entrypoint of the dll, which is the one about the MSDN page on DllMain talks about), so calling LoadLibrary from there has exactly the same effect as calling it from your fake-DllMain.
Thus, following that advice you'll obtain the same negative effects of calling directly LoadLibrary in the DllMain, and you'll also hide the problem in a seemingly-unrelated position, which will make the next maintainer work hard to find where this bug is located.
As for delayload: it may be an idea, but you must be really careful not to call any function of the referenced dll in your DllMain: in fact, if you did that you would trigger a hidden call to LoadLibrary, which would have the same negative effects of calling it directly.
Anyhow, in my opinion, if you need to refer to some functions in a dll the best option is to link statically against its import library, so the loader will automatically load it without giving you any problem, and it will resolve automatically any strange dependency chain that may arise.
Even in this case you mustn't call any function of this dll in DllMain, since it's not guaranteed that it's already been loaded; actually, in DllMain you can rely only on kernel32 being loaded, and maybe on dlls you're absolutely sure that your caller has already loaded before the LoadLibrary that is loading your dll was issued (but still you shouldn't rely on this, because your dll may also be loaded by applications that don't match these assumptions, and just want to, e.g., load a resource of your dll without calling your code).
As pointed out by the article I linked before,
The thing is, as far as your binary is concerned, DllMain gets called at a truly unique moment. By that time OS loader has found, mapped and bound the file from disk, but - depending on the circumstances - in some sense your binary may not have been "fully born". Things can be tricky.
In a nutshell, when DllMain is called, OS loader is in a rather fragile state. First off, it has applied a lock on its structures to prevent internal corruption while inside that call, and secondly, some of your dependencies may not be in a fully loaded state. Before a binary gets loaded, OS Loader looks at its static dependencies. If those require additional dependencies, it looks at them as well. As a result of this analysis, it comes up with a sequence in which DllMains of those binaries need to be called. It's pretty smart about things and in most cases you can even get away with not following most of the rules described in MSDN - but not always.
The thing is, the loading order is unknown to you, but more importantly, it's built based on the static import information. If some dynamic loading occurs in your DllMain during DLL_PROCESS_ATTACH and you're making an outbound call, all bets are off. There is no guarantee that DllMain of that binary will be called and therefore if you then attempt to GetProcAddress into a function inside that binary, results are completely unpredictable as global variables may not have been initialized. Most likely you will get an AV.
(again, emphasis added)
By the way, on the Linux vs Windows question: I'm not a Linux system programming expert, but I don't think that things are so different there in this respect.
There are still some equivalents of DllMain (the _init and _fini functions), which are - what a coincidence! - automatically took by the CRT, which in turn, from _init, calls all the constructors for the global objects and the functions marked with __attribute__ constructor (which are somehow the equivalent of the "fake" DllMain provided to the programmer in Win32). A similar process goes on with destructors in _fini.
Since _init too is called while the dll loading is still taking place (dlopen didn't return yet), I think that you're subject to similar limitations in what you can do in there. Still, in my opinion on Linux the problem is felt less, because (1) you have to explicitly opt-in for a DllMain-like function, so you aren't immediately tempted to abuse of it and (2), Linux applications, as far as I saw, tend to use less dynamic loading of dlls.
In a nutshell
No "correct" method will allow you to reference to any dll other than kernel32.dll in DllMain.Thus, don't do anything important from DllMain, neither directly (i.e. in "your" DllMain called by the CRT) neither indirectly (in global class/static fields constructors), especially don't load other dlls, again, neither directly (via LoadLibrary) neither indirectly (with calls to functions in delay-loaded dlls, which trigger a LoadLibrary call).
The right way to have another dll loaded as a dependency is to - doh! - mark it as a static dependency. Just link against its static import library and reference at least one of its functions: the linker will add it to the dependency table of the executable image, and the loader will load it automatically (initializing it before or after the call to your DllMain, you don't need to know about it because you mustn't call it from DllMain).
If this isn't viable for some reason, there's still the delayload options (with the limits I said before).
If you still, for some unknown reason, have the inexplicable need to call LoadLibrary in DllMain, well, go ahead, shoot in your foot, it's in your faculties. But don't tell me I didn't warn you.
I was forgetting: another fundamental source of information on the topic is the [Best Practices for Creating DLLs][6] document from Microsoft, which actually talks almost only about the loader, DllMain, the loader lock and their interactions; have a look at it for additional information on the topic.
Addendum
No, not really an answer to my question. All it says is: "It's not possible with dynamic linking, you must link statically", and "you musn't call from dllmain".
Which *is* an answer to your question: under the conditions you imposed, you can't do what you want. In a nutshell of a nutshell, from DllMain you can't call *anything other than kernel32 functions*. Period.
Although in detail, but I'm not really interested in why it doesn't work,
You should, instead, because understanding why the rules are made in that way makes you avoid big mistakes.
fact is, the loader is not resolving dependenies correctly and the loading process is improperly threaded from Microsoft's part.
No, my dear, the loader does its job correctly, because *after* LoadLibrary has returned, all the dependencies are loaded and everything is ready to be used. The loader tries to call the DllMain in dependency order (to avoid problems with broken dlls which rely on other dlls in DllMain), but there are cases in which this is simply impossible.
For example, there may be two dlls (say, A.dll and B.dll) that depend on each other: now, whose DllMain is to call first? If the loader initialized A.dll first, and this, in its DllMain, called a function in B.dll, anything could happen, since B.dll isn't initialized yet (its DllMain hasn't been called yet). The same applies if we reverse the situation.
There may be other cases in which similar problems may arise, so the simple rule is: don't call any external functions in DllMain, DllMain is just for initializing the internal state of your dll.
The problem is there is no other way then doing it on dll_attach, and all the nice talk about not doing anything there is superfluous, because there is no alternative, at least not in my case.
This discussion is going on like this: you say "I want to solve an equation like x^2+1=0 in the real domain". Everybody says you that it's not possible; you say that it's not an answer, and blame the math.
Someone tells you: hey, you can, here's a trick, the solution is just +/-sqrt(-1); everybody downvotes this answer (because it's wrong for your question, we're going outside the real domain), and you blame who downvotes. I explain you why that solution is not correct according to your question and why this problem can't be solved in the real domain. You say that you don't care about why it can't be done, that you can only do that in the real domain and again blame math.
Now, since, as explained and restated a million times, under your conditions your answer has no solution, can you explain us why on earth do you "have" to do such an idiotic thing as loading a dll in DllMain? Often "impossible" problems arise because we've chosen a strange route to solve another problem, which brings us to deadlock. If you explained the bigger picture, we could suggest a better solution to it which does not involve loading dlls in DllMain.
PS: If I statically link DLL2 (ole32.dll, Vista x64) against DLL1 (mydll), which version of the dll will the linker require on older operating systems?
The one that is present (obviously I'm assuming you're compiling for 32 bit); if an exported function needed by your application isn't present in the found dll, your dll is simply not loaded (LoadLibrary fails).
Addendum (2)
Positive on injection, with CreateRemoteThread if you wanna know. Only on Linux and Mac the dll/shared library is loaded by the loader.
Adding the dll as a static dependency (what has been suggested since the beginning) makes it to be loaded by the loader exactly as Linux/Mac do, but the problem is still there, since, as I explained, in DllMain you still cannot rely on anything other than kernel32.dll (even if the loader in general intelligent enough to init first the dependencies).
Still, the problem can be solved. Create the thread (that actually calls LoadLibrary to load your dll) with CreateRemoteThread; in DllMain use some IPC method (for example named shared memory, whose handle will be saved somewhere to be closed in the init function) to pass to the injector program the address of the "real" init function that your dll will provide. DllMain then will exit without doing anything else. The injector application, instead, will wait for the end of the remote thread with WaitForSingleObject using the handle provided by CreateRemoteThread. Then, after the remote thread will be ended (thus LoadLibrary will be completed, and all the dependencies will be initialized), the injector will read from the named shared memory created by DllMain the address of the init function in the remote process, and start it with CreateRemoteThread.
Problem: on Windows 2000 using named objects from DllMain is prohibited because
In Windows 2000, named objects are provided by the Terminal Services DLL. If this DLL is not initialized, calls to the DLL can cause the process to crash.
So, this address may have to be passed in another manner. A quite clean solution would be to create a shared data segment in the dll, load it both in the injector application and in the target one and have it put in such data segment the required address. The dll would obviously have to be loaded first in the injector and then in the target, because otherwise the "correct" address would be overwritten.
Another really interesting method that can be done is to write in the other process memory a little function (directly in assembly) that calls LoadLibrary and returns the address of our init function; since we wrote it there, we can also call it with CreateRemoteThread because we know where it is.
In my opinion, this is the best approach, and is also the simplest, since the code is already there, written in this nice article. Have a look at it, it is quite interesting and it probably will do the trick for your problem.
相关文章