There is a dll for implementation in an already running process that does something and then unloads itself. All this happens at the very stage of implementation (with DLL_PROCESS_ATTACH ).

 library Test; uses Winapi.Windows; procedure DllMain(Reason: integer); begin if Reason = DLL_PROCESS_ATTACH then begin // что-то делаем FreeLibraryAndExitThread(HInstance, 0); // выгружаемся end; end; begin DllProc := @DllMain; DllMain(DLL_PROCESS_ATTACH); end. 

Probably, during the unloading, some kind of blocking occurs, as a result of which sometimes the process hangs tight. Sometimes this does not happen, but it is still impossible to close the process properly after that.

Example:
1) Run, say, a calculator
2) Introduce this dll into it (Process Hacker, for example)

Then I was able to catch three types of process behavior:
a) it hangs tight
b) ceases to respond to the closure of the cross
c) When you close the main window, it remains to hang in the list of processes.

Despite the fact that the dll itself successfully unloads and even terminates its flow - it spoils something in the process.

If you remove the unloading from the code, and do it separately, everything works as it should.

Question: how to get rid of this behavior while maintaining the functionality of self-unloading dll?


UPD1: If you replace FreeLibraryAndExitThread with FreeLibrary everything becomes even worse: the process immediately drops with a BEX64 error.

  • 2
    which thread do you mean stop by calling FreeLibraryAndExitThread ? - Igor
  • @Igor, the one in which the dll code is executed, i.e. the one that was created during its implementation. - diversenok

1 answer 1

DLLMain is called by the system under general locking, so there are significant restrictions on the execution of something inside this function.

Warning DLL entry point. See General Best Practices for Specific Windows APIs that are unsafe to call in DllMain. If you want to do that, you can do that in an initialization function for the DLL. It can be used to call the initialization function.

those. The easiest way to solve a problem is to call a separate initialization function from this DLL, which you can export along with all other functions and structures.

See also the list of tasks that can not be performed in DllMain:

  • Call LoadLibrary or LoadLibraryEx (either directly or indirectly). This can cause a deadlock or a crash.
  • Call GetStringTypeA, GetStringTypeEx, or GetStringTypeW (either directly or indirectly). This can cause a deadlock or a crash.
  • Synchronize with other threads. This can cause a deadlock.
  • Acquire a synchronization of the loader lock. This can cause a deadlock.
  • Initialize COM threads by using CoInitializeEx. Under certain conditions, this function can call LoadLibraryEx.
  • Call the registry functions. These functions are implemented in Advapi32.dll. If you have a DLL, you can uninstall the memory, the DLL can access it.
  • Call CreateProcess. Creating a process can load another DLL.
  • Call ExitThread. Executing a DLL during a detachment can be lost again, causing a deadlock or a crash.
  • Call CreateThread. It can be risky.
  • Create a named pipe or other named object (Windows 2000 only). 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.
  • C Run-Time (CRT). If the CRT DLL is not initialized, the process can crash.
  • Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.
  • Use managed code.

And the list of tasks that are allowed in DllMain:

  • Initialize static data structures.
  • Create and initialize synchronization objects.
  • Allocate memory and initialized dynamic data structures (avoiding the functions listed above.)
  • Set up thread local storage (TLS).
  • Open, read from, and write to files.
  • Call functions in Kernel32.dll (except for functions that are listed above).
  • Set global pointers to NULL, putting the initial members of the dynamic members. In Microsoft Windows Vista ™, you can only use it in the multithreaded environment.

Details can be obtained from General Best Practices.


Another way to call something in DllMain from the banned list above is to create a thread in DllMain in which to perform all the necessary actions. The thread will bypass the global lock already outside of DllMain, so it is possible to execute code without restrictions in it.

Here, too, there are some nuances, details - in the blog ms:
Does creating a thread from DllMain deadlock or doesn't it?

  • Yes, it was really risky. However, it is likely that some of these restrictions apply only to the situation when the application itself tries to load the dll, and not to the implementation? - diversenok
  • And yes, it turns out, if the process is not going to cause anything in this dll - it will not be able to unload itself either, that is, do you need someone to do it from the side? - diversenok
  • When implementing, LoadLibrary is similarly called, i.e. This method does not differ from the usual. Yes, with injection, you will have a problem, cause something from this DLL. Another way to call something in DllMain from the banned list above is to create a thread in DllMain in which to perform all the necessary actions. The thread will bypass the global lock already outside the DllMain, so it is possible to execute code without restrictions (rendered in response). - mega
  • @mega, and how will a new thread help you not to bury the primitive level synchronization of the whole process ? After all, the loader will not remove the lock until it has finished loading all the libraries and their dependencies. - ߊߚߤߘ
  • Do not remove, of course. And the stream will not start in DllMain, if you are talking about it. But it will definitely start after, as soon as it can. See the discussion on the last link. - mega