Skip to content

Eric Zimmerman Tools Hands on Labs

Choose one browser simulation for focused practice, then complete the Full Lab to collect real Windows traces and explain a finding in a disposable training VM.

PECmd Interactive Lab

JumpList Explorer Interactive Lab

Full Lab

Windows VM / real Windows artefacts

Eric Zimmerman Tools Full Lab

Three guided activities

Case SUSA-EZ-060 / follow the file

Open a file. Find its traces. Explain what they show.

The support team asks whether Windows records link Notepad to a handover file. Create harmless activity in a training VM, then investigate it with PECmd and JumpList Explorer. Leave a small case folder another analyst can check.

Tick each checkpoint after saving its evidence. Progress is self reported; this page cannot check your VM.

Investigation brief

Case questionWhat do the collected records show about Notepad and handover.txt?
Your task

Find an execution related trace with PECmd and a file reference with JumpList Explorer. Compare their paths and times. Record missing results too.

Real case note: This is a small live triage exercise. In an incident, collect existing artefacts under your evidence plan, usually from an acquired image or approved collection. Create training activity only in this disposable VM. Live files can change while being copied; this lab's hashes check the collected copies, not a complete disk acquisition.

Activity 1: Create and collect

Your first winKeep real Windows artefacts from one known action.
1. Set up the case and open the training file

Edit only the two tool paths before running this block. It stops if either lab folder already exists. For another attempt, choose a fresh case number and folders; keep previous evidence.

$ezPECmd = 'C:\Tools\EZ\PECmd.exe'                  # Replace with your installed path
$ezJumpList = 'C:\Tools\EZ\JumpListExplorer.exe'     # Replace with your installed path
$ezCase = 'C:\Cases\SUSA-EZ-060'
$ezTraining = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'SUSA-EZ-060'
$ezTarget = Join-Path $ezTraining 'handover.txt'
foreach ($ezTool in @($ezPECmd, $ezJumpList)) {
    if (-not (Test-Path -LiteralPath $ezTool -PathType Leaf)) { throw "Check the tool path: $ezTool" }
}
if ((Test-Path -LiteralPath $ezCase) -or (Test-Path -LiteralPath $ezTraining)) { throw 'Lab folder exists. Use a fresh case and training folder.' }
New-Item -ItemType Directory -Path $ezCase, $ezTraining, "$ezCase\sources", "$ezCase\working", "$ezCase\parsed", "$ezCase\notes", "$ezCase\exports" -ErrorAction Stop | Out-Null
'Training handover: release window 17:00 UTC.' | Set-Content -LiteralPath $ezTarget -Encoding UTF8
@(
    'Case SUSA-EZ-060; disposable VM; question: what links Notepad to handover.txt?'
    "Computer: $env:COMPUTERNAME; account: $env:USERDOMAIN\$env:USERNAME"
    "Windows: $([Environment]::OSVersion.VersionString); time zone: $((Get-TimeZone).Id)"
    "Setup UTC: $((Get-Date).ToUniversalTime().ToString('o')); target: $ezTarget"
    "PECmd: $ezPECmd; JumpList Explorer: $ezJumpList"
) | Set-Content -LiteralPath "$ezCase\notes\case-context.txt"
Get-FileHash -Algorithm SHA256 -LiteralPath $ezPECmd, $ezJumpList |
    Export-Csv -LiteralPath "$ezCase\notes\tool-sha256.csv" -NoTypeInformation
$ezTarget

Now open Notepad from Start. Choose File > Open, select All files if needed, and open the full path printed above. Read the training line, then close Notepad. Save the approximate start and finish times in notes\case-context.txt, including the VM's time zone. Do not replace those times with an artefact timestamp later.

Open JumpList Explorer and record its version from Help > About or its version display. Run & $ezPECmd -h in the same PowerShell window and record the version it reports. Close the viewer for now.

Look for: your file contains release window 17:00 UTC; the case notes identify the host, account, tool versions and time window. Keep this PowerShell window open.

2. Collect Prefetch and this user's Jump Lists

Prefetch files can hold execution related records for a program. Jump Lists can hold an application's recent file references. You need both to compare their different roles. Wait briefly after closing Notepad, then run this block once. It records skipped, empty and inaccessible sources rather than inventing results.

