views:

784

answers:

5

Take a standard Windows application. It loads a DLL using LoadLibrary to call a function in it (we'll call this DLL_A). That function loads another DLL (we'll call it DLL_B). The application now unloads the DLL_A DLL using FreeLibrary as it no longer requires it.

The question is: Is DLL_B still in memory and loaded?

Is this something I can depend upon, or is it undocumented?

A: 

Read the Remarks section for a detailed explanation.

The key thing to note is:

The system maintains a per-process reference count for each loaded module

and further down

When a module's reference count reaches zero or the process terminates, the system unloads the module from the address space of the process

From MSDN:

Frees the loaded dynamic-link library (DLL) module and, if necessary, decrements its reference count. When the reference count reaches zero, the module is unloaded from the address space of the calling process and the handle is no longer valid.

dirkgently
+7  A: 

No. DLL_B will not be unloaded. The LoadLibrary() call made by DLL_A will increment the load count for DLL_B. Since there is no corresponding FreeLibrary() call for DLL_B, the refcount will not go to zero.

From the LoadLibrary() docs:

The system maintains a per-process reference count on all loaded modules. Calling LoadLibrary increments the reference count. Calling the FreeLibrary or FreeLibraryAndExitThread function decrements the reference count. The system unloads a module when its reference count reaches zero or when the process terminates (regardless of the reference count).

Michael Burr
And the rub is that you aren't supposed to call FreeLibrary() in DLLMain on a process detach, so how would you actually go about cleaning up DLL_B?
Eric Petroelje
One common method is to have init() and deinit() calls for the DLL_A that load/unload DLL_B (and perform other init/cleanup functions). It's intrusive, but it's straightforward.
Michael Burr
In this case, I *want* DLL_B to be left behind until the main app closes. Indeed, this is ideal for me. My intention is to inject the DLL and a thread it will create into the app.
mj2008
I hate to suggest this because what you're wanting to do sounds ugly (but sometimes that's reality) - you might want to have DLL_B do a LoadLibrary() on itself while it has the thread active so it'll stay loaded regardless how someone else loads/unloads it.
Michael Burr
Or you could have the main app do a load library on DLL_B... which will ensure DLL_Bs reference count is greater than zero until the app closes.
Chris Lively
That's definitely the proper thing to do - I'm under the impression that mj2008 does not have control over the main app and needs to do this 'behind it's back' (I hope this isn't malware related...).
Michael Burr
Rest assured, this is just trying to add function to an old app via a DLL that I have control over. Related to my recent Q on sending a window message from a service, and not being possible. The added DLL could do it for me.
mj2008
A: 

DLLs in windows are reference counted. When A is unloaded you are decrementing the reference count on A, if it hits zero it will unload, and (assuming no bugs in the code) decrement the reference count on B. If the refcount on B goes to zero it will then be unloaded. It is possible DLL C has a refcount on B, and unloading A will not unload B.

grieve
A: 

Yes... Unantedend DLL are up in memory for at least two minutes without being dropped by windows.

Unless the dll_a free dll_b.

gbianchi
+2  A: 

You will have a handle leak in the case:

Program -Load> Dll A 
          -Load> Dll B 
        -Unload> Dll A

No code is implicitly executed by a module being unloaded to unload the modules that it loaded.

Since no code is executed to decrease the reference count, the module B will never be unloaded.

Here are the rules for loading / unloading dlls:

  • Each call to LoadLibrary and LoadLibraryEx will increment the reference count for that module. This is in the context of the calling process only, not across process boundaries.
  • Each call to FreeLibrary or FreeLibraryAndExitThread will decrement the reference count.
  • When the reference count reaches 0, it will be unloaded.
  • When Windows sees that your program is closed, any leaked unloaded modules will then be unloaded.
  • Depending on what you are doing, DllCanUnloadNow might be useful to you.

Still in memory vs still loaded:

There is no guarantee that your module will be released from memory at a certain time when the reference reaches 0. But you should consider the module as if it is unloaded when the reference count reaches 0.

Stopping the DLL from being unloaded:

To force the DLL from being unloaded you could try

  • The system calls DllMain with the DLL_PROCESS_DETACH flag. You could try to not return from this via some kind of blocking operation.
  • You could try to call LoadLibrary from within the DLL that you want to not be able to unload. (Self load)

Edit:

You mentioned your goal is to injet code into the running program and that you wanted to leak the handle on purpose.

That is fine, but if you run this operation a lot it can lead to a crash in your source Program because too many handles will be used, or eventually too much memory will be used.

You can return FALSE from your DllMain to stop it from being loaded so that you don't waste memory. You do this when fdwReason is DLL_PROCESS_ATTACH. You can read more about it here.

If you are trying to emulate a DLL and add in your own extra functionality, you will need to implement all of the functions that the source DLL implements and delegate each call back to the source DLL.

Brian R. Bondy
I'm quite happy leaking the handle as the DLL_B will actually be performing a useful function. I want to "inject" it into the application.
mj2008
Updated my response to include this.
Brian R. Bondy
TVM - the multi-load issue is something I will ponder.
mj2008