DEV Community

Cover image for Why I Wrote Our Windows Endpoint Security Agent in Rust
Chuks Awunor
Chuks Awunor

Posted on

Why I Wrote Our Windows Endpoint Security Agent in Rust

An endpoint security agent has an awkward job. It runs on every machine you protect, usually with elevated privileges, around the clock. It parses untrusted input all day: process command lines, file paths, network data, event logs. And if it crashes or leaks memory, the customer notices before you do.

In other words, it is exactly the kind of software where memory bugs turn into security bugs.

When I built the Windows endpoint agent for the GuardsArm SOC, I wrote it in Rust. This post covers why, and what the tradeoffs looked like in practice.

The threat model for the agent itself

People tend to think about what an agent detects. Fewer think about the agent as an attack surface. But consider what it is:

  • A long-running privileged process
  • Reading data an attacker can influence
  • Present on every endpoint in the fleet

A buffer overflow or use-after-free in that process is not just a crash. It is a potential privilege escalation on every machine you are supposed to be protecting. Security tooling has a long history of exactly this problem.

Memory-safe languages remove whole classes of these bugs at compile time. That is the main reason the US Office of the National Cyber Director's 2024 report pushed the industry toward memory-safe languages for critical software. For an endpoint agent, that argument is hard to ignore.

Why not C#, Go, or C++?

C++ gives you the performance and the low-level Windows access, but you carry the memory safety burden yourself. Tooling helps. Discipline helps. Neither is a guarantee.

C# / .NET is the natural Windows choice and is memory-safe. The costs are a runtime dependency on every endpoint, garbage collection pauses, and a larger footprint. For an agent that should be close to invisible on a busy workstation, that matters.

Go is memory-safe and ships as a single binary, but it also carries a garbage-collected runtime, and calling into the Win32 API is less pleasant than it looks.

Rust gave us memory safety without a garbage collector, predictable resource usage, a single self-contained binary, and direct access to the Windows API through the windows crates.

What it looks like day to day

A few things stood out once the agent was in production:

The compiler does the arguing up front. Rust is slower to write at first. The borrow checker forces decisions about ownership and lifetimes that other languages let you defer. Those decisions are exactly the ones that cause crashes in a long-running process, so I would rather make them at compile time.

Unsafe is contained. Talking to Win32 still means unsafe blocks. The difference is that they are small, explicit, and easy to audit. When something goes wrong, you know where to look.

Concurrency without fear. An agent collects telemetry from several sources at once and ships it upstream. The type system catches data races before they ship.

Footprint stays flat. No GC means memory and CPU usage stay predictable. Users do not open Task Manager and wonder why the security tool is eating their laptop.

The tradeoffs

It is not free:

  • Hiring is harder. There are fewer Rust developers than C# developers, especially with Windows internals experience.
  • Some Windows APIs are awkward to wrap safely, and you end up writing thin safe abstractions over them yourself.
  • Compile times are longer than Go.

For most line-of-business apps, those tradeoffs might not be worth it. For software that runs privileged on every endpoint and reads hostile input all day, I think they are.

Takeaway

If you are building security tooling, the tool itself is part of your attack surface. Picking a memory-safe language for the parts that touch untrusted data is one of the cheapest risk reductions available.

The agent feeds the GuardsArm SIEM, which powers our 24/7 managed SOC for regulated organizations in Canada and the US. Happy to answer questions in the comments about Rust on Windows or building detection pipelines.

Top comments (0)