PeNet, reading Windows PE headers without touching a Windows API
Portable Executable (PE) library written in .Net
At a glance
- What is it?
- PeNet is a C# library for parsing Portable Executable headers that depends on no native Windows APIs, so it runs on Linux and macOS as well as Windows, and it also computes import hashes for malware analysis. It is a file-format library first, and an analysis toolkit second.
- Who is it for?
- PeNet suits a .NET developer who needs to read PE headers in a Linux CI job, on a build agent, or inside a scanning service where loading the file into the Windows loader is not an option, and it suits malware analysts who want import hashes without shelling out to a native tool.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A PE reader that has no Windows in it
PeNet is described as a parser for Windows Portable Executable headers, completely written in C# and not relying on any native Windows APIs. That single design constraint is what defines the library.
A PE file is a container format: headers describing sections, imports, relocations, resources and metadata, followed by the code and data. Parsing it is normally a matter of reading bytes at documented offsets, and doing that from C# is entirely possible without Windows. What a native API would add is the ability to ask the operating system to do the parsing, or to load the image. PeNet deliberately does neither.
The payoff is portability. A .NET developer on Linux or macOS can read a Windows executable in a unit test, in a build pipeline, or in a service that never touches a Windows machine. For the security side of that, being able to compute an import hash on a Linux analysis box rather than shipping the sample to a Windows sandbox is a difference in workflow, not just convenience.
The library is published as a NuGet package named PeNet, and the badges show the version and download counters from NuGet. The API reference is hosted as PeNet Documentation and API Reference, generated with docfx, and there is a separate browser-based tool at penet.secana.org for an overview of PeNet or to analyse PE files without writing code.
The audience is small and specific: a C# developer who has to read Windows binaries from something that is not Windows.
Import hashes, the feature that comes from malware analysis
The project documentation is plain about why this library exists beyond header parsing. Alongside the core parser, it supports the creation of Import Hashes, which it describes as a feature often used in malware analysis, and it can extract a Certificate Revocation List, compute different hash sums, and do other useful work with PE files.
An import hash is a fingerprint of what a binary asks the operating system to load. Two samples that import the same set of DLLs and functions, in the same order, with the same ordinals, produce the same import hash, which gives an analyst a way to cluster malware families that were recompiled from the same source or template and then given different names. It is a similarity signal, not a verdict, and it is the kind of value that only makes sense once you already have a corpus of samples.
That shapes who should use PeNet and who should not. If you are building a scanner, a triage pipeline or a similarity index, import hashes give you a cheap, stable key. If you are building a general-purpose PE reader, the import hash is one method among many and costs you nothing extra.
The CRL extraction deserves a note too, because it is the kind of thing that is easy to overlook. A signed executable carries a certificate chain, and that chain can be checked against a revocation list. Being able to pull the CRL out of a file as data, rather than asking the Windows trust provider, is exactly the kind of operation that a native-API-free library can still do.
The hash sums mentioned alongside it cover the usual ground, and the documentation points to the API reference for the exact set of overloads, since the project offers several different hash sums rather than a single method.
The repository layout says it is a multi-project solution
The top-level entries show what a real library repository looks like, and one detail is worth calling out immediately: there is a `PeNet.sln`.
A solution file at the root means this is not a single project with a handful of files. There is a `src/` directory and a `test/` directory, and the split between them is the standard one. There is a `docfx/` directory, which is where the generated API documentation site comes from, and a `docs/` directory alongside it. The `resource/` directory holds the images used in the project, including the banner image in the project description.
Two files reveal the working habits. `PeNet.sln.DotSettings` is a ReSharper settings file, committed alongside the solution so that every contributor's IDE picks up the same code analysis configuration. And there is a `flake.nix` with a `flake.lock`, which means the build environment is declared in Nix and the exact dependency versions are pinned in the lock file. That is a deliberate choice to make a build reproducible rather than dependent on whatever the machine happens to have installed.
There is also an `.envrc`, the file that direnv reads to load environment variables into your shell when you enter the directory, so the local development setup is scripted rather than described in prose.
The build and test pipeline runs on Azure DevOps, with separate build and test badges pointing at the same pipeline definition, and the badges also cover the NuGet version, the download count and the project licence. The release process is documented in a single sentence: to release a new version, create a new GitHub Release, and the CI/CD pipeline will automatically create a new NuGet package and upload it to NuGet.org. Publishing is therefore a tag-and-release action rather than a manual step, and the version number in the package is not something you type into a file by hand.
Adding PeNet to a project, and where to look afterwards
The project documentation does not include a code sample, and it does not include a package installation snippet either. It points you at the API reference for the API and at the web tool for trying it out, and the package is on NuGet under the name PeNet, which is where a .NET developer would look for it.
The practical setup is therefore a matter of adding the PeNet package reference to your project and then reading the API reference, which is the documentation the project considers authoritative. That is not a criticism so much as a description of how a format library is usually learned: the type definitions in the API reference tell you what the headers expose, and the parser's object model is the thing you design against.
The browser tool is the other half of the first-use story. penet.secana.org is presented as the place to go for an overview of PeNet or to analyse PE files, and it is worth using before you write any code, because a file inspector shows you the structure the library will hand back, and you can check that the fields you care about are present in the header layout you expect.
One thing to verify when you pick a version. The release record shows v6.1.0 on 2026-04-30, v6.1.1 on 2026-05-02 and v6.1.2 on 2026-05-07, so three point releases of the same minor landed within a week. That pattern is normal for a library that is being adjusted, but it means the difference between 6.1.0 and 6.1.2 may matter to you even though the version numbers look adjacent, and the changelog is the place to check.
The last push to the repository was on 2026-10-15, and the project is not archived, so the code base is current even though the most recent published package is from May.
What a header parser cannot do for you
The limits of PeNet follow directly from what it is, and knowing them prevents a bad integration.
It parses. It does not load, map, execute or emulate. Nothing in the project description suggests the library can run a PE image or resolve its imports against a real system, and a header parser cannot tell you whether the binary would actually run on a given machine, because that question needs the loader and the environment. If your requirement is to run a sample in a sandbox, PeNet is the wrong tool and a Windows sandbox or a Linux emulation layer is the right one.
It also does not tell you whether a file is malicious. Import hashes cluster samples, they do not classify them, and a hash that matches a known family still needs human or automated triage to confirm. Anyone planning to use PeNet as a verdict rather than a signal is misusing it.
The third limit is scope of format. The library is a PE parser, so it says nothing about ELF, Mach-O or any other executable format, and the repository's own topics list it under PE, Windows and portable executable. A cross-platform build tool that needs to read binaries of several kinds will still need another library for the non-Windows ones.
There is a fourth, quieter point. Because the library does not depend on native Windows APIs, it is easy to run the same code everywhere, which means a bug in a header field that only manifests on an unusual file will not be caught by a test suite that only runs on one platform with one sample set. Format parsers live or die by their test corpus, and the documentation does not describe the size or shape of that corpus, which is a gap to think about if you are depending on it for a production scanner.
Licence, releases and the choice against a native tool
The licence is Apache-2.0, with the LICENSE file at the top of the repository and the badge pointing at it. That is the permissive grant with an explicit patent clause, and it means you can use PeNet in a commercial product, modify it and redistribute it, provided you keep the copyright notice and the licence text with it.
The alternative to PeNet is not another .NET library, it is a native tool. The usual comparison is a Python script wrapping a native parser, or a command line utility you shell out to. The difference in approach is real. PeNet gives you the parsed structure as typed objects inside your own process, with no subprocess, no output parsing and no platform requirement on the tool you invoke. A native tool gives you a mature, continuously maintained implementation of the format, and a process boundary.
The trade is between integration cost and coverage. A native tool is likely to know more about the corners of the format and to have seen more real-world samples, and it needs no maintenance from you. PeNet gives you an API your code can call directly, and the entire analysis pipeline stays in one language and one process, which is what you want when the parsed output feeds into the rest of your application rather than into a report.
There is a third path worth naming, which is the tool set that Windows itself ships. A project that only ever runs on Windows and only ever analyses Windows binaries has an option that requires no dependency at all, though it ties the code to the platform and to a runtime that is not available in a Linux CI container.
For a .NET team whose analysis runs cross-platform, PeNet is the option that keeps everything in process and in one language. Just confirm the specific header features you rely on exist in the version you pin, since the API surface is documented separately from the project description and the 6.1.x line was moving quickly.
Editorial conclusion
PeNet suits a .NET developer who needs to read PE headers in a Linux CI job, on a build agent, or inside a scanning service where loading the file into the Windows loader is not an option, and it suits malware analysts who want import hashes without shelling out to a native tool. It does not suit a project that needs to execute or load the binary, since PeNet parses the file structure and stops there, and it does not suit a codebase that wants a single package with everything included, because the source is split into several projects under src/. Verify first that the PE features your workflow needs are exposed by the version you take, since the 6.1.x line moved quickly through several point releases in a matter of days, and that the runtime you target matches the .NET Standard surface the library builds against.
Frequently asked questions
What is PeNet and what does it do?
PeNet is a C# library for parsing Windows Portable Executable headers, written entirely in C# and not relying on any native Windows APIs. It also supports creating Import Hashes, extracting a Certificate Revocation List, and computing different hash sums.
Can PeNet be used on Linux and macOS?
The project states that PeNet is completely written in C# and does not rely on any native Windows APIs, which is what makes it usable outside Windows. The package is on NuGet under the name PeNet, and the API reference is published as PeNet Documentation and API Reference.
What is an import hash and why does PeNet compute one?
The project documentation describes import hash creation as a feature often used in malware analysis. It gives a way to cluster binaries that import the same libraries and functions, which helps group samples that share a common source or build template.
How do I get a new version of PeNet?
The release process is documented as creating a new GitHub Release, after which the CI/CD pipeline automatically creates a new NuGet package and uploads it to NuGet.org. The build and test pipeline runs on Azure DevOps.
What licence is PeNet released under?
Apache-2.0, with the LICENSE file at the top of the repository. It permits use in commercial and closed-source products, modification and redistribution, provided the copyright notice and licence text are retained.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/secana-penet)Was this useful?