diff --git a/art/logo.txt b/art/logo.txt new file mode 100644 index 00000000..9e74bef3 --- /dev/null +++ b/art/logo.txt @@ -0,0 +1,7 @@ +██╗ ██╗ █████╗ ██╗ ██╗ █████╗ ██████╗ ██╗ ██╗███████╗ █████╗ +██║ ██║██╔══██╗╚██╗ ██╔╝██╔══██╗██╔══██╗██║ ██║██╔════╝██╔══██╗ +███████║███████║ ╚████╔╝ ███████║██████╔╝██║ ██║███████╗███████║ +██╔══██║██╔══██║ ╚██╔╝ ██╔══██║██╔══██╗██║ ██║╚════██║██╔══██║ +██║ ██║██║ ██║ ██║ ██║ ██║██████╔╝╚██████╔╝███████║██║ ██║ +╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ +by Yamato Security \ No newline at end of file diff --git a/src/detections/configs.rs b/src/detections/configs.rs index 248f1bcb..d3a8db51 100644 --- a/src/detections/configs.rs +++ b/src/detections/configs.rs @@ -52,6 +52,7 @@ fn build_app<'a>() -> ArgMatches<'a> { --rfc-2822 'Output date and time in RFC 2822 format. Example: Mon, 07 Aug 2006 12:34:56 -0600' --rfc-3339 'Output date and time in RFC 3339 format. Example: 2006-08-07T12:34:56.485214 -06:00' --verbose 'Output check information to target event file path and rule file.' + -q 'Quiet Output Logo' -r --rules=[RULEDIRECTORY] 'using target of rule file directory' -L --level=[LEVEL] 'Specified execute rule level(default: LOW)' -u --utc 'Output time in UTC format(default: local time)' diff --git a/src/detections/detection.rs b/src/detections/detection.rs index d6ab5583..19101b17 100644 --- a/src/detections/detection.rs +++ b/src/detections/detection.rs @@ -2,6 +2,7 @@ extern crate csv; use crate::detections::rule::AggResult; use serde_json::Value; +use std::collections::HashMap; use tokio::{runtime::Runtime, spawn, task::JoinHandle}; use crate::detections::print::AlertMessage; @@ -51,6 +52,11 @@ impl Detection { // ルールファイルのパースを実行 let mut rulefile_loader = ParseYaml::new(); let result_readdir = rulefile_loader.read_dir(rulespath.unwrap_or(DIRPATH_RULES), &level); + Detection::print_rule_load_info( + rulefile_loader.rulecounter, + rulefile_loader.parseerror_count, + rulefile_loader.ignore_count, + ); if result_readdir.is_err() { AlertMessage::alert( &mut std::io::stderr().lock(), @@ -201,6 +207,21 @@ impl Detection { )); return ret; } + pub fn print_rule_load_info( + rc: HashMap, + parseerror_count: u128, + ignore_count: u128, + ) { + let mut total = parseerror_count + ignore_count; + rc.into_iter().for_each(|(key, value)| { + println!("{} Rules: {}", key, value); + total += value; + }); + println!("Ignored Rule Count: {}", ignore_count); + println!("Rule Parse Errors Count: {}", parseerror_count); + println!("Total Detection Rules: {}", total); + println!(""); + } } #[test] diff --git a/src/main.rs b/src/main.rs index 0fde7241..dff81b19 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,10 @@ use std::{ const MAX_DETECT_RECORDS: usize = 40000; fn main() { + if !configs::CONFIG.read().unwrap().args.is_present("q") { + output_logo(); + println!(""); + } if configs::CONFIG.read().unwrap().args.args.len() == 0 { println!( "{}", @@ -114,6 +118,7 @@ fn analysis_files(evtx_files: Vec) { .value_of("level") .unwrap_or("INFO") .to_uppercase(); + println!("Analyzing Event Files: {:?}", evtx_files.len()); let rule_files = detection::Detection::parse_rule_files( level, configs::CONFIG.read().unwrap().args.value_of("rules"), @@ -212,6 +217,12 @@ fn _output_with_omikuji(omikuji: Omikuji) { println!("{}", content); } +fn output_logo() { + let fp = &format!("art/logo.txt"); + let content = fs::read_to_string(fp).unwrap(); + println!("{}", content); +} + #[cfg(test)] mod tests { use crate::collect_evtxfiles; diff --git a/src/yaml.rs b/src/yaml.rs index fdd5ec71..47044bbb 100644 --- a/src/yaml.rs +++ b/src/yaml.rs @@ -3,6 +3,7 @@ extern crate yaml_rust; use crate::detections::configs; use crate::detections::print::AlertMessage; +use std::collections::HashMap; use std::ffi::OsStr; use std::fs; use std::io; @@ -13,11 +14,19 @@ use yaml_rust::YamlLoader; pub struct ParseYaml { pub files: Vec<(String, yaml_rust::Yaml)>, + pub rulecounter: HashMap, + pub ignore_count: u128, + pub parseerror_count: u128, } impl ParseYaml { pub fn new() -> ParseYaml { - ParseYaml { files: Vec::new() } + ParseYaml { + files: Vec::new(), + rulecounter: HashMap::new(), + ignore_count: 0, + parseerror_count: 0, + } } pub fn read_file(&self, path: PathBuf) -> Result { @@ -64,6 +73,7 @@ impl ParseYaml { read_content.unwrap_err() ), )?; + self.parseerror_count += 1; return io::Result::Ok(ret); } @@ -78,6 +88,7 @@ impl ParseYaml { yaml_contents.unwrap_err() ), )?; + self.parseerror_count += 1; return io::Result::Ok(ret); } @@ -94,8 +105,24 @@ impl ParseYaml { .filter_map(|(filepath, yaml_doc)| { // ignoreフラグがONになっているルールは無視する。 if yaml_doc["ignore"].as_bool().unwrap_or(false) { + self.ignore_count += 1; return Option::None; } + self.rulecounter.insert( + yaml_doc["rulesection"] + .as_str() + .unwrap_or("other") + .to_string(), + self.rulecounter + .get( + &yaml_doc["rulesection"] + .as_str() + .unwrap_or("other") + .to_string(), + ) + .unwrap_or(&0) + + 1, + ); if configs::CONFIG.read().unwrap().args.is_present("verbose") { println!("Loaded yml FilePath: {}", filepath); } @@ -115,7 +142,6 @@ impl ParseYaml { }) .collect(); self.files.extend(files); - return io::Result::Ok(String::default()); } }