Skip to content
← All posts
· Delta1 Labs Glass.NETDecompilerGuide.NET

Understand unfamiliar third-party .NET code with Glass.NET

You depend on a DLL you have no source for — an abandoned package, an internal library whose author left, a vendor SDK with thin docs — and you need to know how it actually works. Here is a concrete investigation workflow in Glass.NET, the free, static-only .NET decompiler that never runs the assembly it inspects.

Sooner or later every .NET project leans on a DLL that nobody can explain. The package was abandoned three years ago and its repository is gone. The internal utility library was written by someone who has long since left the company. The vendor SDK ships with documentation that stops exactly where your question starts. The behaviour is wrong, or surprising, or simply undocumented — and the one thing that would settle it, the source, is the one thing you do not have.

You do not need it. A compiled .NET assembly carries a near-complete, recoverable description of what it does: IL plus rich metadata naming every type, method and field. Glass.NET reads that and reconstructs readable C#, turning “I have no idea what this dependency does” into “let me read the method and find out.” It is free, and it is strictly static — it reads metadata and IL and reconstructs C#, and never executes the assembly it inspects. That last point matters most here: you can point Glass at a dependency you do not trust, because nothing in the DLL ever runs.

Let’s make it concrete. Say you depend on an abandoned NuGet package — call it Acme.Resilience — that wraps your outbound HTTP calls in a retry policy. Intermittently, a call that should have failed fast hangs for the better part of a minute; another that should have retried gives up at once. The docs are a paragraph in a dead README. You need to know what RetryPolicy.ExecuteAsync actually does: how it computes its backoff, which exceptions it treats as retryable, and whether it ever talks to the network on its own. Here is how to find out.

Get the assembly on disk and open it

First, find the DLL. For a NuGet dependency it is sitting in the global packages cache — %USERPROFILE%\.nuget\packages\<id>\<version>\lib\<tfm>\ on Windows, ~/.nuget/packages/... elsewhere. For an internal library or a vendor drop, it is wherever your build copied it, usually bin/.

Open it in Glass.NET: drag the file onto the window, or File ▸ Open Assembly. Glass loads the metadata and lists the assembly in a tree. You do not need a matching PDB or the original source — a plain managed .dll is enough, and Glass reconstructs the C# without symbols (there is more on that in decompiling without PDB symbols). And because opening, reading and exporting are all static, this first step is already safe: a package you would hesitate to run is fine to open, because Glass never runs it.

Orient before you read

Resist the urge to dive straight into a method. Spend thirty seconds getting the shape of the thing first. Expand the assembly into its namespaces ▸ types ▸ members tree — this mirrors the structure the compiler recorded, so you immediately see whether you are dealing with three tidy types or a sprawl of a hundred. For Acme.Resilience you would expect a small public surface — RetryPolicy, maybe a RetryOptions, an exception type or two — sitting above a handful of internal helpers.

The Assembly Information view is the other orientation tool, and it answers a question you want answered early: what can this thing even do? It shows the target framework, the product attributes, and — the useful part — the referenced assemblies. If a “retry helper” references System.Net.Http that is expected; if it references System.Diagnostics.Process or something that has no business being in a backoff library, you have learned something before reading a single line. Glass’s exposure report is a fast companion here: run it to gauge how readable the assembly is. A plain open-source-style library should read clearly; if it comes back heavily obfuscated, that is itself a signal worth questioning. (That is the view from the other side of the sibling obfuscator Nebula.NET, whose code-virtualization is a Pro feature — but an ordinary dependency has no reason to look like that.)

Decompile the entry point

Now go to the code you actually call. The fastest route is Go To All (Ctrl+T), the fuzzy quick-open across every type and member: type ExecuteAsync and jump straight to it, no scrolling the tree. Click the member and Glass decompiles it to C# on demand.

Read ExecuteAsync as the entry point and read it narrowly — you are not trying to understand the whole library, only the path your call takes through it. You will see the retry loop, the call to whatever computes the delay, the predicate that decides whether an exception is retryable, and where the actual work gets invoked. That alone often answers the question. But the interesting logic usually lives one level down, in the helpers this method calls — so follow them.

Follow the calls to understand control flow

This is where a navigation-first decompiler earns its place, because understanding unfamiliar code is mostly about following calls, not reading one method top to bottom.

  • Go to Definition (F12, or Ctrl+Click) on the call to the delay helper jumps you straight into it. Now you can read exactly how the backoff is computed — and whether that hang you saw is an exponential delay with no cap, so a late retry waits 60 seconds by design.
  • Find All References (Shift+F12) on the RetryOptions.MaxAttempts field, streaming into the Find Results panel, shows everywhere the limit is read — so you can confirm whether the “give up immediately” case is a default of 1 being honoured literally somewhere you did not expect.
  • Call and type hierarchy let you walk up and down the structure — who calls the method you are in, what a given type derives from or implements — which is far faster than guessing from names.

References resolve from the decompiler’s own syntax tree, so F12 lands on the right symbol rather than a text match. Navigation history and a namespace ▸ type ▸ member breadcrumb keep you oriented as you move, so you can step back out the way you came in.

Drop to IL when the C# is ambiguous

An async method like ExecuteAsync is reconstructed from a compiler-generated state machine. The decompiler recognises that lowering and gives you back clean async/await C# most of the time — but “most of the time” is the catch. When the reconstructed C# reads oddly, or you cannot tell exactly where an exception is caught versus where the awaited delay sits in the state machine, switch the same member to the IL view. The IL is what actually runs, so it is the ground truth; reading it is the tie-breaker when the C# is ambiguous. You do not need to live in the IL — reach for it on the handful of methods where the high-level view rounds off a detail that matters. (Reading IL walks through what you are looking at, and how to decompile a .NET DLL to C# covers the broader workflow.)

Export it to a project for a proper look

Once you want to do more than read — grep the whole assembly for every HttpClient, Task.Delay or Process.Start, point an analyzer at it, annotate what you have figured out, or keep the library on disk as a permanent reference — export it to a buildable project. In the GUI that is File ▸ Export to Project; from a script it is the glass export subcommand:

glass export Acme.Resilience.dll --output ./Acme.Resilience.Source

That writes a .csproj plus the reconstructed .cs files and extracted resources — a project your toolchain understands, not a dump. A small library like this usually compiles straight back; a large or unusual one may need a short manual pass for unresolved references or exotic compiler-generated code. Either way you now have the source on disk to explore in your own IDE, exactly as if the author had shipped it — and, again, the export never ran the binary to produce it. The full deep-dive on that is in turning a DLL back into a buildable project.

The point

An unfamiliar dependency is not a black box. Open it, orient with the type tree and assembly information, decompile the entry point, follow the calls to see the real control flow, drop to IL where the C# is ambiguous, and export to a project when you want it on disk. In an afternoon you can know precisely what Acme.Resilience does with your HTTP calls — the uncapped backoff, the over-eager give-up, whatever it turns out to be — instead of guessing from behaviour. And because Glass.NET is free and strictly static, you did it all without running a single line of code you did not trust. Download Glass.NET, open the DLL that is puzzling you, and read it for yourself.

Try Nebula.NET

Harden your .NET code in minutes — start with the free edition.