about:blog Archives

reversing, python, cookies
Friends: aj deadrom1 kiwie


Defeating ESET1013 - Malware Analyst

ESET1023

I stumbled upon joineset.com, and though this could be a nice pretext to try some reversing under Windows : I never did any on this platform.

Get the crackme and enjoy!

Part One.

PEiD tells us

UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo [Overlay]

I already reversed some UPX things on GNU/Linux (Yes, it's a multi-platform packer), should be easy on Windows. Of course (it would be too easy), if you try to unpack it using upx -d, you'll get an error:

upx: EsetCrackme2013.exe: CantUnpackException: file is modified/hacked/protected; take care!!!

Either this is a trick, and the used packer has a false signature, or it's a modified version of UPX. I go for the later: the "Unpacker for UPX" plugin from PEiD manage to unpack it. Yes, it's lame, but I rather spend time on interesting things.

If you open the unpacked.exe binary, you'll see a classic function prelude, and a short loop that push some zeros, followed by some crap, and a mega-shitload of mov. It prints the introduction text, and then it does some classic anti-debugging stuff.

Anti-debugging

First, a call to OutputDebugStringA, with a parameter full of "%s". After some Google-fu, it seems that OllyDbg 1.10 does not like this.

Next, a call to the classic isDebuggerPresent.

Something less obvious, using segments.

00407C55   64:8B05 18000000 mov eax,dword ptr fs:[18]
00407C5C   8B40 30          mov eax,dword ptr ds:[eax+30]
00407C5F   0FB640 02        movzx eax,byte ptr ds:[eax+2]
00407C63   83F8 01          cmp eax,1
00407C66   75 07            jnz short EsetCrac.00407C6F
00407C68   6A 00            push 0
00407C6A   E8 9DBDFFFF      call <jmp.&KERNEL32.ExitProcess>

This is actually checking the BeingDebugged flag in the PEB, which is roughly the same than calling isDebuggerPresent.

00407CA9   58               pop eax
00407CAA   64:8B05 18000000 mov eax,dword ptr fs:[18]
00407CB1   90               nop
00407CB2   8B40 30          mov eax,dword ptr ds:[eax+30]
00407CB5   50               push eax
00407CB6   58               pop eax
00407CB7   83C0 00          add eax,0
00407CBA   0FB640 02        movzx eax,byte ptr ds:[eax+2]
00407CBE   83F8 00          cmp eax,0
00407CC1   74 0D            je short EsetCrac.00407CD0
00407CC3   83C3 00          add ebx,0
00407CC6   83E8 00          sub eax,0
00407CC9   6A 00            push 0
00407CCB   E8 3CBDFFFF      call <jmp.&KERNEL32.ExitProcess>

This is the same trick, again.

Timing

It loads a couple of dll, gets some functions with GetprocAddress (the equivalent of our dlsym), then we can see a bloc of binary operations on eax.

add     eax, 42h
sub     eax, 41h
cmp     eax, 44h
cmp     eax, 5Fh
mov     eax, 46h
shl     eax, 4Fh
shr     eax, 4Fh
cmp     eax, 44h
mov     eax, 2Dh
shl     eax, 42h
sar     eax, 41h
cmp     eax, 44h
rcl     eax, 5Fh
mov     eax, 46h
ror     eax, 4Fh
rol     eax, 4Fh
rcr     eax, 44h
pop     eax

If you look carefully, you can see that it does not do anything, since there is a pop eax at the end. If you convert every hex values, you'll get this:

BAD_FOOD-BAD_FOOD

Next, you can juggle with F8 and "New Origin Here" to force the crackme to decrypt its secret messages. Every time you get a comparison (Nothing fancy, classic timing anti-debugging technique), you must avoid the jump to 0x00407e0c, or to ExitProcess. You don't even need to understand the decryption function at 0x00403A94. And there you go:

******************************************************************************
*                                                                            *
* Eset Crackme 2013                                                          *
* -----------------                                                          *
*                                                                            *
* This program was designed to test your skills in program reverse           *
* engineering.                                                               *
*                                                                            *
* Your task will be to analyze "EsetCrackme2013.exe" file.                   *
*                                                                            *
* The analysis of the program code reveals information about the program's   *
* functions, payload execution conditions, etc.                              *
*                                                                            *
* Do not get discouraged, frustrated, nor fooled!                            *
*                                                                            *
* Program code can contain hidden files, texts, conditional tasks, debugging *
* protection and so on. Do not hesitate to send us your results even if      *
* they're only partial. You can also attach a step-by-step analysis so that  *
* we can see how you think. Send your results/remarks/analysis to:           *
* analyst@eset.sk                                                            *
*                                                                            *
* Good luck :-)                                                              *
*                                                                            *
******************************************************************************
Press [ENTER] to continue.

