Tags

, , ,

My continuing adventures in the ESET 2013 CrackMe Challenge

Last time, we discovered that the crack_me_2.zip binary contains two BMP resources, and one of those resources were extracted and dropped into one of my temporary directories.  This “BMP” was actually an executable:

crack_me_2_dotnet_eastereggUpon double-clicking it displays a cute little photo.  We also learned that the executable to the left was not just any executable but a .NET executable and an obfuscated one at that.

crack_me_2_dotnet_decompiled

The strings all appear to be encrypted with some sort of byte array scheme. Classes and functions are all labeled with only the letters A through F in upper and lowercase.

However, even though this executable is obfuscated, we did see an indication that this rabbit bitmap is actually a pipe-server.  The pipe server that our crack_me_2 binary was searching for.  Upon first running the crack_me_2, we didn’t have a pipe-server running so the function returned a -1.  Crack_me_2 then proceeded to create the .tmp

crack_me_2_named_pipefile that resulted in the “Easter Egg” pipe-server .NET file.  Below is the disassembled code confirming our named pipe “Vincent_Vega” server:

easter_egg_pip_server

And the code that creates the pipe:

ester_egg_pipe2

To disassemble a .NET file you may use ILSpy or .NET Reflector.  I used both.  Started with ILSpy to statically study the Easter Egg and Reflector for debugging (dynamic analysis).

Upon re-starting crack_me_2 with this Easter Egg pipe server running, I set a break point on CreateFileW and instead of EAX = -1, EAX = 0x2C so we got a handle to our named pipe.  Now that a pipe server is running, the program skips all the instructions to create its .tmp Easter Egg file and jumps to here:

hamberger_messgae

We see another base 64 encoded string:

base64_royale_with_cheese

With the pipe-server running, the pipe sends the message “Le Big Mac”, and then compares that with our “Royale with Cheese”:

hamburger_message2

In the above screen shot, on the right, you can see the current state of the registers.  EAX contains the string “Le Big Mac”, while ESI holds “Royale with Cheese”.  These two registers are compared.  If they were equal, the Hamburger message box would appear with the message: “Hamburgers.  The cornerstone of any nutritious breakfast.”   The Pulp Fiction theme continues as the hamburger message is another quote delivered by Jules (Played by Samuel L. Jackson) in the film’s diner scene.  Since the messages do not match, no dialog box will be displayed.  However, we can hack the code, change our “Royale with Cheese” to “Le Big Mac” if we wanted to display the message.

Either way, hamburger message or no, when we continue to single-step, the program now takes us to:

loading_malcho_dll

Another set of base64 encoded strings.  They translate to:

base64_malcho_dll

base64_malcho_dll_function

So crack_me_2 is going to attempt to load a MALCHO.DLL that exports a base64 encoded function that gets a password.

It looks as though, once MALCHO.DLL is loaded, crack_me_2 will extract a password from the Easter Egg pipe server.  This password will then be used to in the following:

gets_decrypts_resource_133

The function call at 0x404A71 is going to extract the second BMP resource in crack_me_2’s resource section. The parameter of 0x85 = 133 in base 10 and we see thanks to PE Explorer that the 133 is the second BMP’s resource ID.

crack_me_2_resources_BMPs

All that is missing is the first parameter — the password that this so-called MALCHO.DLL is supposed to retrieve for us.  But where is this dll?  I have to admit that this stymied me for a few days.  I searched up and down the code looking for this MALCHO.DLL.  I even tried googling for it.  No hits for the dll, but there were links to reports that contained the name, Juraj Malcho, head of — you guessed it — ESET’s Virus Laboratory.  Could he be the author???  I was wondering about the dll’s name because I did not see any reference to a Malcho in Pulp Fiction.

After a couple of days searching to no avail, I decided to ask the Reverse Engineering Malware Research group on linkedIn (my thanks to Mr. Mahmoudnia) for some hints.  It turns out that there is no MALCHO.DLL; I have to make my own MALCHO.DLL.  They key is in the .NET Easter Egg binary.