if (Test-Path -LiteralPath "$ezCase\notes\collection.txt") { throw 'Collection already recorded. Keep it; use a fresh case for another attempt.' }
$ezGroups = [ordered]@{
    Prefetch = Join-Path $env:WINDIR 'Prefetch'
    AutomaticDestinations = Join-Path $env:APPDATA 'Microsoft\Windows\Recent\AutomaticDestinations'
    CustomDestinations = Join-Path $env:APPDATA 'Microsoft\Windows\Recent\CustomDestinations'
}
"Collection start UTC: $((Get-Date).ToUniversalTime().ToString('o'))" | Set-Content -LiteralPath "$ezCase\notes\collection.txt"
foreach ($ezName in $ezGroups.Keys) {
    $ezFrom = $ezGroups[$ezName]
    $ezTo = Join-Path "$ezCase\sources" $ezName
    New-Item -ItemType Directory -Path $ezTo -ErrorAction Stop | Out-Null
    "${ezName}: $ezFrom -> $ezTo" | Add-Content -LiteralPath "$ezCase\notes\collection.txt"
    try {
        $ezFiles = @(Get-ChildItem -LiteralPath $ezFrom -File -ErrorAction Stop)
        "Files found: $($ezFiles.Count)" | Add-Content -LiteralPath "$ezCase\notes\collection.txt"
        foreach ($ezFile in $ezFiles) {
            try { Copy-Item -LiteralPath $ezFile.FullName -Destination $ezTo -ErrorAction Stop }
            catch { "Copy failed: $($ezFile.FullName); $_" | Add-Content -LiteralPath "$ezCase\notes\collection.txt" }
        }
    } catch { "Source unavailable: $ezFrom; $_" | Add-Content -LiteralPath "$ezCase\notes\collection.txt" }
}
"Collection finish UTC: $((Get-Date).ToUniversalTime().ToString('o'))" | Add-Content -LiteralPath "$ezCase\notes\collection.txt"
Get-Content -LiteralPath "$ezCase\notes\collection.txt"

Check sources in File Explorer and read collection.txt. Keep the exact original paths: the Jump Lists belong to the account recorded in the case notes. An access error means the collection failed for that path; it does not mean the artefact was absent.

Next, make working copies and compare their SHA-256 hashes. A hash is a file fingerprint: matching hashes show matching bytes.

