Skip to content

Lesson 6.1 · Dynamic Analysis· 40 min

Behavioural Monitoring

Run a sample through a repeatable detonation routine with Process Monitor, Sysmon, registry diffs and Autoruns, then turn what you see into host IOCs.

Objectives

  • Follow a repeatable detonation routine: snapshot, baseline, record, run, stop, diff, revert
  • Filter, read and save a Process Monitor capture, and know which operations matter
  • Deploy Sysmon with a community configuration and query the events a run produced
  • Compare before/after registry and autostart state to isolate what a sample changed
  • Organise observations into host IOCs and recognise samples that hide from a short run

Static triage tells you what a sample could do. Running it tells you what it does — at least on this machine, on this day, in the time you gave it. That qualification is the whole art of behavioural monitoring. A run is an experiment, and like any experiment it is only worth something if you control the starting conditions, record everything while it happens, and can compare the result against a known baseline.

This lesson turns the lab from Building a Safe Analysis Lab into a measuring instrument. You already know from Processes, Threads and DLLs what the objects on the screen mean; here the focus is on capturing their history and distilling it into indicators. The companion lesson, Simulating and Capturing Network Traffic, covers the network half of the same run.

The detonation routine

Every run should follow the same seven steps, in the same order. The routine is boring on purpose: when something unexpected shows up, you want to be sure it came from the sample and not from a step you skipped.

StepWhat you doWhy
1. SnapshotRevert the analysis VM to its clean snapshotNothing from a previous sample contaminates this one
2. BaselineRecord registry state and autostart entries; note the clock timeGives you something to diff against
3. RecordStart Process Monitor, confirm Sysmon is logging, start the packet captureShort-lived activity is lost if you start late
4. RunLaunch the sample the way a victim would, and interact if neededWrong launch method, wrong behaviour
5. StopWait a planned interval, then stop and save every recordingUnsaved Procmon data dies with the snapshot
6. DiffTake the "after" registry and autostart state; compareSurfaces changes you did not spot live
7. RevertExport artefacts out of the VM, then revertThe sample never survives the session

During the run, keep Process Explorer or System Informer open to catch what logs show poorly: a process that stays resident, a window it opens, or a sample that has not exited because it is waiting for something.

Two details decide whether the routine works in practice. First, artefacts must leave the VM before step 7: logs, PML files, the registry diff and any dropped files go to the REMnux helper over the internal network, never through a shared folder, and dropped executables travel in an infected-password archive like any other sample. Second, write the plan down before step 4: how long you will wait, whether you will reboot, what you will click. A plan you wrote before the run keeps you honest when you read the results.

Tip: Keep a running text file per sample with the SHA-256, the snapshot name, the start and stop times, and anything you did by hand during the run. Timestamps from that file are what let you line up Procmon, Sysmon and the packet capture afterwards.

Process Monitor

Process Monitor records file system, registry, process/thread and network activity from a kernel driver, with a stack trace for every event. It is the highest-resolution view you have without a debugger, and it produces a lot of data: an idle Windows machine generates thousands of events per second.

Capturing without drowning

Procmon's filters control only what is displayed; every event is still kept, so you can widen the filter later without re-running the sample — at the cost of memory on a long run. Two settings help:

  • File → Backing Files writes events to a PML file on disk instead of the page file. Point it at a folder you will export.
  • Filter → Drop Filtered Events discards events that fail the filter at capture time. Use it only once you trust your filter, because dropped events are gone for good.

For repeatable runs, drive Procmon from the command line:

powershell
# Start capturing to a backing file, minimised, with a saved filter set
Procmon64.exe /AcceptEula /Quiet /Minimized /LoadConfig C:\analysis\lab.pmc /BackingFile C:\analysis\run1.pml

# ... run the sample, wait ...

Procmon64.exe /Terminate
# Convert the log to CSV for scripting
Procmon64.exe /OpenLog C:\analysis\run1.pml /SaveAs C:\analysis\run1.csv

Filters that matter

Start from a filter on the sample and widen it as the process tree grows:

FilterRelationValuePurpose
Process Nameissample.exeShow only the sample
OperationisProcess CreateFind every child it launches, then add those names
Pathcontains\CurrentVersion\RunClassic persistence writes, from any process
OperationisRegSetValue / WriteFileOnly changes, not reads
Pathends with.exe / .dllDropped executables

Tools → Process Tree shows every process that existed during the capture, including ones that have already exited, with their command lines. Start there: it tells you which process names belong in your filter. Tools → File Summary and Registry Summary then give per-path counts, a quick way to spot the one directory the sample hammered.

