Disassembler Mechanized: Part 1

We have practiced much of disassembling by existing assembly de-compilation tools such Reflector, ILSpy and so on so far. Although such prevalent tools provide a huge number of advantages and are widespread across crackers, security professionals and reverse engineers, but nowadays they are commercialized or rarely available even for a testing point of view. This research paper is specially designed to learn the construction of our own custom Disassembler for diagonsing an assembly, both the C# and the IL code as well as integrating the creation of the code injection operability too that demonstrates the inclusion of malware or spyware into an existing executable or library file.

Creation of a custom disassembler using the .NET Framework is, in fact, a challenging undertaking because either .NET Framework languages such C# and VB.Net have some limitations to duly implement the tactics of source code extraction or converting the MSIL code into C# code. We eventually, could not achieve such a desirable implementation by .NET Framework alone. We must interoperate with other frameworks or import the third-party APIs. Hence, we shall move forward beyond the .NET constraint boundary by combining or importing open source project code into C# code to make possible the development of this amazing reverse engineering tool a reality. In the first part of this paper, we shall concentrate on user interface design along with obtaining the necessary information related to the target assembly.

Prerequisites

This time, we are not using any ready-made tools such as earlier. In fact, we will get the taste of real coding by developing a reverse engineering tool that streamlines the task of disassembling. So, the user's machine must be configured with the following software:

  • Visual Studio 2010
  • Mono.Ceil.dll 9.5.0
  • Icsharp.Decompiler.dll
  • Nuget Package Manager
  • Icsharp.NRefactory.dll
Project Planning Blueprints

This project is basically composed of multiple modules and each module is assigned a specific functionality to perform. This article will demonstrate the making of this project in a development life cycle manner. Here, the task that we will implement is defined as in the following:
  • Software Design
  • Package Installation and Configuration
  • Namespace and Reference Definition
  • Loading the assembly origin Information
  • Showing Assembly Members
  • Disassembling C# source code
  • Disassembling MSIL code 
  • Message Box Injection
  • Spyware Injection
  • Testing
  • Code Analysis
So, in this first article, we will not illustrate the design process, rather we shall embark on our voyage using a package installation (NuGet package), then move ahead by showing the assembly information as well as the member loading process.

Software Design

The custom disassembler cum injector provides a user friendly, easy-to-operate end-user design that is even manipulated by laymen. Though such an undertaking can be exposed in a command line view too, they are relatively cumbersome and pose fewer features. As per this software internal functionality, it typically first asks to upload a .NET built assembly that could be a .dll, .exe or anything else. Later, it displays the aspects about assembly origin in a comprehensive manner as well as showing entire members such as namespaces, methods and property-related specifications of the assembly in the TreeView control. The moment we select or expand any member of the assembly we get the decompiled view of the business logic in both C# code and MSIL code in a separate tab consequently. Finally, the most significant features such as Message Box and Spyware/Malware injection could be encountered on behalf of a selection of members from the tree view control. We will craft the following design to de-compile the assembly and inject malware as in the following:



That software uses a couple of controls arranged in a specific order to get the final design. It is presumed that the programmer must have proper experience in both designing and coding in .NET technology. Hence, it would be a waste to elaborate on the user controls and arbitrary control design of C# Windows Forms. It is doesn't matter indeed, how you design your software. Rather, the matter of concern is that the business logic and features implementation should be postulated in proper order.

Getting Started

The contstruction of this software is not similar to a common Windows Forms application. We must deal with open source APIs such as the Mono project and Nuget package because the Visual Studio IDE alone is not enough to make this happen. This segment begins the process by providing the initial configurations. So, open the Visual Studio IDE and go to new project type from the File menu and select the Windows Forms Application type from the project templates and assign the project name as gotnetInjector as in the following:



The Loader Form shall be the entry point and having incorporated the disassembling and injection characteristics. Afterward, add one more form as about to the solution, the functionality typically, scattered across multiple form modules.



Adding External References

Now, right-click to dotnetInjecter | References and choose Add reference, here import some external DLL file references to consume their special classes as in the following:
  • Mono.ceil.dll
  • ICSharp.decompile.dll
NuGet Package Installation

The NuGet Gallery is the central package repository used by all package consumers and developers. NuGet is a special package manager like Linux Yum, for the Microsoft development platform including .NET. The NuGet client tools provide the ability to produce and consume packages.



Once the package is downloaded and installed as an add-in in the Visual Studio IDE, we have found its entry as Library Package Manager in the Tools Menu as in the following:



