message print
This commit is contained in:
@@ -2,6 +2,7 @@ alias,event_key
|
||||
EventID,Event.System.EventID
|
||||
Channel,Event.System.Channel
|
||||
CommandLine,Event.EventData.CommandLine
|
||||
ParentProcessName,Event.EventData.ParentProcessName
|
||||
Signed,Event.EventData.Signed
|
||||
ProcessName,Event.EventData.ProcessName
|
||||
AccessMask,Event.EventData.AccessMask
|
||||
@@ -12,4 +13,7 @@ ServiceName,Event.EventData.ServiceName
|
||||
ImagePath,Event.EventData.ImagePath
|
||||
ContextInfo,Event.EventData.ContextInfo
|
||||
Path,Event.EventData.Path
|
||||
ScriptBlockText,Event.EventData.ScriptBlockText#Name
|
||||
ScriptBlockText,Event.EventData.ScriptBlockText#Name
|
||||
MemberName,Event.EventData.SubjectUserName
|
||||
MemberSid,Event.EventData.SubjectUserSid
|
||||
TargetSid,Event.EventData.TargetSid
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
use crate::models::event;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Common {
|
||||
record_id: u64,
|
||||
date: String,
|
||||
record_id_list: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Common {
|
||||
pub fn new() -> Common {
|
||||
Common {
|
||||
record_id: 0,
|
||||
date: "".to_string(),
|
||||
record_id_list: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disp(&self) {
|
||||
for (record_id, date) in self.record_id_list.iter() {
|
||||
println!("date:{:?} record-id: {:?}", date, record_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detection(&mut self, system: &event::System, event_data: &HashMap<String, String>) {
|
||||
self.check_record_id(system);
|
||||
}
|
||||
|
||||
//
|
||||
// Record IDがシーケンスになっているかチェック
|
||||
//
|
||||
fn check_record_id(&mut self, system: &event::System) {
|
||||
let event_record_id: u64 = system.event_record_id.parse().unwrap();
|
||||
if self.record_id > 0 && event_record_id - self.record_id > 1 {
|
||||
self.record_id_list.insert(
|
||||
self.record_id.to_string() + " - " + &system.event_record_id.to_string(),
|
||||
self.date.to_string() + " - " + &system.time_created.system_time.to_string(),
|
||||
);
|
||||
}
|
||||
self.record_id = event_record_id;
|
||||
self.date = system.time_created.system_time.to_string();
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ fn build_app() -> clap::App<'static, 'static> {
|
||||
.arg(Arg::from_usage("--credits 'Zachary Mathis, Akira Nishikawa'"))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventKeyAliasConfig {
|
||||
key_to_eventkey: HashMap<String, String>,
|
||||
}
|
||||
|
||||
@@ -83,29 +83,28 @@ impl Detection {
|
||||
// selection rule files and collect message
|
||||
let mut message = Message::new();
|
||||
selection_rules.iter_mut().for_each(|rule| {
|
||||
&event_records
|
||||
.iter()
|
||||
.filter(|event_record| rule.select(event_record))
|
||||
.for_each(|event_record| {
|
||||
let event_time = Detection::get_event_time(event_record);
|
||||
// TODO ログから日付がとれない場合に本当は時刻不明という感じで表示したい。
|
||||
// しかし、Messageクラスのinsertメソッドが、UTCクラスのインスタンスを必ず渡すようなインタフェースになっているので、
|
||||
// やむなくUtc.ymd(1970, 1, 1).and_hms(0, 0, 0)を渡している。
|
||||
event_records.iter().for_each(|event_record| {
|
||||
if !rule.select(event_record) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Messageクラスのinsertメソッドの引数をDateTime<UTC>からOption<DateTime<UTC>>に変更して、
|
||||
// insertメソッドでOption::Noneが渡された場合に時刻不明だと分かるように表示させるような実装にした方がいいかも
|
||||
let utc_event_time = event_time
|
||||
.and_then(|datetime| {
|
||||
let utc = Utc.from_local_datetime(&datetime.naive_utc()).unwrap();
|
||||
return Option::Some(utc);
|
||||
})
|
||||
.or(Option::Some(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)));
|
||||
message.insert(utc_event_time.unwrap(), event_record.to_string())
|
||||
});
|
||||
let event_time = Detection::get_event_time(event_record);
|
||||
let utc_event_time = event_time
|
||||
.and_then(|datetime| {
|
||||
let utc = Utc.from_local_datetime(&datetime.naive_utc()).unwrap();
|
||||
return Option::Some(utc);
|
||||
})
|
||||
.or(Option::None);
|
||||
message.insert(
|
||||
utc_event_time,
|
||||
event_record,
|
||||
Some(rule.yaml["output"].as_str().unwrap().to_string()),
|
||||
)
|
||||
});
|
||||
});
|
||||
|
||||
// output message
|
||||
message.debug();
|
||||
message.print();
|
||||
}
|
||||
|
||||
fn get_event_time(event_record: &Value) -> Option<DateTime<FixedOffset>> {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
mod common;
|
||||
pub mod configs;
|
||||
pub mod detection;
|
||||
pub mod print;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
extern crate chrono;
|
||||
extern crate lazy_static;
|
||||
use crate::detections::configs;
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -21,18 +25,75 @@ impl Message {
|
||||
}
|
||||
|
||||
/// メッセージを設定
|
||||
pub fn insert(&mut self, time: DateTime<Utc>, message: String) {
|
||||
match self.map.get_mut(&time) {
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
mut time: Option<DateTime<Utc>>,
|
||||
event_record: &Value,
|
||||
output: Option<String>,
|
||||
) {
|
||||
if Option::None == output {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = &self.parse_message(event_record, output);
|
||||
|
||||
if Option::None == time {
|
||||
time = Option::Some(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0));
|
||||
}
|
||||
|
||||
match self.map.get_mut(&time.unwrap()) {
|
||||
Some(v) => {
|
||||
v.push(message.to_string());
|
||||
}
|
||||
None => {
|
||||
let m = vec![message.to_string(); 1];
|
||||
self.map.insert(time, m);
|
||||
self.map.insert(time.unwrap(), m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_message(&mut self, event_record: &Value, output: Option<String>) -> String {
|
||||
if Option::None == output {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
let mut return_message: String = output.unwrap();
|
||||
let mut hash_map: HashMap<String, String> = HashMap::new();
|
||||
let re = Regex::new(r"%[a-zA-Z0-9-_]+%").unwrap();
|
||||
for caps in re.captures_iter(&return_message) {
|
||||
let full_target_str = &caps[0];
|
||||
let target_length = full_target_str.chars().count() - 2; // The meaning of 2 is two percent
|
||||
let target_str = full_target_str
|
||||
.chars()
|
||||
.skip(1)
|
||||
.take(target_length)
|
||||
.collect::<String>();
|
||||
|
||||
if let Some(array_str) = configs::singleton()
|
||||
.event_key_alias_config
|
||||
.get_event_key(target_str.to_string())
|
||||
{
|
||||
let split: Vec<&str> = array_str.split(".").collect();
|
||||
let mut tmp_event_record: &Value = event_record.into();
|
||||
for s in split {
|
||||
if let Some(record) = tmp_event_record.get(s) {
|
||||
tmp_event_record = record;
|
||||
}
|
||||
}
|
||||
hash_map.insert(
|
||||
full_target_str.to_string(),
|
||||
tmp_event_record.as_str().unwrap_or("").to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (k, v) in &hash_map {
|
||||
return_message = return_message.replace(k, v);
|
||||
}
|
||||
|
||||
return_message
|
||||
}
|
||||
|
||||
/// メッセージを返す
|
||||
pub fn get(&self, time: DateTime<Utc>) -> Vec<String> {
|
||||
match self.map.get(&time) {
|
||||
@@ -45,6 +106,15 @@ impl Message {
|
||||
pub fn debug(&self) {
|
||||
println!("{:?}", self.map);
|
||||
}
|
||||
|
||||
/// 最後に表示を行う
|
||||
pub fn print(&self) {
|
||||
for (key, values) in self.map.iter() {
|
||||
for value in values.iter() {
|
||||
println!("{} : {}", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -53,11 +123,39 @@ fn test_create_and_append_message() {
|
||||
let poke = Utc.ymd(1996, 2, 27).and_hms(1, 5, 1);
|
||||
let taka = Utc.ymd(2000, 1, 21).and_hms(9, 6, 1);
|
||||
|
||||
message.insert(poke, "TEST".to_string());
|
||||
message.insert(poke, "TEST2".to_string());
|
||||
message.insert(taka, "TEST3".to_string());
|
||||
let json_str = r#"
|
||||
{
|
||||
"Event": {
|
||||
"EventData": {
|
||||
"CommandLine": "hoge"
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let event_record: Value = serde_json::from_str(json_str).unwrap();
|
||||
|
||||
message.insert(
|
||||
Some(poke),
|
||||
&event_record,
|
||||
Some("CommandLine1: %CommandLine%".to_string()),
|
||||
);
|
||||
message.insert(
|
||||
Some(poke),
|
||||
&event_record,
|
||||
Some("CommandLine2: %CommandLine%".to_string()),
|
||||
);
|
||||
message.insert(
|
||||
Some(taka),
|
||||
&event_record,
|
||||
Some("CommandLine3: %CommandLine%".to_string()),
|
||||
);
|
||||
message.insert(
|
||||
Option::None,
|
||||
&event_record,
|
||||
Some("CommandLine4: %CommandLine%".to_string()),
|
||||
);
|
||||
|
||||
let display = format!("{}", format_args!("{:?}", message));
|
||||
let expect = "Message { map: {1996-02-27T01:05:01Z: [\"TEST\", \"TEST2\"], 2000-01-21T09:06:01Z: [\"TEST3\"]} }";
|
||||
let expect = "Message { map: {1970-01-01T00:00:00Z: [\"CommandLine4: hoge\"], 1996-02-27T01:05:01Z: [\"CommandLine1: hoge\", \"CommandLine2: hoge\"], 2000-01-21T09:06:01Z: [\"CommandLine3: hoge\"]} }";
|
||||
assert_eq!(display, expect);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod detections;
|
||||
pub mod models;
|
||||
pub mod omikuji;
|
||||
pub mod yaml;
|
||||
|
||||
@@ -8,12 +8,7 @@ use yamato_event_analyzer::detections::detection;
|
||||
use yamato_event_analyzer::omikuji::Omikuji;
|
||||
|
||||
fn main() -> Result<(), DeError> {
|
||||
let filepath: String = configs::singleton()
|
||||
.args
|
||||
.value_of("filepath")
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
if filepath != "" {
|
||||
if let Some(filepath) = configs::singleton().args.value_of("filepath") {
|
||||
parse_file(&filepath);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
extern crate serde;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct Data {
|
||||
#[serde(rename = "Name")]
|
||||
pub name: Option<String>,
|
||||
#[serde(rename = "$value")]
|
||||
pub text: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct TimeCreated {
|
||||
#[serde(rename = "SystemTime")]
|
||||
pub system_time: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
struct Execution {
|
||||
#[serde(rename = "ProcessID")]
|
||||
process_id: i32,
|
||||
#[serde(rename = "ThreadID")]
|
||||
thread_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct Provider {
|
||||
#[serde(rename = "Name")]
|
||||
pub name: Option<String>,
|
||||
#[serde(rename = "Guid")]
|
||||
guid: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct System {
|
||||
#[serde(rename = "Provider")]
|
||||
pub provider: Provider,
|
||||
#[serde(rename = "EventID")]
|
||||
pub event_id: String,
|
||||
#[serde(rename = "Version")]
|
||||
version: Option<String>,
|
||||
#[serde(rename = "Level")]
|
||||
level: String,
|
||||
#[serde(rename = "Task")]
|
||||
task: String,
|
||||
#[serde(rename = "Opcode")]
|
||||
opcode: Option<String>,
|
||||
#[serde(rename = "Keywords")]
|
||||
keywords: String,
|
||||
#[serde(rename = "TimeCreated")]
|
||||
pub time_created: TimeCreated,
|
||||
#[serde(rename = "EventRecordID")]
|
||||
pub event_record_id: String,
|
||||
#[serde(rename = "Correlation")]
|
||||
correlation: Option<String>,
|
||||
#[serde(rename = "Execution")]
|
||||
execution: Option<Execution>,
|
||||
#[serde(rename = "Channel")]
|
||||
pub channel: String, // Security, System, Application ...etc
|
||||
#[serde(rename = "Computer")]
|
||||
computer: String,
|
||||
#[serde(rename = "Security")]
|
||||
security: String,
|
||||
#[serde(rename = "Message")]
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct EventData {
|
||||
#[serde(rename = "Data")]
|
||||
pub data: Option<Vec<Data>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct UserData {
|
||||
#[serde(rename = "LogFileCleared")]
|
||||
pub log_file_cleared: Option<LogFileCleared>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct LogFileCleared {
|
||||
#[serde(rename = "SubjectUserSid")]
|
||||
pub subject_user_sid: Option<String>,
|
||||
#[serde(rename = "SubjectUserName")]
|
||||
pub subject_user_name: Option<String>,
|
||||
#[serde(rename = "SubjectDomainName")]
|
||||
pub subject_domain_name: Option<String>,
|
||||
#[serde(rename = "SubjectLogonId")]
|
||||
pub subject_logon_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
pub struct Evtx {
|
||||
#[serde(rename = "System")]
|
||||
pub system: System,
|
||||
#[serde(rename = "EventData")]
|
||||
pub event_data: Option<EventData>,
|
||||
#[serde(rename = "UserData")]
|
||||
pub user_data: Option<UserData>,
|
||||
}
|
||||
|
||||
impl Evtx {
|
||||
//
|
||||
// 文字列データを取得する
|
||||
//
|
||||
fn get_string(v: &Data) -> String {
|
||||
let mut ret = "".to_string();
|
||||
if let Some(text) = &v.text {
|
||||
ret = text.to_string();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//
|
||||
// EventDataをHashMapとして取得する
|
||||
//
|
||||
pub fn parse_event_data(&self) -> HashMap<String, String> {
|
||||
let mut values = HashMap::new();
|
||||
|
||||
if let Some(event_data) = &self.event_data {
|
||||
if let Some(data) = &event_data.data {
|
||||
for v in data.iter() {
|
||||
if let Some(name) = &v.name {
|
||||
values.insert(name.to_string(), Evtx::get_string(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
values
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod event;
|
||||
Reference in New Issue
Block a user