[yAtEs] crackme v1.0

by

{Cronos}

Intro

Now I won't mess about with this tutorial. This is a high-rated crackme and this isnt a tutorial for newbies. I'll bring in the level straight away so as not to get bogged down.

Target

Our target is [yAtEs] crackme v1.0. The most interesting thing about this is that it includes PE-PRO protected by the crackme. The idea is to find a valid serial number for the program to run.

Analysis

OK, we note it has a PE section (marked 'data') which contains code and everything else looks like nonsense (actually encrypted). What follows is a dump of the main code, interspersed with some comments.
start:
1000:0049709d 6a00                 push   00h
1000:0049709f 6880000000           push   80h
1000:004970a4 6a03                 push   03h
1000:004970a6 6a00                 push   00h
1000:004970a8 6a00                 push   00h
1000:004970aa 6800000080           push   80000000h
1000:004970af 6853704900           push   offset filename
1000:004970b4 e88b010000           call   CreateFileA
This first bit just opens a file called 'technics.dat' which will contain the serial number.

1000:004970b9 83f8ff               cmp    eax, -01h
1000:004970bc 0f8437010000         jz     4971f9h
1000:004970c2 a37c704900           mov    [49707ch], eax
1000:004970c7 6a00                 push   00h
1000:004970c9 6890704900           push   offset buff_length
1000:004970ce 6a0c                 push   0ch
1000:004970d0 6880704900           push   offset var_a
1000:004970d5 ff357c704900         push   dword ptr [49707ch]
1000:004970db e852010000           call   _ReadFile
Now it reads in 12 bytes from that file into a buffer at offset var_a. So the length of the serial number is 12 and we can create a 12 byte file called technics.dat.

1000:004970e0 a190704900           mov    eax, [buff_length]
1000:004970e5 a184704900           mov    eax, [var_b]
1000:004970ea a378704900           mov    [res_3], eax
1000:004970ef b870704900           mov    eax, offset res_1
1000:004970f4 e89f000000           call   497198h
1000:004970f9 833d7070490000       cmp    dword ptr [res_1], 00h
1000:00497100 0f85f3000000         jnz    4971f9h
1000:00497106 833d7470490000       cmp    dword ptr [res_2], 00h
1000:0049710d 0f85e6000000         jnz    4971f9h
Now here we have part of the serial number (the centre of it) being put into res_3 and we call a subroutine at 497198h. The jumps to 4971f9h are jumps to a messagebox routine telling us the serial is incorrect. We note that we need both res_1 and res_2 to be zero to get past this. In fact 497178 is a decryption routine which we will need to crack.

1000:00497113 a16c704900           mov    eax, [49706ch]
1000:00497118 8b18                 mov    ebx, [eax]
1000:0049711a 891d80704900         mov    [var_a], ebx
1000:00497120 a16c704900           mov    eax, [49706ch]
1000:00497125 8b5804               mov    ebx, [eax+04h]
1000:00497128 891d84704900         mov    [var_b], ebx
1000:0049712e a180704900           mov    eax, [var_a]
1000:00497133 8b1d84704900         mov    ebx, [var_b]
1000:00497139 a370704900           mov    [res_1], eax
1000:0049713e 891d74704900         mov    [res_2], ebx
1000:00497144 e84f000000           call   497198h
1000:00497149 a170704900           mov    eax, [res_1]
1000:0049714e 8b1d74704900         mov    ebx, [res_2]
1000:00497154 8b0d6c704900         mov    ecx, [49706ch]
1000:0049715a 8901                 mov    [ecx], eax
1000:0049715c 8b0d6c704900         mov    ecx, [49706ch]
1000:00497162 83c104               add    ecx, 04h
1000:00497165 8919                 mov    [ecx], ebx
1000:00497167 83056c70490008       add    dword ptr [49706ch], 08h
1000:0049716e ff0594704900         inc    dword ptr [497094h]
1000:00497174 a194704900           mov    eax, [497094h]
1000:00497179 3d80d70000           cmp    eax, d780h
1000:0049717e 7593                 jnz    497113h
1000:00497180 6a00                 push   00h
1000:00497182 6800704900           push   offset PE_Pro_message
1000:00497187 6860704900           push   offset CorrectKey_message
1000:0049718c 6a00                 push   00h
1000:0049718e e887000000           call   _MessageBoxA
1000:00497193 e9d859fdff           jmp    46cb70h
Now this longer piece of code is actually decrypting the PE_Pro program itself (which is just the PE section containing the file) and then prints a message saying 'correct key'. We won't actually be concerned with this part any further