You can install, remove, update and list any packages using PowerShell command-line commands or using the Manage NuGet Packages dialog box in the Package Manager Console dedicated Visual Studio window. Both options are accessible from the Visual Studio main menu. The following figure shows the PowerShell view of the package manage where we can manipulate it using built-in commands like a command prompt to install, list or delete package as in the following:



In order to list the available packages at the NuGet repository, issue the following commands:



As we have stated earlier in the software requirement section, we need need to import or install the ICSharp.Decompile.dll. It is likely that the NuGet Repository has that package that incorporates all the relevant library files. So install this package of version especially 5.0.0.6 using this command as in the following.

Note: It is necessary to be have an internet connection when manipulating a NuGet Package.

PM> Install-Package ICSharpCode.NRefactory -Version 5.0.0.6

The moment this command is issued, it connects to the Nuget.org server and first downloads all the relevant or dependent packages and later on installs them one by one as in the following:



Whereas the package would be installed successfully, some libraries DLLs are automatically added in the Reference as in the following:



The NuGet add-in creates dependency charts of the packages that are installed in the current package in order to set up a correlation.



If we want to remove any package and its associate entry from the reference tab, then we can issue the following command:

PM> Uninstall-Package ICSharpCode.NRefactory -Version 5.0.0.6

When we install a package, NuGet copies files to your solution and automatically makes whatever changes are needed, such as adding references and changing your package.config file. If you decide to remove the library, NuGet removes files and reverses whatever changes it made in your project so that no chaos remains. Here, we can observe the changes made in the package.config file after package installation as in the following:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <packages>  
  3.     <package id="ICSharpCode.NRefactory" version="5.0.0.6" targetFramework="net40-Client" />  
  4.     <package id="Mono.Cecil" version="0.9.5.4" targetFramework="net40-Client" />  
  5. </packages>  
Importing API

I assume that the developer would already have created the design. So, after making the entire relevant configuration, import the open source projects API that we have download via NuGet, namespace definition in the Loader.cs file.

Importing Open Source API

  1. using Mono;  
  2. using Mono.Cecil;  
  3. using Mono.Cecil.Cil;  
  4. using ICSharpCode.Decompiler;  
  5. using ICSharpCode.Decompiler.Ast;  
We also need a sort of .NET special classes, however import the following namespace too, in the Loader class file as in the following.

Importing .NET API
  1. using System.Reflection;  
  2. using System.IO;  
  3. using System.Net;  
  4. using System.Diagnostics;  
Assembly Information Loading

This segment illustrates the real coding implemented behind this project. Hence first, in the Loader class, define a Boolean type variable and LoadedAsmbly property of AssemblyDefinition type as in the following:
  1. public partial class Loader : Form  
  2.     {  
  3.   
  4.                public AssemblyDefinition LoadedAsmbly { getset; }  
  5.                public bool status = false;  
  6.                  ……………….  
  7.          }  
We must upload the assembly file to show its information. So we will attach a File OpenDialog box on the button click and the choose a file there that will display the uploaded file complete path in the text box as in the following:



Therefore, generate the button click event and place the following function and set the Boolean variable to false. As in the ResetData() method, typically clean the control values and in the openAssesmbly() method implement the File open dialog box.
  1. private void btnLoadAssem_Click(object sender, EventArgs e)  
  2.         {  
  3.             status = false;  
  4.             ResetData();  
  5.             openAssembly();  
  6.         }  
The openAssembly() method, first shows a File open dialog box and puts the selected file in the TextBox control and calls the LoadAsmOrigin() method that displays an assembly origin related method. Later, on behalf of the status Boolean variable, call another method as in the following:
  1. private void openAssembly()  
  2.         {  
  3.             OpenFileDialog openAsm = new OpenFileDialog();  
  4.             openAsm.Filter = "Executable | *.exe";  
  5.             if (openAsm.ShowDialog() == DialogResult.OK)  
  6.             {  
  7.                 txtURL.Text = openAsm.FileName;  
  8.                 LoadAsmOrigin();  
  9.                 if (status==false)  
  10.                 {                      
  11.                     LoadAsmContents();  
  12.                 }  
  13.             }  
  14.         }  
Exposing Assembly Origin

