WER crash call-path hashing and pre-crash behavior fingerprinting¶
Windows Error Reporting (WER) records information when applications crash, hang, or trigger certain exceptions. It can provide useful pre-incident telemetry for exploit hunting, unstable payloads, application abuse, and recurring failures.
Two complementary detection paths¶
WER crash
|
+-----------------------------+
| |
v v
Crash call-path signature Pre-crash behavior
| |
v v
crash_path_hash behavior/API fingerprint
| |
+-------------+---------------+
|
v
anomaly scoring
- The crash call-path signature asks: Is this failure path unusual?
- The behavioral/API fingerprint asks: Was the process doing something unusual immediately before the crash?
- When both are unusual, confidence and investigative priority should increase.
Neither path makes a crash malicious by itself. Exploitation, injection, shellcode execution, and unstable post-exploitation activity can sometimes leave secondary crash or error artifacts, but ordinary defects, compatibility problems, security tools, and administrative software can do the same.
Collect and retain¶
| WER evidence | Why it matters |
|---|---|
| Process/application name | Identifies the crashing workload |
| Application version | Separates vulnerable or unusual builds |
| Faulting module | Locates the component involved |
| Exception code | Distinguishes access violations and other failure classes |
| Fault offset | Helps cluster similar crashes within a build/module |
| Crash bucket | Microsoft's coarse grouping of related failures |
| Ordered crash call path | Captures the function/module path around the failure |
| WER report identifier | Stable case-level identifier for joining report artifacts |
| Report/bucket identifiers | Connect repeated or related crash submissions |
| Host, user, and time | Enables scope, sequence, and prevalence analysis |
High-value Windows events¶
| Event ID | Log/provider | Meaning |
|---|---|---|
| 1000 | Application Error | Application fault details |
| 1001 | Windows Error Reporting | WER report, bucket, and report identifier data |
| 1002 | Application Hang | Application stopped responding |
Event availability and field shape vary by Windows version, application, policy, and collection configuration.
WER report locations¶
WER report identifiers¶
Preserve the original identifiers before normalization:
| Identifier | Use |
|---|---|
Report Id / report identifier |
Join event data to a specific report/artifact |
| Bucket ID / failure bucket | Group Windows-classified similar failures |
| Event name | Distinguish crash/hang/report families |
| Application and module version | Prevent false clustering across builds |
| Exception code and fault offset | Refine similarity and triage |
Do not treat a WER report ID as a malware identifier. It identifies a report instance; use bucket and normalized crash-path fingerprints for clustering.
Source basis: crash call-path signatures¶
Tiepolo and Sorensen describe this workflow in Chapter 6 of Advanced Cyber Threat Intelligence and Hunting. Their method creates a compact signature from the top N non-OS frames or canonical function names, then uses it to cluster rare, new, repeated, or cross-host crashes.1
The underlying debugger artifact is an ordered call stack: the nested function and module frames active near the failure. It is not a fingerprint of stack memory. This page calls the normalized output crash_path_signature to keep that distinction explicit.
The source method is:
- Remove common operating-system scaffolding such as
kernel32.dllandntdll.dllfrom the clustering view. - Retain the top N application or library call frames near the fault.
- Prefer canonical
module!functionnames when symbols exist. - Otherwise use a stable module-relative offset, not an absolute virtual address.
- Lowercase, remove formatting noise, preserve frame order, concatenate, and hash.
- Measure signature rarity, recurrence, host diversity, and velocity.
Obtain the actual crash call path¶
WER event records commonly provide crash metadata, not a complete symbolized call path. The signature pipeline needs a report artifact, dump, or endpoint source that exposes ordered call frames.
| Source | Use |
|---|---|
| Application Error and WER events | Identify the process, module, exception, offset, report ID, and crash time |
Report.wer and WER archive/queue |
Recover report fields and locate related artifacts |
| WER LocalDumps or another crash-dump policy | Preserve a user-mode dump from which the call path can be reconstructed |
| EDR or application crash telemetry | Use vendor-provided call frames when available |
| WinDbg/CDB or another symbol-aware parser | Extract ordered frames from the dump |
Microsoft documents per-application and global LocalDumps configuration under HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps. Scope dump collection to selected applications, set retention limits, protect dump storage, and validate that the chosen dump type contains enough state for call-path reconstruction.
WER event
→ report ID and crash metadata
→ Report.wer or dump artifact
→ symbol-aware call-frame extraction
→ ordered frames
→ canonical crash call path
→ crash-path signature
Create the crash-path signature¶
Keep the crash-path signature focused on the ordered call frames. Store application version, exception code, faulting module, and WER bucket as adjacent fields for filtering or a separate composite cluster key.
Raw frames
→ remove common OS-only frames from the clustering view
→ retain the top N application/library frames
→ canonicalize module!function or module+relative_offset
→ lowercase and remove whitespace
→ join in frame order
→ hash the canonical sequence
chrome.dll!v8::internal::codestubassembler::callbuiltin>chrome.dll!blink::document::evaluatescript
from hashlib import sha256
COMMON_OS_MODULES = {
"kernel32.dll",
"kernelbase.dll",
"ntdll.dll",
}
def canonical_call_path(frames, top_n=8):
selected = []
for raw_frame in frames:
frame = "".join(raw_frame.lower().split())
module = frame.split("!", 1)[0].split("+", 1)[0]
if module in COMMON_OS_MODULES:
continue
selected.append(frame)
if len(selected) == top_n:
break
if not selected:
raise ValueError("no application or library frames remained")
return ">".join(selected)
canonical = canonical_call_path(frames)
crash_path_hash_full = sha256(canonical.encode("utf-8")).hexdigest()
crash_path_signature = crash_path_hash_full[:24] # 12-byte display key
The book uses MD5/SHA-1 as examples of fast clustering hashes. SHA-256 or BLAKE2 is a safer modern default with negligible cost at this scale. Store the full digest for collision resolution and use a shorter display key only for analyst convenience.
Do not hash raw debugger output directly. Paths, absolute addresses, symbol decoration, thread headers, and formatting changes make raw text unstable. Keep the raw ordered frames and canonical input beside the signature so the result remains explainable and the normalization can be revised.
application|application_version|exception_code|faulting_module|crash_path_signature
Recommended record¶
| Field | Purpose |
|---|---|
wer_report_id |
Report-instance join key |
wer_bucket_id |
Native WER grouping |
crash_path_hash |
Environment-controlled normalized call-path cluster |
crash_path_normalized |
Explainable hash input |
application / application_version |
Workload/build context |
faulting_module / version |
Fault location |
exception_code / fault_offset |
Crash characteristics |
host / user |
Scope and ownership |
first_seen / last_seen / count |
Prevalence and timeline |
Behavioral/API sequence fingerprinting¶
WER as the temporal anchor¶
WER event
→ identify the affected process and crash time
→ retrieve endpoint telemetry immediately preceding the crash
→ normalize relevant API calls or lower-level process actions
→ create a behavioral/API-sequence fingerprint
→ compare historical, host, process, peer-group, and cross-host prevalence
WER does not provide the API-call sequence. This path depends on EDR, ETW, specialized tracing, or other endpoint telemetry with enough visibility to reconstruct the actions preceding the crash. Normalize behavior rather than raw event text so equivalent lower-level actions can cluster despite different providers or API layers.
Higher-risk sequence elements¶
These operations increase suspicion when they are rare, occur in a meaningful sequence, and are tightly aligned to the crash. None is inherently malicious by itself.
| Behavior | Focused API examples |
|---|---|
| Remote process access | OpenProcess, NtOpenProcess |
| Remote memory allocation | VirtualAllocEx, NtAllocateVirtualMemory |
| Writing into another process | WriteProcessMemory, NtWriteVirtualMemory |
| Writable-to-executable or other execution-enabling protection change | VirtualProtect, VirtualProtectEx, NtProtectVirtualMemory |
| Remote thread creation | CreateRemoteThread, NtCreateThreadEx |
| Unbacked or unusual thread start | Correlate the start address with module and memory-region metadata |
| Execution from private or unbacked memory | Correlate thread execution with allocation, write, and protection history |
Use the High-Risk API Population and Fileless Memory Telemetry pages for deeper collection and population-analysis guidance; this page uses those signals only as pre-crash context.
Detection Ideas¶
Signals to prototype
- New or historically unseen WER crash-path signature
- Rare crash-path signature for a host, process, or peer group
- Sudden increase in WER/crash frequency on a host
- Same unusual crash-path signature appearing across multiple hosts
- New or rare pre-crash behavioral/API fingerprint
- Same unusual behavioral fingerprint appearing across multiple systems
- Rare behavior immediately followed by a crash
- Rare behavior plus one or more high-risk memory/process operations
- High-risk API/process behavior followed by an unusual WER event
- New crash signature and new behavioral fingerprint occurring together
- Temporal clustering of similar crashes or behavioral fingerprints across hosts
- Significant deviation from a host or peer-group baseline
Conceptual risk progression
Candidate anomaly features¶
| Crash-path features | Behavioral and prevalence features |
|---|---|
crash_path_signature_rarity |
behavior_sequence_rarity |
crash_path_signature_age |
behavior_sequence_age |
crash_rate_vs_baseline |
high_risk_api_count |
hosts_seen_with_crash_path |
hosts_seen_with_sequence |
process_prevalence |
host_prevalence |
peer_group_prevalence |
cross_host_velocity |
time_since_first_seen |
— |
This does not need to begin as a machine-learning model. Prototype the joins, windows, normalization, rarity, prevalence, baselines, and false-positive handling in Splunk SPL first. If the signal is useful and the telemetry is dependable, operationalize it through Enterprise Security detections, Splunk UBA custom content, MLTK, or the behavioral-analytics framework appropriate to the environment.
Why this matters for threat hunting¶
Exploitation and exploit attempts can cause abnormal exceptions, crashes, hangs, faulting instructions, unusual call paths, faulting modules, and report clusters. WER can supply an earlier point in the timeline than detections focused only on post-exploitation behavior.
Most detections emphasize what happens after successful exploitation:
WER may preserve evidence of what happened before or during that transition:
A crash is not proof of exploitation. Its value comes from correlation with process, memory, file, identity, and network telemetry.
WER correlation patterns¶
WER + Sysmon¶
WER crash
→ Sysmon 1 Process Create
→ Sysmon 10 ProcessAccess
→ Sysmon 8 CreateRemoteThread
→ Sysmon 3 Network Connection
Pivot next: Process GUID, source/target image, access rights, remote-thread start fields, destination, and timeline gaps.
WER + EDR¶
Crash or exception
→ abnormal crash call path
→ executable MEM_PRIVATE
→ unbacked thread start
→ injection or shellcode evidence
Pivot next: memory region type/protection, allocation and protection transitions, raw thread start, loaded-module boundaries, and responsible process lineage.
WER + network¶
Inbound content
→ application parses content
→ crash
→ distinct WER crash path
→ subsequent callback
Pivot next: source session, request/object, DNS, TLS/HTTP metadata, callback process, peer-host prevalence, and whether the callback preceded or followed restart/recovery.
WER + file telemetry¶
Pivot next: file origin, Mark-of-the-Web, hash, signer, creating/downloading process, application child processes, and execution across other hosts.
Crash-path signature hunt¶
The analytical value comes after the hash is created. Group crashes by crash_path_signature, then rank signatures by novelty, rarity, recurrence, host diversity, and rate of appearance.
by application, application_version, crash_path_signature
count crashes
count distinct hosts and users
calculate first_seen and last_seen
calculate crashes per hour
retain exception_code, faulting_module, and module_offset
Prioritize:
- A signature not seen during the baseline window
- A rare signature appearing on several hosts in a short period
- Repeated instances on one exposed or high-value host
- The same signature plus the same exception code, module, or relative offset
- A crash cluster aligned with common inbound content or web activity
- A crash cluster followed by unusual process, memory, identity, or network activity
Keep application and module versions in the grouping or filter logic. Patches and software releases can change symbols and offsets without changing security relevance.
A crash is not proof of exploitation. Patch defects, compatibility problems, JIT behavior, and ordinary instability can produce similar evidence.
What to pivot to next¶
Also pivot to signer/hash, application and module version, document/download origin, EDR memory/thread telemetry, Sysmon 1/3/7/10/11/22/25, and Windows Security events in the same session.
Acting on the telemetry¶
| Finding | Action |
|---|---|
| Common known-benign cluster | Baseline with version and signer constraints |
| Rare unexplained cluster | Preserve report/dump and investigate host timeline |
| Repeated vulnerable-build cluster | Patch or mitigate; track residual exposure |
| Cross-host suspicious cluster | Scope enterprise-wide and create a hunt/detection |
| Missing call-path/report details | Improve WER, event, dump, symbol, or EDR collection |
| Security-control crash | Treat visibility loss as a separate incident signal |
Operational summary¶
Collect WER identifiers and crash artifacts, extract symbolized call frames, and create an explainable normalized crash-path signature. Use the same crash time to retrieve preceding endpoint activity and create a normalized behavioral/API fingerprint when telemetry supports it. Baseline each dimension independently, then prioritize rare, repeated, or cross-host combinations without treating WER or high-risk operations as malicious by themselves.
Sources¶
- Microsoft Learn, Collecting User-Mode Dumps.
- Microsoft Learn, k, kb, kc, kd, kp, kP, kv: Display Stack Backtrace.
-
Gianluca Tiepolo and Dan Sorensen, Advanced Cyber Threat Intelligence and Hunting, first edition, Packt, 2026, Chapter 6, "Correlating crashes and instability to uncover exploitation," supplied EPUB extraction lines 214–307. ↩