1000:00497198 60                   pushad 
1000:00497199 bd20000000           mov    ebp, 20h
1000:0049719e b82037efc6           mov    eax, c6ef3720h
1000:004971a3 8b1570704900         mov    edx, [res_1]
1000:004971a9 8b0d74704900         mov    ecx, [res_2]
1000:004971af 8b3578704900         mov    esi, [res_3]

1000:004971b5 8bfa                 mov    edi, edx
1000:004971b7 c1ef05               shr    edi, 05h
1000:004971ba 03fe                 add    edi, esi
1000:004971bc 8bda                 mov    ebx, edx
1000:004971be c1e304               shl    ebx, 04h
1000:004971c1 03de                 add    ebx, esi
1000:004971c3 33fb                 xor    edi, ebx
1000:004971c5 8d1c10               lea    ebx, [eax+edx]
1000:004971c8 33fb                 xor    edi, ebx
1000:004971ca 2bcf                 sub    ecx, edi
1000:004971cc 8bf9                 mov    edi, ecx
1000:004971ce c1ef05               shr    edi, 05h
1000:004971d1 03fe                 add    edi, esi
1000:004971d3 8bd9                 mov    ebx, ecx
1000:004971d5 c1e304               shl    ebx, 04h
1000:004971d8 03de                 add    ebx, esi
1000:004971da 33fb                 xor    edi, ebx
1000:004971dc 8d1c08               lea    ebx, [eax+ecx]
1000:004971df 33fb                 xor    edi, ebx
1000:004971e1 2bd7                 sub    edx, edi
1000:004971e3 2db979379e           sub    eax, 9e3779b9h
1000:004971e8 4d                   dec    ebp
1000:004971e9 75ca                 jnz    4971b5h
1000:004971eb 891570704900         mov    [res_1], edx
1000:004971f1 890d74704900         mov    [res_2], ecx
1000:004971f7 61                   popad  
1000:004971f8 c3                   ret    
This is the most important part of the program, the decryption routine which we will analyse in detail. Note specifically the starting values for res_1, and res_2, which are 9184ea05h and 26440640h respectively.

1000:004971f9 6a00                 push   00h
1000:004971fb 6800704900           push   offset PE_Pro_message
1000:00497200 6817704900           push   offset Sorry_Message
1000:00497205 6a00                 push   00h
1000:00497207 e80e000000           call   _MessageBoxA
_ExitProcess:
1000:0049720c ff258c014700         jmp    dword ptr ExitProcess
And the final part is just the sorry message code.

So the next stage is the analysis of the decryption routine in detail, in fact I prefer to rewrite it as pseudo-code. Here it is in pseudo code, which will be comparable to the real code above. I have included some code just before the call and the code just after.
read var_a,var_b,var_c from file
res_1=9184ea05
res_2=26440640
res_3=var_b

eax=c6ef3720
edx=res_1
ecx=res_2
esi=res_3

do
{
ebp=32
edi=edx
edi=edi>>5+esi
ebx=edx
ebx=ebx<<4+esi
edi=edi^ebx
ebx=eax+edx
edi=edi^ebx
ecx-=edi
edi=ecx
edi=edi>>5+esi
ebx=ecx
ebx=ebx<<4+esi
edi=edi^ebx
ebx=eax+acx
edi=edi^ebx
edx-=edi

eax-=9e3779b9
dec ebp
} while ebp!=0

