Decompiling/Recompiling to fix a real issue – It’s been a while!

I won’t be using ‘real world’ coding samples here, for obvious reasons, but I’ll lay out a scenario so you can (hopefully!) see how a spot of decompiling/recompiling could come in handy.

An issue rolled its way to me that related to a legacy/archaic .Net website utilising a WCF service.

First issue, important parts of the source code were not available to me. I’m sure you’ll agree, this is not a great position to be in considering we had only just reached the starting block!

After a small amount of toiling, I hit the inevitable point whereby I’d solved any IIS/WCF configuration issues and now, desperately, needed to get into the meat of the source code. The only option was to extract the dlls that made up the application and supporting services and get decompiling!

There are some great tools for this. If you want to pay money and want a nice little selection of addition perks, then Redgates .Net Reflector is still a great pick:

Redgate .Net Reflector

On the free end of the spectrum I prefer (and this certainly seems to be a common pick amongst my colleagues) Teleriks JustDecompile:

Telerik JustDecompile

Armed with the dll dump and JustDecompile I went to work. Logical steps to enable basic logging at the service level had solved a handful of issues which is all fine and dandy. Unfortunately, I hit a dead end when the application surfaced an ‘unknown error’ in the UI and there was no sniff of an exception in the trace logs; bloody dark days if you ask me!

I took the offending dlls and started digging. A situation similar to the following presented itself:

JustDecompile Showing Exception Handling Structure.
JustDecompile Showing Exception Handling Structure.

Using the powers of imagination simply insert 20 or 30 lines of code (some to other helper methods all using types capable of throwing exceptions) in place of the MethodThatCanThrowAMultitudeOfExceptionTypes and you’re all set!

So, the core issue here is that setting an integer variable really isn’t giving us the feedback we need in every exception case (I don’t like this approach across the board but I digress!). Based on knowledge further up the call chain I knew that I must be hitting the general ‘Exception’ handler here which made my plight even worse; I could be hitting any old exception type (insert big sigh here).

Without the source files, I’m unable to directly manipulate the original code and improve my situation. Enter, ildasm.exe (launched here from a Visual Studio command prompt):

Example of Running ildasm.exe.
Example of Running ildasm.exe.

A full explanation of this tool can be found here:

Ildasm.exe

The tool, in a nutshell, can be used to inspect an assembly’s manifest file in a visual way and, most importantly, access the Common Intermediate Language (CIL, MSIL or just IL) that describe the assembly. I won’t go into an in-depth discussion of what IL is but, in short, it comprises of the platform-agnostic instructions formed after compilation along with type metadata. This code is compiled, on the fly, when required by a JIT (just-in-time) compiler that is geared towards the specific platform environment. In addition, the manifest file describes the actual assembly. Although lower-level in nature, IL is still fairly readable and can be dumped into an IL file from ildasm.exe by using the File > Dump command as shown below.

Dump Options for ildasm.exe.
Dump Options for ildasm.exe.

If you want to read up on the nature of what CIL is/how it is used, here is a wiki-based primer:

Common Intermediate Language

Anyway, here is the CIL for our troublesome member as it stands (along with a screen shot to show you roughly what you can expect to see after producing an IL file):

Generating and Inspecting CIL (IL).
Generating and Inspecting CIL (IL).
  .method public hidebysig instance int32 
          DoSomethingReallyCool() cil managed
  {
    // Code size       42 (0x2a)
    .maxstack  1
    .locals init (int32 V_0,
             int32 V_1)
    IL_0000:  nop
    IL_0001:  ldc.i4.0
    IL_0002:  stloc.0
    .try
    {
      IL_0003:  nop
      IL_0004:  ldarg.0
      IL_0005:  call       instance void RandomApplication.MyApplication::MethodThatCanThrowAMultitudeOfExceptionTypes()
      IL_000a:  nop
      IL_000b:  nop
      IL_000c:  leave.s    IL_0023

    }  // end .try
    catch [mscorlib]System.InvalidOperationException 
    {
      IL_000e:  pop
      IL_000f:  nop
      IL_0010:  ldc.i4.1
      IL_0011:  stloc.0
      IL_0012:  nop
      IL_0013:  leave.s    IL_0023

    }  // end handler
    catch [mscorlib]System.IO.IOException 
    {
      IL_0015:  pop
      IL_0016:  nop
      IL_0017:  ldc.i4.2
      IL_0018:  stloc.0
      IL_0019:  nop
      IL_001a:  leave.s    IL_0023

    }  // end handler
    catch [mscorlib]System.Exception 
    {
      IL_001c:  pop
      IL_001d:  nop
      IL_001e:  ldc.i4.3
      IL_001f:  stloc.0
      IL_0020:  nop
      IL_0021:  leave.s    IL_0023

    }  // end handler
    IL_0023:  nop
    IL_0024:  ldloc.0
    IL_0025:  stloc.1
    IL_0026:  br.s       IL_0028

    IL_0028:  ldloc.1
    IL_0029:  ret
  } // end of method MyApplication::DoSomethingReallyCool

The key issue, as previously stated, was the catch statement for the general ‘Exception’ type (IL defined as follows):

    catch [mscorlib]System.Exception 
    {
      IL_001c:  pop
      IL_001d:  nop
      IL_001e:  ldc.i4.3
      IL_001f:  stloc.0
      IL_0020:  nop
      IL_0021:  leave.s    IL_0023

    }  // end handler

This certainly isn’t going to be a masterclass in IL coding but just as a demonstration I’m going to alter the catch handler as follows (using IL geared to throw the exception back up to the calling code). I’m sure this is an art in itself but I’m definitely not the one to teach you (maybe I’ll add it to the hit list as an interesting learning topic!). The statement begins with a ‘label’ (starting with ‘IL_’ and ending with a colon); one golden rule to remember here is not to duplicate any labels across statements. Doing so will result in errors when you try to recompile these instructions back into an exe or dll.

    catch [mscorlib]System.Exception 
    {
      IL_001c:  pop
      IL_001d:  nop
      IL_001e:  rethrow
    }  // end handler

With this change made I can now use another tool, called ilasm.exe, as follows to recompile these instructions back into an exe (although, this was a dll in the scenario I previously dealt with. Ilasm.exe allows for additional command flags to be specified, such as /dll, to determine the output file type):

Example of Running ilasm.exe.
Example of Running ilasm.exe.

Another word of warning at this point; I made the mistake here of recompiling my dll at the time (in the test scenario) using an incorrect version of ilasm.exe (targeting an incorrect .Net Framework). I lucked out by simply changing the IIS Application Pool to run under a newer .Net Framework version to save on recompiling the dll again. Certainly something to keep in mind however.

Full details on this specific tool can be found here:

Ilasm.exe

Looking at this recompiled assembly you can see that I now have the desired ‘throw’ statement, exactly where I need it:

JustDecompile Showing Updated Code.
JustDecompile Showing Updated Code.

In my test scenario I was able to push this dll into the given environment, use service trace logs to identify the exception (including stack trace and additional information) and finally fix all of the remaining issues.

So, if you’re stuck in future with .Net code you can’t directly alter and you have (caveat, you’ll have to work harder if the assembly is strongly-named/signed) an exe/dll that you need to make changes to in order to get meaningful outputs, consider this approach.

Until the next time, thanks all!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.