		Tutorial for Pusillus Crackme 1.0 by TSCube
		-------------------------------------------

+----------+
| 1. Intro |
+----------+

Hey, while having a look at my collection of crackmes, I found an old tutorial for an old but
rather interesting crackme from Pusillus.
I made a little translation and... here it is : enjoy ! :)

note : this crackme involves a lot of magic/stuff, you've been warned.

+--------------------------+
| 2. The protection scheme |
+--------------------------+

As usual : 'BPX HMEMCPY' etc...

* Referenced by a CALL at Address:
|:004010B0   
|
:004010C9 56                      push esi
:004010CA 57                      push edi
:004010CB 51                      push ecx
:004010CC 33F6                    xor esi, esi
:004010CE 33FF                    xor edi, edi
:004010D0 B908000000              mov ecx, 00000008
:004010D5 BE44304000              mov esi, 00403044 -> serial


begin_loop_1
-------------

This loop will XOR each of the serial letters with 0x32
... as it loops 8 times, we can conclude that the serial is 8 letters long

:004010DA 803632                  xor byte ptr [esi], 32
:004010DD 46                      inc esi
:004010DE E2FA                    loop 004010DA

end_loop_1
-----------


:004010E0 BE44304000              mov esi, 00403044 -> XORED serial
:004010E5 B904000000              mov ecx, 00000004 -> we will loop 4 times


begin_loop_2
------------

This loop will use the serial to compute 4 magic values, which will be stored in an array at 
@40304C

:004010EA 8A06                    mov al, byte ptr [esi] -> letter
:004010EC 8A5E01                  mov bl, byte ptr [esi+01] -> next letter
:004010EF 32C3                    xor al, bl
:004010F1 88874C304000            mov byte ptr [edi+0040304C], al
:004010F7 83C602                  add esi, 00000002 -> skip 2
:004010FA 47                      inc edi
:004010FB E2ED                    loop 004010EA

end_loop_2
----------


:004010FD BE4C304000              mov esi, 0040304C -> array from previous loop
:00401102 8A06                    mov al, byte ptr [esi] -> first magic value
:00401104 8A5E01                  mov bl, byte ptr [esi+01] -> second
:00401107 32C3                    xor al, bl
:00401109 8A5E02                  mov bl, byte ptr [esi+02] -> third
:0040110C 8A4E03                  mov cl, byte ptr [esi+03] -> fourth
:0040110F 32D9                    xor bl, cl
:00401111 32C3                    xor al, bl

AL now contains a magic value computed from the array

:00401113 B908000000              mov ecx, 00000008 -> we will loop 8 times
:00401118 BE44304000              mov esi, 00403044 -> XORED serial


begin_loop_3
------------

This loop will XOR each byte of the XORED serial with the magic value

:0040111D 3006                    xor byte ptr [esi], al -> AL = magic value
:0040111F 46                      inc esi
:00401120 E2FB                    loop 0040111D

end_loop_3
----------


:00401122 B908000000              mov ecx, 00000008
:00401127 BE44304000              mov esi, 00403044
:0040112C BF08304000              mov edi, 00403008 -> constant array

This array contains 8 bytes : 0x71 0x18 0x59 0x1B 0x79 0x42 0x45 0x4C


begin_loop_4
--------------

This loop checks if our array at @403044 is equal to a constant array at @403008

:00401131 8A06                    mov al, byte ptr [esi]
:00401133 3A07                    cmp al, byte ptr [edi]
:00401135 751D                    jne 00401154 --> FUCK OFF
:00401137 46                      inc esi
:00401138 47                      inc edi
:00401139 E2F6                    loop 00401131

end_loop_4
------------

:0040113B 6A40                    push 00000040

[REGISTERED IF WE GET HERE]




+-------------------------+
| 3. Let's take an exemple |
+-------------------------+

entered serial = "12345678" <=> 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38


first loop
-----------
(all bytes are XORED with 0x32)

=> 0x03 0x00 0x01 0x06 0x07 0x04 0x05 0x0A


second loop
------------
(a 4 byte array is computed from the above 8 bytes)

1st byte = 0x03 XOR 0x00 = 0x03
2nd byte = 0x01 XOR 0x06 = 0x07
3rd byte = 0x07 XOR 0x04 = 0x03
4th byte = 0x05 XOR 0x0A = 0x0F


third loop
----------
(a magic value is computed from the 4 byte array)

magic_value = (0x03 XOR 0x07) XOR (0x03 XOR 0x0F) = 0x08


fourth loop
-----------
(the array from the first loop is XORED with the magic_value)

=> 0x0B 0x08 0x09 0x0E 0x0F 0x0C 0x0D 0x02


last loop
---------
... well, the 8 bytes must be equal to :
0x71 0x18 0x59 0x1B 0x79 0x42 0x45 0x4C



+------------------------------+
| 4. Let's solve the crackme ! |
+------------------------------+

First, in case you have not noticed it yet, you can directly compute the magic_value from
the XORED array from loop1 WITHOUT using the 4 bytes array

XORED array = 0x03 0x00 0x01 0x06 0x07 0x04 0x05 0x0A
magic_value =  0x03 XOR 0x00 XOR 0x01 XOR 0x06 ... XOR 0x0A = 0x08