* Hidden part #1. Text picked from the following URL:
* http://www.virusradar.com/en/Win32_Virut.E/description

O noon of life! O time to celebrate!
O summer garden!
Relentlessly happy and expectant, standing: -
Watching all day and night, for friends I wait:
Where are you, friends? Come! It is time! It's late!

* Hidden part #2. Text picked from the following URL:
* http://www.virusradar.com/en/Win32_Ridnu.NAA/description

DEAR MY PRINCESS
WHEN THE STARS FILL THE SKY I WILL MEET YOU MY LOVELY PRINCESS
I MISS YOU SO MUCH MY PRINCESS
IN MY DEAREST MEMORY I SEE YOU REACHING OUT TO ME
I WILL REMEMBER YOU AS LONG AS YOU REMEMBER ME
IN YOUR DEAREST MEMORY DO YOU REMEMBER LOVING ME
PLEASE DO NOT FORGET OUR PAST
DID YOU KNOW THAT I HAD MIND ON YOU
I NEVER WISH TO LOSE YOU AGAIN
SHALL I BE THE ONE FOR YOU
I WANNA TAKE YOU TO MY PALACE
I WILL TAKE YOU TO OUR UTOPIA
I AM FALLING IN LOVE WITH YOU
I WILL BE WAITING FOR YOU
I DO NOT WANT TO SAY GOOD BYE TO YOU
PLEASE DO NOT FORGET YOUR PRINCE
I SAW YOU SMILING AT ME WAS IT REAL OR JUST MY FANTASY
YOU WILL ALWAYS IN MY HEART
YOU ALWAYS IN MY DREAMS
I ALWAYS SEE YOU IN MY DREAMS
I HAVE BEEN POISONED BY YOUR LOVE
I MISS YOU I AM STILL LOOKING FOR YOU
I WILL BE THERE I WILL BE WAITING FOR YOU
PLEASE COME BACK TO OUR BEAUTY ISLAND
I MISS YOUR CUTE SMILE

* Hidden part #3.

Continue with the next ESET crackme here:
http://www.joineset.com/download/bmV4dF9maWxl/crack_me_2.zip

End of the program.

I'll remember to include some poems in my botnet.

Part Two

Get the 2nd crackme. If you try to open it with your archive manager, you'll see some UPX files, marked as executables: This is not a zip archive, it's a binary. PEiD detects it as a "Windows Icon Graphics format", and tells us that the EP Section is UPX1. This smells like a trap. Get a better userbd.txt and rescan it: PEiD is now happily telling you that the crackme is likely packed with:

RLPack V1.21 (aPlib 0.43) -> ap0x * Sign.By.fly * 20080504 *

Unpacking

