Decompilation of C# code made easy with Visual Studio

Avatar

Mark

Have you ever found yourself debugging a .NET project or memory dump only to be confronted with a No Symbols Loaded page? Or maybe experienced an exception occurring in a 3rd party .NET assembly but had no source code to figure out why? You can now use Visual Studio to decompile managed code even if you don’t have the symbols, allowing you to look at code, inspect variables and set breakpoints.

We have recently released a new decompilation and symbol creation experience in the latest preview of Visual Studio 2019 version 16.5 that will aid debugging in situations where you might be missing symbol files or source code. As we launch this feature, we want to ensure that we are creating the most intuitive workflows so please provide feedback.

Decompilation and PDB generation with ILSpy

Decompilation is the process used to produce source code from compiled code. In order to accomplish this we are partnering with ILSpy, a popular open source project, which provides first class, cross platform symbol generation and decompliation. Our engineering team is working to integrate ILSpy technology into valuable debugging scenarios.

What are symbol files? Why do you need them?

Symbol files represent a record of how the compiler translates your source code into Common Intermediate Language (CIL), the CIL is compiled by the Common Language Runtime and executed by the processor. The .NET compiler symbol files are represented by program database files (.pdb), and these files are created as part of the build. The symbol file maps statements in the source code to the CIL instructions in the executable.

Debuggers are able to use the information in the symbol file to determine the source file and line number that should be displayed, and the location in the executable to stop at when you set a breakpoint. Debugging without a symbol file would make it difficult to set breakpoints on a specific line of code or even step through code.

Visual Studio currently provides the option to debug code outside your project source code, such as .NET or third-party code your project calls by specifying the location of the .pdb (and optionally, the source files of the external code). However, in many cases finding the correct symbol files or source code may not be feasible.

By integrating decompilation directly into your debugging experiences we hope to provide developers with the most direct route to troubleshooting issues in 3rd party managed code. We are initially integrating the decompilation experiences into the Module Window, No Symbols Loaded, and Source Not Found page.

No Symbols Loaded/Source Not Found

There are a several ways in which Visual Studio will try to step into code for which it does not have symbols or source files available:

  • Break into code from a breakpoint or exception.
  • Step into code.
  • Switch to a different thread.
  • Change the stack frame by double-clicking a frame in the Call Stack window.

Under these circumstances, the debugger displays the No Symbols Loaded or Source Not Found page and provides an opportunity to load the necessary symbols or source.

In the following example I have opened a crash dump in Visual Studio and have hit an exception in framework code. I do not have the original source code so If I try to switch to the main thread, I see the No Symbols Loaded page. However, it is now possible to decompile the code directly on this page and see the origins of the exception.

Image vs decompilation no symbols loaded

Module Window

During debugging the Modules window is a great place to get information related to the assemblies and executables currently in memory. To open the Modules window, select Debug > Windows > Modules.

Once you have identified a module that requires decompilation, you can right-click on the module and select “Decompile Source to Symbol File”. This action creates a symbol file containing decompiled source which in turn permits you to step into 3rd party code directly from your source code.

It will also be possible to extract source code to disk by right clicking on a module with embedded source and clicking “Extract Embedded Source”. This process exports source files to a Miscellaneous files folder for further analysis. In the following example I open an extracted .cs file and set a break point to better understand the 3rd party code I am using.

Shows decompilation and source extraction from Visual Studio Module window

Some Considerations

Decompilation of the CIL format, used in .NET assemblies, back into a higher-level language like C# has some inherent limitations:

  • Decompiled source does not always resemble the original source code. Decompilation is best used to understand how the program is executing and not as a replacement for the original source code.
  • Debugging code that was decompiled from an assembly that was built using compiler optimizations may encounter the following issues:
    • Breakpoints not always binding to the matching sourcing location
    • Stepping may not always step to the correction
    • Async/await and yield state-machines may not be fully resolved
    • Local variables may not have accurate names
    • Some variables may not be able to be evaluated in the IL Stacks is not empty
  • Source code extracted from an assembly are placed in the solution as Miscellaneous file:
    • The name and location of the generated files is not configurable.
    • They are temporary and will be deleted by Visual Studio.
    • Files are placed in a single folder and any folder hierarchy that the original sources had is not used.
    • The file name for each file has a checksum hash of the file.
  • Decompilation of optimized or release modules produces non-user code. If the debugger breaks in your decompiled non-user code, for example, the No Source window will appear. In order to disable Just My Code navigate to Tools > Options (or Debug > Options) > Debugging > General, deselect Enable Just My Code.
  • Decompilation will only generate source code files in C#.

Try it now!

Download the preview and try out decompilation and let us how it works for you! Please reach out and give us feedback over at Developer Community. Finally, we also have a survey for collecting feedback on the new experiences here. We look forward to hearing from you.

40 comments