Operations worth reading

OperationWhat it tells you
Process Create / Process StartChildren, their command lines and the parent
Load ImageDLLs loaded, including ones from unusual paths
CreateFileOpens and creates; check the Disposition and Result columns
WriteFile, SetRenameInformationFile, SetDispositionInformationFileWrites, renames and deletions — droppers often write, rename, then delete themselves
RegCreateKey, RegSetValue, RegDeleteValueRegistry changes; double-click for the data written
TCP Connect, UDP SendNetwork activity attributed to a process (details in the network lesson)

Failed operations are evidence too. A NAME NOT FOUND on a registry key or file that the sample checks for, early in the run, is often an environment check or an infection marker: the sample is asking "have I been here before?" or "is this an analysis machine?".

Saving the evidence

Save the capture as a native PML file (File → Save, all events) before you revert. A PML can be reopened on any Windows machine with a different filter, which you cannot do with a CSV. Export a filtered CSV alongside it for your notes and scripts.

Sysmon with a community configuration

Procmon is a microscope you switch on for one run. Sysmon is a flight recorder: a service and driver that writes selected events to the Microsoft-Windows-Sysmon/Operational event log, with stable ProcessGuid values that survive PID reuse. It is also the same telemetry many SOCs collect in production, which makes it the natural source for detections you hand over.

Sysmon without a configuration logs little; with a verbose one it logs too much. Start from a maintained community configuration — SwiftOnSecurity's sysmon-config or Olaf Hartong's sysmon-modular — and install it once, in the clean snapshot:

powershell
Sysmon64.exe -accepteula -i C:\tools\sysmonconfig.xml
# Later, update the config without reinstalling
Sysmon64.exe -c C:\tools\sysmonconfig.xml

The event IDs you will use most:

Event IDEventTypical finding
1Process creationCommand line, hashes, parent, ProcessGuid
3Network connectionDestination IP and port per process
7Image loadedDLL loads (often disabled in production configs)
8CreateRemoteThreadThread started in another process
10Process accessHandle opened to another process, e.g. lsass.exe
11File createDropped files in watched locations
12 / 13 / 14Registry create/delete, value set, renamePersistence keys
22DNS queryName looked up and by which process
23 / 26File delete (archived / logged)Self-deletion, cleanup

Query the run with PowerShell, bounded by the start time from your notes:

powershell
$start = Get-Date '2026-09-29 14:02:00'
Get-WinEvent -FilterHashtable @{
    LogName   = 'Microsoft-Windows-Sysmon/Operational'
    StartTime = $start
} | Select-Object TimeCreated, Id, Message | Format-List

# Keep a copy of the raw log for later
wevtutil epl Microsoft-Windows-Sysmon/Operational C:\analysis\run1.evtx

Warning: Community configurations are tuned for production, where noise costs money. They deliberately exclude many file and registry paths. A quiet Sysmon log proves only that nothing matched the config, not that nothing happened. Procmon is your ground truth; Sysmon shows what a defender's sensor would have caught.

Before/after diffs

Live monitors show events; diffs show net change. A sample that writes a key and deletes it again leaves a Procmon trail but no diff; a change made by a process you forgot to include in your Procmon filter shows up in the diff anyway. Use both.

Registry snapshots

Regshot is the classic tool: 1st shot before the run, 2nd shot after, Compare for a text or HTML report of keys and values added, deleted and modified. It is old but still works, and it can include a directory tree in the scan. Expect noise — random seeds, MRU lists, timestamps Windows updates on its own — and learn what your clean VM changes by itself by doing one run with no sample at all.

Autostart comparison

Autoruns knows every place Windows starts code from — Run keys, services, scheduled tasks, WMI subscriptions, Winlogon and Explorer extensions, drivers, and more. Its console twin produces a CSV you can compare between baseline and after-run:

powershell
autorunsc64.exe -accepteula -nobanner -a * -c -h -s > C:\analysis\autoruns-before.csv
# ... run ...
autorunsc64.exe -accepteula -nobanner -a * -c -h -s > C:\analysis\autoruns-after.csv
Compare-Object (Get-Content C:\analysis\autoruns-before.csv) (Get-Content C:\analysis\autoruns-after.csv)

The GUI can do the same with File → Compare against a saved .arn file, highlighting new entries. Any new entry is a candidate persistence mechanism; the planned lesson Persistence Mechanisms catalogues what each location means.

Automated sandboxes

