# eBPF for Windows: running Linux-style eBPF bytecode on Windows 11 and Server 2022

> Microsoft's eBPF for Windows hosts the existing uBPF and PREVAIL components behind a Windows kernel execution context and a libbpf-compatible API. The preferred deployment path compiles bytecode to C with bpf2c rather than JIT-compiling it.

**microsoft/ebpf-for-windows** — eBPF implementation that runs on top of Windows

- Repository: https://github.com/microsoft/ebpf-for-windows
- Stars: 3,566 · Forks: 310
- Language: C
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/microsoft-ebpf-for-windows

## What eBPF for Windows solves, and who it is for

Windows has no general mechanism for loading small, verified programs into the kernel to observe or filter traffic at a hook point. Linux does, and an ecosystem of toolchains, verifiers and libraries grew around it. This project takes that Linux-side machinery, keeps the bytecode format and the libbpf API surface, and supplies the Windows-specific hosting layer in between. The README is explicit that the project is a work-in-progress and that it takes existing eBPF projects as submodules rather than forking them.

The intended audience is narrow. You need Windows 11 or later, or Windows Server 2022 or later, per the Getting Started section. You are expected to already have a Linux eBPF workflow: clang produces the ELF object, and the same source-level constructs should carry over. The README frames the goal as source code compatibility for code that uses common hooks and helpers that apply across OS ecosystems, which is a deliberately hedged promise. Programs built on Linux-internal data structures are out of scope.

So the realistic user is a team with an existing eBPF program for a portable use case such as DoS protection or observability, plus a Windows fleet it cannot instrument with the usual Linux tooling. The README names DoS protection and observability as the motivating use cases.

## Three ways bytecode reaches the Windows kernel

The architecture diagram in the repository describes a single front end and three back ends. Clang compiles source to eBPF bytecode stored in ELF format. From there the paths diverge.

The preferred path is native mode. The bytecode goes to bpf2c, which hands it to the PREVAIL verifier. If the program passes verification, bpf2c converts each instruction into equivalent C statements, documented in docs/NativeCodeGeneration.md, and the standard Visual Studio toolchain builds that C into a Windows driver module ending in .sys. The README calls this the preferred way of deploying eBPF programs and ties the preference to security under HVCI.

The second path is a JIT compiler. A user-mode service, eBPFSvc.exe, JIT compiles bytecode through the uBPF JIT into native code that is then passed to the kernel-mode execution context. The third path is an interpreter, also from uBPF, which loads bytecode directly into the kernel-mode execution context. The README notes that the interpreter exists only in debug builds and not in release builds because it is considered less secure.

For the JIT and interpreter paths, eBPFSvc is what ensures the programs pass all verifier checks. Loading and attaching go through ebpfapi.dll, which exposes libbpf APIs, and can be driven by an application, by bpftool, or by the Netsh command line tool. Loaded programs attach to hooks and call helpers exposed by the eBPF shim, which wraps public Windows kernel APIs so that eBPF works on existing Windows versions. The README states that many helpers already exist and that more hooks and helpers will be added over time.

## Installing eBPF for Windows and running a first program

The README does not reproduce installation commands. It points to docs/GettingStarted.md for trying the project out, and that is where the actual steps live. What the README does establish is the platform floor: Windows 11 or later, or Windows Server 2022 or later. If you are on Windows 10, stop here.

Once the environment from the Getting Started Guide is in place, the loading surface is ebpfapi.dll and its libbpf-compatible entry points. A typical sequence loads an ELF object produced by clang and attaches it to a hook. The exact API names come from the libbpf project, so the call shapes should look familiar if you have written a Linux loader:

```c
// Sketch of the load path: ebpfapi.dll exposes libbpf APIs.
// Confirm exact names and headers in docs/GettingStarted.md
// before compiling, since the README does not list them.
```

The README also names two command line front ends that sit on the same library. bpftool and the Netsh command line tool both consume ebpfapi.dll, so a shell-driven check of what is loaded does not require writing a loader first.

For the native path, the step that differs from Linux is bpf2c. You feed it the verified bytecode, get C back, and build that C with Visual Studio into a .sys driver. The README's native code generation document is the reference for what the emitted C looks like. Expect the output to be a build artifact you sign and ship, not something you generate at runtime.

If a program fails, the README directs you to docs/TroubleshootingGuide.md, and there is a separate tutorial on debugging eBPF verification failures in docs/debugging.md. Verification failures are the common early obstacle, and that second document exists specifically for them.

## HVCI is the constraint that shapes the whole design

The clearest limitation in the README is the HyperVisor-enforced Code Integrity answer. With HVCI enabled, eBPF programs cannot be JIT compiled. They can still run in native mode. That single sentence rules out the JIT and interpreter paths on any hardened Windows deployment, which in practice is most managed enterprise fleets.

The reasoning the README gives is that HVCI restricts what code the kernel will execute, and JIT-compiled code generated at runtime does not fit that model. Native mode does, because the bytecode becomes C, the C becomes a driver, and the driver goes through the normal signing and build pipeline like any other kernel component. That is why the README calls native mode the most secure option and the preferred one.

The cost is real. You give up the ability to load a program and have it running in the same session. Native mode inserts a compile-and-build step, and the resulting .sys file has to be produced and signed through your existing driver pipeline. For iterating on a program, that is a slow loop, and it means the person writing eBPF has to be comfortable with the Visual Studio and driver build tooling, not just with clang and a loader.

