### CLI Usage Examples Source: https://context7.com/securityronin/usnjrnl-forensic/llms.txt Examples demonstrating various command-line operations for usnjrnl-forensic, including processing disk images, pre-extracted artifacts, and specific detection capabilities. ```APIDOC ## CLI Usage Examples ### Description Examples demonstrating various command-line operations for usnjrnl-forensic, including processing disk images, pre-extracted artifacts, and specific detection capabilities. ### Examples **Rapid triage from E01 image with unallocated space carving:** ```bash usnjrnl-forensic --image evidence.E01 --carve-unallocated --report triage.html ``` **Process pre-extracted artifacts with full path resolution:** ```bash usnjrnl-forensic -j $J -m $MFT --csv output.csv ``` **QuadLink correlation with all four artifacts:** ```bash usnjrnl-forensic -j $J -m $MFT --mftmirr $MFTMirr --logfile $LogFile --sqlite analysis.db ``` **Timeline export in multiple formats simultaneously:** ```bash usnjrnl-forensic --image evidence.E01 --csv timeline.csv --jsonl timeline.jsonl --body timeline.body ``` **Detect timestomping with MFT correlation:** ```bash usnjrnl-forensic -j $J -m $MFT --detect-timestomping ``` **Save extracted artifacts for later analysis:** ```bash usnjrnl-forensic -i evidence.E01 --output-dir ./extracted --report triage.html ``` ``` -------------------------------- ### Install usnjrnl-forensic for Pre-Extracted Artifacts Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md Install the usnjrnl-forensic binary without direct disk image support, intended for use with pre-extracted NTFS artifacts. ```bash cargo install usnjrnl-forensic ``` -------------------------------- ### Real-Time USN Journal Monitoring in Rust Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md Implement a JournalSource to poll live USN journals on Windows. This example shows how to initialize a JournalMonitor and process events like new records, journal wraps, and errors. ```rust use usnjrnl_forensic::monitor::{JournalMonitor, MonitorConfig, MonitorEvent}; // Your JournalSource implementation reads from FSCTL_READ_USN_JOURNAL let monitor = JournalMonitor::new(source, MonitorConfig::default())?; for event in monitor.poll_once() { match event { MonitorEvent::NewRecord(record) => { /* process */ }, MonitorEvent::JournalWrap { old_usn, new_usn } => { /* handle wrap */ }, MonitorEvent::Error(msg) => { /* log error */ }, } } ``` -------------------------------- ### Path Reconstruction Example Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md Illustrates the difference in path resolution before and after applying the Rewind algorithm for journal analysis. ```text Before Rewind: UNKNOWN\UNKNOWN\malware.exe After Rewind: .\Users\admin\AppData\Local\Temp\malware.exe ``` -------------------------------- ### CLI Usage Examples Source: https://context7.com/securityronin/usnjrnl-forensic/llms.txt Common command-line operations for processing disk images and artifacts, including triage, correlation, and export formats. ```bash # Rapid triage from E01 image with unallocated space carving usnjrnl-forensic --image evidence.E01 --carve-unallocated --report triage.html # Process pre-extracted artifacts with full path resolution usnjrnl-forensic -j $J -m $MFT --csv output.csv # QuadLink correlation with all four artifacts usnjrnl-forensic -j $J -m $MFT --mftmirr $MFTMirr --logfile $LogFile --sqlite analysis.db # Timeline export in multiple formats simultaneously usnjrnl-forensic --image evidence.E01 --csv timeline.csv --jsonl timeline.jsonl --body timeline.body # Detect timestomping with MFT correlation usnjrnl-forensic -j $J -m $MFT --detect-timestomping # Save extracted artifacts for later analysis usnjrnl-forensic -i evidence.E01 --output-dir ./extracted --report triage.html ``` -------------------------------- ### Install usnjrnl-forensic with E01/Raw Image Support Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md Install the usnjrnl-forensic binary with support for E01 and raw disk image formats using cargo. This version runs on Windows, macOS, and Linux without runtime dependencies. ```bash cargo install usnjrnl-forensic --features image ``` -------------------------------- ### Run usnjrnl-forensic with Example Artifacts Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md This command demonstrates running usnjrnl-forensic on a local evidence file, enabling unallocated space carving and specifying an output report file. It shows the typical output messages indicating the progress and results of the analysis. ```bash $ usnjrnl-forensic --image evidence.E01 --carve-unallocated --report triage.html [*] Opening disk image: evidence.E01 [+] 847,293 USN records parsed [+] 112,448 MFT entries parsed [+] 5,378 USN records recovered from $LogFile [+] 771 ghost records found in $LogFile (not present in $UsnJrnl) [+] Carved 1,247 USN records + 89 MFT entries from unallocated space [+] All paths fully resolved (0 UNKNOWN) [+] Triage report written to triage.html ``` -------------------------------- ### Add --report Flag Usage Example in Markdown Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/docs/plans/2026-03-07-rapid-triage-report-plan.md Illustrates how to use the --report flag to generate a triage report from an E01 image. This section is added to the README's usage examples. ```markdown #### Generate a triage report ```bash usnjrnl-forensic --image evidence.E01 --carve-unallocated --report triage.html ``` Produces a self-contained HTML file with a Story tab (question-driven forensic narrative) and an Explore tab (full timeline workbench). Opens in any browser — no server needed. ``` -------------------------------- ### Build usnjrnl-forensic from Source Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md Clone the repository and build the usnjrnl-forensic tool from source with release optimizations and E01/raw image support. ```bash git clone https://github.com/SecurityRonin/usnjrnl-forensic cd usnjrnl-forensic cargo build --release --features image ``` -------------------------------- ### Header Rendering Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/report/template.html Initializes the header rendering logic. ```javascript /* ── Render Header ────────────────────────────────────────────── */ function renderHeader() { var m = DATA. ``` -------------------------------- ### Initialize UI Components Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/report/template.html Calls functions to render the header, stat cards, story, sidebar, apply filters, and render the data table. Includes a timeout for resizing and drawing sparklines to ensure canvas sizing. ```javascript /* ── Initialize ───────────────────────────────────────────────── */ function init() { renderHeader(); renderStatCards(); renderStory(); renderSidebar(); applyFilters(); /* Delay sparkline render to ensure canvas is sized */ setTimeout(function() { if (state.activeTab === 'explore') { resizeSparkline(); drawSparkline(); } renderTable(); }, 50); } init(); ``` -------------------------------- ### MFT Mirror Tampering Detection Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md Example output indicating detected inconsistencies between the $MFT and its mirror ($MFTMirr), which could signify tampering with critical metadata. ```text [!] $MFTMirr INCONSISTENCY DETECTED: Entry 2 ($LogFile): 14 byte differences ``` -------------------------------- ### Application State Management Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/report/template.html Initializes the global state object for tracking UI filters, pagination, and selection. ```javascript /* ── State ────────────────────────────────────────────────────── */ var state = { activeTab: 'story', searchText: '', reasonFilters: [], sourceFilters: { allocated: true, carved: true, ghost: true }, timeZoom: null, filteredRecords: [], filteredIndices: null, page: 0, pageSize: 500, selectedRow: -1 }; ``` -------------------------------- ### Commit README and Cargo.toml Changes in Bash Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/docs/plans/2026-03-07-rapid-triage-report-plan.md Stage README.md, Cargo.toml, and Cargo.lock files, then commit with a message noting documentation updates and version bump. ```bash git add README.md Cargo.toml Cargo.lock git commit -m "docs: add --report to README, bump to v0.5.0" ``` -------------------------------- ### Run comparative forensic tools Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/docs/VALIDATION.md Execute additional forensic utilities to compare record counts and resolved paths. ```bash python3 usnjrnl_rewind.py -u mftecmd_usnjrnl.csv -m mftecmd_mft.csv output_dir/ ``` ```bash usn.py --csv -f '$J' -o usnpy.csv ``` ```bash ntfs_parser --usn '$MFT' '$J' dfir_ntfs.csv ``` ```bash usnrs-cli --mft '$MFT' '$J' > usnrs.txt ``` -------------------------------- ### Navigate to Explore View Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/report/template.html Sets the global state for filtering and navigation to the 'Explore' view, then updates UI elements like search input and checkboxes. ```javascript function viewInExplore(indices) { state.filteredIndices = new Set(indices); state.searchText = ''; state.reasonFilters = []; state.timeZoom = null; state.page = 0; state.selectedRow = -1; switchTab('explore'); var searchInput = document.getElementById('search-input'); if (searchInput) searchInput.value = ''; document.querySelectorAll('.reason-cb').forEach(function(cb) { cb.checked = false; }); document.querySelectorAll('.source-cb').forEach(function(cb) { cb.checked = true; }); state.sourceFilters = { allocated: true, carved: true, ghost: true }; applyFilters(); drawSparkline(); renderTable(); } ``` -------------------------------- ### Reconstruct File Paths with RewindEngine Source: https://context7.com/securityronin/usnjrnl-forensic/llms.txt Use RewindEngine to process USN journal entries in reverse chronological order for complete path reconstruction. Seed the engine with MFT data to establish the initial filesystem state. ```rust use usnjrnl_forensic::rewind::{RewindEngine, EntryKey, RecordSource, ResolvedRecord}; use usnjrnl_forensic::usn::parse_usn_journal; use usnjrnl_forensic::mft::MftData; // Parse MFT to seed the rewind engine with current filesystem state let mft_raw = std::fs::read("$MFT").expect("Failed to read MFT"); let mft_data = MftData::parse(&mft_raw).expect("Failed to parse MFT"); // Seed the rewind engine from MFT entries let mut engine = mft_data.seed_rewind(); println!("Seeded engine with {} MFT entries", engine.lookup_len()); // Parse USN journal records let usn_data = std::fs::read("$J").expect("Failed to read journal"); let records = parse_usn_journal(&usn_data).expect("Failed to parse journal"); // Run rewind algorithm to resolve all paths let resolved: Vec = engine.rewind(&records); // Every record now has a complete path for r in &resolved { println!("{} [{}] {} (source: {:?})", r.record.timestamp.format("%Y-%m-%d %H:%M:%S"), r.record.reason, r.full_path, r.source ); } // Check path resolution quality let unknown_count = resolved.iter() .filter(|r| r.parent_path.contains("UNKNOWN")) .count(); println!("Unresolved paths: {} / {} ({:.1}%)", unknown_count, resolved.len(), (unknown_count as f64 / resolved.len() as f64) * 100.0); // Manual path resolution for specific entries let path = engine.resolve_path(&EntryKey::new(12345, 3)); println!("Entry 12345 seq 3 path: {}", path); ``` -------------------------------- ### Render Sparkline and Handle Interactions Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/report/template.html Logic for bucketizing timestamps, drawing bars on a canvas, and managing mouse events for range selection. ```javascript h === 0) { sparklineBuckets = []; return; } var minTs = Math.min.apply(null, timestamps); var maxTs = Math.max.apply(null, timestamps); sparklineMinTs = minTs; sparklineMaxTs = maxTs; if (minTs === maxTs) { maxTs = minTs + 60000; } /* Bucket into N bins */ var numBuckets = Math.min(Math.max(Math.floor(w / 4), 50), 300); var bucketDuration = (maxTs - minTs) / numBuckets; sparklineBucketWidth = bucketDuration; var buckets = new Array(numBuckets).fill(0); timestamps.forEach(function(ts) { var idx = Math.floor((ts - minTs) / bucketDuration); if (idx >= numBuckets) idx = numBuckets - 1; if (idx < 0) idx = 0; buckets[idx]++; }); sparklineBuckets = buckets; var maxCount = Math.max.apply(null, buckets); if (maxCount === 0) maxCount = 1; var barW = w / numBuckets; var padding = 4; for (var i = 0; i < numBuckets; i++) { var barH = (buckets[i] / maxCount) * (h - padding * 2); if (buckets[i] > 0 && barH < 2) barH = 2; var x = i * barW; var y = h - padding - barH; ctx.fillStyle = '#58a6ff'; if (buckets[i] > maxCount * 0.8) ctx.fillStyle = '#f85149'; else if (buckets[i] > maxCount * 0.5) ctx.fillStyle = '#d29922'; ctx.fillRect(x + 0.5, y, barW - 1, barH); } /* Time labels */ ctx.fillStyle = '#7d8590'; ctx.font = '9px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace'; ctx.textAlign = 'left'; ctx.fillText(fmtTimestamp(new Date(minTs).toISOString()), 2, 10); ctx.textAlign = 'right'; ctx.fillText(fmtTimestamp(new Date(maxTs).toISOString()), w - 2, 10); } /* Sparkline brush selection */ var brushState = { dragging: false, startX: 0, currentX: 0 }; var sparkCanvas = document.getElementById('sparkline'); sparkCanvas.addEventListener('mousedown', function(e) { if (sparklineBuckets.length === 0) return; brushState.dragging = true; brushState.startX = e.clientX - this.getBoundingClientRect().left; brushState.currentX = brushState.startX; e.preventDefault(); }); sparkCanvas.addEventListener('mousemove', function(e) { if (!brushState.dragging) return; brushState.currentX = e.clientX - this.getBoundingClientRect().left; drawSparkline(); var ctx = this.getContext('2d'); var x1 = Math.min(brushState.startX, brushState.currentX); var x2 = Math.max(brushState.startX, brushState.currentX); if (x2 - x1 > 2) { ctx.fillStyle = 'rgba(88,166,255,0.15)'; ctx.fillRect(x1, 0, x2 - x1, this.height); ctx.strokeStyle = 'rgba(88,166,255,0.4)'; ctx.lineWidth = 1; ctx.strokeRect(x1 + 0.5, 0.5, x2 - x1 - 1, this.height - 1); } }); document.addEventListener('mouseup', function() { if (!brushState.dragging) return; brushState.dragging = false; if (sparklineBuckets.length === 0) return; var x1 = Math.min(brushState.startX, brushState.currentX); var x2 = Math.max(brushState.startX, brushState.currentX); var w = sparkCanvas.width; var range = sparklineMaxTs - sparklineMinTs; if (range <= 0) return; state.filteredIndices = null; if (x2 - x1 < 5) { /* Small click — zoom to bucket ± padding */ var bucketIdx = Math.floor(x1 / (w / sparklineBuckets.length)); if (bucketIdx < 0) bucketIdx = 0; if (bucketIdx >= sparklineBuckets.length) bucketIdx = sparklineBuckets.length - 1; var bucketStart = sparklineMinTs + bucketIdx * sparklineBucketWidth; var zoomPadding = sparklineBucketWidth * 5; setTimeZoom(bucketStart - zoomPadding, bucketStart + sparklineBucketWidth + zoomPadding); } else { /* Brush drag — precise range selection */ var startTs = sparklineMinTs + (x1 / w) * range; var endTs = sparklineMinTs + (x2 / w) * range; setTimeZoom(startTs, endTs); } }); document.getElementById('sparkline-reset').addEventListener('click', function() { state.filteredIndices = null; clearTimeZoom(); }); window.addEventListener('resize', function() { if (state.activeTab === 'explore') { resizeSparkline(); drawSparkline(); } }); /* ── Keyboard Shortcuts ──────────────────────────────────────── */ document.addEventListener('keydown', function(e) { if (state.activeTab !== 'explore') return; if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') return; if (e.key === 't' || e.key === 'T') { var fi = document.getElementById('time-from'); if (fi) { fi.focus(); e.preventDefault(); } } else if (e.key === 'Escape') { state.filteredIndices = null; clearTimeZoom(); e.preventDefault(); } else if (e.key === '[' && state.timeZoom) { var dur = state.timeZoom.end - state.timeZoom.start; var shift = dur * 0.25; setTimeZoom(state.timeZoom.start - shift, state.timeZoom.end - shift); e.preventDefault(); } else if (e.key === ']' && state.timeZoom) { var dur = state.timeZoom.end - state.timeZoom.start; var shift = dur * 0.25; setTimeZoom(state.timeZoom.start + shift, state.timeZoom.end + shift); e.preventDefault(); } else if ((e.key === '+' || e.key === '=') && state.timeZoom) { var center = (state.timeZoom.start + state.timeZoom.end) / 2; var halfDur = (state.timeZoom.end - state.timeZoom.start) / 4; setTimeZoom(center - halfDur, center + halfDur); e.preventDefault(); } else if (e.key === '-' && state.timeZoom) { va ``` -------------------------------- ### Run Triage Tests Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/docs/plans/2026-03-07-rapid-triage-report-plan.md Command to execute the library tests for the triage module. ```bash cargo test --lib triage::tests ``` -------------------------------- ### Export Timeline from Disk Images Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/README.md Generate timeline reports from E01 or raw disk images using CSV or SQLite output formats. ```bash usnjrnl-forensic --image evidence.E01 --csv timeline.csv usnjrnl-forensic --image evidence.E01 --carve-unallocated --sqlite analysis.db ``` ```bash usnjrnl-forensic -i evidence.E01 --output-dir ./extracted --sqlite analysis.db ``` ```bash usnjrnl-forensic --image disk.raw --csv output.csv ``` -------------------------------- ### Test Malware Query Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/docs/plans/2026-03-07-rapid-triage-report-plan.md Tests the `run_triage` function by creating a `TriageQuestion` for malware detection and verifying it matches an executable file in the system32 directory. ```rust #[test] fn test_malware_query_matches_exe_in_system32() { let questions = vec![TriageQuestion { ``` -------------------------------- ### Run usnrs-cli with MFT lookup Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/docs/VALIDATION.md Use the usnrs-cli tool to parse the USN journal, outputting to CSV and performing path resolution via MFT lookup. ```bash usnrs-cli --csv -o output.csv --mft '$MFT' '$UsnJrnl_$J' ``` -------------------------------- ### Test Report Generation and Export Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/docs/plans/2026-03-07-rapid-triage-report-plan.md Unit tests for verifying the report data construction and HTML export functionality using mock input data. ```rust fn make_test_input() -> (Vec, Vec, JournalClearingResult) { let record = UsnRecord { mft_entry: 100, mft_sequence: 1, parent_mft_entry: 5, parent_mft_sequence: 5, usn: 1000, timestamp: DateTime::from_timestamp(1700000000, 0).unwrap(), reason: UsnReason::FILE_CREATE, filename: "test.exe".to_string(), file_attributes: FileAttributes::ARCHIVE, source_info: 0, security_id: 0, major_version: 2, }; let resolved = vec![ResolvedRecord { record, full_path: ".\\Windows\\System32\\test.exe".to_string(), parent_path: ".\\Windows\\System32".to_string(), }]; let clearing = JournalClearingResult { clearing_detected: false, first_usn: Some(1000), timestamp_gaps: vec![], confidence: 0.0, }; (resolved, vec![], clearing) } #[test] fn test_build_report_data_basic() { let (resolved, ghosts, clearing) = make_test_input(); let input = ReportInput { image_name: "test.E01", resolved: &resolved, mft_data: None, timestomping: &[], secure_deletion: &[], ransomware: &[], journal_clearing: &clearing, ghost_records: &ghosts, carved_usn_count: 0, carved_mft_count: 0, carving_bytes_scanned: 0, carving_chunks: 0, carving_usn_dupes: 0, carving_mft_dupes: 0, }; let questions = crate::triage::queries::builtin_questions(); let data = build_report_data(&input, &questions); assert_eq!(data.meta.record_count, 1); assert_eq!(data.records.len(), 1); assert_eq!(data.records[0].filename, "test.exe"); assert_eq!(data.records[0].extension, "exe"); assert!(data.records[0].reasons.contains(&"FILE_CREATE".to_string())); } #[test] fn test_export_report_produces_html() { let (resolved, ghosts, clearing) = make_test_input(); let input = ReportInput { image_name: "test.E01", resolved: &resolved, mft_data: None, timestomping: &[], secure_deletion: &[], ransomware: &[], journal_clearing: &clearing, ghost_records: &ghosts, carved_usn_count: 0, carved_mft_count: 0, ``` -------------------------------- ### Render Explore Sidebar UI Source: https://github.com/securityronin/usnjrnl-forensic/blob/main/report/template.html Constructs the HTML for the sidebar in the 'Explore' view, including search input, time range controls, reason flags, and source filters. ```javascript function renderSidebar() { var html = ''; /* Search */ html += ''; /* Time Range */ html += ''; /* Reasons */ html += ''; /* Source */ html += ''; /* Carving stats */ if (DATA.carving_stats) { var cs = DATA.carving_stats; html += '