Partial Solution to Panda Challenge 2010 Edition: 1st challenge

So, I've picked up on this rather late (Sunday around 10 PM - GMT +3:00), but it doesn't matter since i'm kind of new when it comes to Reverse Engineering. This was the message from PandaLabs:

As promised, this 1st challenge is here: 1st-challenge.exe. To solve it, you have to create one valid license key for 999 users of this program.
The license key has to be sent to pandachallenge at pandasecurity dot com before next Monday at 17:00 (GMT+2). To avoid any problem with the mail filters, any attachment has to be compressed (zip or rar) with password (panda). Remember that there is no need to register, just download the file and enjoy!
The 1st one to send a valid license key will be the winner of the iPad. Good luck all!

So begins the story...Load the file in OllyDbg. this is what you'll see:

Hit Alt+E to see the Modules Window, select 1st-challenge.exe, right click on it and click View Names (Ctrl+N). Type "ReadFile", right click on it and select "Toggle breakpoint on import".
What we've done so far is when the ReadFile API is hit, Olly breaks. Run the program, and our breakpoint has a hit. Since we can see that we're inside the kernel32.dll (this is the dll that contains our ReadFile API).

Please look at the EDX register. It says "UNICODE "wnloads\\license.k". This license.k is the file that this challenge reads. Create a file called license.k in you 1st-challenge.exe directory, come back in Olly and redo the steps explained until here. Now select Debug-Execute till user code from the menu (or just press Alt+F9).

Scroll up a few lines and you'll see something like this:

0041B629    PUSH 0                                       ; /pOverlapped = NULL
0041B62B   LEA EBX,[LOCAL.1]         ; |
0041B62E   PUSH EBX            ; |pBytesRead = 0142FD40
0041B62F   PUSH ECX ; |BytesToRead = 74D718AF (1960253615.)
0041B630   PUSH EDX                                   ; |Buffer = 0142FCB8
0041B631   PUSH EAX                                   ; |hFile = 00000001
0041B632   CALL              ; \ReadFile

