Leçon 6.1 · Analyse dynamique· 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.
Cette leçon n’est disponible qu’en anglais pour le moment.
Objectifs
- 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.
| Step | What you do | Why |
|---|---|---|
| 1. Snapshot | Revert the analysis VM to its clean snapshot | Nothing from a previous sample contaminates this one |
| 2. Baseline | Record registry state and autostart entries; note the clock time | Gives you something to diff against |
| 3. Record | Start Process Monitor, confirm Sysmon is logging, start the packet capture | Short-lived activity is lost if you start late |
| 4. Run | Launch the sample the way a victim would, and interact if needed | Wrong launch method, wrong behaviour |
| 5. Stop | Wait a planned interval, then stop and save every recording | Unsaved Procmon data dies with the snapshot |
| 6. Diff | Take the "after" registry and autostart state; compare | Surfaces changes you did not spot live |
| 7. Revert | Export artefacts out of the VM, then revert | The 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:
# 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.csvFilters that matter
Start from a filter on the sample and widen it as the process tree grows:
| Filter | Relation | Value | Purpose |
|---|---|---|---|
| Process Name | is | sample.exe | Show only the sample |
| Operation | is | Process Create | Find every child it launches, then add those names |
| Path | contains | \CurrentVersion\Run | Classic persistence writes, from any process |
| Operation | is | RegSetValue / WriteFile | Only changes, not reads |
| Path | ends with | .exe / .dll | Dropped 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
| Operation | What it tells you |
|---|---|
Process Create / Process Start | Children, their command lines and the parent |
Load Image | DLLs loaded, including ones from unusual paths |
CreateFile | Opens and creates; check the Disposition and Result columns |
WriteFile, SetRenameInformationFile, SetDispositionInformationFile | Writes, renames and deletions — droppers often write, rename, then delete themselves |
RegCreateKey, RegSetValue, RegDeleteValue | Registry changes; double-click for the data written |
TCP Connect, UDP Send | Network 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:
Sysmon64.exe -accepteula -i C:\tools\sysmonconfig.xml
# Later, update the config without reinstalling
Sysmon64.exe -c C:\tools\sysmonconfig.xmlThe event IDs you will use most:
| Event ID | Event | Typical finding |
|---|---|---|
| 1 | Process creation | Command line, hashes, parent, ProcessGuid |
| 3 | Network connection | Destination IP and port per process |
| 7 | Image loaded | DLL loads (often disabled in production configs) |
| 8 | CreateRemoteThread | Thread started in another process |
| 10 | Process access | Handle opened to another process, e.g. lsass.exe |
| 11 | File create | Dropped files in watched locations |
| 12 / 13 / 14 | Registry create/delete, value set, rename | Persistence keys |
| 22 | DNS query | Name looked up and by which process |
| 23 / 26 | File delete (archived / logged) | Self-deletion, cleanup |
Query the run with PowerShell, bounded by the start time from your notes:
$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.evtxWarning: 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:
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:
| Category | Example | Notes |
|---|---|---|
| Dropped files | %APPDATA%\Microsoft\svcupd.exe, SHA-256 | Normalise user paths to environment variables; hash every dropped file |
| Registry | HKCU\...\CurrentVersion\Run value svcupd | Record key, value name and data |
| Processes | rundll32.exe child with an unusual command line | Parent/child pair plus command line is more durable than a name |
| Mutexes and named objects | Global\x7f3a... | Find them in the handle view while the process runs |
| Services and tasks | Service name, binary path, task name | Also 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.
-
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.exeOn 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 -
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\RunCountFrom 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_DWORDvalue set underHKCU\Software\LabDemo. No network, no child processes, no persistence. -
Snapshot and baseline. In the Windows analysis VM, revert to the clean snapshot, copy
labdemo.exein, and record the baseline: Regshot 1st shot (tick Scan dir and give it%TEMP%),autorunsc64toautoruns-before.csv, and the current time in your notes file. -
Record. Start Procmon with a backing file, clear the display, and set the filter Process Name is
labdemo.exe. Confirm Sysmon is running withGet-Service Sysmon64. -
Run
labdemo.exefrom a command prompt, wait ten seconds, then stop the Procmon capture and save it aslabdemo.pml. -
Read Procmon. Find the
CreateFileon theLabDemodirectory, theCreateFileandWriteFileonmarker.txt(check the Length in the detail column), and theRegCreateKey/RegSetValuepair underHKCU\Software\LabDemo. Double-click theRegSetValueevent 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. -
Read Sysmon. Query the log from your start time. You should find an Event 1 for
labdemo.exewith 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%\*.txtand an arbitraryHKCUkey are not locations those configs watch. -
Diff. Take Regshot's 2nd shot and Compare; run
autorunsc64again and compare the CSVs. The registry diff should list the new key and value; the autostart diff should be empty. -
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.