Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions Docs/Endpoint-Investigations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Captured endpoint investigations

EventViewerX reads captured CMTrace and plain-text logs, reconstructs attributable
Intune application attempts, and interprets captured `dsregcmd /status` output.
The workflow runs offline. It copies and hashes original files before parsing,
retains diagnostic records and collection context in an investigation session,
and produces findings with evidence coordinates and next checks.

The endpoint extension uses the existing investigation manifest and integrity
checks. Windows event observations and their detection plan can be supplied to
`EventInvestigationSession.CreateEndpoint` alongside endpoint artifacts. Text
records keep their source format and never acquire synthetic event IDs or UTC
timestamps.

## Create and replay through the CLI

```powershell
evx investigation create --directory .\Case-001 --log .\AppWorkload.log,.\AppActionProcessor.log --dsreg .\dsreg-status.txt --device Endpoint-001 --context User --captured-at 2026-10-09T10:00:00Z --expected-join Hybrid --html .\Case-001.html
evx investigation inspect --directory .\Case-001
evx investigation replay --directory .\Case-001 --html .\Case-001-replay.html
```

Use a new session directory and new report files. Presentation exports belong
outside the session. `inspect` returns the retained analysis; `replay` evaluates
the verified canonical diagnostic records using the same collection context.
A different engine build requires `--allow-different-engine` and marks the
comparative result incomplete. Neither command executes a live collection query.

`--facts` accepts a JSON object. `--registry` retains a registry snapshot without
importing it. `--attachment` retains other original evidence without executing it.
These artifacts are hashed and listed in the manifest; unsupported policy facts
are preserved for inspection rather than used to invent conclusions.

Declare `--device` only when the inputs belong to that endpoint. Cross-file
application correlation also requires known instants and a specific execution
identity in the records. A source generation containing any unknown instant stays
entirely in byte order. Unknown context and generic `User` context, regardless of
casing, keep attempts source-local. CLI capture options apply to all supplied
files; use the typed API or PowerShell capture descriptors when files have
different contexts, offsets, or capture times.

## Create and replay through PowerShell

```powershell
Import-Module PSEventViewer

$capture = [EventViewerX.EventEndpointCapture]@{
ExpectedJoin = 'Hybrid'
Inputs = @(
[EventViewerX.EventEndpointInput]@{
Path = '.\AppWorkload.log'
Kind = 'Log'
Device = 'Endpoint-001'
UtcOffset = [TimeSpan]::FromHours(2)
}
[EventViewerX.EventEndpointInput]@{
Path = '.\dsreg-status.txt'
Kind = 'DsRegCmd'
ExecutionContext = 'User'
CapturedAt = [DateTimeOffset]::Parse('2026-10-09T10:00:00Z')
}
)
}

$session = Export-EVXInvestigation -Path .\Case-001 -EndpointCapture $capture
$opened = Open-EVXInvestigation -Path .\Case-001
$analysis = $opened | Invoke-EVXInvestigation -Endpoint -HtmlPath .\Case-001.html
$analysis.Applications | Select-Object ApplicationId, Context, AttemptId, LastPhase, Outcome
$analysis.Findings | Select-Object Title, Status, Explanation, NextChecks
```

Add native event observations through the pipeline and supply `-Manifest` and
`-Plan` to retain their declared event window and detection rules. Without native
events, the endpoint capture creates an empty native plan. The default
`Invoke-EVXInvestigation` continues to replay native Windows-event detection;
`-Endpoint` selects diagnostic analysis.

## Read a bounded log batch

```powershell
$options = [EventViewerX.EventDiagnosticReadOptions]@{
FinalFile = $false
MaximumRecords = 1000
UtcOffset = [TimeSpan]::FromHours(2)
}
$batch = Get-EVXDiagnostic -Path .\IntuneManagementExtension.log -Options $options
$batch.Records | Select-Object Source, LineStart, LineEnd, Timestamp, TimeQuality, Message
# After durably accepting the batch, retain its checkpoint for the next read.
$next = Get-EVXDiagnostic -Path .\IntuneManagementExtension.log -Options $options -Checkpoint $batch.Checkpoint
```

The reader supports UTF-8 and BOM-marked UTF-16. It bounds bytes per batch,
records per batch, and retained bytes per frame. Incomplete trailing frames stay
pending. Oversized completed frames carry an explicit diagnostic and cannot
produce application conclusions. A frame larger than the batch bound cannot
advance until the bound is increased; session capture reports that gap instead
of repeatedly retrying it. Concurrent truncation discards an unfinished frame and
reports the changed input so the next read can validate its generation.

The checkpoint fingerprints physical file identity, the prefix, and the preceding
4 KiB. Rotation, empty truncation, and changes in those checked bytes start a new
generation. In-place historical changes elsewhere are outside the bounded check;
use an immutable copied investigation for full-file integrity. The collector must
durably accept records before committing a checkpoint. The CLI `diagnostics read`
can retain the position with `--checkpoint`, but cannot make stdout and an external
consumer transaction atomic.

CMTrace offsets are signed minutes. An embedded offset takes precedence; use
`--utc-offset-minutes` or `UtcOffset` when the writer's offset is known. Missing
offsets remain unknown. The reader never applies the collector's local timezone.

## Interpret a number in context

```powershell
Get-EVXDiagnostic -Code '-2147024891' -Kind HResult
Get-EVXDiagnostic -Code '3010' -Kind WindowsInstaller
evx diagnostics code --value 0x80070005 --kind HResult
```

