Search code examples
c++windowsdlldll-injection

Controlling the order of dllmain() calls while being injected to another process


Is there a way to somehow control the order DLLs are loaded? This is mainly related to the limitation one must obey while present within DllMain() function.

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
    {
        SHGetKnownFolderPath()
    }
    case DLL_PROCESS_DETACH:
    {
        DllMainProcessDetach(hinstDLL, lpReserved);
        return TRUE;
    }
    default:
        break;
    }

    return TRUE;
}

In that particular code, i use SHGetKnownFolderPath() to retrieve a certain directory, Once SHGetKnownFolderPath is executed, i get the following call stack: enter image description here

after disassembling stack's code, i realized the problematic call is Ole32's CoTaskMemAlloc() function.

So i once again set another debugging session with only 1 function called within DllMain() : CoTaskMemAlloc() and disassembled the code:

enter image description here

EAX register which holds g_pMalloc's address is null, which appears not to be initialized by dllcrt.


Solution

  • I don't think you can safely call shell functions in DllMain. There is a long list of things you can't do due to the way process initialization is done in Windows.

    You should never perform the following tasks from within 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 object that is owned by code that is waiting to acquire 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 Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash.
    • Call CreateProcess. Creating a process can load another DLL.
    • Call ExitThread. Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash.
    • Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is 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.
    • Use the memory management function from the dynamic C Run-Time (CRT). If the CRT DLL is not initialized, calls to these functions can cause the process to crash.
    • Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized.
    • Use managed code.

    The following tasks are safe to perform within DllMain:

    • Initialize static data structures and members at compile time.
    • Create and initialize synchronization objects.
    • Allocate memory and initialize 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 the functions that are listed above).
    • Set global pointers to NULL, putting off the initialization of dynamic members. In Microsoft Windows Vista™, you can use the one-time initialization functions to ensure that a block of code is executed only once in a multithreaded environment.