Skip to content
BAM Records Process Termination Time, Not Just Execution

BAM Records Process Termination Time, Not Just Execution

August 24, 2026Digital Forensics
Author
Chicken0248
Published
August 24, 2026
Category
Digital Forensics
Artifact
BAM / DAM
Introduction

"When did this process stop running?" If you've done DFIR on Windows, you've asked this. The usual answers are Security Event ID 4689 and Sysmon Event ID 5. But most systems don't have the right audit policy enabled, and Sysmon is rarely pre-deployed on compromised machines.

This post shows that the Background Activity Moderator (BAM), a Windows kernel driver present since Windows 10 version 1709, records process termination timestamps. Not "last execution time" as most DFIR guides call it. BAM overwrites its FILETIME on process exit, so for any process that has terminated, the stored value is when it ended.

Key finding: BAM's kernel callback fires on both process creation and termination, overwriting the same FILETIME each time. The final stored value for a terminated process is the termination timestamp.

Credit: The kernel-level analysis in this post builds heavily on Maxim Suhanov's "BAM internals" research (2020), which first documented the internal workings of bam.sys through reverse engineering. Additional kernel extension mechanism insights from Yarden Shafir's research, and practical testing methodology from Psmths' windows-forensic-artifacts.

The Problem

Say a malicious installer ran on a compromised box. You need to know when it stopped running. What can you check?

ArtifactRecords Termination?Availability
Security Event ID 4689Yes, exact timeRequires "Audit Process Termination" policy enabled, rarely configured by default
Sysmon Event ID 5Yes, exact timeRequires Sysmon deployment, not present on most systems
PrefetchNoRecords last run time (process start), not termination
SRUM AppTimelineEndTime field, but rounded to the minuteAvailable on Windows 10+, but imprecise
AmcacheNoRecords execution metadata, not lifecycle events
BAMYes, exact timeAvailable by default on Windows 10 1709+

On most systems, 4689 is off and Sysmon was never installed. SRUM has an EndTime field, but it's rounded to the nearest minute. BAM fills the gap: enabled by default, and stores timestamps with FILETIME precision (100-nanosecond intervals).

