Files
hayabusa/src/main.rs
garigariganzy 76103d31f3 Feature/event stats#105 (#137)
Event集計機能実装

Co-authored-by: HajimeTakai <takai.wa.hajime@gmail.com>
2021-09-20 23:53:45 +09:00

188 lines
5.8 KiB
Rust

extern crate serde;
extern crate serde_derive;
use evtx::{EvtxParser, ParserSettings};
use std::{
fs::{self, File},
path::PathBuf,
vec,
};
use yamato_event_analyzer::detections::detection;
use yamato_event_analyzer::detections::detection::EvtxRecordInfo;
use yamato_event_analyzer::detections::print::AlertMessage;
use yamato_event_analyzer::omikuji::Omikuji;
use yamato_event_analyzer::{afterfact::after_fact, detections::utils};
use yamato_event_analyzer::{detections::configs, timeline::timeline::Timeline};
// 一度にtimelineやdetectionを実行する行数
const MAX_DETECT_RECORDS: usize = 40000;
fn main() {
if let Some(filepath) = configs::CONFIG.read().unwrap().args.value_of("filepath") {
analysis_files(vec![PathBuf::from(filepath)]);
} else if let Some(directory) = configs::CONFIG.read().unwrap().args.value_of("directory") {
let evtx_files = collect_evtxfiles(&directory);
analysis_files(evtx_files);
} else if configs::CONFIG.read().unwrap().args.is_present("credits") {
print_credits();
}
}
fn collect_evtxfiles(dirpath: &str) -> Vec<PathBuf> {
let entries = fs::read_dir(dirpath);
if entries.is_err() {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
AlertMessage::alert(&mut stdout, format!("{}", entries.unwrap_err())).ok();
return vec![];
}
let mut ret = vec![];
for e in entries.unwrap() {
if e.is_err() {
continue;
}
let path = e.unwrap().path();
if path.is_dir() {
path.to_str().and_then(|path_str| {
let subdir_ret = collect_evtxfiles(path_str);
ret.extend(subdir_ret);
return Option::Some(());
});
} else {
let path_str = path.to_str().unwrap_or("");
if path_str.ends_with(".evtx") {
ret.push(path);
}
}
}
return ret;
}
fn print_credits() {
let stdout = std::io::stdout();
let mut stdout = stdout.lock();
match fs::read_to_string("./credits.txt") {
Ok(contents) => println!("{}", contents),
Err(err) => {
AlertMessage::alert(&mut stdout, format!("{}", err)).ok();
}
}
}
fn analysis_files(evtx_files: Vec<PathBuf>) {
let mut detection = detection::Detection::new(detection::Detection::parse_rule_files());
for evtx_file in evtx_files {
let ret = analysis_file(evtx_file, detection);
detection = ret;
}
after_fact();
}
// Windowsイベントログファイルを1ファイル分解析する。
fn analysis_file(
evtx_filepath: PathBuf,
mut detection: detection::Detection,
) -> detection::Detection {
let filepath_disp = evtx_filepath.display();
let parser = evtx_to_jsons(evtx_filepath.clone());
if parser.is_none() {
return detection;
}
let mut tl = Timeline::new();
let mut parser = parser.unwrap();
let mut records = parser.records_json_value();
let tokio_rt = utils::create_tokio_runtime();
loop {
let mut records_per_detect = vec![];
while records_per_detect.len() < MAX_DETECT_RECORDS {
// パースに失敗している場合、エラーメッセージを出力
let next_rec = records.next();
if next_rec.is_none() {
break;
}
let record_result = next_rec.unwrap();
if record_result.is_err() {
let evtx_filepath = &filepath_disp;
let errmsg = format!(
"Failed to parse event file. EventFile:{} Error:{}",
evtx_filepath,
record_result.unwrap_err()
);
AlertMessage::alert(&mut std::io::stdout().lock(), errmsg).ok();
continue;
}
let record_info =
EvtxRecordInfo::new((&filepath_disp).to_string(), record_result.unwrap().data);
records_per_detect.push(record_info);
}
if records_per_detect.len() == 0 {
break;
}
// timeline機能の実行
tl.start(&records_per_detect);
// ruleファイルの検知
detection = detection.start(&tokio_rt, records_per_detect);
}
tokio_rt.shutdown_background();
detection.add_aggcondtion_msg();
tl.tm_stats_dsp_msg();
return detection;
}
fn evtx_to_jsons(evtx_filepath: PathBuf) -> Option<EvtxParser<File>> {
match EvtxParser::from_path(evtx_filepath) {
Ok(evtx_parser) => {
// parserのデフォルト設定を変更
let mut parse_config = ParserSettings::default();
parse_config = parse_config.separate_json_attributes(true); // XMLのattributeをJSONに変換する時のルールを設定
parse_config = parse_config.num_threads(utils::get_thread_num()); // 設定しないと遅かったので、設定しておく。
let evtx_parser = evtx_parser.with_configuration(parse_config);
return Option::Some(evtx_parser);
}
Err(e) => {
eprintln!("{}", e);
return Option::None;
}
}
}
fn _output_with_omikuji(omikuji: Omikuji) {
let fp = &format!("art/omikuji/{}", omikuji);
let content = fs::read_to_string(fp).unwrap();
println!("{}", content);
}
#[cfg(test)]
mod tests {
use crate::collect_evtxfiles;
#[test]
fn test_collect_evtxfiles() {
let files = collect_evtxfiles("test_files/evtx");
assert_eq!(3, files.len());
files.iter().for_each(|file| {
let is_contains = &vec!["test1.evtx", "test2.evtx", "testtest4.evtx"]
.into_iter()
.any(|filepath_str| {
return file.file_name().unwrap().to_str().unwrap_or("") == filepath_str;
});
assert_eq!(is_contains, &true);
})
}
}