To help me better understand this .NET Easter Egg, I used a tool called DeReactor to deobfuscate this .NET file.

After setting up the named pipe-server, most of the action appears to be here:

deobfuscated_dynamic_routine

deobfuscated_dynamic_routine_part2

The deobfuscated file calls it method_8() in Class_06; its called Method A of Class D in the obfuscated file.  This took me a while to figure out what was going on here.  It turns out that this Easter Egg .Net creates threads that dynamically — and it is one of three routines that does this (the whole chunk of code is below; feel free to scroll past all that).

protected void A([In] List<byte> obj0)
{
Encoding.ASCII.GetString(obj0.ToArray(), 1, obj0.Count - 1);
}

protected void B()
{
string str1 = string.Format(.d(), (object) null, (object) null);
string str2 = string.Format(.E(), (object) null, (object) null);
string str3;
lock (D.A)
str3 = !this.a ? str2 : str1;
this.A.A(str3);
}

private void b()
{
this.A.Add((object) new G((object) OpCodes.Ldc_I4, (object) (this.A & (int) byte.MaxValue), (object) .e()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4, (object) (this.a & (int) byte.MaxValue), (object) .F()));
this.A.Add((object) new G((object) OpCodes.Xor, (object) null, (object) .f()));
}

private void A([In] object obj0)
{
try
{
Thread thread = obj0 as Thread;
if (thread == null)
{
thread = new Thread(new ParameterizedThreadStart(this.A));
thread.IsBackground = true;
thread.Start((object) Thread.CurrentThread);
this.a = this.A(MethodBase.GetCurrentMethod());
this.a.Set();
Thread.Sleep(500);
}
while (!this.A)
{
if (Debugger.IsAttached || Debugger.IsLogging())
this.a();
if (!thread.IsAlive)
this.a();
Thread.Sleep(1000);
}
lock (D.B)
{
if (--this.B != 0)
return;
this.B.WaitOne();
this.A.A.WaitOne();
this.A.Set();
}
}
catch (Exception ex)
{
}
}

private int A([In] MethodBase obj0)
{
byte[] ilAsByteArray = obj0.GetMethodBody().GetILAsByteArray();
int num1 = 0;
foreach (byte num2 in ilAsByteArray)
num1 += (int) num2;
return num1;
}