The result retains the original number, its unsigned and hexadecimal forms,
HRESULT facility and severity where applicable, and a next check. A process exit
code needs the application's configured return-code mapping. Windows Installer
0, 1641, and 3010 have success semantics; a required restart still needs reboot
policy and later detection evidence. [Windows Installer return-code contract](https://learn.microsoft.com/en-us/windows/win32/msi/error-codes).

## Read conclusions and preserve evidence

Application analysis recognizes applicability, requirements, dependencies,
detection, download, enforcement, restart, and reporting messages. Records need
one explicit application ID. Unattributed lines remain in evidence and never
inherit an app from a nearby line or thread. Attempt boundaries without explicit
IDs are marked inferred. An observed client detection result does not prove
service-side reporting or later deployment state. A negative detection during an
unfinished installation remains uncertain; post-enforcement failure needs a
completion or exit result, or an explicit terminal failure. [Microsoft's documented application lifecycle](https://learn.microsoft.com/en-us/troubleshoot/mem/intune/app-management/develop-deliver-working-win32-app-via-intune).

DSRegCmd analysis retains section-local values, unknowns, and conflicting fields.
Join-state mismatch needs an explicit intended join mode. User PRT and Hello
results need the affected user's context and policy. MDM URLs alone do not prove
device enrollment. Capture time must be supplied by the collector; a file's
modification time is not a collection receipt. [Microsoft's DSRegCmd field contracts](https://learn.microsoft.com/en-us/entra/identity/devices/troubleshoot-device-dsregcmd).

Captured DSRegCmd text and JSON facts support UTF-8 and BOM-marked UTF-16, including
Windows PowerShell 5.1 redirection. Canonical session JSON remains strict UTF-8.
Replay checks the hash of the diagnostic bytes actually consumed as well as the
session's preflight integrity checks.

Original artifacts and canonical diagnostic records remain sensitive. Reports
omit raw log messages and DSRegCmd fields by default; application identifiers and
conclusions can still be sensitive. Use `--include-sensitive` or
`-IncludeSensitiveEvidence` only when the report recipient needs raw evidence.
This report option is data minimization, not anonymization. Keep an original
session separate from a presentation export.
49 changes: 38 additions & 11 deletions Docs/Export-EVXInvestigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,30 @@ schema: 2.0.0
Captures a reproducible investigation with copied input evidence, canonical observations, effective rules, and verified outputs.

## SYNTAX
### __AllParameterSets
### Events (Default)
```powershell
Export-EVXInvestigation [-Path] <string> -InputObject <Object> -Manifest <EventInvestigationManifest> -Plan <EventDetectionPlan> [-InputPath <string[]>] [-Coverage <EventDetectionCoverage>] [-WhatIf] [-Confirm] [<CommonParameters>]
```

### Endpoint
```powershell
Export-EVXInvestigation [-Path] <string> -EndpointCapture <EventEndpointCapture> [-InputObject <Object>] [-Manifest <EventInvestigationManifest>] [-Plan <EventDetectionPlan>] [-Coverage <EventDetectionCoverage>] [-WhatIf] [-Confirm] [<CommonParameters>]
```

## DESCRIPTION
Use a new directory. The manifest declares the full input window, including correlation warm-up. Existing evidence is never overwritten.

## EXAMPLES

### EXAMPLE 1
```powershell
Export-EVXInvestigation -InputObject 'Value' -Manifest 'Value' -Plan 'Value'
Export-EVXInvestigation -Path 'C:\Path' -InputObject 'Value' -Manifest 'Value' -Plan 'Value'
```


### EXAMPLE 2
```powershell
Export-EVXInvestigation -Path 'C:\Path' -EndpointCapture 'Value'
```


Expand All @@ -32,7 +43,7 @@ Expected and observed collection coverage.

```yaml
Type: EventDetectionCoverage
Parameter Sets: __AllParameterSets
Parameter Sets: Events, Endpoint
Aliases: None
Possible values:

Expand All @@ -43,16 +54,32 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -EndpointCapture
Endpoint capture descriptors, intended join state, and finite evidence bounds. Originals are copied before parsing; commands are never executed.

```yaml
Type: EventEndpointCapture
Parameter Sets: Endpoint
Aliases: None
Possible values:

Required: True
Position: named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -InputObject
Canonical observation or detached event to retain.

```yaml
Type: Object
Parameter Sets: __AllParameterSets
Parameter Sets: Events, Endpoint
Aliases: None
Possible values:

Required: True
Required: False
Position: named
Default value: None
Accept pipeline input: True (ByValue)
Expand All @@ -64,7 +91,7 @@ Original input files to copy and hash inside the session.

```yaml
Type: String[]
Parameter Sets: __AllParameterSets
Parameter Sets: Events
Aliases: None
Possible values:

Expand All @@ -80,11 +107,11 @@ Source receipts, query identity, time range, parser versions, and execution limi

```yaml
Type: EventInvestigationManifest
Parameter Sets: __AllParameterSets
Parameter Sets: Events, Endpoint
Aliases: None
Possible values:

Required: True
Required: False
Position: named
Default value: None
Accept pipeline input: False
Expand All @@ -96,7 +123,7 @@ New investigation directory.

```yaml
Type: String
Parameter Sets: __AllParameterSets
Parameter Sets: Events, Endpoint
Aliases: None
Possible values:

Expand All @@ -112,11 +139,11 @@ Effective detection plan to retain and evaluate.

```yaml
Type: EventDetectionPlan
Parameter Sets: __AllParameterSets
Parameter Sets: Events, Endpoint
Aliases: None
Possible values:

Required: True
Required: False
Position: named
Default value: None
Accept pipeline input: False
Expand Down
Loading
Loading