Meh, ap0x is someone who likes to play with packers. Fortunately, this one is really easy to unpack (It's my first unpacking on Windows, yay!). This is how trivial packers are implemented:

  1. pushad, to save registers
  2. The decompression/check/crypto/zomg routine
  3. popad to restore the previously saved registers
  4. jmp to the OEP (Original EntryPoint).

PEiD tells us that the OEP is at 0x000B319A; if you go there with OllyDbg (With Hide Debugger, because I'm lazy.), you'll see a popad.

Blablabla, things and stuffs, and you land on 0x04B330D: a popad followed by a jmp, likely to the OEP. Put an breakpoint on the jmp 0x408034, run the crackme, single step (F8) to jump to the OEP (0x408034). Since this packer is basic, the crackme is likely completely decrypted in memory. Time to dump it. You can either use the "PEiD Generic Unpacker" plugin, or do it (semi-)manually. I chose the later. I used LordPE to dump the process, and ImpREC (Best icon ever) to rebuild the IAT. So far, nothing funky, but heh, it's my first time.

First password

Load the dumped binary in OllyDbg, set Make first pause at Entry point of main module, restart the binary (Ctrl+F2), and step a little bit. The program gets some startup info, sets its heap, gets command line arguments, and you'll land on 0x407FEB (You may not have the same addresses if you have a different dump than mine), that takes 4 arguments, the latest being MZ@.

Step inside it, and you'll land in WinMain, with a unique call inside it: DialogBoxParamW. Let's put an hardware breakpoint on the lpDialogFunc parameter, and let the process continue.

A lstrcmpA, with an hardcoded string: Pigs are filthy animals.

00404309  |. 75 74          jnz short dumped_.0040437F
0040430B  |. 8B35 80114200  mov esi,dword ptr ds:[421180]            ;  user32.GetDlgItem
00404311  |. 68 80000000    push 80                                  ; /Count = 80 (128.)
00404316  |. 8D85 6CFFFFFF  lea eax,dword ptr ss:[ebp-94]            ; |
0040431C  |. 50             push eax                                 ; |Buffer
0040431D  |. 68 E9030000    push 3E9                                 ; |/ControlID = 3E9 (1001.)
00404322  |. 57             push edi                                 ; ||hWnd
00404323  |. FFD6           call near esi                            ; |\GetDlgItem
00404325  |. 50             push eax                                 ; |hWnd
00404326  |. FF15 78114200  call near dword ptr ds:[421178]          ; \GetWindowTextA
0040432C  |. 68 845F4200    push dumped_.00425F84                    ; /String2 = "Pigs are filthy animals"
00404331  |. 8D8D 6CFFFFFF  lea ecx,dword ptr ss:[ebp-94]            ; |
00404337  |. 51             push ecx                                 ; |String1
00404338  |. FF15 44104200  call near dword ptr ds:[421044]          ; \lstrcmpA

Let's do a wild guess, and try this. It works, yay! If you read a little bit further, you'll see the same procedure, but this time with "I did it". If you try this as the second password, you'll get a popup saying "No, you didnt". I cheated for this part: those two strings where base64 encoded, and appeared when I ran strings on the binary: I ruined the joke, sorry :/

Second password

So, it's not that easy. Back to strings, just under the previous password offset, you can see:

25f84 Pigs are filthy animals
25f9c SSBkaWQgaXQh : I did it!
25fac Tm9wZSB5b3UgZGlkbid0Lg== : Nope you didn't.
25fc8 INFO
25fd0 T2ggbWFuLEkgc2hvdCBNYXJ2aW4gaW4gdGhlIGZhY2U= : Oh man,I shot Marvin in the face
26000 Le Big Mac
2600c Um95YWxlIHdpdGggQ2hlZXNl : Royale with Cheese
2605c Hamburger message
26070 Hamburgers. The cornerstone of any nutritious breakfast!
260ac TUFMQ0hPLkRMTA== : MALCHO.DLL%
260c0 Z2V0UGFzc3dvcmQ= : getPassword

It seems that there are fans of Pulp Fuction at ESET.

After those strings, there is a call to 407b10, which seems to be a base64 decoding function. During the rest Mh, there is a big gap between 0x2600c and 0x2605c, could it be some padding ? After a quick check in IDA, the gap contains two unicode strings:

Burgers!

Imports are called "Intermodular Calls" in OllyDbg, let's see which function can be used to interact with a pipe. Or even simpler, right-clic, xref, an here we go:

00404815  |. 8D45 F4        lea eax,dword ptr ss:[ebp-C]
00404818  |. 64:A3 00000000 mov dword ptr fs:[0],eax
0040481E  |. 6A 00          push 0                                   ; /hTemplateFile = NULL
00404820  |. 68 00000040    push 40000000                            ; |Attributes = OVERLAPPED
00404825  |. 6A 03          push 3                                   ; |Mode = OPEN_EXISTING
00404827  |. 6A 00          push 0                                   ; |pSecurity = NULL
00404829  |. 6A 01          push 1                                   ; |ShareMode = FILE_SHARE_READ
0040482B  |. 8BD9           mov ebx,ecx                              ; |
0040482D  |. 68 00000080    push 80000000                            ; |Access = GENERIC_READ
00404832  |. 68 28604200    push dumped_.00426028                    ; |FileName = "\\.\pipe\Vincent_Vega"
00404837  |. C685 A2FDFFFF >mov byte ptr ss:[ebp-25E],1              ; |
0040483E  |. C703 00000000  mov dword ptr ds:[ebx],0                 ; |
00404844  |. C783 0C020000 >mov dword ptr ds:[ebx+20C],0             ; |
0040484E  |. FF15 68104200  call near dword ptr ds:[421068]          ; \CreateFileW
00404854  |. 8BCB           mov ecx,ebx
00404856  |. 8BF8           mov edi,eax
00404858  |. E8 33FEFFFF    call dumped_.00404690
0040485D  |. 83FF FF        cmp edi,-1
00404860  |. 0F84 D5000000  je dumped_.0040493B

It opens the named pipe for reading with CreateFileW, that returns INVALID_HANDLE_VALUE on failure, which is likely to be defined as -1.

I saw more than one call to CreateFileW in the "Intermodular Calls" window. I removed every hardware breakpoints, to keep only one on 4042c0. The first toggled one is the Vincent_vega one. Am I supposed to create a named pipe ? This seems convoluted.

004048E8  |.  B8 0C604200   mov eax,0042600C                         ; ASCII "Um95YWxlIHdpdGggQ2hlZXNl"  ; Royale with Cheese
004048ED  |.  E8 1E320000   call 00407B10
004048F2  |>  C745 FC FFFFF mov dword ptr ss:[local.1],-1
004048F9  |.  8B30          mov esi,dword ptr ds:[eax]
004048FB  |.  8D45 CC       lea eax,[local.13]
004048FE  |.  8BFF          mov edi,edi
00404900  |>  8A08          /mov cl,byte ptr ds:[eax]                ; this line
00404902  |.  3A0E          |cmp cl,byte ptr ds:[esi]                ; and this one smell like a strcmp
00404904  |.  75 1A         |jne short 00404920
00404906  |.  84C9          |test cl,cl
00404908  |.  74 12         |jz short 0040491C
0040490A  |.  8A48 01       |mov cl,byte ptr ds:[eax+1]
0040490D  |.  3A4E 01       |cmp cl,byte ptr ds:[esi+1]
00404910  |.  75 0E         |jne short 00404920
00404912  |.  83C0 02       |add eax,2
00404915  |.  83C6 02       |add esi,2
00404918  |.  84C9          |test cl,cl
0040491A  |.^ 75 E4         \jnz short 00404900
[...] ; Obfuscation
00404929  |.  50            push eax                                 ; /Type
0040492A  |.  68 5C604200   push 0042605C                            ; |Caption = "Hamburger message"
0040492F  |.  68 70604200   push 00426070                            ; |Text = "Hamburgers. The cornerstone of any nutritious breakfast!"
00404934  |.  50            push eax                                 ; |hOwner
00404935  |.  FF15 74114200 call near dword ptr ds:[421174]          ; \USER32.MessageBoxA
0040493B  |>  57            push edi                                 ; /hObject = INVALID_HANDLE_VALUE
0040493C  |.  FF15 40104200 call near dword ptr ds:[421040]          ; \KERNEL32.CloseHandle
  1. The crackme try to read from a named pipe. Let's pretend this pipe exists.
  2. The pipe is read
  3. What is read is compared to "Royale with Cheese"
  4. A hamburger-related popup will be shown if they match

An embedded payload

00404955  |.  50            push eax                                 ; /Buffer => offset LOCAL.151
00404956  |.  68 04010000   push 104                                 ; |Bufcount = 260.
0040495B  |.  FF15 48104200 call near dword ptr ds:[421048]          ; \KERNEL32.GetTempPathW
00404961  |.  85C0          test eax,eax
00404963  |.  74 6B         jz short 004049D0
00404965  |.  8D8D A4FDFFFF lea ecx,[local.151]
0040496B  |.  51            push ecx                                 ; /FileName => offset LOCAL.151
0040496C  |.  6A 00         push 0                                   ; |Unique = 0
0040496E  |.  6A 00         push 0                                   ; |PrefixString = NULL
00404970  |.  8BD1          mov edx,ecx                              ; |
00404972  |.  52            push edx                                 ; |PathName => offset LOCAL.151
00404973  |.  FF15 4C104200 call near dword ptr ds:[42104C]          ; \KERNEL32.GetTempFileNameW
00404979  |.  85C0          test eax,eax
0040497B  |.  74 53         jz short 004049D0
0040497D  |.  8D85 A4FDFFFF lea eax,[local.151]
00404983  |.  50            push eax                                 ; /Arg2 => offset LOCAL.151
00404984  |.  68 83000000   push 83                                  ; |Arg1 = 83
00404989  |.  B9 D05F4200   mov ecx,00425FD0                         ; |ASCII "T2ggbWFuLEkgc2hvdCBNYXJ2aW4gaW4gdGhlIGZhY2U="
0040498E  |.  E8 3DFDFFFF   call 004046D0                            ; \dumped_.004046D0
00404993  |.  6A 00         push 0                                   ; /hTemplate = NULL
00404995  |.  68 80000000   push 80                                  ; |Attributes = FILE_ATTRIBUTE_NORMAL
0040499A  |.  6A 03         push 3                                   ; |CreationDistribution = OPEN_EXISTING
0040499C  |.  6A 00         push 0                                   ; |pSecurity = NULL
0040499E  |.  6A 00         push 0                                   ; |ShareMode = 0
004049A0  |.  68 00000040   push 40000000                            ; |DesiredAccess = GENERIC_WRITE
004049A5  |.  8D8D A4FDFFFF lea ecx,[local.151]                      ; |
004049AB  |.  51            push ecx                                 ; |FileName => offset LOCAL.151
004049AC  |.  FF15 68104200 call near dword ptr ds:[421068]          ; \KERNEL32.CreateFileW

This piece of code creates a temporary file, that seems to be related to "Oh man,I shot Marvin in the face" to the Vincent_vega thing. There is a second reference to dumped_.004046D0, but for now, we'll focus on this one.

.NET

PEiD tells us that the file is in fact Microsoft Visual C# / Basic .NET [Overlay]. I tried to launch it, but got an error message. I spent some time digging the binary to get what I was doing wrong. I did not had the .NET Framework because my VM was too old. Once installed, the binary can be run successfully! It displays a picture of a bunny.

Let's be paranoid: this can't only be a picture. Why put it in a binary ?

$ strings -e b ./44.exe
2fbe this.Icon
37a1c incent_Vega

Since I don't know anything about .NET, so I juggled between ILSpy and Reflector (DotPeek sucked to much). Fortunately, .NET looks like some bastardized Java:

this.A = new NamedPipeServerStream(.a(), PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

It's creating a named pipe. If you launch this binary, and then the crackme, you'll get the burger message.

Obfuscation

The C# code is pretty ugly, and the methods are weirdly named. If you check with DNiD, it'll tell you that the .net binary is likely packed with Obfuscator. They are several desobfuscator for it, like netdeob or iMPROVE.NET, but since most of the obfuscation in the binary is related to dynamic building of bytecode, there is to point to use them. And let's be honest: this packer sucks.

Dynamic string

I stumbled upon this function, which is obviously looking like a decryption loop:

static Class0() {
    string_0 = new string[0x5b];
    byte_0 = new byte[] {0x84, 0xfd, ..., 0x81, 0x80};
    for (int i = 0; i < byte_0.Length; i++)
        byte_0[i] = (byte) ((byte_0[i] ^ i) ^ 170);
}

Python to the rescue!

s = ''
for (i,j) in enumerate(byte_0):
    try:
        s += unichr(0xff & (j ^ i ^ 170))
    except:
        pass
print(s)

This prints:

.Vincent_VegaWaitForConnection_Callback: Exception OperationCanceledExceptionA.CCE2AC65C-F447-470B-8561-EBEA96B8C5D8Another instance is already running.iexploreRoyale with Cheese{0}{1}Le Big Mac{0}{1}L_000000030L_[...]_000000140L_000000580Invalid argumentpictureBox1.ImagepictureBox1$this.IconEasterEggFormEaster Egg
  1. .Vincent_Vega is the pipe name
  2. .iexplore is interesting
  3. Royale with Cheese and Le Big Mac were previously met.

With a call to EnumChildWindow, it seems that the binary is looking for an Internet Explorer window. So, with a little bit of guessing, this may lead to a third binary on http://joineset.com

Dynamic analysis

Tools for .NET dynamic analysis sucks. DILE sucks less, but it still sucks hard; I only used it for its IL Instructions tab. So, I went with a pen, paper and the aforementioned tab.

Dynamic building

I stumbled upon this method:

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

    // Inlined a function for clarity
    this.A.Add(new G(OpCodes.Ldc_I4, this.A & 0xff, Class0.e()));
    this.A.Add(new G(OpCodes.Ldc_I4, this.a & 0xff, Class0.F()));
    this.A.Add(new G(OpCodes.Xor, null, Class0.f()));

    this.A.Add(new G(OpCodes.Ldloc_0, null, Class0.aH()));
    this.A.Add(new G(OpCodes.Ldc_I4_1, null, Class0.ah()));
}

It seems that the program is building dynamically code. If you look at cross-references for the A arrayList variable, you'll see this:

protected void c() {
    Type[] parameterTypes = new Type[] { typeof(byte[]), typeof(byte[]) };
    this.A = new DynamicMethod(Class0.aI(), typeof(bool), parameterTypes, 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 label = 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(new G(OpCodes.Brtrue_S, label2, Class0.ai()));
    this.A.Add(new G(OpCodes.Br_S, label, Class0.aJ()));
    this.A.Add(new G(label6, null, Class0.aj()));
    this.A.Add(new G(OpCodes.Brtrue_S, label3, Class0.aK()));
    this.A.Add(new G(OpCodes.Br_S, label4, Class0.ak()));
    this.A.Add(new G(label3, null, Class0.aL()));
    this.A.Add(new G(OpCodes.Br_S, label5, Class0.al()));
    this.A.Add(new G(label2, null, Class0.aM()));
    this.A.Add(new G(label5, null, Class0.am()));
    this.A.Add(new G(label, null, Class0.aN()));
    this.A.Add(new G(OpCodes.Brtrue_S, label6, Class0.an()));
    this.A.Add(new G(OpCodes.Br_S, label4, Class0.aO()));
    this.A.Add(new G(label4, null, Class0.ao()));
    this.A.Add(new G(OpCodes.Ldloc_3, null, Class0.aP()));
    this.A.Add(new G(OpCodes.Xor, null, Class0.ap()));
    this.A.Add(new G(OpCodes.Ret, null, Class0.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(Class0.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 = (e<bool, byte[], byte[]>) this.A.CreateDelegate(typeof(e<bool, byte[], byte[]>));
}

So:

  1. Some OpCodes are added to an ArrayList named A.
  2. The ArrayList A is sorted, according to the method below.
  3. OpCodes are put into a DynamicMethod, also named A, that takes two bytes arrays as argument, and returns a boolean. Smells like some kind check routine, isn't it ?

Here is the G class:

public class G : IComparable {
    public object a;
    public object A;
    public object B;
    public G(object object_0, object object_1, object object_2) {
        this.A = object_0;
        this.a = object_1;
        this.B = object_2;
    }
    private int A(object obj) {
        return string.Compare(this.B as string, ((G) obj).B as string);
    }
}

The G class represents an Opcode, its index, and an optional argument. As seen previously, the decryption routine contains words, but also numbers. Those numbers are used to compute the index.

This is an example of an index:

public static string aK() {
    return (string_0[0x48] ?? smethod_0(0x48, 0x372, 11));
}

private static string smethod_0(int int_0, int int_1, int int_2){
    string str = Encoding.UTF8.GetString(byte_0, int_1, int_2);
    string_0[int_0] = str;
    return str;
}

So, nothing hardcore, I went with pen and paper, and after 2 hours and a couple of head-banging-against-the-wall, I ended with an incomplete Python implementation. I bruteforced the rest. Fortunately, it can be reversed to a simple string comparison.

tab = [0x89, 0xc1, 0xcb, 0xd4, 0xcc, 0xcd, 0xcf, 0xfe,
    0xfa, 0xb2, 0xfe, 0xf6, 0xcc, 0xad, 0xfc, 0xd1,
    0xaf, 0xf8, 0xf5, 0xc4, 0xea, 0xfd, 0xbf, 0xec,
    0xc9, 0xc8, 0xe7, 0xc1, 0xc2, 0xed, 0xe3, 0xa9,
    0xf2, 0xfd, 0xf0]

s = ''
for (cpt, i) in enumerate(tab):
    s += unichr(0xff & (i ^ (0xa6 - cpt)))
print(s)

/download/bmV4dF9maWxl/cGEkJHdk.txt

Yay, let's append it to http://joineset.com, and open it in our browser!

Jules:You read the bible, Ringo?

Pulp Fiction, once again. What if we try this in the second field of the crackme? Nop, it doesn't work. Where can we also put a password?

Macho.dll

Back to the crackme, if you step a little bit further, you'll see that it is calling "LoadLibraryA" on MALCHO.DLL (And not MACHO.DLL. I lost one hour because of this.), and then calls GetProcAddress on a "GetPassword" function, likely to decrypt a resource, since:

  1. There is a password involved
  2. There is a convoluted function at 401210 with 3 loops and binary operations
  3. A call to CreateFileW+WriteFile+Closehandle

This smells like a resource decryption. Wait. Let's check again.

00404A13  |. 85C0           test eax,eax
00404A15  |. 0F84 A8000000  je dumped_.00404AC3
00404A1B  |. 68 C0604200    push dumped_.004260C0                    ; /ProcNameOrOrdinal = &quot;Z2V0UGFzc3dvcmQ=&quot;
00404A20  |. 50             push eax                                 ; |hModule
00404A21  |. FF15 78104200  call near dword ptr ds:[421078]          ; \GetProcAddress

Haha, nice try ESET, nice try! If you decode 3dvcmQ=, you'll find GetPassword, but this string is not decoded! Time to patch the binary (Since a function can't have a '=' in its name), to call a more friendly function, like GetPassword. You can also patch your dll.

Time to code the dll:

char* GetPassword() {
    return "Jules:You read the bible, Ringo?";
}

If you don't want to install Visual Studio, you can build the dll on GNU/Linux, with mingw. Don't forget to check exports with PEiD, since my dll exported "GetPassword@0"!

Now, put the produced MALCHO.dll next to our crackme, run it, and a new file should appear in %temp%. PEiD tells us that this is not a PE, fortunately, TRiD is 60% sure that it's a Java file. You can also open it with your hexeditor, to see that the file starts with 0xCAFEBABE, and contains some Java garbage (Yes, it's a pleonasm). If you dump it in jd-gui, you'll get a small Java class named Bible:

import java.io.PrintStream;

public class Bible {
  public static void main(String[] args) {
    System.out.println("Ezekiel 25:17");
    System.out.println("-----------------------------------------------------------------------------------------------------------------");
    System.out.println("The path of the righteous man is beset on all sides by the inequities of the selfish and the tyranny of evil men.");
    System.out.println("Blessed is he, who in the name of charity and good will, shepherds the weak through the valley of darkness,");
    System.out.println("for he is truly his brother's keeper and the finder of lost children.");
    System.out.println("And I will strike down upon thee with great vengeance and furious anger those who would attempt to poison");
    System.out.println("and destroy my brothers.And you will know my name is the Lord when I lay my vengeance upon thee.");
    System.out.println("-----------------------------------------------------------------------------------------------------------------");
    System.out.println("Great! You found all easter eggs!");
  }
}

Anti-debugging tricks

Let's make a stage whisper and talk about anti-debugging routine that I found in this second crackme.

Back to the binary!

I started to play again with the binary, since I was pretty sure that it could not be over: I found all easter eggs, but I'm suspecting that I did not find every secrets. How could I interact with the binary? I did not see any network-related things, I already found the dll trick, I found the two passwords, I … Mh. Remember the second password? The popup said "No, you didn't".

Time to ask the intertubes if it's possible to hook GUI callbacks, and how I can do it with OllyDBG. Let's be clever: I initially though that I'll put breakpoints on the "check" buttons, but it's smarter to break on the "edit" fields, because this is where the content of the input field will be retrieved. Remember, We're not really putting breakpoints on buttons or fields, but on their callbacks.

So:

  1. Break on entrypoint
  2. View -> List of windows -> Breakpoint on ClassProc
  3. Run, step, run, …

The first breakpoint is on the first field, the Piggy thing. The second on is, err, on the middle of nowhere, on 0x405F50.

Time for some pleasing x86 reversing, especially after messing with .Net. Since this is likely a comparison, I focused on comparisons. At 406055, there is an obvious loop, verifying that [eax+edx] == [eax]. None of them is pointing to our string, smells like hashing. Did you guess which one it is? If you take a look at the previous address, 406052, you can see that you can follow in dump the value put in esi. Copy a big portion of this array, and save it somewhere.

Restarting the process, quickly stepping while looking for our sting to appear in the left margin, step step step step step and we land on 0x404263, where our string is xored with what seems to be an array. If you put a breakpoint on 0x404260, you'll see that our string is in fact xored in place. On the previous line, the value with which our string is xored is taken from the stack and put in ebx. Right clic, follow in stack, copy the array, hoping that it's constant and not some kind of weird hashing scheme, and some Python later:

a = [0x65, 0xf6, 0x6a, 0x22, 0xd5, 0x64, 0x08, 0x9d, 0x4c, 0x23, 0x6c, 0x01, 0xa7, 0xe4, 0x7d, 0x60, 0x75, 0x9d, 0x21, 0x9b]
b = [0x3f, 0x93, 0x0e, 0x05, 0xa6, 0x44, 0x6c, 0xf8, 0x2d, 0x47, 0x40, 0x21, 0xc5, 0x85, 0x1f, 0x19, 0x53, 0x6e, 0x56, 0x73]

print ''.join(map(lambda (x, y): chr(x ^ y), zip(a, b)))

This outputs:

Zed's dead, baby&w

Nice. If you enter this string in the second input, the crackme will lock its interface. Meh.

Are we not over yet?!

Time to take a look with process monitor. It seems that our crackme is calling userinit.exe, creating a new process.

Since this binary is filled with base64-encoded strings, I guess that you already put breakpoints on every call to 0x407B110, which is the unbase64 function. If you continue to step beyond 0x406055, you'll see some unbase64

XHVzZXJpbml0LmV4ZQ==
\userinit.exe

a2VybmVsMzIuZGxs
kernel32.dll

ICAgICBUSEUgRU5ELg0KVGhlIG1vdmllIGlzIG92ZXIu
    THE END.
The movie is over.

bG9jYWxob3N0
localhost

d2dldA==
wget

UE9TVA==
POST

aW5kZXgucGhw
index.php

Q29udGVudC1UeXBlOmFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZA==
Content-Type:application/x-www-form-urlencoded

a2V5PQ==
key=

SnVsZXM= 
Jules

Ymx1cA==
blup

d2luaW5ldC5kbGw=
wininet.dll

Then, at 0x406344, a call to VirtualAllocEx, and at 0x406363, another to WriteProcessMemory. Smells like some process injection(I have no idea why Windows allows to inject arbitrary code into some process, nor why it is possible to map a memory space as writable, then as executable).

Here, our crackme is launching userinit.exe, injects some code into it, and uses CreateRemoteThread (at 0x4063B3) to execute the injected (shell)code. So, put a breakpoint there, check the 4th argument on the stack: 0x0A0000. Launch a second instance of OllyDBG, attach it to userinit.exe, put another breakpoint on 0x0a0000, step in the first instance and ... yay, the breakpoint is hit in the second one! L0gic reminded me that this is also possible in Linux, with ptrace-black-magic.

The routine is lightly obfuscated (or just ugly). Roughly, it generates a random key, and POST it on http://localhost:8080/index.php. Our answer must decrypt to "Jules". If it does, a popup is shown "THE END.\nThe movie is over.". Then, a second POST request is issued, that can be decrypted with the previously send key: "That's all. Congratulations!". This is deserving a screenshot:

Final screenshot

I don't like php, and I'm sure that I don't want to deploy it on Windows. This is why I wrote the server in Python!

import wsgiref.simple_server

key = ''

def check(key, value):
    j = 0
    tab = [i for i in range(256)]
    for i in range(256):
        j = (j + ord(key[i % len(key)]) + tab[i]) % 256
        tab[i], tab[j] = tab[j], tab[i]

    j = 0
    ret = [0 for i in value]
    for i in range(1, len(value)+1):
        j = (j + tab[i]) % 256
        tab[i], tab[j] = tab[j], tab[i]
        ret[i-1] = ord(value[i-1]) ^ tab[(tab[i] + tab[j]) % 256]

    return ''.join(map(chr, ret))

def callback(environ, start_response):
    global key
    status = '200 OK'
    headers = [('Content-type', 'text/plain')]
    start_response(status, headers)
    request_body_size = int(environ.get('CONTENT_LENGTH', 0))
    data = environ['wsgi.input'].read(request_body_size).split('=')
    if data[0] == 'key':
        key = data[1]
        return [check(key, 'Jules'), ]
    elif data[0] == 'r':
        print "Decrypted: %s" % check(key, data[1])
    return ["Hello World"]

print("Serving on port 8080...")
wsgiref.simple_server.make_server('', 8080, callback).serve_forever()

Various things

Conclusion

It took me a week to completely reverse this crackme and to produce this writeup, my first under Windows, without even opening IDA. I felt that most of the tools (Olly, ImpRec, DLSpy, ProcDump, PEiD, …) are relics from the past, that will soon be incompatible with new Windows versions. Or maybe it's just me, not using the same tools that cool kids are using today on Windows.

Anyway, this was a fun crackme, and I learned a lot. Thank you ESET!