AppInit_DLLs and AppCertDLLs: the RegRipper appinitdlls plugin
8 min read
AppInit_DLLs and AppCertDLLs are two of the oldest DLL-injection persistence mechanisms Windows ships, and the RegRipper appinitdlls/appcertdlls plugins exist to answer one question fast: is either of them enabled, and is it pointing at a DLL you cannot account for? Both let a single registry write force an attacker's library into the address space of large swaths of running processes without touching a single executable on disk. That is what makes them powerful. It is also why Microsoft has spent the last decade strangling one of them. Knowing which is still live on the build you are looking at is the whole game.
The trap to avoid up front: these two artifacts live in different hives. AppInit_DLLs is in SOFTWARE. AppCertDLLs is in SYSTEM. If you load one hive and not the other you will check half the attack surface and call the box clean.
AppInit_DLLs: SOFTWARE, and loaded into everything that links user32.dll
The key is SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows. Three values matter:
AppInit_DLLs— a string (historically space- or comma-delimited) listing one or more DLL paths.LoadAppInit_DLLs— a DWORD master switch.0means the list is ignored;1means it is honored.RequireSignedAppInit_DLLs— a DWORD that, when set, requires the listed DLLs to be Authenticode-signed before they will load.
The loading mechanism is the dangerous part. Any DLL named here is loaded into every process that links user32.dll — which, on a desktop, is effectively every interactive process. The library's DllMain runs in the context of each of those processes during initialization. One value, arbitrary code in hundreds of process contexts. This is MITRE T1546.009, AppInit DLLs.
The reading discipline: a non-empty AppInit_DLLs value is only armed when LoadAppInit_DLLs is 1. A DLL path sitting in AppInit_DLLs with LoadAppInit_DLLs at 0 is dormant. That nuance is exactly why you read the flag and the list together, never one without the other. RegRipper's appinitdlls plugin pulls both from the Windows key in the same pass; so does the parser on this site. The note we attach to the result is blunt about why: AppInit_DLLs are loaded into every GUI process — classic injection.
A worked example. Suppose you read the Windows key and get:
AppInit_DLLs : C:\ProgramData\Intel\drvhost.dll
LoadAppInit_DLLs : 1
That is an armed AppInit hook. drvhost.dll will load into every user32-linked process at startup. The next question is whether it is legitimate, and the path is your first tell: a DLL in C:\ProgramData\Intel\ rather than under Program Files or System32 is exactly the kind of plausible-but-wrong location attackers favor. Pull the file from the MFT, check its Authenticode signature, and check whether anything legitimate installed it.
Why modern Windows mostly kills this
Here is the part that decides whether you are looking at a live threat or a museum piece. Microsoft began constraining AppInit_DLLs years ago, and on modern Windows the mechanism is largely disabled by default. The two relevant constraints:
- Signing enforcement. On Windows 8 and later, AppInit DLLs must be Authenticode-signed;
RequireSignedAppInit_DLLsdefaults to enforcing this. An unsigned DLL named inAppInit_DLLssimply will not load. - Secure Boot. When Secure Boot is enabled, the AppInit_DLLs mechanism is disabled outright regardless of the registry values. The values can be present and
LoadAppInit_DLLscan be 1, and nothing loads.
The practical consequence for you: on a current, Secure Boot-enabled Windows 10/11 host, a populated AppInit_DLLs value is most likely either a leftover, a misconfiguration, or an attacker who did not realize the mechanism is inert on this build. It is still worth flagging — defenders should know the value is set — but do not assume it executed. Confirm the build version and the Secure Boot state before you write "this DLL was injected into every process" into a report. I would hedge here: the exact enforcement behavior has shifted across servicing updates, so treat "AppInit is dead on modern Windows" as the default expectation, not an absolute, and verify on the specific build in front of you.
On older systems — Windows 7, Server 2008 R2, and unpatched or Secure Boot-disabled boxes — AppInit_DLLs remains fully functional and is a legitimate, high-value finding.
AppCertDLLs: SYSTEM, and loaded via the CreateProcess family
This is the one that did not get neutered the same way, and it lives in a different hive. The key is:
SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls
Each value under that key names a DLL. The loading trigger is different from AppInit: an AppCertDLL is loaded into a process whenever that process calls a function in the CreateProcess family — CreateProcess, CreateProcessAsUser, WinExec, CreateProcessWithLogonW, and friends. In practice that means the DLL gets pulled into any process that spawns child processes, which on a working system is a great many of them. This is MITRE T1546.008-adjacent in spirit but tracked under the broader event-triggered-execution umbrella; RegRipper and most reference material file AppCertDLLs alongside AppInit as a DLL-injection persistence pair.
Two things make AppCertDLLs notable in 2026:
- It is not subject to the AppInit signing/Secure Boot kill switch. The constraints that disabled AppInit do not apply to AppCertDlls, so on modern Windows AppCertDlls is the more likely of the two to actually fire.
- The key usually does not exist at all on a clean system. Where
AppInit_DLLsis a value that ships present-but-empty,Session Manager\AppCertDllsis frequently absent entirely. That changes how you read it.
The reading discipline for AppCertDlls is therefore simpler and, honestly, more damning: the existence of the key with any value in it is the finding. There is no master enable flag to check the way there is for AppInit. RegRipper's appcertdlls plugin enumerates the values under the key and reports each name and DLL path; the note we surface in this parser says it plainly — AppCertDlls are loaded into every process that calls CreateProcess/WinExec, so any entry here is an injection/persistence mechanism.
A worked example of the output you would see:
Name : svc
DLL : C:\Windows\Temp\netcfg.dll
A DLL under C:\Windows\Temp\ registered as an AppCertDll is not a configuration accident. That gets pulled into every process that spawns children, runs its DllMain each time, and survives reboots because it lives in the registry, not in a startup folder. Treat it as confirmed persistence pending file analysis.
How to actually read these in an investigation
The workflow is short because the artifacts are small. For each, the analyst's job is to answer "enabled, and pointing at something I can vouch for?"
AppInit_DLLs (SOFTWARE hive):
- Read
SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows. - If
AppInit_DLLsis empty, you are done — nothing is hooked. - If it is non-empty, read
LoadAppInit_DLLs. If it is 0, the hook is dormant. If it is 1, it is armed (subject to the build's signing/Secure Boot enforcement). - Check
RequireSignedAppInit_DLLsand establish the Windows build and Secure Boot state to judge whether an unsigned DLL could actually have loaded. - For every listed DLL, verify path, signature, and provenance.
AppCertDLLs (SYSTEM hive):
- Read
CurrentControlSet\Control\Session Manager\AppCertDlls— remembering that "CurrentControlSet" resolves throughSelect\Current, which any competent parser handles for you. - If the key is absent or empty, done.
- If any value exists, the DLL it names is your finding. Verify path, signature, and provenance, then assume injection until proven otherwise.
The thing that catches people is step zero on each: loading the right hive. AppInit needs SOFTWARE, AppCertDlls needs SYSTEM. They are conceptually a pair and they are physically in two separate files.
Tools
- RegRipper's
appinitdllsplugin (source) reads theWindowskey and reportsAppInit_DLLsand theLoadAppInit_DLLsflag together. Its companionappcertdllsplugin enumerates theSession Manager\AppCertDllsvalues from the SYSTEM hive. Run both; they cover different hives. - Eric Zimmerman's RECmd with a persistence batch file will pull both keys into CSV in one pass if you point it at SOFTWARE and SYSTEM.
- The parser on this site surfaces both as persistence-category findings —
appinit_dllsfrom SOFTWARE,appcertdllsfrom SYSTEM — and lets you analyze a hive in your browser without standing up a Perl environment.
For the full list of what RegRipper extracts, see the RegRipper plugins reference.
Where this fits in the persistence picture
AppInit_DLLs and AppCertDLLs are the injection-flavored end of registry persistence. They sit next to the load-order and shell-hijack mechanisms: the Winlogon Notify and Shell/Userinit hooks that run code at logon, and the Run keys and Image File Execution Options that launch or debugger-hijack named binaries. Where Run keys launch a process, AppInit/AppCert force a DLL into processes that were going to run anyway. That distinction matters: an AppCertDll never appears as its own process in the timeline, which is exactly why an attacker reaches for it.
The bottom line for triage: check both hives, read the enable flag for AppInit and the mere existence of the key for AppCertDlls, and weigh AppInit findings against the build's Secure Boot and signing posture before you call them live. On a modern, Secure Boot host, an AppCertDll is the one more likely to have actually executed — and it is the one sitting quietly in SYSTEM where a SOFTWARE-only sweep will never see it.