Skip to content

Commit

Permalink
initial support for record format in evtx2bodyfile
Browse files Browse the repository at this point in the history
  • Loading branch information
janstarke committed Sep 28, 2024
1 parent 4d2094a commit 1b573a4
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 36 deletions.
23 changes: 10 additions & 13 deletions src/bin/evtx2bodyfile/evtx_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle};
use ouroboros::self_referencing;
use serde_json::Value;

use crate::output_formatter::OutputFormatter;
use crate::output_writer::OutputWriter;

pub(crate) struct EvtxFile(Input);

Expand Down Expand Up @@ -58,24 +58,21 @@ impl From<&Input> for EvtxFile {
}

impl EvtxFile {
pub(crate) fn print_records<F>(self, formatter: F, treat_errors_as_warnings: bool) -> Result<()>
pub(crate) fn print_records<F>(self, treat_errors_as_warnings: bool) -> Result<()>
where
F: OutputFormatter,
F: OutputWriter<std::io::Stdout>,
{
let mut formatter = F::from(std::io::stdout());
let bar = self.create_progress_bar().unwrap();
for value in self.into_iter() {
match formatter.record_to_string(&value) {
Ok(s) => println!("{s}"),
Err(why) => {
if treat_errors_as_warnings {
log::warn!("Error while reading record: {why}");
} else {
bar.finish_and_clear();
return Err(why);
}
if let Err(why) = formatter.output(&value) {
if treat_errors_as_warnings {
log::warn!("Error while reading record: {why}");
} else {
bar.finish_and_clear();
return Err(why);
}
}

bar.inc(1);
}
bar.finish_and_clear();
Expand Down
14 changes: 9 additions & 5 deletions src/bin/evtx2bodyfile/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use std::io::Stdout;

use anyhow::{bail, Result};
use cli::Cli;
use dfir_toolkit::common::FancyParser;
use evtx_file::EvtxFile;
use output_formatter::BodyfileOutputFormatter;
use output_format::OutputFormat;
use output_writer::{BodyfileOutputWriter, RecordOutputWriter};

mod bf_data;
mod cli;
mod evtx_file;
mod output_format;
mod output_formatter;
mod output_writer;
#[macro_use]
mod macros;

Expand All @@ -24,11 +27,12 @@ fn main() -> Result<()> {

for input in cli.evtx_files().iter() {
let file = EvtxFile::from(input);

match cli.format() {
output_format::OutputFormat::Bodyfile => {
file.print_records(BodyfileOutputFormatter, !cli.strict())?
}
OutputFormat::Bodyfile => file.print_records::<BodyfileOutputWriter<Stdout>>(!cli.strict())?,
OutputFormat::Record => file.print_records::<RecordOutputWriter<Stdout>>(!cli.strict())?,
}
}

Ok(())
}
5 changes: 5 additions & 0 deletions src/bin/evtx2bodyfile/output_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ use strum_macros::Display;
#[derive(ValueEnum, Clone, Display)]
pub(crate) enum OutputFormat {

/// bodyfile format
#[strum(serialize = "bodyfile")]
Bodyfile,

/// flow record format (<https://docs.rs/flow-record>)
#[strum(serialize = "record")]
Record
}
18 changes: 0 additions & 18 deletions src/bin/evtx2bodyfile/output_formatter.rs

This file was deleted.

121 changes: 121 additions & 0 deletions src/bin/evtx2bodyfile/output_writer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use std::io::Write;

use chrono::{DateTime, Utc};
use dfirtk_eventdata::{ActivityId, EventId, ProcessId, RelatedActivityId};
use evtx::SerializedEvtxRecord;
use flow_record::prelude::{FlowRecord, Serializer};
use flow_record::{
prelude::{rmpv, FieldType},
ToMsgPackValue,
};
use flow_record::derive::FlowRecord;

use crate::bf_data::BfData;

#[derive(Default)]
pub(crate) struct BodyfileOutputWriter<W: Write>(W);
pub(crate) struct RecordOutputWriter<W: Write>(Serializer<W>);

pub(crate) trait OutputWriter<W>: From<W>
where
W: Write,
{
fn output(&mut self, record: &SerializedEvtxRecord<serde_json::Value>) -> anyhow::Result<()>;
}

impl<W> From<W> for BodyfileOutputWriter<W>
where
W: Write,
{
fn from(writer: W) -> Self {
Self(writer)
}
}

impl<W> From<W> for RecordOutputWriter<W>
where
W: Write,
{
fn from(writer: W) -> Self {
Self(Serializer::new(writer))
}
}

impl<W> OutputWriter<W> for BodyfileOutputWriter<W>
where
W: Write,
{
fn output(&mut self, record: &SerializedEvtxRecord<serde_json::Value>) -> anyhow::Result<()> {
let bf_data = BfData::try_from(record)?;
let s = bf_data.try_into_mactime()?;
writeln!(self.0, "{s}")?;
Ok(())
}
}

impl<W> OutputWriter<W> for RecordOutputWriter<W>
where
W: Write,
{
fn output(&mut self, record: &SerializedEvtxRecord<serde_json::Value>) -> anyhow::Result<()> {
let event = WindowsEvent::try_from(record)?;
self.0.serialize(event)?;
Ok(())
}
}

#[derive(FlowRecord)]
#[flow_record(version = 1, source = "evtx2bodyfile", classification = "evtx")]
struct WindowsEvent {
record_timestamp: DateTime<Utc>,
event_id: u16,
event_record_id: u64,
activity_id: String,
related_activity_id: String,
process_id: u64,
}

impl TryFrom<&SerializedEvtxRecord<serde_json::Value>> for WindowsEvent {
type Error = anyhow::Error;

fn try_from(record: &SerializedEvtxRecord<serde_json::Value>) -> Result<Self, Self::Error> {
let record_timestamp = record.timestamp;
let event_id = EventId::try_from(record)?.0;
let event_record_id = record.event_record_id;
let activity_id = ActivityId::try_from(record)?.value().to_string();
let related_activity_id = RelatedActivityId::try_from(record)?.to_string();
let process_id = ProcessId::try_from(record)?.0;
Ok(Self {
record_timestamp,
event_id,
event_record_id,
activity_id,
related_activity_id,
process_id,
})
}
}
struct ValueWrapper<'v>(&'v serde_json::Value);

impl<'v> From<&'v serde_json::Value> for ValueWrapper<'v> {
fn from(value: &'v serde_json::Value) -> Self {
Self(value)
}
}

impl<'v> ToMsgPackValue for ValueWrapper<'v> {
fn to_msgpack_value(self) -> rmpv::Value {
match self.0 {
serde_json::Value::Null => rmpv::Value::Nil,
serde_json::Value::Bool(_) => todo!(),
serde_json::Value::Number(_number) => todo!(),
serde_json::Value::String(_) => todo!(),
serde_json::Value::Array(_vec) => todo!(),
serde_json::Value::Object(_map) => todo!(),
}
}

fn field_type() -> FieldType {
FieldType::String
}
}

0 comments on commit 1b573a4

Please sign in to comment.