Windows Persistence Forensics: Services, Scheduled Tasks, and Autoruns
In the earlier posts in this Windows artefacts series, we looked at a common DFIR mistake: taking an artefact that supports a claim and letting it become the claim. Prefetch, Shimcache, and Amcache all create that temptation in different ways. They give you program names, paths, timestamps, and enough context to feel conclusive. The problem is that they can look like answers before you’ve proved the conclusion.
Persistence artefacts are different, but they can still mislead you.
In short: Windows persistence artefacts usually prove configuration, not execution. They show that the system was configured to run something again under defined conditions. They don’t, by themselves, prove that the payload ran, succeeded, was malicious, or was understood by a user.
When you find a service, a scheduled task, or an autorun entry pointing to an unexpected binary, you’re usually looking at a configuration decision. Someone, or something, changed system state in a way designed to survive reboot, logon, or time. That’s a stronger starting point than a cache entry or inventory record. The system isn’t just remembering that something might have happened. It’s been instructed to make something happen again.
That can give you more confidence about system state, but only for the claim the artefact actually supports. Persistence tells you the mechanism exists. You still need separate evidence for who introduced it, whether it fired, what ran, and what happened after.
This post is about keeping those claims clear in your investigation and in your reporting.
What Windows Persistence Artefacts Prove
| Question | Short answer |
|---|---|
| What are Windows persistence artefacts? | Configuration traces that show the system was set up to run something again, such as services, scheduled tasks, Run keys, autoruns, WMI subscriptions, or IFEO entries. |
| Does persistence prove execution? | No. Persistence proves configured repeatability. Execution still needs runtime evidence. |
| Does persistence prove malware? | No. Legitimate software, administrators, EDR tools, responders, and attackers all use persistence mechanisms. |
| What does persistence prove best? | It can strongly support that system state was changed to make future execution possible under defined conditions. |
Why Persistence Feels Stronger Than Execution-Adjacent Evidence
Persistence artefacts often “click” for analysts in a way that Prefetch or Shimcache doesn’t.
Execution-adjacent artefacts often record observation. Windows saw something happen, or saw something that might relate to execution, and left a trace behind. Persistence artefacts record instruction. A service, task, or autorun entry describes something the system was told to do again.
Persistence gives you durable configuration artefacts to inspect, not just a faint trace of activity. From there, you can separate the configured mechanism from whether it ran and what happened next.
A persistent entry can be created by a user, administrator, installer, updater, management platform, EDR tool, remote support agent, responder, or malware. Legitimate software uses persistence constantly, sometimes aggressively. Enterprises use it to enforce policy, keep agents alive, run updates, and maintain management tooling. Attackers use the same mechanisms because they work and because they blend into that noise.
A service, task, or Run key tells you the mechanism is configured. To show runtime, you need service start events, task history, process creation logs, EDR telemetry, Prefetch, or side effects from the payload.
It’s usually cleaner to split persistence analysis into three questions:
- What was the system configured to do?
- Did that mechanism actually run?
- What happened as a result?
These artefacts are strongest on the first question. For the second, you need logs, timestamps, or telemetry. For the third, you usually need downstream evidence.
Persistence and Execution Aren’t the Same Timeline Event
Persistence and execution are related, but they’re not the same thing.
Execution is an event; something ran, or is believed to have run.
Persistence is a capability; something is positioned to run again under predictable conditions.
An attacker can execute without persistence. A user can persist something without malicious intent. An installer can create persistence without user awareness. A scheduled task can sit on a host for years because it belongs to legitimate software and does exactly what it’s meant to do.
This distinction matters when you build a timeline.
If you see Prefetch for example.exe at 10:14 and a new scheduled task created at 10:15 that calls example.exe, you might have a plausible chain: execution followed by persistence. If the task was created at 09:00 and Prefetch appears at 10:14, you might have the reverse: persistence existed first, then execution occurred later because the trigger conditions were met.
If the task exists but there is no corroborating execution evidence, don’t fill the gap with assumptions. The task might never have run. It might only run under conditions that weren’t met. It might have run and left no Prefetch because of system settings, application behaviour, or timing.
In a timeline, label each claim clearly:
- A persistence mechanism was configured
- The persistent payload executed (if you have evidence to support that)
- The payload caused impact (if you have evidence for that too)
That’s how persistence strengthens a timeline: it gives you an anchor without forcing you to pretend every later step is proven.
The Question Persistence Answers Best
Analysts get into trouble when they treat every artefact as if it answers the same question.
Prefetch often helps you answer:
Did this executable run, and under what context?
Shimcache and Amcache often help you answer:
Has this binary been present and referenced in execution-related contexts?
Persistence artefacts help you answer:
Has the system been configured so that this program is likely to run again?
I frequently see persistence evidence asked to carry too much. It can give you a file path, account context, trigger, and sometimes a timestamp. Use those for baselining, comparison, and timeline work; treat everything else as a separate claim.
Services: Persistence That Looks Like the Operating System
At a Glance
- What it is: A Windows service is a configured background component that can start automatically, on demand, or under service control manager conditions.
- Where to look: Service Control Manager output, the
SYSTEMregistry hive underCurrentControlSet\Services, service-related event logs, EDR telemetry, and the configured binary or service DLL path. - How to parse it: Use
sc.exe queryorsc.exe qcon a live system, PowerShell or WMI/CIM queries where appropriate, and registry parsing withRECmdfor offline hives. - Useful fields: Service name, display name, description, ImagePath, ServiceDll, start type, service type, account context, dependencies, and recovery actions.
- Be careful with: A service entry proves configuration. It doesn’t prove the service started, that the payload behaved maliciously, or that the account context represents attacker privilege.
Windows services are designed for persistence. They allow long-running components to start automatically, run in the background, and operate without an interactive user session.
That also makes them noisy. On enterprise endpoints, services are everywhere. The OS installs them. Legitimate software installs them. Endpoint tooling installs them. Remote support tools install them. Attackers know this and use services because they can look boring.
A service configuration is useful because it usually tells you what should run, how it should start, and under which account context. If a service is set to start automatically, the system has been configured for repeated execution at boot or service start. That’s meaningful evidence of durable system state.
The trap people often fall into is treating a suspicious service as “malware installed” without doing the work. Sometimes it’s malware. Sometimes it’s a legitimate agent. Sometimes it’s a forensic tool. Sometimes it’s an attacker hiding behind a legitimate-looking service name, path, or display name.
How to Work with Service Evidence
Start with the basics. What’s the service name, display name, description, and configured path? Does the path point to a known Windows location, a vendor directory, a user profile, or a temporary folder? Is the binary signed? Does it align with installed software?
Then anchor it in time. When did the service appear? Does that line up with software installation, patching, endpoint onboarding, responder activity, or the incident window? If you don’t have a reliable service creation time, use surrounding artefacts. Event logs, installation logs, file system timestamps, and EDR telemetry may give you a better anchor than registry timestamps alone.
Then look for behaviour. A service entry tells you what should run. It doesn’t prove it ran in the window you care about. If you need execution, look for service start events, process creation logs, EDR telemetry, Prefetch where applicable, network connections, or file system activity consistent with the payload.
Be careful with account context. A service configured to run as LocalSystem can supports the narrower claim that, if started, the service would run with high privileges. It doesn’t, by itself, prove attacker privilege. Legitimate installers and management platforms create LocalSystem services all the time. If you want to claim attacker privilege, you need evidence showing how that configuration was introduced.
Scheduled Tasks: Persistence That Looks Like Administration
At a Glance
- What it is: A scheduled task is a Task Scheduler definition that links triggers, actions, settings, and an execution principal.
- Where to look: Task Scheduler,
schtasksoutput, task XML,%SystemRoot%\System32\Tasks, Task Scheduler operational logs, EDR telemetry, and related process creation evidence. - How to parse it: Use
schtasks /query /fo LIST /vfor readable output,schtasks /query /xmlfor task definitions, PowerShell task cmdlets, or event log parsing usingEVTXEcmd. - Useful fields: Task path, author, registration time, triggers, actions, command line arguments, principal, run level, last run time, and task history.
- Be careful with: A task definition proves the system was configured to run something. It doesn’t prove the trigger fired, that the action succeeded, or that
LastRunTimeis complete and reliable.
Scheduled tasks are another common persistence mechanism because they provide a structured way to run programs repeatedly or in response to triggers. They’re used for maintenance, updates, telemetry, cleanup, policy enforcement, and management activity. They can run under user or system contexts. They can trigger at logon, startup, on a schedule, when idle, or when specific conditions are met.
Tasks often feel more precise than services because they expose triggers and schedules. That precision is useful, but it can also mislead. Task creation isn’t proof of immediate execution. A trigger isn’t proof that the task ran. Conditions like idle state, AC power, missed schedules, sleeping laptops, disabled task history, and rolled logs can all create gaps.
How to Work with Task Evidence
A scheduled task usually gives you four useful things:
- A name and location in the task folder hierarchy
- One or more triggers
- One or more actions, usually a command line or script interpreter with arguments
- A principal, meaning the account context and run level
A task definition gives you a mechanism set up to run again. Runtime needs another source: task history, process creation, EDR, Prefetch, PowerShell logs, network telemetry, or file system side effects.
Also keep the enterprise baseline in mind. Legitimate updaters and management tools often create tasks that run PowerShell, call scripts, use uncommon vendor paths, or run at logon. That’s not automatically malicious. Conversely, attackers often choose names and folder paths that resemble vendor or OS tasks.
A single endpoint can mislead you. Stacking and least-frequency-of-occurrence analysis often makes the answer clearer. If the task is present everywhere with the same software installed, your interpretation changes. If it only appears on one host during the incident window and points to an unsigned binary in a user profile, your confidence changes again.
Autoruns: Persistence That Looks Like Normal User Activity
At a Glance
- What it is: Autoruns are a broad set of autostart locations that cause programs, scripts, DLLs, shortcuts, or helper components to load during boot, logon, shell initialisation, or application startup.
- Where to look:
RunandRunOncekeys, Startup folders, Winlogon locations, shell extensions, AppInit DLLs, services, scheduled tasks, WMI entries, and other autostart locations. - How to parse it: Use
AutorunsorAutorunscfor broad collection, then confirm interesting entries against the underlying registry, file system, user profile, and endpoint telemetry. - Useful fields: Autorun location, value name, command line, user context, target path, signer, hash, file timestamps, and whether the entry appears across other endpoints.
- Be careful with: Autorun is an umbrella term, not one artefact. Per-user autoruns don’t prove user authorship, and “looks legitimate” is only a lead.
Autorun mechanisms cover places where Windows starts programs during logon, startup, or shell initialisation. Common examples include registry Run keys and Startup folders, though there are many others.
Autoruns sit close to the user experience, which makes them easy to overinterpret. “It’s in HKCU, so the user did it.” “It runs at logon, so the user must have seen it.” Those conclusions are often wrong.
Autoruns are still configuration state. They still represent a mechanism set up to run again. They just exist in a part of the system that installers, user profile migrations, application frameworks, policy systems, and remote tooling all touch.
How to Work with Autorun Evidence
Autorun evidence is usually a registry value, shortcut, or file that points to an executable, script interpreter, or command line. The path is often the most important clue.
A binary in Program Files with a known publisher signature is different from an unsigned binary in a user profile. A script launched from a temp directory is different from one launched from a management platform directory. A value created during software installation means something different from a value created during the incident window.
Avoid anthropomorphising HKCU. Per-user doesn’t mean user-authored. It usually means the entry exists in the context of a user profile. It might have been created by an installer, per-user application component, login script, enterprise tool, remote action, or malware running under that user’s context.
Autoruns can strongly support that something was configured to run at logon, and sometimes for which user. They don’t prove the user knew, who created the entry, whether the payload executed, or whether it achieved impact.
The misconception to avoid is treating an autorun as a confession. It shows configuration, not attribution.
WMI Event Subscriptions: Persistence Without Obvious Configuration Files
At a Glance
- What it is: A WMI event subscription links an event filter to a consumer through a binding. In persistence cases, that usually means “when this condition occurs, run this action.”
- Where to look: WMI repository data, commonly in the
root\subscriptionnamespace, with attention to__EventFilter, event consumers, and__FilterToConsumerBinding. - How to parse it: Query WMI/CIM for filters, consumers, and bindings, then reconstruct which trigger maps to which action. Offline WMI repository parsing can help when live access isn’t appropriate.
- Useful fields: Filter name, WQL query, event namespace, consumer type, command line or script content, binding relationship, and creator or security context where available.
- Be careful with: A filter without a consumer, or a consumer without a binding, might not represent working persistence. The subscription proves configuration; execution still needs separate evidence.
WMI event subscriptions are useful because they often don’t appear where analysts expect persistence to live. They might not leave a simple registry value or Startup folder item that turns up in basic triage. They’re also used legitimately in some environments, though less commonly than services and scheduled tasks.
If you find a WMI filter and consumer, you have evidence of configuration state. It might strongly suggest intentional persistence. It still doesn’t tell you whether it ran, what it did, or who created it without additional evidence.
So absence from common autorun locations doesn’t prove absence of persistence. It might just sit outside the places you checked.
Image File Execution Options: Persistence That Hijacks Expected Execution
At a Glance
- What it is: Image File Execution Options (IFEO) are per-image settings that Windows can apply when a named executable starts. The persistence-relevant case is usually a
Debuggervalue that redirects execution through another command. - Where to look:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<image.exe>, GFlags output, and related process creation evidence. - How to parse it: Review the IFEO registry keys for target image names and values such as
DebuggerorGlobalFlag. Then test whether the target executable was actually launched during the period of interest. - Useful fields: Target image name,
Debuggercommand,GlobalFlag, key last-write time, related silent process exit settings, and any process creation evidence for the target or redirected payload. - Be careful with: IFEO persistence may sit dormant until the target executable is launched. If the payload runs, that doesn’t automatically mean the user intentionally launched the payload.
IFEO “Debugger” hijacking is useful because it shows a different kind of persistence. A service or scheduled task can run autonomously. IFEO persistence often waits for a target executable to be launched, then redirects execution through the configured debugger value.
That can confuse the timeline. The persistence mechanism exists, but it might not produce execution until the target program is invoked. If you later observe the payload running, it’s easy to attribute that to direct user action. The real story could be that the user launched a benign program and Windows redirected execution because of the IFEO configuration.
Treat the IFEO entry as configuration state. Treat the payload execution as a separate claim. Treat user action as a separate claim again.
Corroboration: Turning Persistence Into a Defensible Finding
If persistence artefacts are configuration state, the investigative path is straightforward:
- Is the configuration legitimate for this environment?
- Did it execute during the period of interest?
- Does that execution explain any observed behaviour or outcome?
Earlier artefact classes still matter here. Prefetch may support execution context. Shimcache and Amcache may support file presence or program reference. They’re not enough by themselves, but they can help you test whether the persistent payload existed on disk, whether it appears to have executed, and whether it fits the timeline.
Corroboration keeps your reports honest: each claim stays attached to the artefact, log, or telemetry that supports it.
If you have a suspicious scheduled task that runs powershell.exe with a script in a user profile, and process creation logs show PowerShell executing that command line shortly after logon, you can support that the task executed as configured.
If you have the same task but no logs, no Prefetch, and no other telemetry, you can still support that the task was configured. You might suspect it executed. You shouldn’t present that suspicion as fact.
If you have the task, execution evidence, and downstream behaviour that aligns with the payload, then you can begin to support claims about what it caused. Even then, make the chain explicit.
A useful reporting pattern is:
The system was configured to execute X at logon.
Evidence suggests X executed at time Y, based on Z.
Observed behaviour B aligns with X’s known function and is temporally consistent.
That isn’t padding; it shows your dependency chain.
Baselining: The Difference Between “Suspicious” and “Unexpected”
Persistence is heavily used by legitimate software. That should shape how you approach the whole artefact class.
In an enterprise, “we found a scheduled task” isn’t interesting. “We found a scheduled task that’s inconsistent with our baseline” is interesting. “We found a scheduled task created during the incident window that points to an unsigned binary in a user profile” is more interesting again.
Baselining isn’t a luxury in persistence investigations. It’s often the fastest way to avoid bad conclusions. Without a baseline, you can mistake normal enterprise tooling for attacker persistence, or miss attacker persistence because it resembles normal tooling.
Baselining can be simple. Is the service present on other endpoints? Is the task present on machines with the same agent installed? Does the autorun appear on freshly imaged devices? Does it line up with a known software rollout? These comparisons can reduce uncertainty faster than deeper analysis of one endpoint in isolation.
When baselining isn’t possible, say so. It changes how strongly you can state the conclusion and should change the language you use.
Common Mistakes in Persistence Work
In practice, persistence mistakes are usually interpretive, not technical.
One is treating persistence as proof of compromise. A persistence mechanism can be benign, administrative, investigative, or malicious. The artefact tells you that a configuration exists, not why it exists.
Another is treating persistence as proof of execution. A task definition isn’t a process start. A service entry isn’t a service start event. A Run key isn’t proof the user logged in during the relevant window. If the narrative requires runtime, you need runtime evidence.
A third is treating persistence as proof of user awareness. Users rarely understand what installers do. They click through prompts, accept defaults, and don’t inspect scheduled tasks or service descriptions. Even administrators might not know exactly what layered enterprise tooling deploys.
A fourth is treating persistence as a measure of sophistication. Simple persistence often works. Sophisticated attackers often still use simple mechanisms because they blend in. Sophistication comes from campaign context and operational trade-offs, not from the mechanism alone.
A fifth is treating “looks legitimate” as “is legitimate”. Attackers can mimic OS components and vendor tooling. Legitimate software can also look strange because it uses PowerShell, runs from uncommon paths, or stores components in per-user directories. Appearance matters, but it still needs corroboration.
Environmental Factors That Change Interpretation
Persistence analysis that ignores environment will overcall or undercall.
Software updaters are a constant source of noise. They create services and tasks, run at logon, execute scripts, download content, and sometimes run with elevated privileges. Without that context, normal updater behaviour can look hostile.
EDR and endpoint tooling are another source. Security agents commonly install privileged services, scheduled tasks, autoruns, and self-protection mechanisms. Response tooling introduced during an incident can also create temporary persistence for monitoring or remediation. If you don’t account for responder activity, you can end up attributing your own changes to an attacker.
Golden images and endpoint provisioning can confuse timelines. Persistence mechanisms might be baked into an image, appear across a fleet, or have timestamps that don’t align neatly with a device’s active lifecycle.
Configuration management and software deployment platforms can introduce services and tasks across hundreds of systems quickly. They can also revert changes, reinstall agents, and create repeated add-remove patterns that look like persistence churn unless you correlate them with deployment activity.
Windows itself also changes. Windows 10 and 11 feature updates, servicing stack changes, and policy settings can affect what artefacts exist and how reliable specific fields are. If your conclusion depends on one timestamp field in one artefact, validate what that field means in that environment.
Keep the Confidence Tied to the Evidence
Persistence artefacts are a place where analysts can often be more confident, but not careless.
A new service with an unexpected binary path introduced during an incident window is strong evidence of a configuration change designed for longevity. A scheduled task that runs a suspicious command at logon under a privileged context is strong evidence of persistence. A Run key pointing to an unsigned executable in a user profile can be strong evidence of a mechanism set up to run again.
None of those statements prove who did it. None prove the user knew. None prove it executed. None prove impact.
A good persistence narrative separates configuration, runtime, and outcome. Describe the configured mechanism, add execution evidence where you have it, and say plainly when runtime or outcome evidence is missing.
Logs give you the next test: service creation, task registration, scheduler launch, process start, or the point where your evidence runs out.
In the next post, we’ll move from “what the system was configured to do” to “what the system can be shown to have done”, and why logging evidence is powerful precisely because it’s both more direct and more fragile than it looks.