protected void C()
{
this.A.Add((object) new G((object) OpCodes.Ldarg_1, (object) null, (object) .G()));
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .g()));
this.A.Add((object) new G((object) OpCodes.Ldloc_2, (object) null, (object) .H()));
this.A.Add((object) new G((object) OpCodes.Ldarg_0, (object) null, (object) .h()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .I()));
this.A.Add((object) new G((object) OpCodes.Ldloc_2, (object) null, (object) .i()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .J()));
this.A.Add((object) new G((object) OpCodes.Add, (object) null, (object) .j()));
this.A.Add((object) new G((object) OpCodes.Stloc_2, (object) null, (object) .K()));
this.A.Add((object) new G((object) OpCodes.Ldlen, (object) null, (object) .k()));
this.A.Add((object) new G((object) OpCodes.Conv_I4, (object) null, (object) .L()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .l()));
this.A.Add((object) new G((object) OpCodes.Ldarg_0, (object) null, (object) .M()));
this.A.Add((object) new G((object) OpCodes.Ldloc_2, (object) null, (object) .m()));
this.A.Add((object) new G((object) OpCodes.Ldelem_U1, (object) null, (object) .N()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .n()));
this.A.Add((object) new G((object) OpCodes.Stloc_3, (object) null, (object) .O()));
this.A.Add((object) new G((object) OpCodes.Ldloc_1, (object) null, (object) .o()));
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .P()));
this.A.Add((object) new G((object) OpCodes.Sub, (object) null, (object) .p()));
this.A.Add((object) new G((object) OpCodes.Add, (object) null, (object) .Q()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .q()));
this.A.Add((object) new G((object) OpCodes.Stloc_0, (object) null, (object) .R()));
this.A.Add((object) new G((object) OpCodes.Stloc_0, (object) null, (object) .r()));
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .S()));
this.A.Add((object) new G((object) OpCodes.Ldarg_1, (object) null, (object) .s()));
this.A.Add((object) new G((object) OpCodes.Ldlen, (object) null, (object) .T()));
this.A.Add((object) new G((object) OpCodes.Conv_I4, (object) null, (object) .t()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .U()));
this.A.Add((object) new G((object) OpCodes.Sub, (object) null, (object) .u()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .V()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .v()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .W()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .w()));
this.A.Add((object) new G((object) OpCodes.Stloc_3, (object) null, (object) .X()));
this.A.Add((object) new G((object) OpCodes.Stloc_S, (object) 4, (object) .x()));
this.A.Add((object) new G((object) OpCodes.Ldloc_S, (object) 4, (object) .Y()));
this.A.Add((object) new G((object) OpCodes.Clt, (object) null, (object) .y()));
this.A.Add((object) new G((object) OpCodes.Stloc_S, (object) 4, (object) .Z()));
this.A.Add((object) new G((object) OpCodes.Ldloc_S, (object) 4, (object) .z()));
this.A.Add((object) new G((object) OpCodes.Ldelem_U1, (object) null, (object) .aA()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .aa()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .aB()));
this.A.Add((object) new G((object) OpCodes.Ceq, (object) null, (object) .ab()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .aC()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .ac()));
this.A.Add((object) new G((object) OpCodes.Stloc_0, (object) null, (object) .aD()));
this.A.Add((object) new G((object) OpCodes.Stloc_1, (object) null, (object) .ad()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_0, (object) null, (object) .aE()));
this.A.Add((object) new G((object) OpCodes.Stloc_2, (object) null, (object) .ae()));
this.A.Add((object) new G((object) OpCodes.Stloc_S, (object) 4, (object) .aF()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .af()));
this.A.Add((object) new G((object) OpCodes.Ldloc_S, (object) 4, (object) .aG()));
this.A.Add((object) new G((object) OpCodes.Nop, (object) null, (object) .ag()));
this.b();
this.A.Add((object) new G((object) OpCodes.Ldloc_0, (object) null, (object) .aH()));
this.A.Add((object) new G((object) OpCodes.Ldc_I4_1, (object) null, (object) .ah()));
}

protected void c()
{
this.A = new DynamicMethod(.aI(), typeof (bool), new Type[2]
{
typeof (byte[]),
typeof (byte[])
}, typeof (D).Module);
ILGenerator ilGenerator = this.A.GetILGenerator();
ilGenerator.DeclareLocal(typeof (int), true);
ilGenerator.DeclareLocal(typeof (int), true);
ilGenerator.DeclareLocal(typeof (int), true);
ilGenerator.DeclareLocal(typeof (bool), true);
ilGenerator.DeclareLocal(typeof (bool), true);
Label label1 = ilGenerator.DefineLabel();
Label label2 = ilGenerator.DefineLabel();
Label label3 = ilGenerator.DefineLabel();
Label label4 = ilGenerator.DefineLabel();
Label label5 = ilGenerator.DefineLabel();
Label label6 = ilGenerator.DefineLabel();
this.A.Add((object) new G((object) OpCodes.Brtrue_S, (object) label2, (object) .ai()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label1, (object) .aJ()));
this.A.Add((object) new G((object) label6, (object) null, (object) .aj()));
this.A.Add((object) new G((object) OpCodes.Brtrue_S, (object) label3, (object) .aK()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label4, (object) .ak()));
this.A.Add((object) new G((object) label3, (object) null, (object) .aL()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label5, (object) .al()));
this.A.Add((object) new G((object) label2, (object) null, (object) .aM()));
this.A.Add((object) new G((object) label5, (object) null, (object) .am()));
this.A.Add((object) new G((object) label1, (object) null, (object) .aN()));
this.A.Add((object) new G((object) OpCodes.Brtrue_S, (object) label6, (object) .an()));
this.A.Add((object) new G((object) OpCodes.Br_S, (object) label4, (object) .aO()));
this.A.Add((object) new G((object) label4, (object) null, (object) .ao()));
this.A.Add((object) new G((object) OpCodes.Ldloc_3, (object) null, (object) .aP()));
this.A.Add((object) new G((object) OpCodes.Xor, (object) null, (object) .ap()));
this.A.Add((object) new G((object) OpCodes.Ret, (object) null, (object) .aQ()));
this.A.Sort();
foreach (G g in this.A)
{
if (g.A is OpCode)
{
if (g.a != null)
{
if (g.a is byte)
ilGenerator.Emit((OpCode) g.A, (byte) g.a);
else if (g.a is int)
{
ilGenerator.Emit((OpCode) g.A, (int) g.a);
}
else
{
if (!(g.a is Label))
throw new Exception(.aq());
ilGenerator.Emit((OpCode) g.A, (Label) g.a);
}
}
else
ilGenerator.Emit((OpCode) g.A);
}
else if (g.A is Label)
ilGenerator.MarkLabel((Label) g.A);
}
this.A = (D.e<bool, byte[], byte[]>) this.A.CreateDelegate(typeof (D.e<bool, byte[], byte[]>));
}

