How to Analyze Scheduled Tasks in Your Browser (Worked Case)
Step by step: load task XML, .job files and the SOFTWARE hive into a free in-browser parser, read hidden and registry-only tasks, fix time zones and export. With a fictional case.
TL;DR. Collect System32\Tasks, SysWOW64\Tasks, C:\Windows\Tasks and the SOFTWARE hive (with its .LOG1/.LOG2), drop them or the triage ZIP into the Scheduled Tasks Parser, read the warnings, confirm the inferred UTC offset, then work down the findings: registry-only tasks, missing SD, hash mismatches first, then paths, encoded commands, LOLBins and privileges. Export CSV, JSON or a timeline CSV. Nothing leaves the browser. The fictional case below, which is the built-in sample, shows each step.
This is the hands-on companion to the scheduled task forensics guide. Acquisition is covered in scheduled task locations and acquisition.
What the tool does, and does not do
The parser is Rust compiled to WebAssembly, running in a Web Worker in your browser. Files are read locally and never uploaded. It:
- reads task XML (UTF-16LE, schema 1.2 and later): RegistrationInfo, every trigger type, principals, settings and
Exec/ComHandler/SendEmail/ShowMessageactions; - reads legacy
.jobfiles: fixed and variable sections, triggers, last run time as stored (local SYSTEMTIME); - reads TaskCache from the SOFTWARE hive:
Tree(Id,Index,SDdecoded to SDDL, key last-write times) andTasks\{GUID}(Path,URI,Author,Date,Hash,Schema,DynamicInfo, decodedActions,Triggersheader and principal); - correlates XML with TaskCache by path, recomputes the hash, and flags registry-only, disk-only and SD-less tasks;
- warns when the hive is dirty. It does not replay transaction logs, carve deleted keys or parse event logs.
Findings are heuristics to order your review, not verdicts.
The fictional case
Everything below is invented for training: the host, account, addresses and files do not exist, and the commands are harmless placeholders. It is exactly the sample you get with Try a sample on the home page, so you can follow along.
A finance workstation, FIN-WKS-07, set to a UTC+02:00 time zone. On 2026-09-14, an alert fires on unusual outbound traffic. Other artifacts show an account nobody recognises, svc_backup. The question for this step: did the intruder leave scheduled tasks behind, and what did they do between 10:00 and 10:55 UTC?
Step 1: Collect the task folders and the SOFTWARE hive
The collection follows the KAPE layout, as produced by:
kape.exe --tsource C: --tdest C:\triage\kape --target ScheduledTasks,RegistryHivesSystem
The resulting ZIP holds C/Windows/System32/Tasks (19 XML files), C/Windows/Tasks (one .job file) and C/Windows/System32/config/SOFTWARE. A Velociraptor Windows.Triage.Targets collection with the same targets works the same way. Hash the archive before you start.
Step 2: Load the files in the browser
Open the tool and drop the ZIP. You can also drop loose files or a folder. The parser walks the archive (KAPE layout, or Velociraptor layout with percent-encoded paths and backslash entry names), picks the task files and the SOFTWARE hive, and ignores the rest. It takes a second or two.
The summary bar for the sample reads: 21 tasks (19 XML, 1 .job, 16 TaskCache entries merged with them), 8 flagged, 1 hidden (no SD), 1 registry only, 1 XML only, 5 recently registered.
Step 3: Read the warnings
Before any conclusion about a task being "missing", read the parser warnings:
| Warning | Meaning | Action |
|---|---|---|
| Dirty hive | Transaction logs hold changes not in the primary file | Replay with a log-aware tool and re-check absences |
| No SOFTWARE hive | Only XML/.job parsed; no hidden-task or registry-only checks possible | Collect the hive |
| No task folder | Only TaskCache parsed | Collect System32\Tasks |
The sample hive is clean: no dirty-hive warning, so "registry only" and "XML only" findings can be read at face value. On a live collection you will often see the dirty warning; replay the logs before trusting absences.
Step 4: Check the time zone
XML Date and StartBoundary values are usually local time without an offset (why). The parser compares each task's XML Date with the UTC DynamicInfo creation FILETIME of the same task and counts the offsets it finds. For the sample it reports UTC+02:00 ×4, UTC+01:00 ×1 and applies +02:00. Two of the comparisons:
| Task | XML Date (zoneless) | DynamicInfo created (UTC) | Difference |
|---|---|---|---|
\IntelGfxTelemetry | 2026-09-14T12:09:58.4413872 | 2026-09-14 10:09:58 | +02:00 |
\MicrosoftEdgeUpdateTaskMachineCore | 2026-03-02T09:44:22 | 2026-03-02 08:44:22 | +01:00 |
The odd one out is not an error: the Edge updater was registered in March, under winter time (CET, UTC+01:00), and the September tasks under summer time (CEST, UTC+02:00). That is why a single offset applied to a whole machine can be wrong for older tasks. For this case every task of interest was registered on 2026-09-14, so +02:00 is right. From now on every time is shown in UTC.
Step 5: Triage the findings
Sort by severity. Of the 21 tasks, 13 are ordinary Microsoft, Office, Edge and feed-sync tasks with no finding. Two more are flagged but benign, and they are worth reading first as a calibration:
\GoogleUpdateTaskMachineUA: Elevated, not Microsoft. It runs as SYSTEM and has no author. That is how Google's updater registers itself.\OneDrive Standalone Update Task-S-1-5-21-…-1104: User-writable path. Its command is under%localappdata%, which is where the per-user OneDrive updater lives.
Findings are pointers, not verdicts. The six tasks below are the ones that matter.
\Microsoft\Windows\UPnP\UPnPHostConfigSync: hidden, no SD
- Author claims
Microsoft Corporation, and the task sits under\Microsoft\Windows\to blend in. - Registered 10:12:40 UTC (XML
Date2026-09-14T12:12:40.0918251 local). - Principal: SYSTEM,
HighestAvailable. Triggers:LogonTriggerand aTimeTriggerfrom 12:12:40 local, repeating every 30 minutes (PT30M). - Action:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -WindowStyle Hidden -EncodedCommand VwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIAAnAHMAeQBuAHQAaABlAHQAaQBjACAAcwBhAG0AcABsAGUAJwA=, decoded by the parser asWrite-Output 'synthetic sample'(a stand-in for a real script). - TaskCache: the
Treekey hasIdandIndex= 2 but noSDvalue. Its last-write time is 10:13:05 UTC, 25 seconds after registration. - DynamicInfo: last run 10:42:40 UTC, last successful run 10:42:41 UTC.
Findings: Hidden: no SD in TaskCache (critical), Encoded PowerShell (high), Recently registered. This is the Tarrask pattern: on the live host, schtasks /query would not list it. The key write 25 seconds after registration is consistent with the SD being removed at 10:13, which also means the intruder had SYSTEM by then.
\IntelGfxTelemetry: hidden setting, SYSTEM, user-writable path
- Author
FIN-WKS-07\svc_backup, description "Intel graphics telemetry", root folder. - Registered 10:09:58 UTC. Principal:
S-1-5-18,HighestAvailable.<Hidden>true</Hidden>. - Triggers:
BootTriggerand aTimeTriggerfrom 12:15:00 local (10:15 UTC), every hour. - Action:
C:\ProgramData\Intel\m64.exe -svc. - DynamicInfo: last run 10:15:00 UTC, last result
0x00041301(SCHED_S_TASK_RUNNING: the program was still running at collection time).
Findings: User-writable path, Elevated, not Microsoft, Hidden setting, Recently registered. Unlike the UPnP task, it keeps its SD: it is hidden only from the console's default view, not from schtasks.
\a8Xq2LmZ: the definition changed after registration
- Random-looking name at the root, registered 10:21:30 UTC by
svc_backup, running assvc_backup(InteractiveToken,LeastPrivilege) at that user's logon. - TaskCache
Actions:C:\Windows\System32\rundll32.exe C:\Users\svc_backup\Downloads\tools\helper.dll,Init. - XML on disk: the same command with
helper.dll,Start. The TaskCacheHashno longer matches the file. - DynamicInfo: last run 10:24:02 UTC.
Findings: XML changed after registration and Registry action differs from XML (high), LOLBin, User-writable path, Random-looking name, Recently registered. Someone edited the XML file directly instead of re-registering the task, so the service still holds the registered version. Read both: the registry says what was registered, the file says what someone wanted it to look like afterwards.
\Microsoft\Windows\Maintenance\CleanupTemp: never registered
- XML file only: no TaskCache entry at all.
- Author claims
Microsoft Corporation;Date12:31:05 local (10:31:05 UTC with the +02:00 offset);Hidden=true; principalsvc_backup;TimeTriggerat 12:35 local. - Action:
C:\Windows\System32\mshta.exe C:\Users\svc_backup\Downloads\tools\cleanup.hta.
Findings: Not registered (medium), LOLBin, User-writable path, Recently registered. A file dropped into the Tasks folder is not a task until the service registers it. Because the hive is clean, the absence from TaskCache is meaningful: this task was prepared but, on this evidence, never scheduled.
\SyncBackup: registry only
- No XML file on disk; TaskCache still has
TreeandTasks\{GUID}. - TaskCache
Date2026-09-14T12:44:10.2059113 (local); DynamicInfo created 10:44:10 UTC, last run 10:47:12 UTC, last successful run 10:53:51 UTC, result 0. - Decoded
Actions:C:\Users\Public\rclone.exe copy C:\Users\Public\data E:\exfil. Triggersheader: start boundary 2026-09-14T12:47:00 local, principal SID ending-1119(svc_backup).
Findings: XML file missing (high), User-writable path, Recently registered. The file was deleted but the task was never unregistered, so it would still appear in schtasks on the live host. The registry is the only surviving definition, and it points at a copy tool writing to E:\exfil, a drive worth identifying.
\Cleanup: a legacy .job
- File:
C:\Windows\Tasks\Cleanup.job, authorFIN-WKS-07\svc_backup. - Command:
cmd.exe /c del /q C:\Users\svc_backup\Desktop\creds.txt. - Trigger: once, 12:52 local. Last run: 2026-09-14 12:52:30 local (10:52:30 UTC), status
0x00041305(SCHED_S_TASK_NOT_SCHEDULED: no further runs planned).
Findings: Legacy .job, User-writable path. A .job file on a modern system is unusual by itself; this one deletes a file from the account's desktop at the end of the session, which looks like cleanup.
Step 6: Narrow the time range and export
Open \SyncBackup and center the time range on it with plus or minus 1 hour. The tool centers on the task's first time, its registration at 10:44:10 UTC, so the window is 09:44:10 to 11:44:10 UTC. The density strip shows the burst between 10:09 and 10:54; the thirteen baseline tasks drop away. You can also type From 2026-09-14 10:00:00 and To 10:55:00 UTC to the second.
Export the filtered view as CSV, JSON, or the Timeline CSV, whose message, datetime and timestamp_desc columns import directly into Timesketch. The reconstructed sequence, all UTC:
| Time | Source | Event |
|---|---|---|
| 10:09:58 | DynamicInfo, XML Date | \IntelGfxTelemetry registered (SYSTEM, boot + hourly) |
| 10:12:40 | DynamicInfo, XML Date | \Microsoft\Windows\UPnP\UPnPHostConfigSync registered (SYSTEM, encoded PowerShell) |
| 10:13:05 | Tree key last-write | SD removed from the UPnP task (inferred) |
| 10:15:00 | DynamicInfo last run | m64.exe -svc started, still running at collection |
| 10:21:30 | DynamicInfo, XML Date | \a8Xq2LmZ registered (rundll32 helper.dll,Init) |
| 10:24:02 | DynamicInfo last run | \a8Xq2LmZ ran; XML later edited to ,Start |
| 10:31:05 | XML Date | CleanupTemp XML written, never registered |
| 10:42:40 | DynamicInfo last run | UPnP task's latest run |
| 10:44:10 | DynamicInfo created | \SyncBackup registered |
| 10:47:12 to 10:53:51 | DynamicInfo | rclone copy to E:\exfil: last run and last successful run |
| after 10:44 | Missing XML | \SyncBackup XML file deleted |
| 10:52:30 | .job last run | Cleanup.job deletes a file from the desktop |
Next steps outside this tool: confirm execution of m64.exe, rclone.exe and rundll32.exe with Prefetch and Amcache, look for 4698/106 and 200/201 in the event logs, identify the E: volume, recover helper.dll and cleanup.hta, and find how svc_backup got SYSTEM. The indicator checklist behind these findings is in how to detect malicious scheduled tasks.