169 lines
6.2 KiB
Rust
169 lines
6.2 KiB
Rust
use crate::detections::{configs::CONFIG, detection::EvtxRecordInfo};
|
|
use prettytable::{Cell, Row, Table};
|
|
|
|
use super::statistics::EventStatistics;
|
|
use hashbrown::HashMap;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Timeline {
|
|
pub stats: EventStatistics,
|
|
}
|
|
|
|
impl Default for Timeline {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl Timeline {
|
|
pub fn new() -> Timeline {
|
|
let totalcnt = 0;
|
|
let filepath = String::default();
|
|
let starttm = "".to_string();
|
|
let endtm = "".to_string();
|
|
let statslst = HashMap::new();
|
|
let statsloginlst = HashMap::new();
|
|
|
|
let statistic =
|
|
EventStatistics::new(totalcnt, filepath, starttm, endtm, statslst, statsloginlst);
|
|
Timeline { stats: statistic }
|
|
}
|
|
|
|
pub fn start(&mut self, records: &[EvtxRecordInfo]) {
|
|
self.stats.evt_stats_start(records);
|
|
self.stats.logon_stats_start(records);
|
|
}
|
|
|
|
pub fn tm_stats_dsp_msg(&mut self) {
|
|
let statics_flag = CONFIG.read().unwrap().args.is_present("statistics");
|
|
if !statics_flag {
|
|
return;
|
|
}
|
|
// 出力メッセージ作成
|
|
//println!("map -> {:#?}", evtstat_map);
|
|
let mut sammsges: Vec<String> = Vec::new();
|
|
sammsges.push("---------------------------------------".to_string());
|
|
sammsges.push(format!("Evtx File Path: {}", self.stats.filepath));
|
|
sammsges.push(format!("Total Event Records: {}\n", self.stats.total));
|
|
sammsges.push(format!("First Timestamp: {}", self.stats.start_time));
|
|
sammsges.push(format!("Last Timestamp: {}\n", self.stats.end_time));
|
|
sammsges.push("Count (Percent)\tID\tEvent\t".to_string());
|
|
sammsges.push("--------------- ------- ---------------".to_string());
|
|
|
|
// 集計件数でソート
|
|
let mut mapsorted: Vec<_> = self.stats.stats_list.iter().collect();
|
|
mapsorted.sort_by(|x, y| y.1.cmp(x.1));
|
|
|
|
// イベントID毎の出力メッセージ生成
|
|
let stats_msges: Vec<String> = self.tm_stats_set_msg(mapsorted);
|
|
|
|
for msgprint in sammsges.iter() {
|
|
println!("{}", msgprint);
|
|
}
|
|
for msgprint in stats_msges.iter() {
|
|
println!("{}", msgprint);
|
|
}
|
|
}
|
|
|
|
pub fn tm_logon_stats_dsp_msg(&mut self) {
|
|
let logon_summary_flag = CONFIG.read().unwrap().args.is_present("logon-summary");
|
|
if !logon_summary_flag {
|
|
return;
|
|
}
|
|
// 出力メッセージ作成
|
|
let mut sammsges: Vec<String> = Vec::new();
|
|
sammsges.push("---------------------------------------".to_string());
|
|
sammsges.push(format!("Evtx File Path: {}", self.stats.filepath));
|
|
sammsges.push(format!("Total Event Records: {}\n", self.stats.total));
|
|
sammsges.push(format!("First Timestamp: {}", self.stats.start_time));
|
|
sammsges.push(format!("Last Timestamp: {}\n", self.stats.end_time));
|
|
sammsges.push("---------------------------------------".to_string());
|
|
for msgprint in sammsges.iter() {
|
|
println!("{}", msgprint);
|
|
}
|
|
|
|
self.tm_loginstats_tb_set_msg();
|
|
}
|
|
|
|
// イベントID毎の出力メッセージ生成
|
|
fn tm_stats_set_msg(&self, mapsorted: Vec<(&std::string::String, &usize)>) -> Vec<String> {
|
|
let mut msges: Vec<String> = Vec::new();
|
|
|
|
for (event_id, event_cnt) in mapsorted.iter() {
|
|
// 件数の割合を算出
|
|
let rate: f32 = **event_cnt as f32 / self.stats.total as f32;
|
|
|
|
// イベント情報取得(eventtitleなど)
|
|
let conf = CONFIG
|
|
.read()
|
|
.unwrap()
|
|
.event_timeline_config
|
|
.get_event_id(*event_id)
|
|
.is_some();
|
|
// statistics_event_info.txtに登録あるものは情報設定
|
|
if conf {
|
|
// 出力メッセージ1行作成
|
|
msges.push(format!(
|
|
"{0} ({1:.1}%)\t{2}\t{3}",
|
|
event_cnt,
|
|
(rate * 1000.0).round() / 10.0,
|
|
event_id,
|
|
&CONFIG
|
|
.read()
|
|
.unwrap()
|
|
.event_timeline_config
|
|
.get_event_id(*event_id)
|
|
.unwrap()
|
|
.evttitle,
|
|
));
|
|
} else {
|
|
// 出力メッセージ1行作成
|
|
msges.push(format!(
|
|
"{0} ({1:.1}%)\t{2}\t{3}",
|
|
event_cnt,
|
|
(rate * 1000.0).round() / 10.0,
|
|
event_id,
|
|
"Unknown",
|
|
));
|
|
}
|
|
}
|
|
|
|
msges.push("---------------------------------------".to_string());
|
|
msges
|
|
}
|
|
// ユーザ毎のログイン統計情報出力メッセージ生成
|
|
fn tm_loginstats_tb_set_msg(&self) {
|
|
println!("Logon Summary");
|
|
if self.stats.stats_login_list.is_empty() {
|
|
let mut loginmsges: Vec<String> = Vec::new();
|
|
loginmsges.push("-----------------------------------------".to_string());
|
|
loginmsges.push("| No logon events were detected. |".to_string());
|
|
loginmsges.push("-----------------------------------------\n".to_string());
|
|
for msgprint in loginmsges.iter() {
|
|
println!("{}", msgprint);
|
|
}
|
|
} else {
|
|
let mut logins_stats_tb = Table::new();
|
|
logins_stats_tb.set_titles(row!["User", "Failed", "Successful"]);
|
|
// 集計件数でソート
|
|
let mut mapsorted: Vec<_> = self.stats.stats_login_list.iter().collect();
|
|
mapsorted.sort_by(|x, y| x.0.cmp(y.0));
|
|
|
|
for (key, values) in &mapsorted {
|
|
let mut username: String = key.to_string();
|
|
//key.to_string().retain(|c| c != '\"');
|
|
//key.to_string().pop();
|
|
username.pop();
|
|
username.remove(0);
|
|
logins_stats_tb.add_row(Row::new(vec![
|
|
Cell::new(&username),
|
|
Cell::new(&values[1].to_string()),
|
|
Cell::new(&values[0].to_string()),
|
|
]));
|
|
}
|
|
logins_stats_tb.printstd();
|
|
println!();
|
|
}
|
|
}
|
|
}
|