To make analyzing this Easter Egg a challenge, these methods are generating MSIL opcodes on the fly and in a “randomish” order.  Note the this.A.Sort() after all those this.A.Add(…) statements.  To better understand what is going on, we will need to run it in Reflector’s Visual Studio debugger plugin.  But first we will have to deal with:

dotNet_anti_debugging

Its checking if we are running the Easter Egg program in a debugger or if we are running any logging tools, if so the program basically does nothing.  So I used Reflector to edit out that snippet, “re-compile” it and set a break point after the this.A.Sort() routine.  Then I dumped out the IL (Intermediate Language) evaluation stack and here is what I got (I put in the comments):

L_000000000: Nop
L_000000010: Ldc.I4.0
L_000000020: Stloc.0
L_000000030: Ldc.I4,88 	//Load value 88 
L_000000031: Ldc.I4,254 //Load value 254 
L_000000032: Xor  	// Xor: 88 xor 254 = 166
L_000000080: Stloc.1	// store the value 166
L_000000090: Ldc.I4.0
L_0000000A0: Stloc.2
L_0000000B0: br.s label
L_0000000CF: label6
L_0000000D0: Nop
L_0000000E0: Ldarg.0	//Load the byte[] array
L_0000000F0: Ldloc.2 	//Load local2 
L_000000100: Ldelem.U1	//Take one byte from byte[] array with index at 
			//local2
L_000000110: Ldloc.1 	//
L_000000120: Ldloc.0
L_000000130: Sub	//local1 - local2
L_000000140: xor	//(local1 - local2) Xor byte[]
L_000000150: Ldarg.1
L_000000160: Ldloc.0
L_000000170: Ldelem.U1
L_000000180: Ceq
L_0000001A0: Ldc.I4.0
L_0000001B0: Ceq
L_0000001D0: Stloc.S,4
L_0000001F0: Ldloc.S,4
L_000000210: brtrue.s label2
L_000000230: Nop
L_000000240: ldloc.0
L_000000250: ldc.i4.1
L_000000260: Add
L_000000270: Stloc.0
L_000000280: Ldloc.0
L_000000290: Ldarg.1
L_0000002A0: Ldlen
L_0000002B0: Conv.I4
L_0000002C0: Ldc.I4.1
L_0000002D0: Sub
L_0000002E0: Ceq
L_000000300: Ldc.I4.0
L_000000310: Ceq
L_000000330: Stloc.S,4
L_000000350: Ldloc.S,4
L_000000370: brtrue.s label3
L_000000390: Ldc_I4.1
L_0000003A0: Stloc.3
L_0000003B0: Br.s label4
L_0000003CF: label3
L_0000003D0: Nop
L_0000003E0: br.s label5
L_0000003FF: label2
L_000000400: Ldc_I4.0
L_000000410: Stloc.0
L_000000420: Nop
L_000000430: Ldloc.2
L_000000440: Ldc_I4.1
L_000000450: Add
L_000000460: Stloc.2
L_000000470: Ldloc.2
L_000000480: Ldarg.0
L_000000490: Ldlen
L_0000004A0: Conv.I4
L_00000041F: label5
L_00000046F: label
L_0000004B0: Clt
L_0000004D0: Stloc_S,4
L_0000004F0: Ldloc_S,4
L_000000510: brtrue.s label6
L_000000530: Ldc_I4_0
L_000000540: Stloc_3
L_000000550: br.s label4
L_00000056F: label4
L_000000570: ldloc.3
L_000000580: Ret