Comments are closed. Login to edit/delete your existing comments

  • Avatar
    Max Mustermueller

    When it is that easy, how do I protect my code from being decompiled then?! Or to rephrase the question: When does .NET finally gets an ahead of time compiler generating native code making it impossible to decompile back to plain source code?

    The decompile issue exists for a long time and developers have to spend a lot of money on protectors, which are defeated by public tools with a single click. Other programming languages don’t have this issue, because they generate native code (which can be read in assembly, despise that being hard enough, but definitely not decompiled back to plain text source code).

    • Avatar
      Mark DownieMicrosoft employee

      As you have indicated decompilation has been around for a long time and these techniques are available for most mature platforms including .NET (this is true of disassembly also). Obfuscators are a popular option for folks seeking an additional layer of protection. I would add that explicit statements from the copyright owner about usage and decompilation is incredibly important. We have made it clear that using the decompiler does require the permission of the copyright holder and further that it is an optional feature.

      • drew
        drew

        I am in agreement with Max. The sharing of source code is one thing and I’m all for it. However, when it’s not intended to be shared that is different. Sure there are decompilers and always have been. But being able to step through my hypothetically new algorithm on a whim is otherwise known as reverse engineering intellectual property. And the warning not to use copyrighted material is not feasible as I’m pretty sure the decompiled code doesn’t include a copyright or any indication of such.

        • Avatar
          Zehev Spitz

          There is a flip side to this. Say my code depends on a library, and one day some change in my code — or an upgrade to the library — results in an exception in the library code or an unexpected result from the call to library code. Assuming no mention is made of this in the documentation, my choices are currently rather limited:

          * If it’s an opensource project, or a project with a good bug tracker, I can raise an issue and hope they get around to fixing it. That might be tomorrow, or next year, depending on how the library’s owners prioritize my issue; it almost certainly won’t be immediate.
          * I might revert the change I’ve made, but that assumes I know which change caused the problem. Also, I had reasons to make the change; those reasons presumably haven’t gone away.
          * I could guard against the problem: for an exception, wrap the library call in an exception handler; for an invalid result, wrap in an if…else. But I have no way of verifying that the same issue won’t happen for a broad class of input values; only the specific input values I have tried.

          Being able to look into the decompiled IL allows me to work towards an immediate resolution, and one that’s broadly appropriate.

        • Stuart Lang
          Stuart Lang

          I think it really important to understand that today, regardless of what features are shipped in Visual Studio, it is trivial for anyone to decompile your C# assemblies, if your users have ReSharper then they probably are already doing it without thinking about it.

          AOT sets out to solve other problems, not necessarily this one, but as a side effect this problem goes away. Otherwise if you want to stop trivial decompilation then you will need obfuscation, which can cost money, and will add complexity.

    • Avatar
      Gavin Williams

      .net already has a native compiler. We’ve had it for years – .net native. When .net 5 is released, all .net dev’s will get to use native compilation – until then, only uwp .net has it.

    • Avatar
      Mark DownieMicrosoft employee

      Decompilation has been around for almost as long as the .NET platform and is available for most other major dev platforms (this is true of disassembly also). So it is important to have terms, conditions and licenses in place to discourage behavior you do not approve of. Obfuscators are a popular option for folks seeking an additional layer of protection.

    • Avatar
      Anthony Jenniges

      Yes.

      ILSpy is one of the best decompilers. However it is missing some of the features that other more scary decompilers have like:
      – decompile assemblies into C# projects/solution
      – debug assemblies while showing decompiled code (and editable code with edit and continue) – very easy to set breakpoints. If you didn’t protect your code, the decompiled code is very close to original

      And if you have a license check (whether license key or checking with license server) developers can step through your code until they find the check and then simply remove that section of code and re-save the assembly.
      Now they get to use if for free.

  • Avatar
    Vasiliy Grebinnyk

    Coool feature. If you care about intellectual property you must use pro obfuscator. I am personally strugling with a No Symbols Loaded page debugging my own code in nuget package. I hope to move all my sharing code into nuget packages if this feature really works.

  • Avatar
    Rene Gillmeister

    It’s really great how Microsoft cares about its customers’ intellectual property. Instead of making sure that at least basic protection is finally available, you are now making it even easier for crackers and other criminals to rob developers of their livelihood. And then to say: “You can protect your source code by your EULA. Simply forbid decompiling…”. I think this feature will cause great grief.

    • Avatar
      Mark DownieMicrosoft employee

      Thanks for the comment Rene. I hear your concern on protecting intellectual property and the desire for some form of protection. Your feedback is definitely welcome as we continue to evolve this feature. If you are willing, we could talk on a call, I would like to hear more about the kinds of protection you would anticipate .NET provide more generally.

      • Avatar
        Rene Gi

        My proposed solution: It should be an opt-in. Only digitally signed software with an Allow Decompilation option in the application manifest should be decompilable. Decompiling programs that do not comply should be considered illegal and their authors should be held liable for damages.

      • Avatar
        Chuck Ryan

        My concern is the obvious script the Visual Studio development team is consistantly using to address the many areas where your customers do not like what you are doing. In the end it all boils down to “we are releasing this no matter what you think, we know best, but we will make some cosmetic changes to appease you”.

      • Avatar
        A Jennig

        ILSpy is the best decompiler but was missing debugging capabilities like another decompiler.

        Did the team consider how integrating ILSpy and giving it debugging/breakpoint abilities makes it more convenient and easier to find any company’s secret sauce? All that needs to be added is the ability to save an assembly after removing the license check (like another tool does) and Microsoft has created the perfect cracking tool – all integrating into Visual Studio – And many of the company’s that will more conveniently lose IP and license revenue are paying subscriptions for the instrument that will hurt them.

        How about getting with the obfuscation company’s BEFORE releasing to public?
        How about allowing obfuscation companies to “tag” assemblies as “do not decompile” with attributes and VS integrated ILSpy will respect that?

    • Avatar
      Patrick Smacchia

      This complain of “how do I protect my code from being decompiled then?!” and “Microsoft does not care about its customers’ intellectual property.” is completely absurd. Since its inception in 2002 .NET compiled code can be decompiled and read crystal clear with popular tools like .NET Reflector, IL Spy, dotPeak… This is a direct consequence of having IL/byte code and a CLR with a JIT compiler.

      Since 2002 those that want to protect their compiled code from decompilation just have to obfuscate it. There are mature free tools (like https://github.com/yck1509/ConfuserEx ) and paid tools available. This is what we do within our .NET shop since 2007 with success. Obfuscators will prevent from decompilation and will protect your intellectual property. However obfuscators won’t prevent solid hackers to hack your code, some advanced tricks based on “integrity checks” need to be used for that, but this is another story. Building and protecting software has never been (and won’t be) an easy task.

      This VS integration is awesome, no more “No Symbols Loaded” or “Source Not Found” is just awesome. Thanks Mark Downies and your team.

      • Avatar
        Keith Patrick

        I’m of the belief that obfuscation is an ultimately pointless endeavor. There’s some WinRT assemblies that don’t like being opened up ILSpy/Reflector, but pretty much anything is still going to be able to be readable. If you want to protect your business logic, you can copyright it (or *maybe* patent it, but that’s a gamble), or you can just host it on a web server, where your business logic stays on the secured server rather than be shipped to curious users.

  • Avatar
    Keith Patrick

    This could be a killer feature. I’ve lost count how many times I’ve installed the .Net Reflector VS addin to get just-in-time decompilation (and very nearly bought it last month). Every time, it’s to debug a single issue where ILSpy code isn’t quite matching up with what the debugger is telling me I’ve got….even if the add-in doesn’t get the source exactly right, just being able to add watches makes all the difference in the world (except, of course, the “Symbols have been optimized away…” message, but even then, going back a few frames usually lets the debugger show enough info to be useful.
    But the catch is, it’s got to work better than Rotor PDBs (I gave up on that years ago since it was a complete crapshoot when it came to getting the source to show up)

  • Avatar
    Hai Ha

    This is a great feature. I do debug of 3rd-party code a lot, most of the time to find out what they are doing wrong or see how they work to fix our code. To those who are worried their application being de-compiled: People do debug of 3rd-party library usually because the library itself involves in an issue. I myself care ZERO about your code because most codes are garbage, don’t over-estimate it. Also if you de-compile an application, it doesn’t make the application yours (prove it to me, for example, you have the whole source code for .net framework on the Internet, try making it yours?). Note that this feature is more about DEBUGGING the de-compiled code inside VS, which is why it is a good feature, not just about de-compiling them.

  • Jan Pavliš
    Jan Pavliš

    I think that instead of placing all generated source code files in a single folder it could be useful to (optionally) place them in a directory tree by namespace of the class in the file – the same way IL Spy does it. Large assemblies contain thousands of classes and this would make finding what I’m looking for easier. Also I would welcome if there was an option to generate not only C# code, but also IL together with C#. IL Spy already has this feature so I guess this shouldn’t be too difficult to implement this in VS too. Sometimes IL Spy is unable to generate C# code properly (especially for code not written in C#) and seeing and debugging at least IL code would help. This would also be great to have just for educational purpose – to easily see what IL code C# compiler generates.

    I’m really surprised how so many people are worried about infringement of their intellectual property by this feature, since many tools for IL decompilation already exist for a very long time. Anyone who wanted to steal somebody’s IP already did it, existence of one another such tool in VS changes nothing. And anyway trying to hide IP when hacker has access to the program (being it decompiled source code, IL or just native code) is battle already lost. Those who want to make it at least more difficult for them already use obfuscator, which maybe discourages some of them. But no protection can prevent experienced hacker to see what program does (or change it) if he really wants to. On the contrary displaying and debugging decompiled code directly in VS will make usage of decompiler very convenient and helpful for many ordinary programmers and I’m really looking forward to use it.