I've been quite busy lately. I made OMF file template I promised few weeks ago, found a remote code execution vulnerability in One Big Company's product and spent some time breaking keygenme by li0nsar3c00l. I'll make a blog post about most of these findings sooner or later.
But today I want to show you something that made me go WTF..
I needed to see how the loop "while true do nothing" looks like in IL. Since C# compiler and .NET JIT compiler are quite smart and optimize code that's certainly an eternal loop, I needed to get creative:
using System;
static class Test
{
static void bla(Int32 param)
{
while (param != 0) {}; // loop loop loop!
Console.WriteLine("1");
}
static void Main()
{
bla(123);
}
}
Nothing fancy, but C# & JIT compiler don't track param values, so they both generate proper code..
Well, I thought it's a proper code, until I looked at generated MSIL:
.method private hidebysig static void bla(int32 param) cil managed { // Code size 28 (0x1c) .maxstack 2 .locals init (bool V_0) IL_0000: nop IL_0001: br.s IL_0005 IL_0003: nop IL_0004: nop IL_0005: ldarg.0 IL_0006: ldc.i4.0 IL_0007: ceq IL_0009: ldc.i4.0 IL_000a: ceq IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: brtrue.s IL_0003 IL_0010: ldstr "1" IL_0015: call void [mscorlib]System.Console::WriteLine(string) IL_001a: nop IL_001b: ret } // end of method Test::bla
WTF WTF WTF? Can anyone explain to me why there are 2 ceq instructions?
I could understand extra nop and stloc/ldloc instructions, but that ceq completely blows my mind.. And it's the same for .NET 2.0 and 4.5 compilers.