res_1=edx
res_2=ecx

if res_1 != 0 or res_2 !=0, invalid key

Now my next task is to start simplifying this and rewriting it, so I take things like edi=edx, edi=edi>>5+esi, ebx=edx, ebx=ebx<<4+esi, edi=edi^ebx and start combining some items. The result is below, my reverse engineered decryption routine.
read var_a,var_b,var_c from file
res_1=9184ea05
res_2=26440640
res_3=var_b

eax=c6ef3720
edx=res_1
ecx=res_2
esi=res_3

// Tea algorithm with a simplified key
for(i=0;i<32;i++)
{
ecx-=(edx>>5+esi)^(edx<<4+esi)^(eax+edx)
edx-=(ecx>>5+esi)^(ecx<<4+esi)^(eax+ecx)

eax-=9e3779b9
}

res_1=edx
res_2=ecx

if res_1 != 0 or res_2 !=0, invalid key

Now, before we go any further this is actually recognisable as the TEA encryption algorithm, which is a pretty strong algorithm. Luckily [yAtEs] has made this quite a bit easier for us by using only a 32-bit key though. Nevertheless we can't reverse it to re-encrypt the final part, we need to recover the key of tha algorithm and the only way to do that is by brute force. So, I take the above, and change it around a bit and write a brute force decoder in C. Here it is:
/* modified from an original Tea encryption program I had, in order to conform to
the algorithm which [yAtEs] is using.
Just a simple brute forcer for part I. I compiled as a win32 console proggie. It takes
about an hour and twenty minutes to try all keys. 
In fact it finds the key 5ef3184a in about 20 minutes or so.
*/

/*
The Tiny Encryption Algorithm, or TEA, is a Feistel cipher invented by David
Wheeler. It is intended for use in applications where code size is at a
premium, or where it is necessary for someone to remember the algorithm and
code it on an arbitrary machine at a later time.

Since its round function is relatively weak, with nonlinearity coming only from
the carry propagation, TEA has 64 rounds. However, its simplicity means that it
runs more quickly in software than many other algorithms with fewer, more
complex, rounds.
*/

#include <stdio.h>

void code(long* v, long k)
{
	unsigned long y=v[0],z=v[1],sum=0xc6ef3720,             /* set up */
             delta=0x9e3779b9, n=32 ;         /* key schedule constant*/

	while (n-->0)
	{                                              /* basic cycle start*/
		z -= ((y<<4)+k) ^ (y+sum) ^ ((y>>5)+k) ;     /* end cycle */
  		y -= ((z<<4)+k) ^ (z+sum) ^ ((z>>5)+k) ;
  		sum -= delta ;
	}
	v[0]=y ;
	v[1]=z ;
}

void main(void)
{	long k, v[2];
	k=0;
	do
	{
		k++;
		if((k&0xffff)==0) printf("%x\n",k);
		v[0]=0x9184ea05;
		v[1]=0x26440640;
		code(v,k);
	}while(v[0]||v[1]);
	printf("%x",k);
}

The code here should be fairly self explanatory, it merely checks all possible keys until the right one is found. It finds the key 5ef3184a in around 20 minutes and we can then remake the keyfile:

00 00 00 00 4a 18 f3 5e 00 00 00 00

The key works and we have finished our objective.

Conclusions

I suspect that [yAtEs] first made this with a longer key and realising it was going to be too hard he must've reduced the key length. A longer key would just make it a bit lame, the current key length is sufficiently interesting. The alternative would be to make us reverse the algorithm by hard coding the key and saying decrypt(serial,key)=0 which would allow us to use encrypt(0,key) to find the serial. In my mind this would be better because you could still use a long key and it would take a greater leap in imagination to reverse the algorithm itself. (unless you recognise TEA of course). Still it was worth doing for the experience and I would rate it 6/10 for difficulty.

{Cronos}