From e18daa2b3ff81ee13a25cdba5d03feca000a5b98 Mon Sep 17 00:00:00 2001 From: daladim Date: Fri, 16 Sep 2022 17:01:42 +0200 Subject: [PATCH 1/4] [doc] Updated README, crate documentation and metadata ...yes, I also added a little self-advertising at the end of the README file :) --- Cargo.toml | 4 +- README.md | 87 +++++----------------------- src/lib.rs | 41 ++++++------- src/native/etw_types/event_record.rs | 2 +- 4 files changed, 36 insertions(+), 98 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3221b9a..c8dfe48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,9 @@ name = "ferrisetw" version = "1.0.0" license = "MIT OR Apache-2.0" description = "Basically a KrabsETW rip-off written in Rust" -authors = ["n4r1b"] +keywords = ["etw", "krabsetw", "event", "tracing", "windows"] +categories = ["api-bindings", "parsing"] +authors = ["n4r1b", "daladim"] edition = "2018" repository = "https://github.com/n4r1b/ferrisetw" diff --git a/README.md b/README.md index 2717fdb..0e5d6f5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,10 @@ # FerrisETW 🦀 -**Basically a [KrabsETW](https://github.com/microsoft/krabsetw/) rip-off written in Rust**, -hence the name [`Ferris`](https://rustacean.net/) 🦀 -All **credits** go to the team at Microsoft who develop KrabsEtw, without it, this project -probably wouldn't be a thing. +This crate provides safe Rust abstractions over the ETW consumer APIs. -## Motivation -Since lately I've been working very closely with ETW and Rust, I thought that having a tool that -would simplify ETW management written in Rust and available as a crate for other to consume would -be pretty neat and that's where this crate comes into play 🔥 +It started as a [KrabsETW](https://github.com/microsoft/krabsetw/) rip-off written in Rust (hence the name [`Ferris`](https://rustacean.net/) 🦀). +All credits go to the team at Microsoft who develop KrabsEtw, without it, this project probably wouldn't be a thing.
+Since version 1.0, the API and internal architecture of this crate is slightly diverging from `krabsetw`, so that it is more Rust-idiomatic. ## Examples You can find a examples within the @@ -16,81 +12,28 @@ You can find a examples within the as well as the [examples](./examples) and the [tests](./tests) folders. If you are familiar with KrabsETW you'll see that is very similar. -In case you've never used KrabsETW before, the examples are very straight forward and should be easy to follow. If you have -any issues don't hesitate in asking. +In case you've never used KrabsETW before, the examples are very straight forward and should be easy to follow. If you have any issues don't hesitate in asking. -The following snippet shows the basic usage of the library -```rust -fn wmi_callback(record: EventRecord, schema_locator: &mut SchemaLocator) { - // We locate the Schema for the Event - match schema_locator.event_schema(record) { - Ok(schema) => { - // We filter the event by EventId - if schema.event_id() == 12 { - // We obtain the Parser for the Schema - let mut parser = Parser::create(&schema); - // We parse the data from the Event based on the names of the fields of the Event - // Type annotations or Fully Qualified Syntax are needed when calling TryParse - let op: String = parser - .try_parse("Operation") - .unwrap_or(String::from("Operation missing")); - let provider_name: String = parser - .try_parse("ProviderName") - .unwrap_or(String::from("ProviderName missing")); - // Could also use String as type - let provider_guid: GUID = - parser.try_parse("ProviderGuid").unwrap_or(GUID::zeroed()); - println!( - "WMI-Activity -> ProviderName {}, ProviderGuid: {:?}, Operation: {}", - provider_name, provider_guid, op - ); - } - } - Err(err) => println!("Error {:?}", err), - }; -} - -fn main() { - // We first build a Provider - let wmi_provider = Provider::new() - .by_guid("1418ef04-b0b4-4623-bf7e-d74ab47bbdaa") // Microsoft-Windows-WMI-Activity - .add_callback(wmi_callback) - .build() - .unwrap(); - - // We enable the Provider in a new Trace and start the trace - // This internally will launch a new thread - let mut trace = UserTrace::new().enable(wmi_provider).start().unwrap(); - - std::thread::sleep(Duration::new(20, 0)); - - // We stop the trace - trace.stop(); -} -``` ## Documentation -I'm having some trouble to get docs.rs to build the documentation for the crate so at the moment is being hosted on my domain. -[FerrisETW Doc](https://www.n4r1b.com/doc/ferrisetw/index.html) +This crate is documented at [docs.rs](https://docs.rs/crate/ferrisetw/latest). ## Notes -- The project is still WIP, there's still plenty of things to evaluate/investigate and things to fix and do better. - Any help would be greatly appreciated, also any issues you may have! +- The project is still WIP. + Feel free to report bugs, issues, feature requests, etc. + Of course, contributing will be happily accepted! + - - The types available for parsing are those that implement the trait TryParse for Parser, basic types are already implemented. In the near future I'll add more :) - -- I tried to keep dependencies as minimal as possible, also you'll see I went with the new [windows-rs](https://github.com/microsoft/windows-rs) instead of + +- I tried to keep dependencies as minimal as possible, also you'll see I went with the new [windows-rs](https://github.com/microsoft/windows-rs) instead of using the [winapi](https://docs.rs/winapi/0.3.9/winapi/). This is a personal decision mainly because I believe the Windows bindings is going to be the "standard" to interact with the Windows API in the near future. -- Although I encourage everyone to use Rust, I do believe that, at the moment, if you plan on interacting - with ETW in a production level and the programming language is not a constraint you should definitely - go with **KrabsETW** as a more robust and tested option. Hopefully in next iterations I'll be able - to remove this disclaimer 😃 - ### Acknowledgments -- First of all, the team at MS who develop KrabsETW!! +- First of all, the team at MS who develop KrabsETW!! - [Shaddy](https://github.com/Shaddy) for, pretty much, teaching me all the Rust I know 😃 +- [n4r1b](https://github.com/n4r1b) for creating this great crate +- [daladim](https://github.com/daladim) for adding even more features diff --git a/src/lib.rs b/src/lib.rs index 3d4a592..9891c6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,9 @@ //! # Event Windows Tracing FTW! -//! **Basically a [KrabsETW] rip-off written in Rust**, hence the name `Ferris` 🦀 +//! This crate provides safe Rust abstractions over the ETW consumer APIs. //! -//! All **credits** go to the team at Microsoft who develop KrabsEtw, without it, this project -//! probably wouldn't be a thing. +//! It started as a [KrabsETW](https://github.com/microsoft/krabsetw/) rip-off written in Rust (hence the name [`Ferris`](https://rustacean.net/) 🦀). +//! All credits go to the team at Microsoft who develop KrabsEtw, without it, this project probably wouldn't be a thing.
+//! Since version 1.0, the API and internal architecture of this crate is slightly diverging from `krabsetw`, so that it is more Rust-idiomatic. //! //! # What's ETW //! Event Tracing for Windows (ETW) is an efficient kernel-level tracing facility that lets you log @@ -30,15 +31,6 @@ //! would simplify ETW management written in Rust and available as a crate for other to consume would //! be pretty neat and that's where this crate comes into play 🔥 //! -//! # Disclaimer -//! This project is still WIP. There's still plenty of things to evaluate/investigate and things to -//! fix and do better. Any help would be greatly appreciated, also any issues you may have! -//! -//! Although I encourage everyone to use Rust, I do believe that, at the moment, if you plan on interacting -//! with ETW in a production level and the programming language is not a constraint you should definitely -//! consider [KrabsETW] as a more robust and tested option. Hopefully in next iterations I'll be able -//! to remove this disclaimer 😃 -//! //! # Getting started //! If you are familiar with KrabsEtw you'll see using the crate is very similar, in case you are not //! familiar with it the following example shows the basics on how to build a provider, start a trace @@ -53,26 +45,27 @@ //! use ferrisetw::trace::{UserTrace, TraceTrait, TraceBaseTrait}; //! //! fn process_callback(record: &EventRecord, schema_locator: &SchemaLocator) { -//! // Within the callback we first locate the proper Schema for the event -//! match schema_locator.event_schema(record) { -//! Ok(schema) => { -//! // At the moment we can only filter by checking the event_id -//! if record.event_id() == 2 { +//! // Basic event scrutinizing can be done directly from the `EventRecord` +//! if record.event_id() == 2 { +//! // More advanced info can be retrieved from the event schema +//! // (the SchemaLocator caches the schema for a given kind of event, so this call is cheap in case you've already encountered the same event kind previously) +//! match schema_locator.event_schema(record) { +//! Err(err) => println!("Error {:?}", err), +//! Ok(schema) => { +//! println!("Received an event from provider {}", schema.provider_name()); +//! +//! // Finally, properties for a given event can be retrieved using a Parser +//! let parser = Parser::create(record, &schema); //! -//! // We build the Parser based on the Schema -//! let mut parser = Parser::create(record, &schema); -//! -//! // Finally, Parse data from the Event, proper error handling should be done //! // Type annotations or Fully Qualified Syntax are needed when calling TryParse //! // Supported types implement the trait TryParse for Parser -//! +//! // In actual code, be sure to correctly handle Err values! //! let process_id: u32 = parser.try_parse("ProcessID").unwrap(); //! let image_name: String = parser.try_parse("ImageName").unwrap(); //! println!("PID: {} ImageName: {}", process_id, image_name); //! } //! } -//! Err(err) => println!("Error {:?}", err), -//! }; +//! } //! } //! //! fn main() { diff --git a/src/native/etw_types/event_record.rs b/src/native/etw_types/event_record.rs index 467add0..29dfc06 100644 --- a/src/native/etw_types/event_record.rs +++ b/src/native/etw_types/event_record.rs @@ -104,7 +104,7 @@ impl EventRecord { } } - /// Returns the ExtendedData from the ETW Event + /// Returns the `ExtendedData` from the ETW Event /// /// Their availability is mostly determined by the flags passed to [`Provider::trace_flags`](crate::provider::Provider::trace_flags) /// From a552a2af41b5ad7e42e97743d204a4205d839ee9 Mon Sep 17 00:00:00 2001 From: daladim Date: Wed, 28 Sep 2022 18:37:47 +0200 Subject: [PATCH 2/4] [doc] Event filters * Some filters are not effetive on Win7 * By-PID filters may not work, even for kernel traces See https://github.com/n4r1b/ferrisetw/issues/51 --- src/native/etw_types.rs | 3 +++ src/provider/event_filter.rs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/native/etw_types.rs b/src/native/etw_types.rs index 04d8103..8c93502 100644 --- a/src/native/etw_types.rs +++ b/src/native/etw_types.rs @@ -289,6 +289,9 @@ impl<'filters> EnableTraceParameters<'filters> { params.native.EnableProperty = trace_flags.bits(); + // Note: > Each type of filter (a specific Type member) may only appear once in a call to the EnableTraceEx2 function. + // https://learn.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-enabletraceex2#remarks + // > The maximum number of filters that can be included in a call to EnableTraceEx2 is set by MAX_EVENT_FILTERS_COUNT let mut win_filter_descriptors: Vec = filters .iter() .map(|efd| efd.as_event_filter_descriptor()) diff --git a/src/provider/event_filter.rs b/src/provider/event_filter.rs index ed6071f..581cc8d 100644 --- a/src/provider/event_filter.rs +++ b/src/provider/event_filter.rs @@ -6,10 +6,15 @@ use windows::Win32::System::Diagnostics::Etw::{EVENT_FILTER_DESCRIPTOR, EVENT_FI use windows::Win32::System::Diagnostics::Etw::{MAX_EVENT_FILTER_EVENT_ID_COUNT, MAX_EVENT_FILTER_PID_COUNT}; /// Specifies how this provider will filter its events +/// +/// Some filters are not effective prior to Windows 8.1 ([source](https://learn.microsoft.com/en-us/windows/win32/api/evntprov/ns-evntprov-event_filter_descriptor#remarks)) #[derive(Debug)] pub enum EventFilter { /// Filter by PID. /// This is only effective on kernel mode logger session. + /// TODO: even for `KernelTrace`, this does not seem to work. + /// Maybe there's a distinction between "a trace run in kernel-mode" and a "System trace"? + /// See ByPids(Vec), /// Filter by ETW Event ID. ByEventIds(Vec), From 104fc75c9d4a92d621845ac6d31b3a66fac8ea60 Mon Sep 17 00:00:00 2001 From: daladim Date: Wed, 21 Sep 2022 10:50:19 +0200 Subject: [PATCH 3/4] [doc] Dead links --- src/native/etw_types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/etw_types.rs b/src/native/etw_types.rs index 8c93502..37ad47e 100644 --- a/src/native/etw_types.rs +++ b/src/native/etw_types.rs @@ -272,7 +272,7 @@ impl<'tracedata> Default for EventTraceLogfile<'tracedata> { /// Newtype wrapper over an [ENABLE_TRACE_PARAMETERS] /// -/// [ENABLE_TRACE_PARAMETERS]: https://microsoft.github.io/windows-docs-rs/doc/bindings/Windows/Win32/Etw/struct.ENABLE_TRACE_PARAMETERS.html +/// [ENABLE_TRACE_PARAMETERS]: https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Diagnostics/Etw/struct.ENABLE_TRACE_PARAMETERS.html #[repr(C)] #[derive(Clone, Copy, Default)] pub struct EnableTraceParameters<'filters>{ @@ -318,7 +318,7 @@ impl<'filters> EnableTraceParameters<'filters> { /// Wrapper over the [DECODING_SOURCE] type /// -/// [DECODING_SOURCE]: https://microsoft.github.io/windows-docs-rs/doc/bindings/Windows/Win32/Etw/struct.DECODING_SOURCE.html +/// [DECODING_SOURCE]: https://learn.microsoft.com/en-us/windows/win32/api/tdh/ne-tdh-decoding_source #[derive(Debug)] pub enum DecodingSource { DecodingSourceXMLFile, From 85082c943f430e0a9770ec8bff5492f2013431a8 Mon Sep 17 00:00:00 2001 From: daladim Date: Fri, 4 Nov 2022 11:41:37 +0100 Subject: [PATCH 4/4] [doc] Doctests are now run when invoking `cargo test` --- .github/workflows/rust.yml | 5 ----- Cargo.toml | 3 --- 2 files changed, 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 652966b..7b75495 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -35,11 +35,6 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - # Cargo test (for the doctests only) - - uses: actions-rs/cargo@v1 - with: - command: test - args: --doc clippy-on-diffs: runs-on: windows-2022 diff --git a/Cargo.toml b/Cargo.toml index c8dfe48..40d92ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,3 @@ widestring = "1.0" zerocopy = "0.6" # thiserror = "~1.0" # anyhow = "~1.0" - -[lib] -doctest = false