Tuesday, April 1, 2014

Crackme Bin by Basse write-up

After cracking the Magic Numbers crackme, I've chosen to crack (from the crackmes collection mentioned in Magic Numbers) one more. This time it's crackme "Bin" by Basse.
Goal: Find the correct key.
We have a GetDlgItemInt command call at 0040105E
0040105E | E8 31010000    | CALL
This function translates the text of a specified control into an integer value. So the key is integer value. And the key after this function call is stored at EAX.
After calling GetDlgItemInt the offset of ASCII data (located at offset 00403000) is copying to ESI register.
Then after checks 4 bytes of ESI data for 0 (null terminator - end of data):
00401068 | 833E 00     | CMP DWORD PTR DS:[ESI],0
we're jumping to 00401071 if not end of ASCII data:
0040106B | 75 04       | JNE SHORT Bin.00401071
otherwise we're jumping to 0040107D if the end of ASCII data:
0040106D | EB 0E       | JMP SHORT Bin.0040107D

The important comparison is performed here:
0040107D | 3D BF96287A | CMP EAX,7A2896BF
the result of function is compared to the hex value 0x7A2896BF (2049480383),
and if the result is equal to this value - the "Congratulation, you found the right key" message is shown:
00401084 | 6A 40       PUSH 40
00401086 | 68 30354000 PUSH OFFSET Bin.00403530 ; ASCII "In the Bin"
0040108B | 68 3B354000 | PUSH OFFSET Bin.0040353B ; ASCII "Congratulation, you found the right key"
00401090 | FF75 08     PUSH DWORD PTR SS:[EBP+8]
00401093 | E8 02010000 CALL

After jumping to 00401071 we moving current 4 bytes of ASCII data to EBX register.
00401071 | 8B1E        | MOV EBX,DWORD PTR DS:[ESI]
Then an important function at 0040110F is called:
00401073 | E8 97000000 | CALL Bin.0040110F
After that the ASCII data pointer at ESI by 4 (dword) increased:
00401078 | 83C6 04     ADD ESI,4
And we jump back to 00401068:
0040107B | EB EB       | JMP SHORT Bin.00401068

Now lets look into the this important function:
We have 4 bytes of ASCII data at EBX register, and the key value at EAX.
Here is the code of this function with comments.
0040110F | 51          | PUSH ECX    ; push ECX onto stack
00401110 | 52          | PUSH EDX    ; push EDX onto stack
00401111 | 8BD3        | MOV EDX,EBX ; copy 4 bytes of data
00401113 | 8BC8        | MOV ECX,EAX ; copy key
00401115 | 40          | INC EAX     ; key = key + 1
00401116 | F7D0        | NOT EAX     ; key = ~(key)
00401118 | 43          | INC EBX     ; data data + 1
00401119 | F7D3        | NOT EBX     ; data = ~(data)
0040111B | 40          | INC EAX     ; key = key + 1
0040111C | 43          | INC EBX     ; data data + 1
0040111D | 23C2        | AND EAX,EDX ; key = key & copy of data
0040111F | 23D9        | AND EBX,ECX ; data = data & copy of key
00401121 | 03C3        | ADD EAX,EBX ; result key + data
00401123 | 5A          | POP EDX     ; restore EDX
00401124 | 59          | POP ECX     ; restore ECX
00401125 | C3          | RETN        ; return result
So this function is could be written this way:
result = (value & (~(key + 1) + 1)) + (key & (~(value + 1) + 1))
If simplify this function it will be:
result = key ^ value, — just exclusive or (xor) operation.
so knowing the final result 0x7A2896BF (2049480383it's easy to find the key.
Here is my python code, to find the key:
```import struct

def invert(x):
return ~x & 0xffffffff

def decode_func(key, value):
return (value & (invert(key + 1) + 1)) + (key & (invert(value + 1) + 1))

def checker(key):
result = key
for i in xrange(0, len(data), 4):
value = struct.unpack('I', data[i:i+4])[0]
result = decode_func(result, value)
return result

key = checker(0x7a2896bf)
print 'The key is: %s' % key
```
So the key is: 0x7a2896bf (2049480383). The same as the final result.