Now F8 it in Olly (a looot of times, yes, don't be scared) until you reach this portion of code:

00401721   |.  895E 2C           MOV DWORD PTR DS:[ESI+2C],EBX
00401724   |.  8B55 DC           MOV EDX,[LOCAL.9]
00401727   |.  8B45 F8           MOV EAX,[LOCAL.2]
0040172A   |.  E8 F1B50100       CALL 1st-chal.0041CD20                                              ;bad boy 1
0040172F   |.  8B45 F8           MOV EAX,[LOCAL.2]
00401732   |.  E8 F9BC0100       CALL 1st-chal.0041D430
00401737   |.  EB 05             JMP SHORT 1st-chal.0040173E
00401739   |>  E8 829D0100       CALL 1st-chal.0041B4C0
0040173E   |>  8D85 5CFFFFFF     LEA EAX,[LOCAL.41]
00401744   |.  E8 17120000       CALL 1st-chal.00402960

You see the call at 0041CD20? This is the badboy1. Press space when you reach at this instruction (don't execute it yet), press the Space button (this is to assemble it), write "nop" and press Enter. This is how it should look like now:

00401724   |.  8B55 DC           MOV EDX,[LOCAL.9]
00401727   |.  8B45 F8           MOV EAX,[LOCAL.2]
0040172A       90                NOP                                                                                ; no more bad boy
0040172B       90                NOP
0040172C       90                NOP
0040172D       90                NOP
0040172E       90                NOP
0040172F   |.  8B45 F8           MOV EAX,[LOCAL.2]
00401732   |.  E8 F9BC0100       CALL 1st-chal.0041D430
00401737   |.  EB 05             JMP SHORT 1st-chal.0040173E
00401739   |>  E8 829D0100       CALL 1st-chal.0041B4C0
0040173E   |>  8D85 5CFFFFFF     LEA EAX,[LOCAL.41]

Press F8 again a few times, until you reach here:

004017A0   |.  DFE0              FSTSW AX
004017A2   |.  9E                SAHF
004017A3   |.  0F85 08010000     JNZ 1st-chal.004018B1                                                   ;  bad boy2
004017A9   |.  8B45 F8           MOV EAX,[LOCAL.2]
004017AC   |.  8B40 24           MOV EAX,DWORD PTR DS:[EAX+24]
004017AF   |.  E8 1C770100       CALL 1st-chal.00418ED0
004017B4   |.  83EC 0C           SUB ESP,0C

Go until JNZ 1st-chal.004081B1, press Space and write JE instead of JNZ. This ensures us that the jump will not be taken. Press F8 a few more times, until you see this:

004017DE   |.  DED9              FCOMPP
004017E0   |.  DFE0              FSTSW AX
004017E2   |.  9E                SAHF
004017E3   |.  74 05             JE SHORT 1st-chal.004017EA
004017E5       E8 D69C0100       CALL 1st-chal.0041B4C0                                              ;  bad boy 3
004017EA   |>  E8 818F0000       CALL 1st-chal.0040A770
004017EF   |.  89C3              MOV EBX,EAX

The last step here is to nop the call made to 0041B4C0 (i think you how by now). Finally, right click, Copy to executables -> All modifications -> Copy all, right click on the new window, Save file, and save it as incomplete-1st-challenge.exe. If you've lost yourself somewhere, you can find the files here.
So, let's recap! What was the challenge? The challenge was that the binary should have written
Registered for 999 user(s)

What does it say now? It says this:
Registered for xxx* user(s)

*xxx is a number from -999 to 999.

This message appears regardless to what the license.k file contains. Sure, I failed, but since the challenge isn't running anymore, the author has written a few words about this challenge:

The challenge is a crackme consisting in a little virtual machine developed in Free Pascal (http://freepascal.org). This VM has 5 general purpose registers (R0-R5), 5 registers for arguments (A0-A5), it has no memory and “texts” are stored in the registers, no matter if the value is a text, a decimal or a whole number, and the virtual machine supports a little more than 10 instructions (mov, hlt, xor, add, sub, …).
When the executable binary is run, it looks for the file “license.k”. This file is the “program” to be run by the virtual machine. Some random values are passed to this program in the arguments from A0 to A3. These random values will be the coefficients in the following elliptic curve:
result = sqrt(a*x**3 + b*x**2 + c)
The program has to move the values that have been passed to it from the arguments A0-A3 to R0-R3, operate with them in order to obtain the final result, put this value in the A0 entry and put the number of users for which it is licensed in A1. As simple as that! 
To make this task not so awful for me, while I was developing it, I created a quite rough (but effective) compiler in Python of some assembler code invented by me. The final assembler code with comments is as follows:
; Given the following equation:
;    sqrt(A*X^3 + B*X^2 + C)
; The coefficients are hold in the following registers:
; A0 -> A
; A1 -> B
; A2 -> C
; A3 -> X
; We can’t operate with any of the A* registers ; so move the values to general purpose registers MOV  R0, A0 ; R0 -> A MOV  R1, A1 ; R1 -> B MOV  R2, A2 ; R2 -> C MOV  R3, A3 ; R3 -> X MOVS  3, R4 ; R4 -> 3 POW  R4, R3, R4 ; R4 -> x^3 MOVS  2, R5 ; R5 -> 2 POW  R5, R3, R5 ; R5 -> x^2 MUL  R0, R0, R4 ; R0 -> ax^3 MUL  R1, R1, R5 ; R1 -> bx^2 ADD  R0, R0, R1 ; R0 -> ax^3 + bx^2 ADD  R0, R0, R2 ; R0 -> ax^3 + bx^2 + c ABS  R0, R0, R0 ; y = abs(ax^3 + bx^2 + c)
SQRT R0, R0     ; y = sqrt(y)
MOV  A0, R0 ; Store the result in A0
MOVS 99, R0
MOV  A1, R0 ; Store in A1 the number of license users ; DUMP ; <- Uncomment this for debugging purposes
And that’s all  The source code of the crackme and the “compiler” is already available (here).

You see the challenge now? It wasn't easy, and VM's are always a pain to debug!!!
As always, feedback is always welcomed! [and help, of course :D]

PS: I know the code look bad, but this theme is bad for writing assembly code. I'll probably replace it in the near future! 

No comments:

Post a Comment