Magic Crackme by Basse
 
Intro


If you start this crackme, you will see that we have to fill in some sort of Matrix.
But if we want to know how to fill the matrix, we have to take a look inside :)

This looks like a math crackme! Nice, let's dig in!

TOOLS USED : IDA

DIFFICULTY : 3/10

The code


This is a math crackme...but we don't know what it expects from us. So let's disassemble this crackme first.
We can quickly find the goodguy message, and the code we want to examine :

00401313                 cmp     [ebp+arg_4], 111h ; WM_COMMAND
0040131A jnz loc_4014F6
00401320 mov edx, [ebp+arg_8]
00401323 shl edx, 10h
00401326 or dx, dx
00401329 jnz short loc_401340
0040132B mov eax, [ebp+arg_8]
0040132E cmp ax, 64h ; TRY-button?
00401332 jnz loc_4013E8
00401338 xor edi, edi
0040133A lea esi, ds:4031D4h
00401340
00401340 loc_401340: ; CODE XREF: sub_4010F9+230j
00401340 ; sub_4010F9+283j
00401340 mov eax, dword_403180[edi*4]
00401347 push offset byte_40304A
0040134C push 2
0040134E push 0Dh
00401350 push eax
00401351 call j_SendMessageA
00401356 inc edi ; counter ++
00401357 mov al, byte_40304A
0040135C cmp al, 41h ; Test for A-F
0040135E jb short loc_40136A
00401360 cmp al, 46h
00401362 ja short loc_40136A
00401364 sub al, 36h ; convert input
00401366 mov [esi], al ; Store at Matrix[counter]
00401368 jmp short loc_401376
0040136A ; ---------------------------------------------------------------------------
0040136A
0040136A loc_40136A: ; CODE XREF: sub_4010F9+265j
0040136A ; sub_4010F9+269j
0040136A cmp al, 30h ; test for 0-9
0040136C jb short loc_401376
0040136E cmp al, 39h
00401370 ja short loc_401376
00401372 sub al, 2Fh ; convert input
00401374 mov [esi], al ; Store at Matrix[counter]
00401376
00401376 loc_401376: ; CODE XREF: sub_4010F9+26Fj
00401376 ; sub_4010F9+273j ...
00401376 cmp edi, 10h ; All elements inserted?
00401379 jnb short loc_40137E ; if yes, take jump
0040137B inc esi ; next element in matrix
0040137C jmp short loc_401340 ; Continue loop
0040137E ; ---------------------------------------------------------------------------
0040137E
0040137E loc_40137E: ; CODE XREF: sub_4010F9+280j
0040137E call sub_4017BA ; Check if every value is only used once
00401383 cmp eax, 1 ; all different values?
00401386 jnz loc_401527 ; if no, jump badguy
0040138C call sub_40193B ; Make sum of all rows, columns, and diagonals
00401391 call sub_401A5C ; Check if sums are equal
00401396 cmp eax, 1
00401399 jnz short loc_4013E3 ; Badguy jump
0040139B jmp short loc_4013CF ; Goodguy jump :-)
0040139B sub_4010F9 endp
0040139B
0040139B ; ---------------------------------------------------------------------------
0040139D aSuccessYouFoun db 'Success! You found one of',0Dh,0Ah ; DATA XREF: .text:004013D6o
0040139D db 'the correct solutions!',0
004013CF ; ---------------------------------------------------------------------------
004013CF
004013CF loc_4013CF: ; CODE XREF: sub_4010F9+2A2j
004013CF push 40h
004013D1 push offset aMagicNumbers ; "Magic numbers"
004013D6 push offset aSuccessYouFoun ; "Success! You found one of\r\nthe correct "...
004013DB push dword ptr [ebp+8]
004013DE call j_MessageBoxA
004013E3
004013E3 loc_4013E3: ; CODE XREF: sub_4010F9+2A0j
004013E3 jmp loc_401527
004013E8 ; ---------------------------------------------------------------------------

I think the code above is very easy to understand. In short, the values that are entered in the matrix are converted from their ascii value to their real value + 1.
So eg. when you enter a 'F' it will be converted to '10h', a '1' to '2h', and so on...

When this is done, we have 3 calls followed by the goodguy/badguy jump.

Let's check the 3 calls so we can figure out what to do :

CALL 1 :

004017BA sub_4017BA      proc near               ; CODE XREF: sub_4010F9+285p
004017BA xor ebx, ebx ; init ebx to 0
004017BC lea esi, ds:4031D4h ; esi= address matrix[]
004017C2 mov ecx, 10h ; init counter to 16
004017C7
004017C7 loc_4017C7: ; CODE XREF: sub_4017BA+16Fj
004017C7 mov al, [esi] ; move matrix[counter] to AL
004017C9 cmp al, 1 ; matrix[counter]==1 ?
004017CB jnz short loc_4017E1 ; Jump to next if not
004017CD test bx, 1 ; digit used before?
004017D2 jnz loc_401935 ; If yes, jump badguy
004017D8 or bx, 1 ; If not, put the bit flag
004017D8 ; (16 bits in bx, 16 different values)
004017DC jmp loc_401927 ; jump continue loop
004017E1 ; ---------------------------------------------------------------------------
004017E1
004017E1 loc_4017E1: ; CODE XREF: sub_4017BA+11j
004017E1 cmp al, 2
004017E3 jnz short loc_4017F9
004017E5 test bx, 2
004017EA jnz loc_401935
004017F0 or bx, 2
004017F4 jmp loc_401927
004017F9 ; ---------------------------------------------------------------------------
004017F9
004017F9 loc_4017F9: ; CODE XREF: sub_4017BA+29j
004017F9 cmp al, 3
004017FB jnz short loc_401811
004017FD test bx, 4
00401802 jnz loc_401935
00401808 or bx, 4
0040180C jmp loc_401927
00401811 ; ---------------------------------------------------------------------------
... 00401927 ; ---------------------------------------------------------------------------
00401927
00401927 loc_401927: ; CODE XREF: sub_4017BA+22j
00401927 ; sub_4017BA+3Aj ...
00401927 inc esi ; point matrix to next value
00401928 dec ecx ; counter --
00401929 jnz loc_4017C7 ; continue loop if not done
0040192F mov eax, 1 ; move 1 to eax and return
00401934 retn
00401935 ; ---------------------------------------------------------------------------
00401935
00401935 loc_401935: ; CODE XREF: sub_4017BA+18j
00401935 ; sub_4017BA+30j ...
00401935 mov eax, 0 ; move 0 to eax and return
0040193A retn
0040193A sub_4017BA endp


