1use crate::{Attribute, Claim, DeviceKey, HistorySource, Provenance, SourceKind, Value};
22
23const CONNECT_EVENT_ID: u32 = 2003;
25const DISCONNECT_EVENT_ID: u32 = 2102;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct DriverFrameworkEvent {
31 pub timestamp: String,
33 pub event_id: u32,
35 pub instance_id: String,
38}
39
40pub fn driver_framework_events<I>(records: I) -> Vec<DriverFrameworkEvent>
45where
46 I: IntoIterator<Item = serde_json::Value>,
47{
48 records
49 .into_iter()
50 .filter_map(|r| driver_framework_event(&r))
51 .collect()
52}
53
54fn driver_framework_event(record: &serde_json::Value) -> Option<DriverFrameworkEvent> {
56 let system = record.pointer("/Event/System")?;
57 if system
58 .pointer("/Provider/#attributes/Name")
59 .and_then(serde_json::Value::as_str)
60 != Some("Microsoft-Windows-DriverFrameworks-UserMode")
61 {
62 return None;
63 }
64 let event_id = event_id(system)?;
65 if event_id != CONNECT_EVENT_ID && event_id != DISCONNECT_EVENT_ID {
66 return None;
67 }
68 let instance_id = instance_id(record)?;
69 if !is_usb_instance(&instance_id) {
70 return None;
71 }
72 let timestamp = system
73 .pointer("/TimeCreated/#attributes/SystemTime")?
74 .as_str()?
75 .to_string();
76 Some(DriverFrameworkEvent {
77 timestamp,
78 event_id,
79 instance_id,
80 })
81}
82
83fn instance_id(record: &serde_json::Value) -> Option<String> {
88 let user_data = record.pointer("/Event/UserData")?.as_object()?;
89 for child in user_data.values() {
90 if let Some(v) = child
91 .pointer("/#attributes/instance")
92 .and_then(serde_json::Value::as_str)
93 {
94 return Some(v.to_string());
95 }
96 if let Some(v) = child.get("InstanceId").and_then(serde_json::Value::as_str) {
97 return Some(v.to_string());
98 }
99 }
100 None
101}
102
103fn event_id(system: &serde_json::Value) -> Option<u32> {
105 let raw = system.get("EventID")?;
106 let n = raw
107 .as_u64()
108 .or_else(|| raw.get("#text").and_then(serde_json::Value::as_u64))?;
109 u32::try_from(n).ok()
110}
111
112fn is_usb_instance(id: &str) -> bool {
118 (id.starts_with("USB\\") || id.starts_with("USBSTOR\\")) && !id.starts_with("USB\\ROOT_HUB")
119}
120
121pub struct DriverFrameworkSource<'a> {
123 events: &'a [DriverFrameworkEvent],
124}
125
126impl<'a> DriverFrameworkSource<'a> {
127 #[must_use]
129 pub fn new(events: &'a [DriverFrameworkEvent]) -> Self {
130 Self { events }
131 }
132}
133
134impl HistorySource for DriverFrameworkSource<'_> {
135 fn claims(&self) -> Vec<Claim> {
136 let mut out = Vec::new();
137 for event in self.events {
138 let start = event.instance_id.rfind('\\').map_or(0, |i| i + 1);
141 let serial = &event.instance_id[start..];
142 if serial.is_empty() {
143 continue;
144 }
145 let Ok(when) = event.timestamp.parse::<jiff::Timestamp>() else {
146 continue;
147 };
148 let attribute = if event.event_id == DISCONNECT_EVENT_ID {
150 Attribute::LastRemoved
151 } else {
152 Attribute::LastConnected
153 };
154 out.push(Claim {
155 device: DeviceKey(serial.to_string()),
156 attribute,
157 value: Value::Timestamp(when.as_second()),
158 provenance: Provenance {
159 source: SourceKind::DriverFramework,
160 locator: format!(
161 "Microsoft-Windows-DriverFrameworks-UserMode/Operational#{} {}",
162 event.event_id, event.instance_id
163 ),
164 },
165 });
166 }
167 out
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use serde_json::json;
175
176 fn record(
179 provider: &str,
180 event_id: u32,
181 root: &str,
182 instance: &str,
183 when: &str,
184 ) -> serde_json::Value {
185 json!({
186 "Event": {
187 "System": {
188 "Provider": { "#attributes": { "Name": provider } },
189 "EventID": event_id,
190 "TimeCreated": { "#attributes": { "SystemTime": when } }
191 },
192 "UserData": {
193 root: {
194 "#attributes": {
195 "instance": instance,
196 "lifetime": "{6c1e8fd0-0000-0000-0000-000000000000}"
197 }
198 }
199 }
200 }
201 })
202 }
203
204 #[test]
205 fn extracts_a_2003_arrival_event() {
206 let rec = record(
207 "Microsoft-Windows-DriverFrameworks-UserMode",
208 2003,
209 "UMDFHostDeviceArrivalBegin",
210 "USB\\VID_0781&PID_5597\\4C530000261130109435",
211 "2020-09-19T04:36:42.874132Z",
212 );
213 let events = driver_framework_events([rec]);
214 assert_eq!(events.len(), 1);
215 assert_eq!(events[0].event_id, 2003);
216 assert_eq!(
217 events[0].instance_id,
218 "USB\\VID_0781&PID_5597\\4C530000261130109435"
219 );
220 assert_eq!(events[0].timestamp, "2020-09-19T04:36:42.874132Z");
221 }
222
223 #[test]
224 fn extracts_a_2102_removal_event() {
225 let rec = record(
226 "Microsoft-Windows-DriverFrameworks-UserMode",
227 2102,
228 "UMDFHostDeviceRequest",
229 "USB\\VID_0781&PID_5597\\4C530000261130109435",
230 "2020-09-19T05:10:00Z",
231 );
232 let events = driver_framework_events([rec]);
233 assert_eq!(events.len(), 1);
234 assert_eq!(events[0].event_id, 2102);
235 }
236
237 #[test]
238 fn reads_the_instanceid_element_form() {
239 let rec = json!({
241 "Event": {
242 "System": {
243 "Provider": { "#attributes": { "Name": "Microsoft-Windows-DriverFrameworks-UserMode" } },
244 "EventID": 2003,
245 "TimeCreated": { "#attributes": { "SystemTime": "2020-09-19T04:36:42Z" } }
246 },
247 "UserData": {
248 "UMDFHostDeviceArrivalBegin": {
249 "InstanceId": "USBSTOR\\Disk&Ven_SanDisk&Prod_Cruzer&Rev_1.00\\4C530000261130109435&0",
250 "LifetimeId": "{guid}"
251 }
252 }
253 }
254 });
255 let events = driver_framework_events([rec]);
256 assert_eq!(events.len(), 1);
257 assert_eq!(
258 events[0].instance_id,
259 "USBSTOR\\Disk&Ven_SanDisk&Prod_Cruzer&Rev_1.00\\4C530000261130109435&0"
260 );
261 }
262
263 #[test]
264 fn a_userdata_child_with_no_instance_field_yields_no_event() {
265 let rec = json!({
268 "Event": {
269 "System": {
270 "Provider": { "#attributes": { "Name": "Microsoft-Windows-DriverFrameworks-UserMode" } },
271 "EventID": 2003,
272 "TimeCreated": { "#attributes": { "SystemTime": "2020-09-19T04:36:42Z" } }
273 },
274 "UserData": {
275 "UMDFHostDeviceArrivalBegin": { "LifetimeId": "{guid}" }
276 }
277 }
278 });
279 assert!(driver_framework_events([rec]).is_empty());
280 }
281
282 #[test]
283 fn non_driver_framework_provider_is_ignored() {
284 let rec = record(
285 "Microsoft-Windows-Kernel-PnP",
286 2003,
287 "UMDFHostDeviceArrivalBegin",
288 "USB\\VID_0781&PID_5597\\4C530000261130109435",
289 "2020-09-19T04:36:42Z",
290 );
291 assert!(driver_framework_events([rec]).is_empty());
292 }
293
294 #[test]
295 fn an_intermediate_power_event_id_is_ignored() {
296 let rec = record(
298 "Microsoft-Windows-DriverFrameworks-UserMode",
299 2100,
300 "UMDFHostDeviceRequest",
301 "USB\\VID_0781&PID_5597\\4C530000261130109435",
302 "2020-09-19T04:36:42Z",
303 );
304 assert!(driver_framework_events([rec]).is_empty());
305 }
306
307 #[test]
308 fn an_internal_non_usb_device_is_ignored() {
309 let rec = record(
310 "Microsoft-Windows-DriverFrameworks-UserMode",
311 2003,
312 "UMDFHostDeviceArrivalBegin",
313 "ACPI\\PNP0A03\\0",
314 "2020-09-19T04:36:42Z",
315 );
316 assert!(driver_framework_events([rec]).is_empty());
317 }
318
319 #[test]
320 fn a_wpdbusenum_symbolic_link_instance_is_skipped() {
321 let rec = record(
324 "Microsoft-Windows-DriverFrameworks-UserMode",
325 2100,
326 "UMDFHostDeviceRequest",
327 "SWD\\WPDBUSENUM\\_??_USBSTOR#DISK&VEN_SANDISK#4C53...&0#{guid}",
328 "2020-09-19T04:36:42Z",
329 );
330 assert!(driver_framework_events([rec]).is_empty());
331 }
332
333 #[test]
334 fn a_usb_root_hub_controller_is_ignored() {
335 let rec = record(
336 "Microsoft-Windows-DriverFrameworks-UserMode",
337 2003,
338 "UMDFHostDeviceArrivalBegin",
339 "USB\\ROOT_HUB30\\5&d01e486&0&0",
340 "2020-09-19T04:36:42Z",
341 );
342 assert!(driver_framework_events([rec]).is_empty());
343 }
344
345 #[test]
346 fn an_event_id_serialized_as_an_object_is_read() {
347 let mut rec = record(
348 "Microsoft-Windows-DriverFrameworks-UserMode",
349 0,
350 "UMDFHostDeviceArrivalBegin",
351 "USB\\VID_0781&PID_5597\\4C530000261130109435",
352 "2020-09-19T04:36:42Z",
353 );
354 rec["Event"]["System"]["EventID"] =
355 json!({ "#text": 2003, "#attributes": { "Qualifiers": "0" } });
356 let events = driver_framework_events([rec]);
357 assert_eq!(events.len(), 1);
358 assert_eq!(events[0].event_id, 2003);
359 }
360
361 fn event(event_id: u32, instance: &str, when: &str) -> DriverFrameworkEvent {
362 DriverFrameworkEvent {
363 timestamp: when.to_string(),
364 event_id,
365 instance_id: instance.to_string(),
366 }
367 }
368
369 #[test]
370 fn arrival_emits_last_connected_keyed_by_serial() {
371 let ev = event(
372 2003,
373 "USB\\VID_0781&PID_5597\\4C530000261130109435",
374 "2020-09-19T04:36:42.874132Z",
375 );
376 let claims = DriverFrameworkSource::new(std::slice::from_ref(&ev)).claims();
377 assert_eq!(claims.len(), 1);
378 assert_eq!(
379 claims[0].device,
380 DeviceKey("4C530000261130109435".to_string())
381 );
382 assert_eq!(claims[0].attribute, Attribute::LastConnected);
383 assert_eq!(claims[0].value, Value::Timestamp(1_600_490_202));
384 assert_eq!(claims[0].provenance.source, SourceKind::DriverFramework);
385 assert!(claims[0]
386 .provenance
387 .locator
388 .contains("DriverFrameworks-UserMode/Operational#2003"));
389 }
390
391 #[test]
392 fn removal_emits_last_removed() {
393 let ev = event(
394 2102,
395 "USB\\VID_0781&PID_5597\\4C530000261130109435",
396 "2020-09-19T05:10:00Z",
397 );
398 let claims = DriverFrameworkSource::new(std::slice::from_ref(&ev)).claims();
399 assert_eq!(claims.len(), 1);
400 assert_eq!(claims[0].attribute, Attribute::LastRemoved);
401 }
402
403 #[test]
404 fn a_usbstor_instance_keys_by_its_own_last_component() {
405 let ev = event(
406 2003,
407 "USBSTOR\\Disk&Ven_SanDisk&Prod_Cruzer&Rev_1.00\\4C530000261130109435&0",
408 "2020-09-19T04:36:42Z",
409 );
410 let claims = DriverFrameworkSource::new(std::slice::from_ref(&ev)).claims();
411 assert_eq!(
412 claims[0].device,
413 DeviceKey("4C530000261130109435&0".to_string())
414 );
415 }
416
417 #[test]
418 fn a_malformed_timestamp_yields_no_claim() {
419 let ev = event(2003, "USB\\VID_0781&PID_5597\\SERIAL", "not-a-timestamp");
420 assert!(DriverFrameworkSource::new(std::slice::from_ref(&ev))
421 .claims()
422 .is_empty());
423 }
424
425 #[test]
426 fn an_instance_id_with_no_serial_component_yields_no_claim() {
427 let ev = event(2003, "USB\\", "2020-09-19T04:36:42Z");
428 assert!(DriverFrameworkSource::new(std::slice::from_ref(&ev))
429 .claims()
430 .is_empty());
431 }
432
433 #[test]
434 fn source_kind_lives_in_the_event_log_container() {
435 assert_eq!(
436 SourceKind::DriverFramework.container(),
437 crate::ArtifactContainer::EventLog
438 );
439 assert!(!SourceKind::DriverFramework.clock_is_local());
440 }
441}