Skip to content

Commit

Permalink
Merge pull request #65 from daladim/more_doc2
Browse files Browse the repository at this point in the history
More doc
  • Loading branch information
daladim authored Nov 14, 2022
2 parents 60e2c13 + 85082c9 commit 56fb61e
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 108 deletions.
5 changes: 0 additions & 5 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -37,6 +39,3 @@ zerocopy = "0.6"
time = { version = "0.3", features = ["large-dates"], optional = true }
# thiserror = "~1.0"
# anyhow = "~1.0"

[lib]
doctest = false
87 changes: 15 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,39 @@
# 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.<br/>
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
[crate documentation on doc.rs](https://docs.rs/ferrisetw),
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
41 changes: 17 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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.<br/>
//! 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
Expand Down Expand Up @@ -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
Expand All @@ -53,26 +45,27 @@
//! use ferrisetw::trace::{UserTrace, TraceTrait};
//!
//! 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() {
Expand Down
7 changes: 5 additions & 2 deletions src/native/etw_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ impl<'callbackdata> EventTraceLogfile<'callbackdata> {

/// 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>{
Expand All @@ -279,6 +279,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<EVENT_FILTER_DESCRIPTOR> = filters
.iter()
.map(|efd| efd.as_event_filter_descriptor())
Expand All @@ -305,7 +308,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,
Expand Down
2 changes: 1 addition & 1 deletion src/native/etw_types/event_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,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)
///
Expand Down
5 changes: 5 additions & 0 deletions src/provider/event_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/n4r1b/ferrisetw/issues/51>
ByPids(Vec<u16>),
/// Filter by ETW Event ID.
ByEventIds(Vec<u16>),
Expand Down

0 comments on commit 56fb61e

Please sign in to comment.