September 26, 2011

.NET Reverse Engineering Basics

This is a tutorial where we will patch a very basic application as an introduction to .NET reversing.

The .NET Framework / Common Language Runtime (CLR) is often used in RAD (Rapid Application Development) due to its speed, reliability, and versatility. However, it is generally avoided when developing proprietary software.

This is primarily because .NET-compliant source code compiles to a stack-based form of bytecode called Common Intermediate Language (CIL)*. Bytecode is a step in between the source code and native code, allowing for unique optimization targeted specifically for the system's architecture at runtime. Since taking a half step back (from bytecode) is much easier than taking a whole step back (from native code), it is usually rather easy to decompile a .NET executable to its original source code. This clearly makes it much harder to secure intellectual property. Although, there are a few attempted solutions (e.g. SmartAssembly).

A myriad of tools is available that allow one to disassemble and/or decompile CIL bytecode (even Microsoft offers IL Disassembler). Notable examples include .NET Reflector (proprietary, non-free) and Simple Assembly Explorer (SAE) (open source). Both are excellent, but I will be using SAE in this tutorial.

To begin, we will need a target executable. I have one prepared that you may download here.

The source code is as follows, written in C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Debugger_Detector
{
  class Debugger_Detector
  {
    [DllImport("kernel32.dll")]
    private static extern bool IsDebuggerPresent();

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int MessageBox(IntPtr hWnd, String Text, String Caption, int Type);

    static void Main()
    {
      if (IsDebuggerPresent())
      {
        MessageBox(IntPtr.Zero, "There is a debugger present!", "Debugger Detector", 0);
      }
      else
      {
        MessageBox(IntPtr.Zero, "There is no debugger present.", "Debugger Detector", 0);
      }
    }
  }
}

It makes an attempt to detect a debugger's presence and displays the results.

Let's try running the executable under a debugger to test. Notice the text in the message box.

Open it up in SAE by browsing to the folder containing the executable and double-clicking it.

Navigate to the Main method in the tree view, and click on it. You should now see the bytecode for that method.

Let's remove the call to IsDebuggerPresent and disable the check. See a listing of the CIL instruction set if you're unsure what something means.

Save it, and run it under the debugger again.

Success!

* CIL was formerly known as Microsoft Intermediate Language (MSIL), but it was renamed at the stipulation of Ecma International in application for standardization.