It looks like this is part of a decryption routine.  In C, it would look something like:

for(int i = 0; i < byteArray.length; i++)
{
    byteArray[i] = (byteArray[i] ^ (166 - i));
}

And what is the byte array? If we examine the original obfuscated start of our routine, we see that there is one byteArray passed in as an argument:

byte_array_screen_shot

and the only byte[] member in Class D contains:

byte_array_contents

which if you run in our psuedo decryption algorithm above, transforms those numbers in the byte array to the ASCII codes that produces the string:

/ download/bmV4dF9maWxl/cGEkJHdk.txt

That looks like a URL. Now this is starting to make sense! In the following:

enumerating_windows_screenshot

My guess is that the above chunk enumerates searching for Internet Explorer and sends the decrypted URL.  If we append our decrypted URL to ESET’s URL:

http://www.joineset.com/download/bmV4dF9maWxl/cGEkJHdk.txt

then click on the link, the site contains:

Jules:You read the bible, Ringo?

So this must be the password that our MALCHO.DLL must retrieve for us to use as the decryption key to the second BMP resource. So now its off to Visual Studio to create a simple MALCHO.DLL:

malcho_dll_code

It simply returns the password as a string.  Also I changed the function name since the base64 encoded name had an equals sign in it.  At first I used the base64 name but the compiler freaked out.  I forgot that you can’t use an ‘=’ in variable names or function names.  The ‘=’ sign is an assignment operator and it confuses the compiler.  So after we load our MALCHO.DLL, we can simply change the function name to getPassord in OllyDbg before we allow the program to call GetProcAddress:

loading_malcho_dll

changing_malcho_function_name

And it loaded!

getPassword_worked

Continuing to step through the program:

gets_decrypts_resource_133

crack_me_2 has now extracted and decrypt the resource 133 BMP and is going to drop it as 2B0.tmp.  I let the Olly run the program to completion and grabbed our new file.  I tried changing the extension to .exe to run it but that did not work.  I tried loading it in PE Explorer but it told me that 2B0 was not a legitimate PE file so I loaded 2B0 into HexWorkshop:

2B0_tmp_is_a_java_class_file

I recognized the magic bytes: CAFEBABE.  This is a java class file.  A java class file called Bible.  And you can see the print statement strings containing the most famous quote in the movie Pulp Fiction:

all_easter_eggs_found

So it seems that we have found all the Easter eggs.  Is that all there is?  I don’t know for sure.  There are still parts of the crack_me_2 code that I have not examined in complete detail.  I did see calls to some registry querying and other registry functions.  Additionally, I found some other anti-debugging checks later in the code, and some of the code doesn’t disassemble correctly; Olly thinks its a bunch of character bytes.  If there is nothing more than why would someone go to all the bother of calling Windows registry functions, add and occlude more code if not to have it do something.  I speak from experience that programmers are generally lazy.  I don’t mean that as a derogatory remark.  Programmers purposely do not add any functionality to a program that will never be used.  Suffice it to say, there may be more to this saga.  After all, I have not tried to put any other Pulp Fiction quotes other than “I did it!” in the second textbox of the starting dialog box.  We could attempt to try all possible Pulp Fiction quotes but there are a lot of them and I could spend the rest of my natural life trying them all.  And since I am lazy I am not going to do that.

Whenever I have some spare time, I will continue to poke around.  If I find anything new, I will post those results here.