Registry Parser
All articles

UserAssist and the ROT13 program-launch history

7 min read

UserAssist is the artifact that, on a good day, tells you exactly which executable a user launched, how many times, when last, and how long the focus lasted. On a bad day, it tells you nothing because the launch was from a service or a scheduled task. Knowing which day you are having is the difference between citing UserAssist as proof and citing it as a hint.

The key, the GUIDs, the encoding

The data lives under HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\ in the user's NTUSER.DAT. Under that path you get a handful of GUID subkeys, each tracking a different category of activity. On modern Windows the two that matter are:

  • {CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}: executables launched.
  • {F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}: shortcut (LNK) files invoked.

Each of these GUIDs has a Count subkey whose values record the launches. The value name is the full path to the launched item, encoded in ROT13. Yes, ROT13. In 2026. The encoding is not a security measure; it is a remnant of an old policy intended to make casual reads less noisy. Anyone who has ever looked at the raw data has cursed at it once and then written the four-line decoder.

HRZR_PGYFRFFNVA decodes to UEME_CTLSESSION. P:\Cebtenz Svyrf\Vagrearg Rkcybere\VRKCYBER.RKR decodes to C:\Program Files\Internet Explorer\IEXPLORE.EXE. The decoder is literally a ±13 shift on alphabetics, leaving digits and punctuation alone.

Two non-launch value names also live in the Count keys and routinely confuse analysts:

  • UEME_CTLSESSION: a counter incremented at session start.
  • UEME_CTLCUACount:ctor: a generic counter related to control activation. Ignore for the most part.

Everything else with a path is a real launch record.

What is in the value data

The value data is a fixed-size binary structure. On Windows 7 and later, it is 72 bytes:

  • Offset 0x00 (4 bytes): session ID where the entry was last touched.
  • Offset 0x04 (4 bytes): run count.
  • Offset 0x08 (4 bytes): focus count (number of times the window gained focus).
  • Offset 0x0C (4 bytes): total focus time in milliseconds, summed across runs.
  • Offset 0x10 to 0x3B: timer-related fields, mostly zeroed in practice.
  • Offset 0x3C (8 bytes): FILETIME of the last run.
  • Offset 0x44 (4 bytes): trailing field, version dependent.

On Windows XP the structure was 16 bytes with a different layout. If you are still hitting XP hives in 2026, you have a different set of problems, but the encoding logic is the same.

A decoded record might look like:

Path:        C:\Users\jdoe\AppData\Local\Programs\PuTTY\putty.exe
Run count:   12
Focus count: 27
Focus time:  3621542 ms (about 60 minutes)
Last run:    2026-05-21 14:22:11 UTC

That single record tells you: this user has run PuTTY twelve times via Explorer, with about an hour of cumulative window focus, with the most recent invocation at the given timestamp.

Why this is one of the best per-user execution artifacts

UserAssist lives in the user's NTUSER.DAT. There is no ambiguity about attribution. When you find a record there, the answer to "who ran this" is the user whose profile owns the hive.

Compare that to Shimcache, which says "this binary was on the system" but cannot tell you who launched it. Compare to AmCache, which records execution but is system-wide. Compare to Prefetch, which tracks execution but does not attribute to a user. UserAssist is the only common artifact that nails attribution from a single source.

It also tracks run counts and focus time. A binary that has been run 47 times with two hours of focus is not the kind of thing an attacker pops in for a single session. A binary that has been run once with three seconds of focus might be exactly that.

What is not tracked

This is the part that catches investigators who lean on UserAssist as if it were a complete execution log.

UserAssist records launches via:

  • Explorer (double-click, single-click on a pinned shortcut).
  • The Start menu.
  • The Run dialog (Win+R).
  • The taskbar.
  • A LNK file invocation.

UserAssist does not record launches via:

  • The command line (cmd.exe, powershell.exe).
  • Scheduled tasks.
  • Services.
  • WMI process creation.
  • Any non-GUI invocation by another process.
  • Remote execution (PsExec, WinRM, RDP-via-rundll).