From now, we'll call b1,b2... b8 the 8 bytes from the XORED array (loop1)


equations to solve :
-------------------

{ b1 XOR magic_value = 0x71
{ b2 XOR magic_value = 0x18
{ b3 XOR magic_value = 0x59
{ b4 XOR magic_value = 0x1B
{ b5 XOR magic_value = 0x79
{ b6 XOR magic_value = 0x42
{ b7 XOR magic_value = 0x45
{ b8 XOR magic_value = 0x4C

BUT, we know that: magic_value = b1 XOR b2 XOR b3 ... XOR b8

The equations become :

{ b1 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x71
{ b2 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x18
{ b3 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x59
{ b4 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x1B
{ b5 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x79
{ b6 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x42
{ b7 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x45
{ b8 XOR (b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x4C

BUT, we know that :
A XOR A XOR B XOR C <=> 0 XOR B XOR C <=> B XOR C (because 'A XOR A = 0' and '0 XOR B = B')

The equations become :

{ b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x71 (1)
{ b1 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x18 (2)
{ b1 XOR b2 XOR b4 XOR b5 XOR b6 XOR b7 XOR b8) = 0x59 (3)
{ b1 XOR b2 XOR b3 XOR b5 XOR b6 XOR b7 XOR b8) = 0x1B (4)
{ b1 XOR b2 XOR b3 XOR b4 XOR b6 XOR b7 XOR b8) = 0x79 (5)
{ b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b7 XOR b8) = 0x42 (6)
{ b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b8) = 0x45 (7)
{ b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7) = 0x4C (8)


Let's isolate b8 in (1) :

b8 = 0x71 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 (1')

Now, if in (2) we replace b8 by (1'), we got :

b1 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 XOR (0x71 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7) = 0x18

Which gives :

b1 XOR 0x71 XOR b2 = 0x18 (9)

We do the same with the equations from (3) to (7) :

{ b1 XOR 71h XOR b2 = 0x18 (9)
{ b1 XOR 71h XOR b3 = 0x59 (10)
{ b1 XOR 71h XOR b4 = 0x1B (11)
{ b1 XOR 71h XOR b5 = 0x79 (12)
{ b1 XOR 71h XOR b6 = 0x42 (13)
{ b1 XOR 71h XOR b7 = 0x45 (14)

{ b2 = b1 XOR 0x69 (9)
{ b3 = b1 XOR 0x28 (10)
{ b4 = b1 XOR 0x6A  (11)
{ b5 = b1 XOR 0x08 (12)
{ b6 = b1 XOR 0x33 (13)
{ b7 = b1 XOR 0x34 (14)

OK, now we to know b1 to solve the whole thing... let's use these 2 equations :

{ b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 = 0x4C (8)
{ b7 = b1 XOR 0x34 (14)

=> b1 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR (b1 XOR 0x34) = 0x4C

=> b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR 0x34 = 0x4C

=> b2 XOR b3 XOR b4 XOR b5 XOR b6 = 0x78

Hold on, we are close to the end; let's use (9) ... (13) :

=> (b1 XOR 0x69) XOR (b1 XOR 0x28) XOR (b1 XOR 0x6A) XOR (b1 XOR 0x08) XOR (b1 XOR 0x33) = 0x78

=> b1 XOR 0x69 XOR 0x28 XOR 0x6A XOR 0x08 XOR 0x33) = 0x78

=> b1 = 0x68


now we can finish :

{ b2 = b1 XOR 0x69 = 0x68 XOR 0x69 = 0x01 
{ b3 = b1 XOR 0x28 = 0x68 XOR 0x28 = 0x40
{ b4 = b1 XOR 0x6A = 0x68 XOR 0x6A = 0x02
{ b5 = b1 XOR 0x08 = 0x68 XOR 0x08 = 0x60
{ b6 = b1 XOR 0x33 = 0x68 XOR 0x33 = 0x5B
{ b7 = b1 XOR 0x34 = 0x68 XOR 0x34 = 0x5C

oups, I was about to forget b8 :

b8 = 0x71 XOR b2 XOR b3 XOR b4 XOR b5 XOR b6 XOR b7 = 0x55


at the end of the first loop, the 8 bytes must be equal to :

=> 0x68 0x01 0x40 0x02 0x60 0x5B 0x5C 0x55

we XOR each value with 0x32 to find the correct 8 bytes :

=> 0x5A 0x32 0x72 0x30 0x52 0x69 0x6E 0x67


These 8 bytes are the ASCII values of the letters of the serial :

serial = "Z3r0Ring"


+----------+
| 5. Outro |
+----------+

I would have been stupid to leave this cool crackme in a dusty corner don't you think ?

    ________     _______     _______
   /__   __/\   /  ____/\   /  ____/\
   \_/  /\_\/  /  /\___\/  /  /\___\/
    /  / /    /  /_/_     /  / / 
   /  / /    /____  /\   /  / /
  /  / /     \___/ / /  /  / /
 /  / /     ____/ / /  /  /_/_
/  / /     /_____/ /  /______/\
\__\/      \_____\/   \______\/ 13/06/2000 (original version : 10/06/1999)

www.tscube.cjb.net