Basically a KrabsETW rip-off written in Rust

Overview

FerrisETW πŸ¦€

Basically a KrabsETW rip-off written in Rust, hence the name Ferris πŸ¦€

All credits go to the team at Microsoft who develop KrabsEtw, without it, this project probably wouldn't be a thing.

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 πŸ”₯

Examples

You can find a few examples within the Examples folder. 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.

The following snippet shows the basic usage of the library

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

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 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 instead of using the 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!!
  • Shaddy for, pretty much, teaching me all the Rust I know πŸ˜ƒ
Comments
  • Panic on Kernel Trace close

    Panic on Kernel Trace close

    Hi there,

    I get a panic when I try to call .stop() on a Kernel Trace. Basic code:

    let provider_io = Provider::kernel(&kernel_providers::FILE_IO_PROVIDER)
        .build()
        .unwrap();
    
    let mut trace = KernelTrace::new()
        .named(String::from("HijackWatcher"))
        .enable(provider_io)
        .start()
        .unwrap();
    
    std::thread::sleep(Duration::new(3, 0));
    trace.stop();
    

    Strack Trace:

    thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', C:\Users\xxx\.cargo\registry\src\github.com-1ecc6299db9ec823\ferrisetw-0.1.1\src\trace.rs:112:30
    stack backtrace:
       0:     0x7ff6de00a782 - std::backtrace_rs::backtrace::dbghelp::trace
                                   at /rustc/897e37553bba8b42751c67658967889d11ecd120/library\std\src\..\..\backtrace\src\backtrace\dbghelp.rs:98
       1:     0x7ff6de00a782 - std::backtrace_rs::backtrace::trace_unsynchronized
                                   at /rustc/897e37553bba8b42751c67658967889d11ecd120/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
       2:     0x7ff6de00a782 - std::sys_common::backtrace::_print_fmt
                                   at /rustc/897e37553bba8b42751c67658967889d11ecd120/library\std\src\sys_common\backtrace.rs:66
       3:     0x7ff6de00a782 - std::sys_common::backtrace::_print::impl$0::fmt
                                   at /rustc/897e37553bba8b42751c67658967889d11ecd120/library\std\src\sys_common\backtrace.rs:45
    

    Windows version:

    OS Name:                   Microsoft Windows 11 Pro
    OS Version:                10.0.22621 N/A Build 22621
    
    opened by pathtofile 4
  • Added Github Actions

    Added Github Actions

    Closes #16

    This is using the basic Rust action automatically suggested by GitHub. The stock https://github.com/BamPeers/rust-ci-github-actions-workflow you suggested seems to run on Ubuntu. I'm not sure forking and customizing it is worth it.

    I think we'll always be able to add clippy, rustfmt, etc. later from this first step

    opened by daladim 4
  • fix: retaining ownership of an array instead of returning a dangling pointer

    fix: retaining ownership of an array instead of returning a dangling pointer

    ENABLE_TRACE_PARAMETERS has a pointer (A) to an array of EVENT_FILTER_DESCRIPTORs, which contains pointers (B) themselves. I cared about the lifetime of pointers A and B, but forgot about the lifetime of the array, which was actually dropped very soon :-(

    Now this array is owned by EnableTraceParameters, which fixes this dangling pointer issue.

    This bug could lead to failing enabling providers, with EtwNativeError(IoError(Os { code: 87, kind: InvalidInput, message: "The parameter is incorrect." }))

    opened by daladim 3
  • Turbofish operator for try_parse

    Turbofish operator for try_parse

    This PR depends on #63 and #65 , as its first commits come from them.

    This makes it possible to use the turbofish operator for try_parse, which is slightly more convenient than the current solution. This improvement was already considered by @n4r1b quite a long time ago, since there was a // TODO: Find a way to use turbofish operator

    opened by daladim 3
  • A bunch of changes

    A bunch of changes

    1. Add a trace builder to clearly separate operations that are only valid at build-time vs. ones that can be performed on a live session
    2. Remove the internal worker thread, preferring to expose that to end-users so that they may decide how to begin processing a trace. Also todo - expose start/end times from ProcessTrace in the API
    3. Basic support for querying/setting trace information, and add an example of dumping all profile sources in the system
    opened by DrChat 3
  • Reviewed visibility

    Reviewed visibility

    This PR depends on #63 , #65 , #66 and #68 (it does not depend on all of them feature-wise, but merging them first will avoid git conflicts, especially in the use ....; lines at the beginning of files)

    This PR (or rather, its last 5 commits) reviews the visibility of many types and modules, so that the user-facing API and documentation only exposes what he will need.

    • This makes it easier to read the doc
    • This leaves us more room to do future changes that otherwise could be breaking (since breaking change in a public API requires a new major version. That's too bad for types that are not supposed to be used).

    Maybe(?) some issues were pre-existing, but I think most of these too-widly-visible items were due to uncautious refactos of mine

    opened by daladim 2
  • Safer traces, part 1: Provider builder

    Safer traces, part 1: Provider builder

    This PR depends on #53 and #55 (as the first few commits are cherry-picked from these branches), that must be merged first.

    This introduces a ProviderBuilder that must be used to create Providers. This way:

    • setters can be removed from Provider, which will ensure it is not modified at runtime (this will help reviewing unsafe blocks and resolving #45 )
    • the compiler now enforces GUID are set. No need to check it at runtime
    opened by daladim 2
  • Fix: re-export types from windows-rs that we expose in our public API

    Fix: re-export types from windows-rs that we expose in our public API

    Closes #46

    @n4r1b , since this PR (and the few previous ones targetting master) add new items, without breaking back-compatibility. I think this could be a good time to release a 0.2 version, what do you think?

    opened by daladim 2
  • Trait bound error caused by diff windows-rs dependence version

    Trait bound error caused by diff windows-rs dependence version

    windows-rs version in ferrisetw's Cargo.toml is 0.39, but windows-rs version in my project is 0.42. This will cause the following error when compiling:

    error[E0277]: the trait bound `Parser<'_>: TryParse<GUID>` is not satisfied
      --> src/main.rs:23:66
       |
    23 |                     let guid: GUID = TryParse::<GUID>::try_parse(&mut parser, "Guid").unwrap();
       |                                      --------------------------- ^^^^^^^^^^^ the trait `TryParse<GUID>` is not implemented for `Parser<'_>`
       |                                      |
       |                                      required by a bound introduced by this call
       |
       = help: the following other types implement trait `TryParse<T>`:
    

    My project can only be compiled by using windows-rs with the same version as ferrisetw.

    Is there any good solution?

    opened by 0xlane 2
  • Windows rs upgrade

    Windows rs upgrade

    Hello, thanks for ferrisetw.

    I've been experimenting with it for a few days now, and I'd like to extend and improve it (expect other merge requests soon :D ). To start with, I'm upgrading to a newer version of windows-rs. (tests are still passing of course. We may want to enable Github Actions on that repo though).

    I've split into several commits for better review-ability, but only the last one actually compiles, so you'd probably want to squash them when merging.

    opened by daladim 2
  • Enable non-threaded or blocking trace session

    Enable non-threaded or blocking trace session

    Hi, I really like the work you've done here, good job!

    For my purposes, I usually want to start a trace, and run it until I quit the program. In the current implementation Ferris spawns a new anonymous thread to do the processing, which means I need to either do a 'sleep forever' or 'wait for user input' hack.

    Would it be possible to either:

    • Run a trace in the same thread (e.g. 'blocking' mode); or
    • Expose the spawned thread in the Trace struct, so I could do a .join on it
    opened by pathtofile 1
  • Merging next_major_version into master

    Merging next_major_version into master

    This MR brings back all the commits added to next_major_version in the course of the last months into master, because I feel like ferrisetw may be ready to release a newer version.

    However, we could try releasing 0.1.2 first (from the current master, e.g. to check docs.rs succeeds at generating its doc)

    opened by daladim 0
  • Manage already-running traces

    Manage already-running traces

    Maybe ferrisetw could be used to list and control already existing traces.

    See QueryAllTracesW : https://learn.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-queryalltracesw

    Could it also stop them? Subscribe to them? etc.

    opened by daladim 1
  • Support more TDH property types

    Support more TDH property types

    Property::new() can return PropertyError::UnimplementedType, depending on the TDH property flags.

    This would be good to support more types (e.g. PropertyFlags::PROPERTY_STRUCT, or PropertyFlags::PROPERTY_PARAM_LENGTH)

    opened by daladim 0
  • Style: replace Box<Arc<T>> with Arc<T>

    Style: replace Box> with Arc

    That's what is suggested by Clippy

    warning: usage of `Box<Arc<CallbackData>>`
       --> src\trace.rs:173:20
        |
    173 |     callback_data: Box<Arc<CallbackData>>,
        |                    ^^^^^^^^^^^^^^^^^^^^^^
        |
        = note: `#[warn(clippy::redundant_allocation)]` on by default
        = note: `Arc<CallbackData>` is already on the heap, `Box<Arc<CallbackData>>` makes an extra allocation
        = help: consider using just `Box<CallbackData>` or `Arc<CallbackData>`
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
    

    In our case, we do not only want the CallbackData to be on the heap, but the ref counter as well. https://github.com/rust-lang/rust/blob/96ddd32c4bfb1d78f0cd03eb068b1710a8cebeef/library/alloc/src/sync.rs#L352 suggests the ref counters (both atomic::usizes) really live on the heap, so this change should be sensible.

    That's to avoid an allocation, on a struct that's only created a handful of times, this currently properly works that way, and it's a private implementation detail of the crate (so it can be changed at anytime without any trouble for the users), so I'm leaving this for later.

    opened by daladim 1
  • Do not ignore the very last events

    Do not ignore the very last events

    Since (a PR to come, probably #63), we're ignoring the very last ETW events that were still in the buffers when we called CloseTrace

    We may want to process them, and drop the memory structures when we're sure every event is processed. See

    /// TODO: it _might_ be possible to know whether we've processed the last buffered event, as
    ///       ControlTraceW(EVENT_TRACE_CONTROL_QUERY) _might_ tell us if the buffers are empty or not.
    ///       In case the trace is in ERROR_CTX_CLOSE_PENDING state, we could call this after every
    ///       callback so that we know when to actually free memory used by the (now useless) callback.
    ///       Maybe also setting the BufferCallback in EVENT_TRACE_LOGFILEW may help us.
    
    opened by daladim 0
  • Memory usage improvements in EventTraceProperties

    Memory usage improvements in EventTraceProperties

    Currently, we have

    EventTraceProperties {
        etw_trace_properties,
        trace_name: [0; 1024],
        log_file_name: [0; 1024],
    };
    

    We could avoid using two arrays of 1024 bytes to store a string that's probably shorter than this. Maybe we could make EventTraceProperties generic on the name length, or really support dynamic allocation of the name

    opened by daladim 0
Owner
n4r1B
n4r1B
A lightning fast version of tmux-fingers written in Rust, copy/pasting tmux like vimium/vimperator

tmux-thumbs A lightning fast version of tmux-fingers written in Rust for copy pasting with vimium/vimperator like hints. Usage Press ( prefix + Space

Ferran Basora 598 Jan 2, 2023
A command-line tool collection to assist development written in RUST

dtool dtool is a command-line tool collection to assist development Table of Contents Description Usage Tips Installation Description Now dtool suppor

GB 314 Dec 18, 2022
Simple ray tracer written in Rust

Simple ray tracer written in Rust from scratch I've just finished my first semester at the Faculty of Applied Mathematics and Computer Science at the

Vladislav 190 Dec 21, 2022
BSV stdlib written in Rust and runs in WASM environments

BSV.WASM A Rust/WASM Library to interact with Bitcoin SV Installation NodeJS: npm i bsv-wasm --save Web: npm i bsv-wasm-web --save Rust: https://crate

null 56 Dec 15, 2022
A wasm interpreter written by rust

A wasm interpreter written by rust

nasa 69 Dec 6, 2022
Rustymind is a driver and parser for NeuroSky MindWave EEG headset written in pure Rust.

Rustymind is a driver and parser for NeuroSky MindWave EEG headset written in pure Rust. You can use it to connect, interact, and plot real time data from the headset.

Junjun Dong 34 Sep 13, 2022
a wasm interpreter written by rust

wai (WebAssembly interpreter) A simple wasm interpreter This is an ongoing project DEMO 2021-06-27.10.23.18.mov Install Install via Homebrew brew inst

nasa 69 Dec 6, 2022
Simple Spreadsheet editor written in Rust

Rexcel Simple Spreadsheet editor written in Rust Keyboard Shortcuts: CTRL + S => Save CTRL + Q => Quit CTRL + W +> Save And Quit CTRL + ALT + S => Sav

Saulane 16 Dec 1, 2022
Re-implementation of Panda Doodle in Rust targetting WASM, a mobile game originally written in C++

Description This is the source code of my game Panda Doodle, which can be played at https://pandadoodle.lucamoller.com/ (it's best playable on touch s

null 79 Dec 5, 2022
Utilities to gather data out of roms. Written in Rust. It (should) support all types.

snesutilities Utilities to gather data out of roms. Written in Rust. It (should) support all types. How Have a look at main.rs: use snesutilities::Sne

Layle | Luca 5 Oct 12, 2022
A program written in pure Rust to query music info from mpd and display it in a notification.

musinfo A program written in pure Rust to query music info from mpd and display it in a notification. Note: Cover art is expected to be placed at /tmp

Cpt.Howdy 10 Aug 16, 2022
ᎩᎦᎨ᎒ (IPA: [gigagei]) is a random quote fetching console utility. Written in Rust.

gigagei ᎩᎦᎨ᎒ (IPA: [gigagei]) is a random quote fetching console utility. Written in Rust. Installing Use latest pre-built binary from releases Buildi

veleth 10 Jun 17, 2022
A rewrite of Phonelink for Windows Forms written in Rust, with cross-platform support.

phonelink-rs A rewrite of Phonelink for Windows Forms written in Rust, with cross-platform support. Usage Clone the repository and build, or download

ahsan-a 4 Aug 6, 2022
A simple omegle API written in Rust

omegalul-rs omegalul-rs is a work-in-progress opensource library for building Omegle clients. Features Current Features Fetching random server from om

NV6 5 Jun 21, 2022
rustBoot is a standalone bootloader, written entirely in Rust

rustBoot is a standalone bootloader, written entirely in Rust, designed to run on anything from a microcontroller to a system on chip. It can be used to boot into bare-metal firmware or Linux.

null 78 Dec 28, 2022
ASM moulinette written in Rust(πŸš€)

BFM: The Blazing Fast Moulinette ASM moulinette written in Rust( ?? ) Build with cargo build, run with cargo run -- <flags> <args> Flags: -h or --help

TimothΓ©e Denizou 5 Sep 17, 2021
Simplified glue code generation for Deno FFI libraries written in Rust.

deno_bindgen This tool aims to simplify glue code generation for Deno FFI libraries written in Rust. Quickstart # install CLI deno install -Afq -n den

Divy Srivastava 173 Dec 17, 2022
MIPS assembler written in Rust

frasm MIPS assembler written in Rust About frasm is an assembler written in Rust speicifally for the MIPs architecture. This is my first time writing

Jacob Mealey 3 Oct 23, 2022
A program written in Rust, that allows the user to find the current location of the International Space Station and see it on a map.

ISS Location ViewFinder A program written in Rust, that allows the user to find the current location of the International Space Station and see it on

Suvaditya Mukherjee 2 Nov 8, 2021