This is by design. The shell registers GUI-driven launches. Programmatic launches do not go through the shell. Attackers who know this rule treat UserAssist as a window into the user's manual behavior, not into the malware's behavior. The same logic applies to you.

A malware payload that the user double-clicked from a phishing link will appear in UserAssist. The same payload run by a scheduled task the malware created will not.

The lid-flapping LastRun

The LastRun FILETIME is updated on every launch. Useful and volatile. If you pull a hive at 14:00 and the user opens the binary at 14:05, the LastRun has moved and the previous one is gone from the live hive. Acquire the hive once, at a known time. Treat that as your reference state.

VSS snapshots are the safety net. If VSS is enabled, pull NTUSER.DAT from each snapshot and reconstruct LastRun changes over time. That is the difference between "last run Tuesday" and "last run Tuesday but used daily for two weeks before that".

Focus count versus run count

Run count increments on launch. Focus count increments when the window gains focus, which can happen multiple times per launch (clicks away, clicks back). Focus time accumulates while the window is active.

A binary with high run count and low focus time was launched and dismissed: a scheduled-task wrapper that opens a console window for a half-second fits this pattern. Moderate run count with very high focus time is something the user actively uses: a spreadsheet, an editor, a chat app.

Looking for malicious launches, the interesting pattern is "high run count, low focus time" combined with a path in %APPDATA% or %LOCALAPPDATA%\Temp. That is a binary launched repeatedly with each invocation terminating quickly.

The decoding example, end to end

A raw UserAssist value name might look like:

P:\Hfref\wqbr\NccQngn\Ybpny\Grzc\flgrk.rkr

ROT13-decode that: C:\Users\jdoe\AppData\Local\Temp\sytex.exe

A binary in \AppData\Local\Temp\ is suspicious by default. A binary there that has UserAssist entries is one the user double-clicked, which means it arrived via download, email attachment, or USB drop, and was opened deliberately.

Look at the value data. Suppose run count is 1, focus count is 1, focus time is 412 ms. The binary ran once, briefly. That fits a dropper. Pivot to the LNK file in Recent\, the Prefetch entry for sytex.exe, the AmCache record for the file. Pull the file itself from the MFT.

That sequence reconstructs the moment a user got infected, with timestamps to the second and full attribution.

Tools

  • RegRipper's userassist plugin. Handles the ROT13, the FILETIME, the run count, the focus time. Output is one line per entry. Read the plugin source if you want to see exactly what is being extracted.
  • Eric Zimmerman's RECmd with the UserAssist batch file. Same outputs in CSV.
  • The parser on this site decodes UserAssist as part of its standard tree view.

Most ad-hoc decoders skip the focus count and focus time. Make sure your tool surfaces all four numbers, not just the path and the LastRun.

Edge cases worth knowing

Roaming profiles can drag UserAssist across hosts. A user who logs into multiple hosts with a roaming profile will have a single set of UserAssist entries reflecting activity on whichever host last wrote to the profile. Cross-correlate against Prefetch (which is per-host) to figure out where each launch actually happened.

Clearing UserAssist is possible but leaves traces. Some "privacy" tools and CCleaner-class utilities will wipe UserAssist values. The clearing itself leaves the GUID subkey present with a recent LastWrite and an empty Count. That pattern alone is a tell. Recovered unallocated cells in the hive may still contain the cleared values; RegRipper's del plugin and yarp's recovery modes will find them.

The Software Distribution and Backup pseudo-paths. Some entries reference shell folder GUIDs rather than real paths. {1AC14E77-02E7-4E5D-B744-2EB1AE5198B7} is System32. {F38BF404-1D43-42F2-9305-67DE0B28FC23} is Windows. A decoder that does not resolve these will show GUIDs in the path, which is jarring but technically correct.

Further reading

UserAssist will not solve your case alone. Combined with Prefetch, AmCache, and the LNK files in Recent\, it answers "who ran what, when, and for how long" with a confidence almost nothing else on the system matches. Just remember what it does not see, and pivot to other artifacts to cover the gap.