I have a challenge compiled in C# .net 4.0. I can see the source completely fine in Reflector. I'd like to change the source. I guess I do this by patching some bytes in the binary.

The problem I'm facing is when loading the binary in Ildasm.exe I cannot convert these ID_xxxx addresses to RVA or find their corresponding bytes in CFF Explorer. What is the typical way to patch a .net exe in this fashion? I read some of lena's tutorials so normally I'd load it in Olly, and I tried this but I cannot see any useful code and most actions seem to happen in various Windows .dll's and not in the .exe itself.

I would most appreciative if someone could give advice or point me to a tutorial. Thank you.

  • And more specifically, I want to patch one call inside of a try block. Ildasm.exe reports the address of IL_0039 (which doesn't correspond in CFF)
    – 4r4r4r
    Jun 14, 2013 at 17:09
  • A bit off topic but what I generally do when the assembly isn't obfuscated is : Use ildasm to disassemble the assembly, modify the IL as I need and reassemble using ilasm.
    – dna
    Jun 14, 2013 at 19:06
  • Yes that sounds like a good idea. Couldn't find any tutorials or figure out how to modify from Ildasm.exe, could you please explain a bit more?
    – 4r4r4r
    Jun 14, 2013 at 20:11
  • Also, I'm trying to NOP out one call. The bytes in question are 28 85. I understand the 28 byte implies a "CALL". I tried changing them to 00, which I think is a NOP (normal 90 byte) for this language but it screwed up.
    – 4r4r4r
    Jun 14, 2013 at 20:12
  • I let you figure out how to NOP the call yourself!
    – dna
    Jun 14, 2013 at 20:38

3 Answers 3


There is a plugin called Reflexil for Reflector which makes it very easy to patch a .NET binary.

Reflexil is an assembly editor and runs as a plug-in for Red Gate's Reflector and Telerik's JustDecompile. Reflexil is using Mono.Cecil, written by Jb Evain and is able to manipulate IL code and save the modified assemblies to disk. Reflexil also supports C#/VB.NET code injection.

I did a write-up on my blog recently with an example.

  • Nice writeup. I think Reflexil is definitely the easiest way to do get the job done but it's good to know how to do other ways too.
    – 4r4r4r
    Jun 16, 2013 at 1:41

Consider the following .NET 4.5 Console application :

using System;

namespace ReversingMSIL101
    class Program
        static void Main(string[] args) {
            if (args.Length > 0 && args[0] == "Secret")

        private static void Authenticated() {
            Console.WriteLine("Gold for the people!");

        private static void Anonymous() {
            Console.WriteLine("Give them copper ...");

Compile and open Ildasm . We will start by loading the executable (File->Open). Once done : dump it (File->Dump). Ildasm respond with a nice dialog full of checkboxes, since we want as much information as it's possible, check everything!

Now let's have a look at our disassembled IL instructions, fire up your preferred editor and open the previously generated dump. We are especially interested in the if statement since it controls if the supplied password is correct; note that you can easly spot it using the generated comments.

//000009:             if (args.Length > 0 && args[0] == "Secret")
IL_0001:  /* 02   |                  */ ldarg.0
IL_0002:  /* 8E   |                  */ ldlen
IL_0003:  /* 69   |                  */ conv.i4
IL_0004:  /* 16   |                  */ ldc.i4.0
IL_0005:  /* 31   | 12               */ ble.s      IL_0019
IL_0007:  /* 02   |                  */ ldarg.0
IL_0008:  /* 16   |                  */ ldc.i4.0
IL_0009:  /* 9A   |                  */ ldelem.ref
IL_000a:  /* 72   | (70)000001       */ ldstr      "Secret" /* 70000001 */
IL_000f:  /* 28   | (0A)000011       */ call       bool [mscorlib/*23000001*/]System.String/*01000013*/::op_Equality(string, string) /* 0A000011 */
IL_0014:  /* 16   |                  */ ldc.i4.0
IL_0015:  /* FE01 |                  */ ceq
IL_0017:  /* 2B   | 01               */ br.s       IL_001a
IL_0019:  /* 17   |                  */ ldc.i4.1
IL_001a:  /* 00   |                  */ nop
IL_001b:  /* 0A   |                  */ stloc.0
IL_001c:  /* 06   |                  */ ldloc.0
IL_001d:  /* 2D   | 08               */ brtrue.s   IL_0027

I let to the reader the task to reverse and analyse the instructions. We will continue by looking at the last one. Long story short : if the first argument isn't the string "Secret" we will take the branch and end up at IL_0027 :

IL_0027:  /* 28   | (06)000003       */ call       void ReversingMSIL101.Program/*02000002*/::Anonymous() /* 06000003 */

Pretty bad heh? So we will simply tweak a bit that last instruction, moreover by replacing it by brfalse.s :

IL_001d:  /* 2B   | 08               */ brfalse.s   IL_0027

And that's it, we are done! Save the file and open a shell, navigate to the folder containing the IL dump and issue ilasm ReversingMSIL101.il inorder to reassemble the dump into an executable!

Now it's the time to verify our work :

C:\Users\dna\Documents\Visual Studio 2012\Projects\ReversingMSIL101\ReversingMSIL101\bin\Debug>ReversingMSIL101.exe IdoNotKnow
Gold for the people!
  • Great tutorial! Thanks for taking the time to write it. I ended up using the bytecode charts at resources.infosecinstitute.com/…. From there, it's an odd scheme but you can rebuilt the sequences of bytes displayed in ILDasm.exe and search for them in a hex editor and change them, but if they aren't changed to an acceptable byte it messes up the program. Still never got the call to nop out properly and will continue searching. Crazy how in these .net programs you can just view the bare source code!!
    – 4r4r4r
    Jun 14, 2013 at 23:19
  • Well a CALL instruction is 0x28 + <4 bytes token> so you have to NOP(0x00) 5 bytes in total.
    – dna
    Jun 14, 2013 at 23:43
  • 1
    After some testing I've found that depending on the call, you can't just nop the 5 call bytes themselves. You have to get the ldloc & stloc calls before it or whatever pushes args, especially if the function looks like Test(test1, test2(test3)).Testing() in .net. I don't like this intermediary language so much. I'd also like to know how to do dynamic analysis in Olly, I can't make much sense of it and I think patching cmp/jnz's would be easier than this.
    – 4r4r4r
    Jun 16, 2013 at 1:39
  • Make sense, I totally forgot to mention unwinding the stack. Well I never done this in Olly but you will have to spot the JIT when he compiles the MSIL methods into native instructions. It might not be that smart since you will loose portability and time but it should be pretty interesting :)
    – dna
    Jun 16, 2013 at 9:53
  • Reflexil handles some type of signing routine automatically, something MS put there to prevent patching that needs to be addresses when changing bytes like this too.
    – 4r4r4r
    Jun 16, 2013 at 21:49

As much as dna answer is great and well explained, this is often impractical for real-world executables, unless they are very simple and short. Dumping something, changing it and "recompiling" is error-prone, especially in obfuscated assemblies and so on.

I would suggest you download Reflexil, it's a Reflector plugin: you can see the IL of the functions you're viewing through Reflector, change whatever you need to change in a very simple way and then just save the modified exe. This is much much less error-prone, since you're not dumping and rebuilding the program from scratch. Also, you can inject anything into the assembly this way, be it classes, new methods, structures.

  • The OP requested a non obfuscated example and in that precise situation, relying on the assembler is less error prone! Since errors will be detected during the assembling process. The injection part is interesting, I usually rely on Cecil (mono-project.com/Cecil) since it's open source and allow to you to add/remove/modify anything in an assembly and save the patched one!
    – dna
    Jun 15, 2013 at 9:19
  • Just a quick reminder that Reflexil not only works with Reflector but ILSpy (which is open source) and JustDecompile as well. Dec 16, 2016 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.