A sandbox runs the same routine for you: snapshot, detonate, record, report, revert. CAPE, the open-source Cuckoo descendant, adds payload dumping and configuration extraction; commercial and hosted services add polish, scale, interactive sessions and shared intelligence. As the Building an Analysis Pipeline lesson shows, they fit naturally as the detonation stage of an automated workflow.

Their limits are the limits of any unattended run, amplified:

  • Fixed time budgets. A few minutes per sample. Anything that waits longer is missed, and the tricks sandboxes use to shorten sleeps are themselves detectable (sleep acceleration detection).
  • Recognisable environments. Sandbox images are shared by many customers, and samples fingerprint them — hypervisor artefacts, recent-file emptiness, no mouse movement (user activity checks).
  • No context. The sandbox does not know the command-line arguments, the parent document or the domain membership the sample expects.
  • Disclosure. Uploading to a public service shares the sample, and often its report, with everyone — including the actor, who can watch for their own hashes. Check your organisation's policy before submitting anything from an incident.

A sandbox report is an excellent first look and a poor last word. When its output is thin, that is a reason to run the sample yourself, not a verdict.

From events to host IOCs

The run is finished when its observations are written down in a form other people can use. Group them by type:

CategoryExampleNotes
Dropped files%APPDATA%\Microsoft\svcupd.exe, SHA-256Normalise user paths to environment variables; hash every dropped file
RegistryHKCU\...\CurrentVersion\Run value svcupdRecord key, value name and data
Processesrundll32.exe child with an unusual command lineParent/child pair plus command line is more durable than a name
Mutexes and named objectsGlobal\x7f3a...Find them in the handle view while the process runs
Services and tasksService name, binary path, task nameAlso appear in Autoruns

For each indicator, note whether it is stable (the same on every run and every victim) or generated (random names, per-host GUIDs). Run the sample twice from the same snapshot, or once on a second VM with a different username; anything that changes is a pattern, not a literal IOC. The triage report format from Module 3 has room for both.

Common pitfalls

The sample waits. Long sleeps, a date check, or waiting for a reboot or a user login all produce an empty run. Look at Procmon's last events for the process: a thread parked in a sleep or wait call is a hint. Timing checks such as GetTickCount deltas may also notice an accelerated clock.

The sample checks the environment. A clean exit after a burst of reads of hardware, BIOS or process-list information is the signature of an evasion check such as the CPUID hypervisor bit or a parent-process test. The planned lesson Anti-VM and Sandbox Evasion covers defeating these.

The sample needs something from you. Command-line arguments, a specific file name, being launched by a document, or — for DLLs — being loaded through the right export with rundll32.exe dllname,Export. Static triage of strings and exports usually says what it expects.

Activity is attributed to someone else. After injection or process hollowing, the interesting writes come from explorer.exe or svchost.exe. If the sample's own process goes quiet, widen the filter to the whole system for the time window and follow the handles it opened.

You are seen. Some samples look for Procmon64.exe or Wireshark in the process list. Renaming tools beats naive checks; Debugging Malware with x64dbg and Tracing API and System Calls show how to find the check itself.

Lab: record a harmless program's footprint

