从dll加载dll? [英] Loading a dll from a dll?

查看:144
本文介绍了从dll加载dll?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我的问题是我无法在process_attach上加载dll,我无法加载dll主程序,因为我没有控制主程序源码。因此,我也不能调用非dllmain函数。

解决方案

在评论中进行的所有辩论之后,认为最好在真实的答案中总结我的职位。



首先,仍然不清楚为什么需要加载DllMain中的一个DLL与LoadLibrary。这绝对是一个坏主意,因为您的DllMain正在运行 另一个调用LoadLibrary,它保存加载程序锁定,如 DllMain的文档


在初始化过程启动期间或调用LoadLibrary后,系统会扫描加载的DLL的列表。对于尚未使用DLL_PROCESS_ATTACH值调用的每个DLL,系统调用该DLL的入口点函数。 此调用是在导致进程地址空间更改的线程的上下文中进行的,例如进程的主线程或调用LoadLibrary的线程。访问入口点由系统在过程范围内。 DllMain中的线程保存加载程序锁,因此不会动态加载或初始化其他DLL。




条目点功能只能执行简单的初始化或终止任务。它不能调用LoadLibrary或LoadLibraryEx函数(或调用这些函数的函数),因为这可能会在DLL加载顺序中创建依赖循环。这可能导致在系统执行其初始化代码之前使用DLL。类似地,入门点函数不能在进程终止期间调用FreeLibrary函数(或调用FreeLibrary的函数),因为这可能导致在系统执行终止代码之后使用DLL。

(加重)



所以,这就是为什么是禁止的;要了解更为深入的解释,请参阅,有关其他可能的例子如果您不遵守DllMain中的这些规则,请参阅 Raymond Chen的博客中的一些帖子。



现在,在Rakis的答案。



由于我已经重复了几次,你认为是DllMain,isn dll的这个真正的 DllMain;相反,它只是一个由dll的真正入口点调用的函数。这又是CRT自动执行其附加的初始化/清理任务,其中有全局对象和类的静态字段的构造(实际上,从编译器的角度来看,这几乎是相同的事情)。之后(或之前,为了清理)它完成这样的任务,它调用你的DllMain。



它以某种方式像这样(显然我没有写所有的错误检查逻辑,只是为了显示它的工作原理):

  / *这实际上是链接器标记为entrypoint的函数dll * / 
BOOL WINAPI CRTDllMain(
__in HINSTANCE hinstDLL,
__in DWORD fdwReason,
__in LPVOID lpvReserved

{
BOOL ret = FALSE;
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
/ *初始化全局CRT结构* /
init_CRT();
/ *构造全局对象和静态字段* /
construct_globals();
/ *调用用户提供的DllMain并从中获取返回码* /
ret = DllMain(hinstDLL,fdwReason,lpvReserved);
break;
case DLL_PROCESS_DETACH:
/ *调用用户提供的DllMain并从中获取返回码* /
ret = DllMain(hinstDLL,fdwReason,lpvReserved);
/ *破坏全局对象和静态字段* /
destruct_globals();
/ *破坏全局CRT结构* /
cleanup_CRT();
break;
case DLL_THREAD_ATTACH:
/ *初始化CRT线程本地结构* /
init_TLS_CRT();
/ *与以前一样,但对于线程本地对象* /
construct_TLS_globals();
/ *调用用户提供的DllMain并从中获取返回码* /
ret = DllMain(hinstDLL,fdwReason,lpvReserved);
break;
case DLL_THREAD_DETACH:
/ *调用用户提供的DllMain并从中获取返回码* /
ret = DllMain(hinstDLL,fdwReason,lpvReserved);
/ *销毁线程本地对象和静态字段* /
destruct_TLS_globals();
/ *破坏线程本地CRT结构* /
cleanup_TLS_CRT();
break;
默认值:
/ *?! * /
/ *调用用户提供的DllMain并从中获取返回码* /
ret = DllMain(hinstDLL,fdwReason,lpvReserved);
}
return ret;
}

没有什么特别之处:它也发生在正常的可执行文件中,你的主要被真正的entrypoint调用,由CRT保留完全相同的目的。



现在,这将清楚为什么Rakis的解决方案不会工作:全局对象的构造函数由真正的DllMain调用(即dll的实际入口点,这是关于DllMain上的MSDN页面的实际入口点),所以从那里调用LoadLibrary完全相同效果就是从你的假的DllMain中调用它。



因此,按照这个建议,您将获得与DllMain直接调用LoadLibrary相同的负面影响,也可以将问题隐藏在一个似乎无关的位置,这将使下一个维护者努力找到这个错误所在的位置。



至于delayload:它可能是想法,但是你一定要小心不要调用任何函数在您的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 vs Windows问题上:我不是一个Linux系统编程专家,但是我不认为在这方面有什么不一样的。



仍然有一些等价的DllMain( _init _fini 功能),这是什么巧合! - 自动采用的CRT,它反过来,从 _init ,调用全局对象的所有构造函数和标记为 __ attribute__构造函数的函数(它们以某种方式相当于虚拟DllMain提供给Win32中的程序员)。类似的过程与 _fini 中的析构函数相同。



由于 _init 也是在dll加载仍然被调用的时候发生( dlopen 还没有返回),我认为你在这里可以做的事情受到类似的限制。尽管如此,在我看来,Linux的问题还是比较少,因为(1)你必须明确地选择一个类似DllMain的功能,所以你不会马上试图滥用它,(2)Linux应用程序,据我所见,往往使用较少的动态加载dll。



简而言之,



否正确方法将允许您引用DllMain中的kernel32.dll以外的任何dll。



因此,不要做任何重要的事情DllMain既不直接(即在您的DllMain由CRT调用)也不间接(在全局类/静态字段构造函数中),特别 不加载其他dll 再次,直接(通过LoadLibrary)既不间接地(通过调用延迟加载的DLL中的函数,触发LoadLibrary调用)。



正确的方法来获取另一个DLL加载作为依赖是to do! - 将其标记为静态依赖关系。只需链接其静态导入库并至少引用其一个函数:链接器将其添加到可执行映像的依赖关系表中,并且加载程序将自动加载它(在调用DllMain之前或之后初始化它)不需要知道它,因为你不能从DllMain调用它)。



如果这是不可行的,因为某些原因,仍然有delayload选项以前我说过的限制)。



如果你仍然,由于某些未知的原因,在DllMain中有一个莫名的需求调用LoadLibrary ,继续,在你的脚上射击,在你的能力。但是不要告诉我我没有警告你。





我忘记了:关于这个话题的另一个基本信息来源是创建DLL的最佳做法微软的文档实际上只涉及到装载机,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);每个人都低估了这个答案(因为你的问题错了,我们要走出真正的领域),你怪谁谁跌倒。我解释你为什么这个解决方案根据你的问题是不正确的,为什么这个问题不能在真实的领域解决。你说你不在乎为什么不能做,只能在真实的领域做到这一点,并再次责怪数学。



现在,因为,如你所说,重申了一百万次,在你的条件下,你的答案没有解决方案,你可以解释一下我们你为什么有做这样一个愚蠢的事情,装载一个dll在DllMain ?通常,不可能的问题出现是因为我们选择了一条奇怪的路线来解决另一个问题,这使我们陷入僵局。如果您解释了更大的图片,我们可以提出一个更好的解决方案,它不涉及在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 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.

这篇关于从dll加载dll?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