This section explains the processing of assembly native information. We first read the assembly path and pass the reference of the select assembly file path in the LoadedAsmbly variable. Then display a couple of invoked property values of the AssemblyDefinition class in the rich text box control as in the following:
  1. private void LoadAsmOrigin()  
  2.         {  
  3.             try  
  4.             {  
  5.                 LoadedAsmbly = AssemblyDefinition.ReadAssembly(txtURL.Text);  
  6.   
  7.                 rtbInfo.Clear();  
  8.                 rtbInfo.AppendText("[Name]::" + LoadedAsmbly.MainModule.Name.ToString() + Environment.NewLine);  
  9.                 rtbInfo.AppendText("[CLR Runtime]::" + LoadedAsmbly.MainModule.Runtime.ToString() + Environment.NewLine);  
  10.                 rtbInfo.AppendText("[Full Name]::" + LoadedAsmbly.MainModule.FullyQualifiedName.ToString() + Environment.NewLine);  
  11.                 rtbInfo.AppendText("[Metadata Token]::" + LoadedAsmbly.MainModule.MetadataToken.ToString() + Environment.NewLine);  
  12.                 rtbInfo.AppendText("[Architecture]::" + LoadedAsmbly.MainModule.Architecture.ToString() + Environment.NewLine);  
  13.                 rtbInfo.AppendText("[EntryPoint]::" + LoadedAsmbly.MainModule.EntryPoint.ToString() + Environment.NewLine);  
  14.                 rtbInfo.AppendText("[Mvid]::" + LoadedAsmbly.MainModule.Mvid.ToString() + Environment.NewLine);  
  15.             }  
  16.             catch  
  17.             {  
  18.                 ResetData();  
  19.                 MessageBox.Show("Couldn't Read Assembly, it is either Unmanaged or obfuscated");  
  20.                 status = true;  
  21.                 return;  
  22.             }  
  23.         }  
It is a good programming practice to put the sensitive code into try/catch blocks so that we can escape from any unexpected occurrence. This software only accepts or disassembles a .NET managed assembly such as Reflector.

Exposing Assembly Members

We have examined in the previous section how to obtain assembly-related information such as Name, entry point and runtime. Now, we shall populate entire members of a .NET assembly, for instance namespace, methods, properties and variables. The purpose is to fulfill by the LoadAsmContents() methods that are called previously in the openAssembly() method.

Typically, we first read the assembly path and enumerate the contents residing in the assembly using an Enumerator. This software does not gather massive information held in any information. We usually are showing the disassembling mainly of methods.
  1. private void LoadAsmContents()  
  2. {  
  3.     LoadedAsmbly = AssemblyDefinition.ReadAssembly(txtURL.Text);  
  4.     TreeNode tn = null;  
  5.     IEnumerator enumerator = LoadedAsmbly.MainModule.Types.GetEnumerator();  
  6.     while (enumerator.MoveNext())  
  7.     {  
  8.       TypeDefinition td = (TypeDefinition)enumerator.Current;  
  9.   
  10.       tn = this.tvMembers.Nodes.Add(td.Name.ToString());  
  11.       IEnumerator enumerator2 = td.Methods.GetEnumerator();  
  12.       while (enumerator2.MoveNext())  
  13.         {  
  14.            MethodDefinition method_definition = (MethodDefinition)enumerator2.Current;  
  15.            if (method_definition.IsConstructor)  
  16.             {  
  17.                tn.Nodes.Add(method_definition.Name.ToString());  
  18.             }  
  19.             tn.Nodes.Add(method_definition.Name.ToString());  
  20.          }  
  21.      }  
  22. }  
Hence, we consume two Enumerator objects here in this context, the first one is to list the main module of the assembly and the second enumerator object is a function to display, especially methods residing in the assembly.

Testing

This paper's objective is accomplished. Now test the application just by running it via F5 and you will see the design that I created. Where the first block represents the reading of an assembly, the second block incorporates the necessary regarding an assembly and finally, the vertical block shows the disassembled contents.



The moment you upload the assembly (either exe or DLL), the next consecutive block renders all the relative information regarding an uploaded assembly. The Assembly Members View box displays all the methods, properties, constructor resides in the assembly as we expand the namespace.

Final Note

So, we have completed the first paper of the Disassembler Mechanized series, where you basically set up the software development platforms first then import or configure a sort of open source package from the Nuget Repository. Later, we revealed the software development strategy that is comprised of multiple layers of functionality development indeed. This software will implement much functionality, so it is not possible to explain the entire mechanism in a single edition. We are therefore presenting limited functionality in this paper, especially until we are able to show the assembly contents view. In the forthcoming article, we shall illustrate the disassembly of C# and MSIL code from an exe or DLL file.