What Most Guides Say (and Why It's Misleading)

Search for "BAM forensics" and every guide says the same thing: BAM records the "last execution time" of a program. Here's how popular resources describe it:

  • "Provides full path of the executed program and last execution date/time"
  • "Last execution time of the program"
  • "64-bit Windows FILETIME timestamp showing when the program was last executed"

The phrase "last execution time" makes people think the timestamp is written when the program starts. If that were true, BAM timestamps should match Prefetch run times. They don't. The BAM timestamp consistently shows up after the Prefetch-recorded start time.

Because of the "last execution time" label, most analysts treat BAM as redundant with Prefetch. It's not. Prefetch = start time, BAM = termination time. They cover different ends of the process lifecycle.

Kernel-Level Analysis: Why BAM Records Termination

The foundational reverse engineering of bam.sys was done by Maxim Suhanov in his 2020 blog post "BAM internals". His analysis revealed that unlike standard process monitoring drivers, BAM does not use PsSetCreateProcessNotifyRoutine(). Instead, it uses the undocumented ExRegisterExtension() API to register its callback via an extension table.

The Callback Registration

The bam.sys driver stores its callback function BampCreateProcessCallback in a function table called BampKernelCalloutTable. This table is passed to ExRegisterExtension() with the magic constant 0x0E0005, which is also found in the kernel's PspInitializeBackgroundActivityModeratorCallouts function.

bam.sys DriverEntry └─► ExRegisterExtension() ├─ Magic: 0x0E0005 ├─ Table: BampKernelCalloutTable └─ Callback: BampCreateProcessCallback ├─ Fires on process CREATION → writes FILETIME └─ Fires on process TERMINATION → overwrites FILETIME

The Dual-Fire Behavior

The key part: BampCreateProcessCallback handles both process creation and termination. The name says "Create" but the kernel invokes it on exit too.

Every time it fires, the callback writes a FILETIME to the registry value for that executable under:

HKLM\SYSTEM\CurrentControlSet\Services\bam\State\UserSettings\<SID>\ └─ \Device\HarddiskVolume3\path\to\executable.exe └─ Value: [FILETIME (8 bytes)] [flags (16 bytes)]

Since it overwrites the same value each time, once a process exits, what's left in the registry is the termination timestamp.

Why the Name Is Misleading

The function is named BampCreateProcessCallback because it's registered through the process-creation callback infrastructure. But the kernel's extension mechanism routes both creation and termination events through the same function. The name is about how it's registered, not when it fires.

Testing & Proof

You can verify this yourself by watching the BAM registry value before and after running a process.

Test Setup

ComponentDetails
OSWindows 10 Pro, Version 10.0.19041 Build 19041
ToolRegistry Explorer / RECmd (Eric Zimmerman)
Test binaryAny GUI application (e.g., notepad.exe, calc.exe)
ObservationMonitor BAM registry key in real-time or via before/after hive snapshots

Test Procedure

Step 1: Open Registry Explorer and navigate to:
HKLM\SYSTEM\CurrentControlSet\Services\bam\State\UserSettings\<Your SID>

Step 2: Launch a test application (e.g., notepad.exe). Note the time you launched it.

Step 3: Refresh the registry view. A new BAM entry appears for notepad.exe. The FILETIME timestamp corresponds to the process creation time.

Step 4: Wait a noticeable amount of time (e.g., 2-3 minutes), then close the application.

Step 5: Refresh the registry view. The first 8 bytes of the BAM value have changed. The new FILETIME corresponds to the process termination time, not the original creation time.

If BAM only recorded "execution time" (start), the value wouldn't change after you close the application. It does change. That's the proof.

What You Should See

After Step 3, decode the first 8 bytes of the BAM value as a FILETIME. It should match (within a second) the time you launched the app. After Step 5, decode again. The FILETIME now matches when you closed the app, not when you opened it. The difference between the two timestamps equals roughly how long you kept the application running.

Try it yourself. The result is unambiguous.

Real-World Application: When SRUM Fails, BAM Delivers

During an investigation, we needed to find when a suspicious executable stopped running on a compromised Windows box. Here's what each artifact gave us:

Artifact Comparison

ArtifactResultUseful?
Prefetch (PECmd)Two run times foundStart times only, not termination
Security Event ID 4689Not availableAudit policy not configured
Sysmon Event ID 5Not availableSysmon not deployed
SRUM AppTimeline EndTimeRounded to nearest minuteImprecise - could not pinpoint exact second
AmcacheNo termination dataExecution metadata only
ActivitiesCacheEndTime = 0Not recorded
BAMExact timestamp with FILETIME precisionConfirmed as process termination time

The Investigation Flow

Started with Prefetch (PECmd). Got execution times, but those are start times only.

Checked Security Event Log for 4689 (Process Termination). Zero hits. Audit policy was never turned on.

Parsed SRUM with SrumECmd. The AppTimeline EndTime values looked promising, but digging into the raw ESE database showed they're rounded to the nearest minute by design. Not good enough when you need the exact second.

Finally, pulled BAM from the SYSTEM hive with RECmd. The FILETIME for the executable came in after both Prefetch start times and all child process activity visible in UsnJrnl. That's termination, not start.

RECmd.exe -f "path\to\SYSTEM" --kn "ControlSet001\Services\bam\State\UserSettings" --nl
Value Name: \Device\HarddiskVolume3\path\to\suspicious.exe Value Data: DA-85-D7-74-DB-E2-DB-01-00-00-00-00-00-00-00-00-00-00-00-00-02-00-00-00 First 8 bytes (FILETIME): DA-85-D7-74-DB-E2-DB-01 Decoded: 2025-06-21 18:36:52 UTC ← Process termination time

Cross-Validation with UsnJrnl

UsnJrnl ($J) gave some supporting context, but with a caveat: UsnJrnl doesn't record which process caused a file change. It only logs the file operation itself (create, modify, delete) and the timestamp. So you can't directly say "this entry was from our target process."

What you can do is look at the pattern. In this case, the last file activity we could tie to the malware's execution chain stopped about 30 seconds before the BAM timestamp. No more related activity after that. It's circumstantial, not definitive, but it's consistent with BAM recording termination rather than start.

Practical Guide for DFIR

When to Use BAM for Termination Time

Check BAM for process termination time when:

  • Security Event ID 4689 is unavailable (audit policy not configured)
  • Sysmon Event ID 5 is unavailable (Sysmon not deployed)
  • SRUM EndTime precision (rounded to minute) is insufficient
  • The system is Windows 10 version 1709 or later

Where to Find BAM Data

Live system: HKLM\SYSTEM\CurrentControlSet\Services\bam\State\UserSettings\<SID> Offline analysis: SYSTEM hive → ControlSet001\Services\bam\State\UserSettings\<SID> DAM (Desktop Activity Moderator) - same structure: SYSTEM hive → ControlSet001\Services\dam\State\UserSettings\<SID>

How to Parse BAM Timestamps

Each BAM entry is a registry value where the value name is the full device path of the executable and the value data is a 24-byte binary structure:

Offset Size Field 0x00 8 FILETIME - process termination timestamp (for exited processes) 0x08 8 Reserved (typically zeros) 0x10 4 Reserved 0x14 4 Flags (typically 0x00000002)

Tools for parsing:

ToolCommand
RECmdRECmd.exe -f SYSTEM --kn "ControlSet001\Services\bam\State\UserSettings" --nl
Registry ExplorerNavigate to the key, decode FILETIME from the first 8 bytes of each value
Pythondatetime.datetime(1601,1,1) + datetime.timedelta(microseconds=filetime//10)
dcode.frdcode.fr/timestamp-converter - paste the decimal FILETIME value, select Windows FILETIME format

How BAM Complements Other Artifacts

QuestionBest Artifact
When did the process start?Prefetch (last run times from PECmd)
When did the process terminate?BAM (FILETIME from registry)
How long did the process run?BAM timestamp − Prefetch last run time
What files did the process access?Prefetch (file references list)
How much resource did the process use?SRUM (AppResourceUseInfo)
Limitations & Caveats

BAM has real limitations. Know them before you rely on it.

LimitationImpact
7-day expiryBAM entries older than 7 days are deleted on system reboot. If the system rebooted after the incident, entries may be gone.
Console applicationsPrograms launched via command-line interface may not generate BAM entries.
Still-running processesIf the process was still running when the image was captured, the BAM timestamp reflects process creation time (the termination overwrite hasn't occurred yet).
Deleted executablesIf the executable is deleted from disk, the BAM entry is removed on the next reboot.
Removable media / network sharesExecutables on removable media or network shares may not generate BAM entries.
Windows versionBAM was introduced in Windows 10 version 1709 (Fall Creators Update). Not available on older systems.
Conclusion

Every DFIR guide calls BAM's timestamp "last execution time." That label is wrong, or at best, incomplete. Here's what we know from the kernel analysis and testing:

  • BampCreateProcessCallback fires on both process creation and termination
  • The FILETIME is overwritten on each event, so the final value reflects the last event
  • For terminated processes, this means the stored timestamp is the termination time
  • BAM is available by default on Windows 10 1709+, unlike Event ID 4689 or Sysmon

If your system doesn't have 4689 or Sysmon (most don't), BAM is probably your best bet for process termination time. Stop treating it as a duplicate of Prefetch. Prefetch tells you when it started. BAM tells you when it ended.

References