Skip to content

Detecting RecentFileCache tampering

How attackers suppress, poison, or wipe RecentFileCache.bcf — and the event-log, scheduled-task, and filesystem signals that catch them doing it.

Published on 5 min read

If RecentFileCache.bcf is useful for triage, it is also worth suppressing for an attacker who wants to stay quiet. This post walks the four realistic ways to tamper with the artifact and the signals each one leaves.

The four suppression strategies

  1. Disable the Application Experience service. sc stop AeLookupSvc then sc config AeLookupSvc start= disabled. New executions stop being recorded.
  2. Disable the ProgramDataUpdater scheduled task. Stops the periodic rewrite. Existing entries stay; new ones may accumulate in memory but never get flushed.
  3. Delete the file. del C:\Windows\AppCompat\Programs\RecentFileCache.bcf (requires elevation). Until the next Microsoft\Windows\Application Experience\ProgramDataUpdater run, the artifact is just gone.
  4. In-memory execution. Do not suppress the artifact at all. Make sure the loader never sees you. Reflective DLL injection, process hollowing, fileless PowerShell, signed-LOLBin abuse.

Each leaves a different fingerprint.

Signal: service control events

Stopping or disabling AeLookupSvc emits Service Control Manager events in the System log.

Event IDSourceWhat it indicates
7036Service Control ManagerService state change (e.g., "stopped")
7040Service Control ManagerStart type changed (e.g., auto -> disabled)
7045Service Control ManagerNew service installed (rare for AppCompat)

Hunt for AeLookupSvc in event payload XML. On a healthy machine the service starts at boot and stays running. A 7036-stopped event during business hours, with no corresponding restart, is loud.

Get-WinEvent -LogName System -FilterXPath "*[System[(EventID=7036 or EventID=7040)]]" |
  Where-Object { $_.Message -like "*AeLookup*" } |
  Select-Object TimeCreated, Id, Message

Pull these from EVTX on an image, or live with Get-WinEvent. Either works.

Signal: scheduled-task history

Disabling \Microsoft\Windows\Application Experience\ProgramDataUpdater writes to the Microsoft-Windows-TaskScheduler/Operational log.

Event IDMeaning
141Task deleted
142Task disabled
200Action started
201Action completed

Look for 141/142 referencing the ProgramDataUpdater task path, or a long gap between 200/201 events on a machine that has been powered on continuously.

Signal: filesystem state

The file itself betrays tampering when you read it carefully.

  • LastWriteTime far in the past on a daily-used machine. Healthy systems see a write roughly every 24 hours. A BCF last written six weeks ago on a workstation logged into daily is a flag.
  • File missing entirely on a Win7 endpoint where logs show ProgramDataUpdater previously ran. The USN journal and $LogFile record the deletion. Check USN_REASON_FILE_DELETE records for the BCF.
  • LastWriteTime exactly matching an attacker-time event (incident-window correlation). Possible deliberate flush.
  • Zero or near-zero entries on a machine that has been running months of normal activity. Either the file was just cleared, or AeLookupSvc has been off since the last reset.

Pull MFT metadata with the MFT parser and compare $STANDARD_INFORMATION against $FILE_NAME for the BCF itself. Timestomping the BCF is rare in practice, but I have seen it once. Worth checking.

Signal: USN journal and $LogFile

If the BCF was deleted or renamed, NTFS records it. Parse with the USN journal parser or usn-parser:

TIMESTAMP                 REASON                              FILE
2026-05-23T14:09:11Z      FILE_DELETE | CLOSE                 RecentFileCache.bcf
2026-05-23T14:09:12Z      FILE_CREATE | CLOSE                 RecentFileCache.bcf

A close-paired delete and create on the same file is the classic clear-by-replacement move. The created file will often be empty or hold only the 20-byte header.

$LogFile (parsed with LogFileParser or MFTECmd's $LogFile mode) captures finer-grained MFT operations and survives shorter windows than the USN journal in some cases.

Signal: cross-artifact contradictions

The richest signal is cross-artifact disagreement.

  • Prefetch shows execution; the BCF shows no corresponding path. Either the binary bypassed the loader (in-memory), or the BCF was tampered with. Check the BCF's LastWriteTime against the Prefetch's last-run time. The Prefetch parser gives you the timestamps.
  • Amcache has entries for executables not in the BCF. Normal if ProgramDataUpdater already promoted them. Suspicious if the timestamps suggest promotion should not have happened yet. The Amcache parser gives you registry-key LastWritten to compare.
  • BCF is exactly the 20-byte header with no records. A clean state that occurs naturally right after ProgramDataUpdater rewrote and nothing has run since. Verify against the file's mtime and the System log before calling it suspicious.

What in-memory execution looks like (when there is nothing to see)

The point of in-memory tradecraft is that no file ever touches disk in a way the loader's AppCompat hook can catch. RecentFileCache will look completely normal. Do not read its silence as exoneration. Pair with:

  • EDR telemetry on CreateRemoteThread, WriteProcessMemory, unbacked executable regions.
  • PowerShell 4104 script-block logging (if enabled).
  • Sysmon event ID 1 (process create) with command line, parent, and image-loaded fields. The EVTX parser reads these.
  • RAM acquisition with Volatility's malfind, dlllist, ldrmodules.
  • Pagefile carving when memory is no longer available.

A short hunt query

Daily-cadence detection rule, pseudo-XQL:

service = "AeLookupSvc" AND
event_id IN (7036, 7040) AND
NOT (event_id = 7036 AND state = "running")
| stats count by host, event_id, _time
| where count > 0

Combined with a scheduled check that RecentFileCache.bcf exists and has been modified within the last 48 hours on every Win7 endpoint, you catch the loud half of suppression attempts. The quiet half (in-memory execution) needs the other half of your stack: EDR, memory, command-line logging.

End of the series

That closes the primer. You now have the format, the lifecycle, the comparison context, the acquisition workflow, the triage heuristics, the validation chain, and the tampering signals. The parser on this site is the front end for the BCF itself. Everything else lives in your usual DFIR toolchain.

Further reading

Related articles

The BCF's lifecycle is one scheduled task. Understand when ProgramDataUpdater runs, when it clears the file, and how to spot a disabled appraiser.
The BCF format is a header, a list of length-prefixed UTF-16LE paths, and that's it. Here is what every byte means and why nothing has changed since 2009.
A SOC-flagged Win7 endpoint, 14 paths in the BCF, three worth a second look. A realistic corroboration chain and how much the BCF actually contributed.