Tags
ESET ChallengeME 2013, ESET CrakMe 2013 Challenge, Malware Analysis, Malware Reverse Engineering
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:
Upon 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.
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
file that resulted in the “Easter Egg” pipe-server .NET file. Below is the disassembled code confirming our named pipe “Vincent_Vega” server:
And the code that creates the pipe:
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:
We see another base 64 encoded string:
With the pipe-server running, the pipe sends the message “Le Big Mac”, and then compares that with our “Royale with Cheese”:
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:
Another set of base64 encoded strings. They translate to:
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:
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.
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:
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:
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:
and the only byte[] member in Class D contains:
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:
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:
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:
And it loaded!
Continuing to step through the program:
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:
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:
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.