meta data for this page
  •  

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
soc:irt:playbooks:windows_disk [2026/06/18 16:25] titannetsoc:irt:playbooks:windows_disk [2026/06/18 16:51] (current) – [Prefetch on Linux] titannet
Line 180: Line 180:
 use std::fs; use std::fs;
 use std::path::Path; use std::path::Path;
 +use chrono::{DateTime, Utc};
  
 /// based on  https://docs.rs/crate/prefetch-core/0.1.0/source/examples/pf_dump.rs /// based on  https://docs.rs/crate/prefetch-core/0.1.0/source/examples/pf_dump.rs
 +
 +
 +fn filetime_to_datetime(ft: i64) -> DateTime<Utc> {
 +    // FILETIME epoch (1601-01-01) -> Unix epoch (1970-01-01)
 +    const EPOCH_DIFF_100NS: i64 = 116_444_736_000_000_000;
 + 
 +    let unix_100ns = ft - EPOCH_DIFF_100NS;
 +    let secs = unix_100ns / 10_000_000;
 +    let nanos = ((unix_100ns % 10_000_000) * 100) as u32;
 + 
 +    DateTime::from_timestamp(secs, nanos)
 +        .expect("invalid timestamp")
 +}
 +
 +fn csv_escape(s: &str) -> String {
 +    let mut out = String::new();
 +    out.push('"');
 +    for c in s.chars() {
 +        match c {
 +            '"' => out.push_str("\"\""),
 +            _ => out.push(c),
 +        }
 +    }
 +    out.push('"');
 +    out
 +}
  
 fn main() { fn main() {
Line 207: Line 234:
  
 //.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pf")) //.filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pf"))
 +    println!( 
 +        "file,scca_len,version,executable,run_count,last_run_times,filenames_count,filenames,volumes" 
 +    );
     for name in files {     for name in files {
-    +        let p = PathBuf::from(&name); 
-        let p = root.join("tests/data").join(&name);+
         let raw = std::fs::read(&p).expect("read fixture");         let raw = std::fs::read(&p).expect("read fixture");
         let scca = prefetch_core::decompress(&raw).expect("decompress");         let scca = prefetch_core::decompress(&raw).expect("decompress");
-        // Write the decompressed payload so the harness can diff it against its +
-        // own (dissect.util) decompression byte-for-byte.+
         std::fs::write(PathBuf::from(&out_dir).join(format!("{name}.scca")), &scca)         std::fs::write(PathBuf::from(&out_dir).join(format!("{name}.scca")), &scca)
             .expect("write scca");             .expect("write scca");
  
         let info = prefetch_core::parse(&raw).expect("parse");         let info = prefetch_core::parse(&raw).expect("parse");
-        let files: Vec<String> = info.filenames.iter().map(|f| escape(f)).collect(); + 
-        let vols: Vec<String> = info+        let filenames = info 
 +            .filenames 
 +            .iter() 
 +            .map(|f| escape(f)) 
 +            .collect::<Vec<_>>() 
 +            .join(";"); 
 + 
 +        let volumes = info
             .volumes             .volumes
             .iter()             .iter()
             .map(|v| {             .map(|v| {
                 format!(                 format!(
-                    "{{\"serial\":{},\"device_path\":\"{}\",\"creation_time\":{}}}",+                    "{{serial:{},device_path:{},creation_time:{}}}",
                     v.serial,                     v.serial,
                     escape(&v.device_path),                     escape(&v.device_path),
-                    v.creation_time+                    filetime_to_datetime(v.creation_time)
                 )                 )
             })             })
-            .collect();+            .collect::<Vec<_>>(
 +            .join(";"); 
 + 
 +        let last_runs = info 
 +            .last_run_times 
 +            .iter() 
 +            .map(|t| filetime_to_datetime(*t).to_string()) 
 +            .collect::<Vec<_>>() 
 +            .join(";"); 
         println!(         println!(
-            "{{\"file\":\"{}\",\"scca_len\":{},\"version\":{},\"executable\":\"{}\",\"run_count\":{},\"last_run_times\":{:?},\"filenames_count\":{},\"filenames\":[{}],\"volumes\":[{}]}}", +            "{},{},{},{},{},{},{},{},{}", 
-            name,+            csv_escape(&name),
             scca.len(),             scca.len(),
             info.version,             info.version,
-            escape(&info.executable),+            csv_escape(&info.executable),
             info.run_count,             info.run_count,
-            info.last_run_times,+            csv_escape(&last_runs),
             info.filenames.len(),             info.filenames.len(),
-            files.iter().map(|f| format!("\"{f}\"")).collect::<Vec<_>>().join(","), +            csv_escape(&filenames), 
-            vols.join(",")+            csv_escape(&volumes)
         );         );
     }     }