May 26th, 2025
0 reactions

Why does Enum­Process­Modules report no modules on a process that was created suspended?

A customer had a test that created a process suspended, and without resuming it, they called Enum­Process­Modules to see what modules are in it. The Enum­Process­Modules reported no modules. Why is that? Shouldn’t it at least report the primary executable?

Recall that in Windows, processes are self-loading, which means that they manage their own module list. When the kernel creates a process, it sets the initial instruction pointer to an internal function inside ntdll.dll, provides information about what the new process should do (for example, the command line arguments), and then lets the process start executing. The function inside ntdll.dll does the work of loading all the modules, adding entries to the module list as it goes.

If the process is created suspended, then it hasn’t started loading itself, which means that there is nothing in the module list.

This is called out in the documentation for Enum­Process­Modules:

The Enum­Process­Modules function is primarily designed for use by debuggers and similar applications that must extract module information from another process. If the module list in the target process is corrupted or not yet initialized, or if the module list changes during the function call as a result of DLLs being loaded or unloaded, Enum­Process­Modules may fail or return incorrect information.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Joshua Hudson

    So it turns out that a newly created process in CREATE_SUSPENDED state has modules load.

    1) The EXE is loaded
    2) ntdll is loaded
    3) kernel32.dll is loaded

    This is in fact contractual behavior (except in the degenerate case of a Win32 process not linked against kernel32.dll at all). The reason this is contractual behavior is CreateRemoteThread(LoadLibrary, mylibrarydll_inremoteprocess), which would have a race condition otherwise. If the source and target process have the same LARGEADDRESSAWARE and HIGHENTROPYVA flags set, the address of kernel32 functions *will* be the same in both. I've lost the actual citation for it but it was documented as...

    Read more
  • alan robinson

    Interestingly, the last 3 notifications about old new thing (including this one) have been marked by google/gmail as SPAM and also SUSPICOUS. Can I infer that google thinks that even suspended processes should have at least one module?