The interpreter's status is a second constraint worth noting. It is present only in debug builds. If you were planning to prototype by interpreting bytecode on a release build, the README says that build does not contain the interpreter at all.

## Where the Linux compatibility promise stops

The FAQ answers the compatibility question directly and without overselling. The intent is source code compatibility for code that uses common hooks and helpers that apply across OS ecosystems. Linux provides many hooks and helpers, and the README says some of them are very Linux specific, for example those using Linux internal data structs, and those would not be applicable to other platforms.

Read that as a scoping statement rather than a guarantee. A program written against a hook that has a Windows equivalent should port. A program that reaches into Linux kernel structures will not, and no amount of build configuration changes that. The practical step before adopting is to enumerate the hooks and helpers your program calls and check them against the published helper definitions in the project's generated documentation. The README links to a hooks enumeration and a helper definitions header, so the list is inspectable.

The other half of the compatibility story is on the application side. Applications that interact with eBPF programs get libbpf APIs, so a loader written against libbpf has a plausible path to Windows. That is a separate concern from whether the eBPF program itself ports, and it is easy to conflate the two when planning a migration.

The README is also honest about maturity. It calls the project a work-in-progress and says more hooks and helpers will be added over time. Treat the current hook and helper set as the boundary of what you can build today, not as a temporary gap that will close on your schedule.

## How it compares with running eBPF on Linux

The obvious alternative is not another Windows product. It is running the same eBPF program on Linux, where the verifier, the JIT, the helper set and the loader ecosystem are the native environment rather than a compatibility layer. If your workload can live on a Linux host, there is no reason to introduce the Windows hosting layer, and the README does not pretend otherwise.

The difference in approach matters most in the deployment step. On Linux, a verifier accepts bytecode and the kernel JITs it, so the loop from source to running program is short. Here, the path the README prefers inserts bpf2c and a Visual Studio build, producing a signed driver. You trade iteration speed for something the Linux JIT path does not give you on Windows: code that satisfies HVCI.

A second difference is the verifier. Both sides verify, but on Windows the verifier is PREVAIL, reached through bpf2c on the native path and enforced by eBPFSvc on the JIT and interpreter paths. A program that passes on Linux is not automatically accepted by PREVAIL, and the README's separate debugging tutorial for verification failures exists because that mismatch is common enough to document.

The third difference is the helper surface. Linux's helper set is large and tied to kernel internals. The Windows shim wraps public Windows kernel APIs, so helpers that have no Windows equivalent simply are not there. Porting is therefore a per-program exercise, not a flag.

## Licence, releases and what upgrades cost

The repository is MIT licensed, with the licence text in LICENSE.txt. MIT is permissive, so it does not impose copyleft obligations on your own code. That said, this project takes other projects as submodules, including the IOVisor uBPF project and the PREVAIL verifier, and those carry their own licences. If you redistribute a build, check the submodule licences rather than assuming the top-level MIT file covers everything. This is a description of what is in the repository, not legal advice.

The release cadence visible from the tags is roughly monthly: v1.4.0 on 2026-07-24, v1.5.0 on 2026-08-20, and v1.6.0 on 2026-09-18. The last push to the default branch was on 2026-09-21. The repository is not archived. That cadence is the upgrade cost you should budget for: a project moving this fast can change hooks, helpers and the native code generation output between releases, and anything you generate with bpf2c is tied to the version that produced it.

The concrete implication is that a .sys driver built from bpf2c output is a versioned artifact. When you move to a new release, plan to regenerate and rebuild it rather than assuming the old binary keeps working. The version.json file in the repository root is where the project tracks its version, which is the thing to diff when you are deciding whether an upgrade touches your build.

## Conclusion

Adopt it if you already write eBPF programs against common hooks and helpers and need them to run on Windows 11 or Windows Server 2022 without a Linux host in the loop. Do not adopt it if your programs depend on Linux-specific hooks and internal data structures, or if your machines run with HVCI enabled and you planned to rely on JIT compilation, since the README states that path does not work under HVCI. Before committing, verify which hooks and helpers your program actually needs against the published helper list, confirm the Getting Started Guide's supported Windows builds match your fleet, and decide whether you can ship the generated driver as a signed .sys file, because that is the deployment mode the project calls preferred.

## FAQ

### Can eBPF programs crash the Windows kernel?

The project routes programs through the PREVAIL verifier before they run, and on the JIT and interpreter paths the eBPFSvc service ensures the programs pass all verifier checks. The interpreter, which the README considers less secure, is present only in debug builds and not in release builds.

### What can eBPF for Windows be used for?

The README names DoS protection and observability as the motivating use cases for extending an OS kernel with eBPF. Loaded programs attach to hooks and call helpers exposed by the eBPF shim, which wraps public Windows kernel APIs.

### What are the limitations of eBPF on Windows?

With HVCI enabled, programs cannot be JIT compiled and must run in native mode, which means going through bpf2c and a Visual Studio driver build. Hooks and helpers that are Linux specific, such as those using Linux internal data structs, are not applicable on Windows.

## Sources

- [Issues](https://github.com/microsoft/ebpf-for-windows/issues)
- [License: MIT](https://github.com/microsoft/ebpf-for-windows/blob/main/LICENSE)
- [microsoft/ebpf-for-windows on GitHub](https://github.com/microsoft/ebpf-for-windows)
- [README](https://github.com/microsoft/ebpf-for-windows/blob/main/README.md)
- [Releases](https://github.com/microsoft/ebpf-for-windows/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/microsoft-ebpf-for-windows
