Visual Studio Code is a free IDE that is great for debugging, compiling, and building .NET Framework applications.
Let's use a simple C# program to demonstrate a very basic workflow for decompiling .NET executables. First, let's use Notepad++ to create a text file on the Windows virtual machine Desktop with the following code:
We will save this file as test.cs. In order to compile it, we'll use the csc.exe compiler from the .NET framework ( or feel free to compile using Visual Studio Code and run 'dotnet build' ).
Once our test.exe is created, let's execute it to make sure it works properly.
We can now open dnSpy and attempt to decompile this executable's code. We'll drag the test.exe file to the dnSpy window, which automatically triggers the decompilation process in dnSpy.
To view the source code of this executable, we'll have to expand the test assembly navigation tree and select test.exe, dotnetapp, and then Program, as shown in Figure 33. According to the output, the decompilation process was successful.
Excellent! We successfully decompiled the executable.
When analyzing and debugging more complex applications, one of the most useful features of a decompiler is the ability to find cross-references to a particular variable or function. We can use cross-references to better understand the code logic. For example, we can monitor the execution flow statically or set strategic breakpoints to debug and inspect the target application. We can demonstrate the effectiveness of cross-references in this process with a simple example.
Let's suppose that while studying a target application, we noticed a few Base64-encoded values in the HTTP requests captured by Burp Suite. Since we would like to better understand where these values are decoded and processed within our target application, we could make the assumption that any functions that handle Base64-encoded values contain the word "base64".
We'll follow this assumption and start searching for these functions in dnSpy. For a thorough analysis we should open all the .NET modules loaded by the web application in our decompiler. However, for the purpose of education, here is an example search for the term "base64" within method names of an application.
The search result provides us with a list of method names containing the term "base64" as shown below.
Let's pick one of the functions and try to find its cross-references. We'll select the Base64UrlDecode function by right-clicking on it and selecting Analyze from the context menu.
The results will appear in the Analyzer window. Specifically, expanding the function name reveals two options: Used By and Uses.
Finally, let's demonstrate how to arbitrarily modify assemblies. We can use this technique to add debugging statements to a log file or alter an assembly's attributes in order to better debug our target application.
In order to demonstrate this technique, we will briefly return to our previous custom executable file and edit it using dnSpy. Let's right-click Program and choose Edit Class.
Then we'll change "Your answer was: " to "You said: ".
And finally, we'll click Compile, then File > Save All to overwrite the original version of the executable file.
If we return to our command prompt and re-run test.exe, the second print statement is now "You said: ".
Using a very basic example application, we have demonstrated how to recover the source code of .NET-based applications and how to find cross-references with the help of our favorite decompiler. We also demonstrated how to modify and save a .NET assembly file. Even if this modification doesn't seem particularly useful, it will come in handy later on in the course when we will have to alter assemblies' attributes in order to better debug our target application.