curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
rustup toolchain install 1.85.0
rustup default 1.85.0
use chrono::{DateTime, Utc};
use std::{env, fs};
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 main() {
let path = env::args()
.nth(1)
.expect("usage: pf_info <file.pf>");
let raw = fs::read(&path).expect("read failed");
let info = prefetch_core::parse(&raw)
.unwrap_or_else(|e| panic!("parse error: {:?}", e));
println!("Executable : {}", info.executable);
println!("Run count : {}", info.run_count);
println!("\nLast run times:");
for ft in &info.last_run_times {
println!(
" {} ({})",
filetime_to_datetime(*ft).to_rfc3339(),
ft
);
}
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!();
}
}
#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::path::PathBuf;
use std::fs;
use std::path::Path;
use chrono::{DateTime, Utc};
/// 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() {
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");
let dir = Path::new("/media/data/IR/case260618/prefetch");
let files: Vec<String> = fs::read_dir(dir)
.unwrap()
.filter_map(Result::ok)
.map(|e| e.path())
.filter(|p| {
p.extension()
.and_then(|ext| ext.to_str())
.map(|ext| ext == "pf")
.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)
);
}
}
fn escape(s: &str) -> String {
s.replace('\\', "\\\\")
}
fsstat -o <offset> $E
istat -o <offset> $E 5 # root node
istat -o <offset> $E <inode from fsstat>
fls -o <offset> -m C: -r $E > data/bodyfile
mactime -b data/bodyfile -d -z UTC yyyy-mm-ddThh:mm:ss > output/disk_timeline.csv
mactime -b data/bodyfile -d -z UTC yyyy-mm-ddThh:mm:ss..yyyy-mm-dd > output/disk_timeline.csv
# dissect
target-query -f hostname,domain,version,ips,install_date,timezone $E
# much more useful for queries on multiple disks at once
target-query -j -f services $E | jq -r '.name'
# JSON output → jq
target-query --list | grep -iE 'userassist|shimcache|amcache|services|powershell_history|browser.history'