Loaded DLLs
A DLL is a file of shared code that a program loads to do its work, and most processes load many. An investigator looks for code that should not be there, such as a Windows DLL name in the wrong folder or a DLL nobody signed. Windows' own DLLs normally load from System32, signed by Microsoft. Start with each DLL's folder and signer, then set the loader's lists against what is mapped.
What It Is
A DLL (dynamic-link library) is a file of ready-made code that many programs share.
- Why Windows Needs It
- Almost every program opens files and draws windows. Windows ships that code once, in DLLs such as kernel32.dll and user32.dll, so no program has to carry its own copy. Each program loads the ones it needs when it starts.
- An Everyday Example
- Notepad, Word and Chrome all load kernel32.dll, so one copy on disk serves them all. A DLL's code runs inside the program that loads it, with that program's name and rights, which is why a DLL that should not be there makes a good disguise.
notepad.exeLoadskernel32.dlluser32.dllwinword.exeLoadskernel32.dlluser32.dllchrome.exeLoadskernel32.dlluser32.dllws2_32.dll
C:\
kernel32.dllFiles and processesuser32.dllWindows and buttonsws2_32.dllThe network
version.dll in C:\Users\Public. A Windows name in a folder anyone can write to. The program that loads it runs its code with that program's name and rights.
- Kept by the Kernel
- The kernel records each image mapped into the process with its file (the VAD), the better witness of what is really there.
- In the Process's Own Memory
- The loader's three lists sit in the PEB, in the process's own memory, which code in the process can change.
A module's folder is the first thing to read, because each folder holds a known kind of module.
Where a process's modules come from
| Folder | What Loads From It | Worth a Look When |
|---|---|---|
C:\ | Windows' own DLLs for 64-bit programs | The file is new, or unsigned even through a catalogue |
C:\ | Windows' own DLLs for 32-bit programs | The file is unsigned |
C:\ | Several versions of the same DLL, so each program gets the one it was built for | The file is unsigned |
The program's own folder | The program's own DLLs | The name copies a Windows DLL, or a user can write to the folder |
User folders such as App | Programs installed for one person | A Windows process loads from one |
The Search Order
When a program names a DLL without its full path, Windows tries a fixed order of places and loads the first match. Below are the main stops. Microsoft's full list adds a few special cases, such as redirection files, API sets, manifests and the 16-bit system folder.
The program's own folder comes before System32, so a DLL with a Windows name planted beside a program loads first, which Microsoft calls DLL preloading or binary planting. A Windows DLL name outside the system folders is the thing to check.
- DLLs already loaded, and Known DLLsOne already loaded, or listed in the KnownDLLs Registry key, is used as it is
- The folder the program loaded fromA planted copy here wins, before System32
- The system folderC:\
Windows\ System32 - The Windows folderC:\Windows
- The current folderThe process's current working folder
- The folders in PATHIn the order the variable lists them
Windows loads the first match, from the top.
Normal
A clean Windows 11 install
| Name | Path | Signed By |
|---|---|---|
version.dll | C:\ | Microsoft Windows |
Suspicious
srv-app-01,
| Name | Path | Signed By |
|---|---|---|
version.dll | C:\ | Not signed |
| PathFound in the program's own folder before System32: Windows' version.dll lives in System32. DLL Search Order Hijacking and Side-Loading | ||
The Loader's Lists
The loader keeps every DLL in three lists in the PEB, in load order, in memory order and in initialisation order. Code in the process can change them, so tools that read the lists (windows.dlllist, ListDLLs, Process Explorer) show only what the lists say.
windows.malware.ldrmodules takes every memory region that starts with an MZ header, the two letters every program and DLL file begins with, private memory included. It says whether each list holds an entry at its base, beside the file the kernel recorded for the region. The kernel writes that path without a drive letter, so set the drive aside when comparing.
A region in no list is a lead, not a verdict. In the published example output, the program's own executable is missing from the initialisation list, and resource files from all three. Every row below is a 64-bit process.
| Region | InLoad | InInit | InMem | MappedPath | What It Means |
|---|---|---|---|---|---|
spoolsv.exe | True | False | True | \ | The program itself, normally missing from the initialisation list |
kernel32.dll | True | True | True | \ | A normally loaded DLL, in all three lists |
spoolsv.exe.mui | False | False | False | \ | A language resource file, normal in no list |
Private region on ws-042 | False | False | False | None | Starts with MZ, in no list and with no file, a lead for windows. |
What Normal Looks Like
On a healthy machine this part looks like this.
- Windows DLLs load from System32 (SysWOW64 for a 32-bit program), some as side-by-side assemblies from WinSxS, signed by Microsoft.
- Programs also load their own DLLs from their install folders, so a module outside System32 is not a finding on its own.
- Many Windows files are signed in a separate catalogue file, not inside the file itself, and only some tools read catalogues.
- A DLL loaded the normal way sits in all three loader lists at the same base, with the same path as the image the kernel mapped.
Why It Matters
A trusted, signed program can still run someone else's code through a DLL it loads from the wrong folder. Module paths and signers show it, and memory shows modules the loader never listed.
Normal and Suspicious
A clean machine sits beside a suspicious one, and the pointer marks where to look.
Normal
A clean Windows 11 install
| Name | Path | Signed By |
|---|---|---|
ntdll.dll | C:\ | Microsoft Windows |
kernel32.dll | C:\ | Microsoft Windows |
Suspicious
ws-042,
| Name | Path | Signed By |
|---|---|---|
updsvc64.exe | C:\ | Not signed |
| Signed ByNot signed, and run from Temp: no publisher vouches for it, so hash it and find where it came from. Renamed, Unsigned or Falsely Signed Binaries | ||
ntdll.dll | C:\ | Microsoft Windows |
What a Malicious Example Shows
Each card is what malicious use of this part looks like, and where it shows.
- A Windows DLL name loaded from a program's own folder, beside a signed program copied to a folder a user can write to.Where it shows: Sysmon event 7 or Device
Image Load Events showing a path outside the system folders, unsigned or signed by someone unexpected. - A module in the loader's lists whose path differs from the file the kernel mapped there.Where it shows: windows.
dlllist's Path against windows. malware. ldrmodules' MappedPath. - A mapped DLL in none of the three loader lists, with its file behind it. Resource files (.mui) are the normal exception.Where it shows: windows.
malware. ldrmodules with False under InLoad, InInit and InMem and a MappedPath. windows.dlllist and ListDLLs do not show it. - A region in private memory that starts with an MZ header, in no list and with no file behind it.Where it shows: windows.
malware. ldrmodules with an empty MappedPath, and windows. malware. malfind on the same region.
Live and in Memory
Each question has the tools that answer it on a running machine, and in a memory image, a copy of its memory saved to a file.
Each module's folder and signer say whether it belongs, and Windows' own DLLs usually load from System32, SysWOW64 or WinSxS, signed by Microsoft. Live tools and windows.dlllist read the loader's own lists. windows.malware.ldrmodules sets those lists against every region that starts with an MZ header, so code the lists never held stands out.
Which DLLs has a process loaded, from where, and who signed them?
On a Running Machine
- ListDLLs (documentation, opens in a new tab)Live Machine A free Microsoft tool that lists the DLLs each process has loaded.
listdlls -uLists the DLLs in any process with their version and signature, or only the unsigned ones. - Process Explorer (documentation, opens in a new tab)Live Machine A free Microsoft tool that works like a detailed Task Manager. It shows each process under the one that started it, with its DLLs and handles. Its DLL view shows every DLL and memory-mapped file the selected process has loaded.
Windows.(documentation, opens in a new tab)Live Machine A Velociraptor artifact. It is a ready-made question Velociraptor asks of running machines, named from general to specific. The same across many machines, with optional hashes and certificate details for each module.System.DLLs
In a Memory Image
windows.dlllist(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. dlllist means DLL list. The DLLs each process's PEB loader list names, with their paths. Its dump option writes them out.
- ListDLLs (documentation, opens in a new tab)Live Machine A free Microsoft tool that lists the DLLs each process has loaded.
Is a module mapped that the loader does not list?
On a Running Machine
Windows.(documentation, opens in a new tab)Live Machine A Velociraptor artifact. It is a ready-made question Velociraptor asks of running machines, named from general to specific. VAD means virtual address descriptors, the records of memory regions. Each run of pages in the live address space with the file mapped there, in device form, to set against the loader's list of modules.System.VAD
In a Memory Image
windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. ldrmodules means loader modules. Every region that starts with an MZ header, private memory included, and whether each loader list holds it. In no list is a lead, since the program itself and resource files have ordinary gaps.malware. ldrmodules
Which loads were recorded as they happened?
On a Running Machine
Device(documentation, opens in a new tab)Logs and Records A Defender XDR table. It holds the records Defender for Endpoint keeps of what happened on each machine, and you search it with KQL. Each module load with the module's path and hash and the process that loaded it. Join DeviceFileCertificateInfo on SHA1 for the signer.Image Load Events
In a Memory Image
windows.dlllist(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. dlllist means DLL list. Only what is loaded at capture, so a DLL loaded and unloaded earlier is gone from its list.
What It Misses
Every way of looking has gaps, so cover these from another source.
- A list of loaded modules misses code that never went through the loader. Reflectively loaded and injected code show only to the injection checks.
- A DLL that loaded and unloaded before you looked is gone from a live list and from the PEB. Only Sysmon event 7 or DeviceImageLoadEvents kept it.
- No memory plugin prints a signer, so write the module out with windows.dlllist's dump option and check its signature offline.
False Positives
These look odd but are normal, so rule them out before you act.
- A mapped image in no loader list is a lead, not a verdict, since the program itself and resource files (.mui) have ordinary gaps.
- Many Windows files are signed through a catalogue, not inside the file, so a check that reads only embedded signatures calls them unsigned. Confirm with Sigcheck or Get-AuthenticodeSignature.
- A module outside System32 is often a program's own DLL, so look harder when a user can write to its folder or its name copies a Windows DLL.
Red Flags It Explains
Each red flag below is confirmed by reading this part.
- Unsigned or Oddly Signed Modules List the modules with their paths and signers (ListDLLs, Process Explorer's DLL view, Windows.System.DLLs), using a check that reads catalogue signatures.
- DLL Search Order Hijacking and Side-Loading List the process's modules with their folders (ListDLLs, Process Explorer, windows.dlllist). A Windows DLL name outside System32, SysWOW64 or WinSxS stands out.
- Abused Built-In and Admin Tools List what rundll32.exe or regsvr32.exe loaded. An unsigned DLL from a user or temporary folder is the code it was used to run.
Checks That Use It
These checks read this part as one of their steps.
Hunt It
The same hunt is written for four tools, so pick the one you use. Defender XDR and Sentinel queries are in KQL, which you paste into Advanced hunting in the Defender portal or into Sentinel's Logs page. Splunk queries are in SPL, for its Search page, and Velociraptor queries are in VQL, which you run as a hunt.
Needs Defender for Endpoint, whose certificate checks on files (catalogue signatures included) fill DeviceFileCertificateInfo. A module it has not checked has no row there and is left out, so a quiet result is not proof. A third-party module in a Windows program, such as a security product's, is a lead to explain, not a verdict.
// Modules loaded into programs that run from the Windows folder, whose file is unsigned or not trusted, rarest firstlet Untrusted = DeviceFileCertificateInfo | where Timestamp > ago( 30d) | where IsSigned == false or IsTrusted == false | distinct SHA1;DeviceImageLoadEvents| where Timestamp > ago( 7d)| where InitiatingProcessFolderPath startswith @"c:\ windows\ "| where SHA1 in ( Untrusted)| summarize Devices = dcount( DeviceId), Loads = count( ), Processes = make_set( InitiatingProcessFileName, 10), FirstSeen = min( Timestamp), LastSeen = max( Timestamp) by FileName, FolderPath, SHA1| order by Devices asc, Loads ascSources
- Microsoft Sysinternals: ListDLLs (opens in a new tab)
- Microsoft Sysinternals: Process Explorer (opens in a new tab)
- Microsoft Sysinternals: Sigcheck (opens in a new tab)
- Microsoft Learn: Get-AuthenticodeSignature (opens in a new tab)
- Microsoft Learn: dynamic-link library search order (opens in a new tab)
- Microsoft Learn: dynamic-link library security (opens in a new tab)
- Microsoft Learn: Windows API sets (opens in a new tab)
- Microsoft Learn: about side-by-side assemblies (opens in a new tab)
- Microsoft Learn: PEB_LDR_DATA structure (opens in a new tab)
- Microsoft Learn: !dlls debugger extension (opens in a new tab)
- Microsoft Sysinternals: Sysmon (opens in a new tab)
- Microsoft Learn: DeviceImageLoadEvents table (opens in a new tab)
- Microsoft Learn: DeviceFileCertificateInfo table (opens in a new tab)
- Velociraptor: Windows.System.DLLs (opens in a new tab)
- Velociraptor: Windows.System.VAD (opens in a new tab)
- Velociraptor blog: verifying executables on Windows (opens in a new tab)
- Volatility 3: windows.dlllist (opens in a new tab)
- Volatility 3: windows.malware.ldrmodules (opens in a new tab)
- Volatility 3 source: ldrmodules.py (opens in a new tab)
- Volatility Foundation: command reference, ldrmodules sample output (opens in a new tab)
- Volatility 3: windows.malware.malfind (opens in a new tab)
- Microsoft Learn: advantages of dynamic linking (opens in a new tab)
- Microsoft Learn: CreateFileW function, in Kernel32.dll (opens in a new tab)
- Microsoft Learn: CreateWindowExW function, in User32.dll (opens in a new tab)
- Microsoft Learn: connect function, in Ws2_32.dll (opens in a new tab)
- SigmaHQ: Potential System DLL Sideloading From Non System Locations (opens in a new tab)
- MITRE ATT&CK T1003.001: OS Credential Dumping: LSASS Memory (opens in a new tab)
- Splunk Security Content: Sysmon EventID 7 data source (opens in a new tab)
MITRE ATT&CK is a free public catalogue of the techniques attackers use. Each technique has an ID, such as T1055 for Process Injection, so defenders can name the same behaviour the same way. Reading this part helps find these techniques.
- T1574.001 (opens in a new tab) Hijack Execution Flow: DLL
- T1036 (opens in a new tab) Masquerading
- T1055.001 (opens in a new tab) Process Injection: Dynamic-link Library Injection
- T1620 (opens in a new tab) Reflective Code Loading