This lab uses a benign program you compile yourself. It creates a directory and a file under %TEMP%\LabDemo, writes a value under HKCU\Software\LabDemo, and exits — a miniature dropper without the malice.

  1. Write and build it on any machine with mingw-w64:

    c
    // labdemo.c — benign behaviour for a monitoring exercise:
    // creates %TEMP%\LabDemo\marker.txt and HKCU\Software\LabDemo\RunCount, then exits.
    #include <windows.h>
    #include <stdio.h>
    
    int main(void) {
        char dir[MAX_PATH], path[MAX_PATH];
        DWORD n = GetTempPathA(MAX_PATH, dir);
        if (n == 0 || n > MAX_PATH - 32) return 1;
    
        lstrcatA(dir, "LabDemo");
        CreateDirectoryA(dir, NULL);                 /* ok if it already exists */
        wsprintfA(path, "%s\\marker.txt", dir);
    
        HANDLE f = CreateFileA(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                               FILE_ATTRIBUTE_NORMAL, NULL);
        if (f == INVALID_HANDLE_VALUE) return 2;
        const char msg[] = "LabDemo was here\r\n";
        DWORD written;
        WriteFile(f, msg, sizeof msg - 1, &written, NULL);
        CloseHandle(f);
    
        HKEY key;
        if (RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\LabDemo", 0, NULL, 0,
                            KEY_SET_VALUE, NULL, &key, NULL) != ERROR_SUCCESS)
            return 3;
        DWORD one = 1;
        RegSetValueExA(key, "RunCount", 0, REG_DWORD, (const BYTE *)&one, sizeof one);
        RegCloseKey(key);
    
        printf("LabDemo: wrote %s and HKCU\\Software\\LabDemo\\RunCount\n", path);
        return 0;
    }
    bash
    x86_64-w64-mingw32-gcc -O1 -s -o labdemo.exe labdemo.c
    file labdemo.exe
    shasum -a 256 labdemo.exe

    On the build machine used for this lesson (your hash will differ with another compiler version):

    text
    labdemo.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
    a8624ff5f3f3b330e302cd2faa0645748a0d0cd02af73221e8bc54f3051beea5  labdemo.exe
  2. Predict before you run. List the imports and strings that describe its behaviour:

    bash
    x86_64-w64-mingw32-objdump -p labdemo.exe | grep -E 'Reg|CreateDirectory|CreateFile|WriteFile|GetTempPath'
    x86_64-w64-mingw32-strings -n 6 labdemo.exe | grep -iE 'labdemo|marker|software'

    Trimmed output:

    text
    	000082e0  <none>  0266  RegCloseKey
    	000082e8  <none>  026e  RegCreateKeyExA
    	000082f0  <none>  02b3  RegSetValueExA
    	00008308  <none>  00c9  CreateDirectoryA
    	00008310  <none>  00d7  CreateFileA
    	00008330  <none>  0325  GetTempPathA
    	00008370  <none>  0648  WriteFile
    LabDemo
    %s\marker.txt
    Software\LabDemo
    LabDemo: wrote %s and HKCU\Software\LabDemo\RunCount

    From this alone, write down what you expect Procmon to show: a directory and file created under the user's temp folder, 18 bytes written, and one REG_DWORD value set under HKCU\Software\LabDemo. No network, no child processes, no persistence.

  3. Snapshot and baseline. In the Windows analysis VM, revert to the clean snapshot, copy labdemo.exe in, and record the baseline: Regshot 1st shot (tick Scan dir and give it %TEMP%), autorunsc64 to autoruns-before.csv, and the current time in your notes file.

  4. Record. Start Procmon with a backing file, clear the display, and set the filter Process Name is labdemo.exe. Confirm Sysmon is running with Get-Service Sysmon64.

  5. Run labdemo.exe from a command prompt, wait ten seconds, then stop the Procmon capture and save it as labdemo.pml.

  6. Read Procmon. Find the CreateFile on the LabDemo directory, the CreateFile and WriteFile on marker.txt (check the Length in the detail column), and the RegCreateKey / RegSetValue pair under HKCU\Software\LabDemo. Double-click the RegSetValue event and check the type and data. Then remove the filter briefly and look at how many events the rest of the system produced in the same ten seconds.

  7. Read Sysmon. Query the log from your start time. You should find an Event 1 for labdemo.exe with its hashes. Check whether Event 11 (file create) and Event 13 (registry value set) appear — with a production-tuned config they may well not, because %TEMP%\*.txt and an arbitrary HKCU key are not locations those configs watch.

  8. Diff. Take Regshot's 2nd shot and Compare; run autorunsc64 again and compare the CSVs. The registry diff should list the new key and value; the autostart diff should be empty.

  9. Export and revert. Copy the PML, the EVTX export, both diffs and your notes to REMnux, then revert the VM.

Questions to answer: Which of LabDemo's actions did your Sysmon configuration record, and what would you add to the config to catch the rest? Why was the Autoruns diff empty even though the registry changed? If LabDemo deleted marker.txt just before exiting, which of your three sources (Procmon, Sysmon, Regshot) would still show that the file existed? Write LabDemo's host IOCs as a table, marking each as stable or generated.

Key takeaways

  • A behavioural run is an experiment: revert, baseline, record, run, stop, diff, revert — the same way every time, with notes and timestamps.
  • Process Monitor is the ground truth for one run; save the full PML, filter from the process tree outwards, and read failed operations as well as writes.
  • Sysmon with a community configuration shows what a production sensor would see; a quiet log reflects the config as much as the sample.
  • Registry and Autoruns diffs reveal net change and persistence that live views miss.
  • Sandboxes automate the routine but share its blind spots: short runs, fingerprintable environments and no context.
  • An empty run is a finding to explain — waiting, environment checks, missing arguments or injection — not proof of a harmless file.