if (Test-Path -LiteralPath "$ezCase\notes\copy-checks.csv") { throw 'Copy check already exists. Do not overwrite it.' }
if (@(Get-ChildItem -LiteralPath "$ezCase\working" -Force).Count -gt 0) { throw 'Working folder contains an earlier attempt. Use a fresh case.' }
foreach ($ezFolder in Get-ChildItem -LiteralPath "$ezCase\sources" -Directory) {
    Copy-Item -LiteralPath $ezFolder.FullName -Destination "$ezCase\working" -Recurse -ErrorAction Stop
}
$ezChecks = @(foreach ($ezFile in Get-ChildItem -LiteralPath "$ezCase\sources" -File -Recurse) {
    $ezRelative = $ezFile.FullName.Substring("$ezCase\sources\".Length)
    $ezSourceHash = (Get-FileHash -LiteralPath $ezFile.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
    $ezWorkingHash = (Get-FileHash -LiteralPath (Join-Path "$ezCase\working" $ezRelative) -Algorithm SHA256 -ErrorAction Stop).Hash
    [pscustomobject]@{File=$ezRelative; Bytes=$ezFile.Length; SourceSHA256=$ezSourceHash; WorkingSHA256=$ezWorkingHash; Match=($ezSourceHash -eq $ezWorkingHash)}
})
if ($ezChecks.Count -eq 0) { throw 'No files collected. Keep collection.txt and resolve the collection problem before continuing.' }
$ezChecks | Export-Csv -LiteralPath "$ezCase\notes\copy-checks.csv" -NoTypeInformation
if ($ezChecks.Match -contains $false) { throw 'Copy mismatch. Stop and check copy-checks.csv.' }
$ezChecks | Group-Object Match | Select-Object Name, Count

Look for: a saved copy-checks.csv with Match=True for every collected file. Examine working only; keep sources unchanged. If a copy step fails, keep the partial attempt and use a new case folder after fixing the problem.

No Notepad Prefetch or Jump List?

Windows settings, app versions and retention affect what is saved. Windows 11's packaged Notepad may behave differently from older Notepad. If you have no matching result, check the recorded user and collection paths first. An empty folder is different from a copy error.

In the training VM only, check Settings > Personalization > Start for the option to show recently opened items in Jump Lists. Record its state and any change. To try again, use a fresh case folder and record a new action window. Do not enable Prefetch or clear recent items on an incident machine just to create a result. A valid collection with no matching records can still lead to a useful, limited finding.

Ready to investigatePause point: keep the case folder. If you reopen PowerShell, set $ezCase and $ezPECmd to the paths in your notes.

Next: find what each tool can tell you about the action.

Continue to the tools

Activity 2: Follow the traces

Your next winFind a program trace and a file reference without confusing them.
1. Run PECmd on the Prefetch working copy

In the same PowerShell window, run the command below. -d names the input folder; --csv names a separate output folder; --csvf names the CSV. This is PowerShell syntax; the browser lab above uses Command Prompt syntax.

if (-not (Get-ChildItem -LiteralPath "$ezCase\working\Prefetch" -Filter '*.pf' -File)) { throw 'No collected Prefetch files. Record this gap; do not run the parser on browser downloads.' }
if (Test-Path -LiteralPath "$ezCase\parsed\Prefetch") { throw 'Parser output exists. Use a new output folder for another run.' }
"`"$ezPECmd`" -d `"$ezCase\working\Prefetch`" --csv `"$ezCase\parsed\Prefetch`" --csvf prefetch.csv" |
    Set-Content -LiteralPath "$ezCase\notes\pecmd-command.txt"
& $ezPECmd -d "$ezCase\working\Prefetch" --csv "$ezCase\parsed\Prefetch" --csvf prefetch.csv 2>&1 |
    Tee-Object -FilePath "$ezCase\notes\pecmd-console.txt"

Open parsed\Prefetch\prefetch.csv in Timeline Explorer if installed, or a CSV viewer. Search for NOTEPAD in the executable name column. Keep the complete parser output, including extra CSVs, and the console log. A parser that finishes without a CSV or reports failed files needs investigation.

Save a selected row or screenshot as exports\prefetch-observation with an appropriate file extension. In notes\observations.txt, write the source .pf filename, executable name, run count, available run times and their time basis. Compare the available run times with your action window. Do not change Windows settings to force a matching time.

Look for: a Notepad execution related record, or a clear note saying which collected files you searched and why no result was available. A run count is not a count of times this particular document was opened. Prefetch alone does not identify who used the program or prove they read handover.txt.

Which timestamp should I compare?

Use the record's run time fields, not the CSV file's modified time. Check your tool's labels and documented time basis; record whether each time is UTC or local. PECmd can retain several run times, but this is not a complete execution history. If several Notepad rows exist, keep their source filenames and compare them rather than picking the first row.

2. Find the file reference in JumpList Explorer
  1. Open your installed JumpList Explorer. Use File to load files from working\AutomaticDestinations and working\CustomDestinations. Choose the load directory option if your version has one; otherwise load the collected .automaticDestinations-ms and .customDestinations-ms files individually. Follow the viewer guide.
  2. Search or filter the target path field for handover.txt. If nothing matches, clear the filter and check the available entries. Inspect both source groups before reporting no match.
  3. Select a matching entry. Check the full path against the target path in case-context.txt. A matching filename in another folder is a different target.
  4. Record the source Jump List filename, displayed application/AppID, entry ID and available path and time fields. AppID is the application's identifier; a friendly name is a mapping, so record an unknown mapping as unknown.
  5. Save selected rows using your version's export option, or save a screenshot and a small transcription in notes\observations.txt. Keep the source filename, entry and field labels with the result in exports.

Look for: an application linked reference to the full target path, or a documented no match result with loaded sources, filters and errors. The result may be absent on your VM. Do not substitute the fictional browser entry.

The entry shows several dates. Which one matters?

Keep the label beside each date. DestList timestamps and the embedded shortcut's target file timestamps describe different things. A target file modified time is not automatically the time the user opened it. Automatic and Custom Jump Lists may offer different fields; do not fill missing fields with guessed values. A file reference does not prove its contents were read, changed or sent elsewhere.

Ready to comparePause point: keep full outputs and source context, not just a cropped row.

Next: combine your observations into a short finding.

Continue to the finding

Activity 3: Save your finding

Your final winExplain what the evidence supports and what remains unknown.
1. Compare two observations and write the handover

Create this small table in notes\handover.txt. Use your actual records; a missing result belongs in the table too.

Source What to write What it can tell you
Notepad Prefetch Source filename, executable, run count, run time and time basis; or gap Execution related activity for the program
Jump List Source filename, application/AppID, entry, full target path and labelled times; or gap An application linked reference to the file

Compare the paths and the time window before drawing a conclusion. These are different artefact types, but their times need not match exactly. Your controlled action note is a training reference, not a replacement for a missing source record.

Add three short sentences below the table:

  • Finding: state what your collected records support. For example, if both match: “The collected Prefetch records support Notepad execution near the training window. The user's Jump List contains a reference to the handover's full path.”
  • Limit: state what you cannot show. These records alone do not prove who read the file, what they read, or whether they copied it elsewhere. With a gap, explain that the retained evidence is insufficient for that part of the question.
  • Next check: name one useful source for the remaining question, such as an available process creation log for account context or an approved file access audit. Check whether it was enabled and retained; do not assume it exists.

Look for: another analyst can find every cited record in your saved sources and outputs. An honest no match finding is better than claiming the action did not happen.

2. Check the copies and keep the finished case

Close the tools and CSV viewer. Recheck the collected sources and working copies against the saved fingerprints. This block stops on a changed file before making the final package manifest.

foreach ($ezRow in Import-Csv -LiteralPath "$ezCase\notes\copy-checks.csv") {
    foreach ($ezArea in @('sources', 'working')) {
        $ezCheckPath = Join-Path "$ezCase\$ezArea" $ezRow.File
        $ezNow = (Get-FileHash -LiteralPath $ezCheckPath -Algorithm SHA256 -ErrorAction Stop).Hash
        if ($ezNow -ne $ezRow.SourceSHA256) { throw "File changed: $ezCheckPath. Keep and explain the mismatch." }
    }
}
if (Test-Path -LiteralPath "$ezCase\package-sha256.csv") { throw 'Package manifest exists. Keep it; record a new manifest under a new name if the case changes.' }
Get-ChildItem -LiteralPath $ezCase -File -Recurse | Where-Object { $_.FullName -ne "$ezCase\package-sha256.csv" } | ForEach-Object {
    [pscustomobject]@{File=$_.FullName.Substring("$ezCase\".Length); SHA256=(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 -ErrorAction Stop).Hash}
} | Export-Csv -LiteralPath "$ezCase\package-sha256.csv" -NoTypeInformation

Copy the entire case folder to approved storage outside the VM, such as a shared training folder that will survive rollback. Check the copied files against package-sha256.csv; the help below gives you a command for this. Keep the verification result alongside the final case so you do not change files already listed in the manifest. Then roll back the disposable VM to its clean snapshot. Keep failures and notes as well as successful results.

How do I check the saved case copy?

Before rollback, use the same PowerShell window in the VM. Replace the example below with the copied case folder's path as seen from the VM. The check compares the manifest itself, then every listed file. It does not change those files.

$ezSavedCase = 'X:\SUSA-EZ-060'  # Replace with the copied case folder's path
$ezManifestHash = (Get-FileHash -LiteralPath "$ezCase\package-sha256.csv" -Algorithm SHA256 -ErrorAction Stop).Hash
if ((Get-FileHash -LiteralPath "$ezSavedCase\package-sha256.csv" -Algorithm SHA256 -ErrorAction Stop).Hash -ne $ezManifestHash) { throw 'Copied manifest differs. Keep and inspect this copy.' }
foreach ($ezRow in Import-Csv -LiteralPath "$ezSavedCase\package-sha256.csv") {
    $ezSavedHash = (Get-FileHash -LiteralPath (Join-Path $ezSavedCase $ezRow.File) -Algorithm SHA256 -ErrorAction Stop).Hash
    if ($ezSavedHash -ne $ezRow.SHA256) { throw "Copied file differs: $($ezRow.File). Keep and inspect this copy." }
}
"Verified manifest and listed files at $ezSavedCase; UTC: $((Get-Date).ToUniversalTime().ToString('o'))"

Save the success message as a screenshot alongside the copied case. On a missing file or mismatch, keep the error and fix the copy before rollback. If the case itself changed after the manifest was made, record why and make a new manifest under a new name; keep the earlier one.

Look for: the outside VM copy opens, its hashes match, and your handover can be checked without the training VM. Do not clear incident artefacts as a cleanup step.

Optional challenge: catch a filename mixup

After preserving the core case, create a second harmless handover.txt in a different folder in the training VM. Open it in Notepad. Collect into a new case folder, then see whether JumpList Explorer distinguishes the two full paths. Explain why a filename only search can mislead you. Preserve both cases.

For later practice, try other command line parsers or ShellBags Explorer with suitable training evidence. Amcache and ShimCache can add inventory or compatibility context; neither alone proves program execution. They are not required for this case.

Ready for another analystYou have collected, examined and explained real Windows artefacts. Keep the case folder as your work sample.

Finish with a case another analyst can check from your saved files.

Review your evidence

Full Lab evidence checklist

Case folder handoverSave the two checkpoints in each activity.
  • CollectionCase context, tool hashes, collection log, original paths, collected sources and checked working copies
  • ExaminationPECmd command, console and complete output; Jump List sources and selected entries; labelled observations or gaps
  • FindingShort handover, hash checks, package manifest and a verified copy outside the VM

Parser flags follow the PECmd documentation; use the author's tool catalogue for releases and runtimes. The recent items setting is described in Microsoft's taskbar guide. Browser ticks record your progress; saved source records support your finding.