In the first call the entire matrix is checked. Every value should only appear once. To check that the author used register BX.
This is a 16-bit register, and the matrix has also 16 elements... indeed, every value is represented with one bit.

If a values is found more than once in the matrix, we return with badguy flag.

CALL 2 :

0040193B sub_40193B      proc near               ; CODE XREF: sub_4010F9+293p
0040193B mov ecx, 4
00401940 mov byte_4031E4, 0
00401947 lea esi, ds:4031D4h
0040194D
0040194D loc_40194D: ; CODE XREF: sub_40193B+19j
0040194D lodsb
0040194E add byte_4031E4, al ; Sum of first row
00401954 loop loc_40194D
00401956 mov ecx, 4
0040195B mov byte_4031E5, 0
00401962 lea esi, ds:4031D8h
00401968
00401968 loc_401968: ; CODE XREF: sub_40193B+34j
00401968 lodsb
00401969 add byte_4031E5, al ; Sum of second row
0040196F loop loc_401968
00401971 mov ecx, 4
00401976 mov byte_4031E6, 0
0040197D lea esi, ds:4031DCh
00401983
00401983 loc_401983: ; CODE XREF: sub_40193B+4Fj
00401983 lodsb
00401984 add byte_4031E6, al ; Sum of third row
0040198A loop loc_401983
0040198C mov ecx, 4
00401991 mov byte_4031E7, 0
00401998 lea esi, ds:4031E0h
0040199E
0040199E loc_40199E: ; CODE XREF: sub_40193B+6Aj
0040199E lodsb
0040199F add byte_4031E7, al ; Sum of 4th row
004019A5 loop loc_40199E
004019A7 mov ecx, 4
004019AC mov byte_4031E8, 0
004019B3 lea esi, ds:4031D4h
004019B9
004019B9 loc_4019B9: ; CODE XREF: sub_40193B+88j
004019B9 lodsb
004019BA add esi, 3
004019BD add byte_4031E8, al ; Sum of first column
004019C3 loop loc_4019B9
004019C5 mov ecx, 4
004019CA mov byte_4031E9, 0
004019D1 lea esi, ds:4031D5h
004019D7
004019D7 loc_4019D7: ; CODE XREF: sub_40193B+A6j
004019D7 lodsb
004019D8 add esi, 3
004019DB add byte_4031E9, al ; Sum of 2nd column
004019E1 loop loc_4019D7
...

The second call, makes the sum of all rows, columns, and diagonals. They are stored in a byte array starting at address 4031E4.

CALL 3 :

00401A5C sub_401A5C      proc near               ; CODE XREF: sub_4010F9+298p
00401A5C lea esi, ds:4031E4h ; pointer to array with sums
00401A62 lodsb
00401A63 mov ecx, 9 ; init counter = 9
00401A68 push offset loc_401A72
00401A6D call sub_401B70 ; Check if all sums are equal
00401A72
00401A72 loc_401A72: ; DATA XREF: sub_401A5C+Co
00401A72 or eax, eax
00401A74 jnz short loc_401A7B ; Jump to do more checks
00401A76 jmp loc_401B6A ; Badguy jump
00401A7B ; ---------------------------------------------------------------------------
00401A7B ...

The Third and last call check if all these sums (that are stored in the array), are equal.

The assignment



Ok, now we know what to solve.
We have to fill a matrix with elements 0-F. They can only be used once.
Every row, column and diagonal should have the same sum.

The elements are converted to 1 - 10h by the crackme.

So, now we can calculate the sum of one row/column/diagonal :

SUM(1 - 16) = 136

136 / 4 (number of rows) = 34

After that I just started filling the matrix, and quickly found the fullowing matrix.
I started entering the largest numbers first. red
I entered them in a way they are not on the same row/colum.
Then the following four numbers, just watching out that all sums remain under 26h. green
The reamining numbers are easy to fill in. Make sure all sums of rows/colums/diagonals is equal (34)


16
5
9
4
10
3
15
6
7
14
2
11
1
12
8
13

=34
=34

=34
=34


Check the sums out, they are all 34.

Enter the matrix in the crackme, press try, and ...we solved it!

Final notes


Another crackme by Basse down ;-)

This solution is only one of the 880 possibilities! Can you find the other 879? :-)

If you have questions, or remarks abou this tutorial, feel free to mail me.

 

Detten
Detten@eudoramail.com

Greetz to Basse for another nice crackme! Keep on sending them :)

Back to tutorials

www.biw-reversing.cjb.net