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 15:44] titannetsoc:irt:playbooks:windows_disk [2026/06/18 16:51] (current) – [Prefetch on Linux] titannet
Line 106: Line 106:
 <code bash> <code bash>
 https://github.com/sethenoka/Install_EZTools.git https://github.com/sethenoka/Install_EZTools.git
 +
 +
 +
 +
 +</code>
 +
 +==== Prefetch on Linux ====
 +
 +<code bash>
  
 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Line 111: Line 120:
 rustup toolchain install 1.85.0 rustup toolchain install 1.85.0
 rustup default 1.85.0 rustup default 1.85.0
 +</code>
  
 +<code rust>
 +use chrono::{DateTime, Utc};
 +use std::{env, fs};
  
-</code>+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;
  
-<code rust> +    DateTime::from_timestamp(secs, nanos) 
-use anyhow::Result; +        .expect("invalid timestamp"
-use prefetch_core::parse;+}
  
-fn main() -> Result<()> +fn main() { 
-    let path = std::env::args()+    let path = env::args()
         .nth(1)         .nth(1)
-        .expect("Usagepf-analyzer <file.pf>");+        .expect("usagepf_info <file.pf>");
  
-    let bytes std::fs::read(path)?;+    let raw = fs::read(&path).expect("read failed");
  
-    let pf = parse(&bytes)?;+    let info prefetch_core::parse(&raw) 
 +        .unwrap_or_else(|e| panic!("parse error: {:?}", e));
  
-    println!("Executable : {}", pf.executable_name); +    println!("Executable : {}", info.executable); 
-    println!("Run count  : {}", pf.run_count);+    println!("Run count  : {}", info.run_count);
  
-    println!("Last run times:"); +    println!("\nLast run times:"); 
-    for ts in &pf.last_run_times { +    for ft in &info.last_run_times { 
-        println!("  {:?}", ts);+        println!( 
 +             {}  ({})", 
 +            filetime_to_datetime(*ft).to_rfc3339(), 
 +            ft 
 +        );
     }     }
  
-    Ok(())+    println!("\nVolumes:"); 
 +    for v in &info.volumes { 
 +        println!( Device: {}", v.device_path)
 + 
 +        let created = filetime_to_datetime(v.creation_time); 
 +        println!("  Created: {}", created.to_rfc3339()); 
 + 
 +        println!("  Serial : {:08X}", v.serial); 
 +        println!()
 +    }
 } }
  
 </code> </code>
 +
  
 <code rust> <code rust>
-use prefetch_core::parse;+#![allow(clippy::unwrap_used, clippy::expect_used)] 
 +use std::path::PathBuf; 
 +use std::fs; 
 +use std::path::Path; 
 +use chrono::{DateTime, Utc};
  
-fn main() -> anyhow::Result<()>+/// based on  https://docs.rs/crate/prefetch-core/0.1.0/source/examples/pf_dump.rs
-    let dir = r"C:\Windows\Prefetch";+
  
-    for entry in std::fs::read_dir(dir)? { 
-        let path = entry?.path(); 
  
-        if path.extension().and_then(|x| x.to_str()) !Some("pf"{ +fn filetime_to_datetime(ft: i64) -> DateTime<Utc>
-            continue;+    // 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
 +}
  
-        let bytes = std::fs::read(&path)?;+fn main() { 
 +    let mut root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 
 +    root.pop(); 
 +    let out_dir std::env::args() 
 +        .nth(1) 
 +        .unwrap_or_else(|| "/tmp/pf_scca".to_string()); 
 +    std::fs::create_dir_all(&out_dir).expect("mkdir out_dir");
  
-        match parse(&bytes) { +    let dir = Path::new("/media/data/IR/case260618/prefetch")
-            Ok(pf) => { +     
-                println!( +     let files: Vec<String> = fs::read_dir(dir) 
-                    "{} | runs={} | last={:?}", +        .unwrap() 
-                    pf.executable_name+        .filter_map(Result::ok) 
-                    pf.run_count+        .map(|e| e.path()) 
-                    pf.last_run_times.first(+        .filter(|p| 
-                ); +            p.extension() 
-            } +                .and_then(|ext| ext.to_str()) 
-            Err(e) => { +                .map(|ext| ext == "pf") 
-                eprintln!("{} -> {}", path.display(), e); +                .unwrap_or(false) 
-            } +        }) 
-        }+        .map(|p| p.to_string_lossy().to_string()) 
 +        .collect(); 
 + 
 +//.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 { 
 +        let p PathBuf::from(&name); 
 + 
 +        let raw = std::fs::read(&p).expect("read fixture"); 
 +        let scca = prefetch_core::decompress(&raw).expect("decompress"); 
 + 
 +        std::fs::write(PathBuf::from(&out_dir).join(format!("{name}.scca")), &scca) 
 +            .expect("write scca"); 
 + 
 +        let info = prefetch_core::parse(&raw).expect("parse"); 
 + 
 +        let filenames = info 
 +            .filenames 
 +            .iter() 
 +            .map(|f| escape(f)) 
 +            .collect::<Vec<_>>() 
 +            .join(";"); 
 + 
 +        let volumes info 
 +            .volumes 
 +            .iter() 
 +            .map(|v| { 
 +                format!( 
 +                    "{{serial:{},device_path:{},creation_time:{}}}", 
 +                    v.serial
 +                    escape(&v.device_path)
 +                    filetime_to_datetime(v.creation_time
 +                ) 
 +            }) 
 +            .collect::<Vec<_>>() 
 +            .join(";"); 
 + 
 +        let last_runs info 
 +            .last_run_times 
 +            .iter() 
 +            .map(|t| filetime_to_datetime(*t).to_string()) 
 +            .collect::<Vec<_>>() 
 +            .join(";"); 
 + 
 +        println!( 
 +            "{},{},{},{},{},{},{},{},{}", 
 +            csv_escape(&name), 
 +            scca.len(), 
 +            info.version, 
 +            csv_escape(&info.executable), 
 +            info.run_count, 
 +            csv_escape(&last_runs), 
 +            info.filenames.len(), 
 +            csv_escape(&filenames), 
 +            csv_escape(&volumes) 
 +        );
     }     }
 +}
  
-    Ok(())+fn escape(s: &str-> String { 
 +    s.replace('\\', "\\\\")
 } }