                                                                      
                                                                  
                                          ܲ    ܲ
          ܲ      ۲ ܰ    
ܲ       ۲   ۲ 
  ۲   ۲         ۲ݰ    ۲ 
۲          ߲޲   ۲        
    ۲                   ܲ       
ް  ܱ          ݰ          
ݱ     ۱ ܲ  ޱ   ޲ݲ   ޱ     ߱
 ޲   ۲߲  ޲ ۲  ۲     ܲ޲   
  ۲۲ ۲۲۲޲  ߲   ۲ܲ۲ 
           ߲      ߲
   ߰  ߲             ߱     ߰    ߲  
  ߱                                                        <Eboy>  
                                                                      
                   E     C      L      i      P      S      E            

                             
                                 p r e s e n t s
                             


                      ͻ
                        How to keygen K4N Keygenme #3  
                      ͼ

level : medium



ͻ
 1) Forewords 
ͼ

Oh yeah, I know what you're thinking : "a member of ECLiPSE who keygens a
crackme made by another member... I'm sure he had the sources !".

Well, in fact I had not enough money for Seifer so he refused to give me the
sources... thx for the help man ! ;-)



ͻ
 2) The algo 
ͼ

Before starting the explanations, here is the algo used by the crackme :

a) A few checks on the name and the serial
b) Get name and compute magic_value (using a buffer at @405030)
c) Use this magic_value to fill an array (using another buffer)
d) Use the serial to modify this array
e) Compare array with string "KEYGENNING4NEWBIES"



ͻ
 3) What the fuck is this crackme doing ??? 
ͼ



	3.1. Let's start with a few checks on the name and the serial
	

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Reference To: USER32.GetDlgItemTextA, Ord:0104h
                                  |
:004011C3 8B3DA8404000            mov edi, dword ptr [004040A8]
:004011C9 68E8030000              push 000003E8
:004011CE 51                      push ecx
:004011CF FFD7                    call edi
:004011D1 8BF0                    mov esi, eax -> ESI = len(name)
:004011D3 85F6                    test esi, esi
:004011D5 0F844B010000            je 00401326 -> fuck off because there is no name
:004011DB 83FE40                  cmp esi, 00000040
:004011DE 0F8742010000            ja 00401326 -> fuck off because name too long
:004011E4 8B4508                  mov eax, dword ptr [ebp+08]
:004011E7 8D5594                  lea edx, dword ptr [ebp-6C]
:004011EA 6A13                    push 00000013
:004011EC 52                      push edx
:004011ED 68E9030000              push 000003E9
:004011F2 50                      push eax
:004011F3 FFD7                    call edi -> after this call, EAX = len(serial)

:004011F5 6BC003                  imul eax, 00000003 -> EAX = EAX * 3
:004011F8 C1E002                  shl eax, 02 -> EAX = EAX * 4
:004011FB 05CD000000              add eax, 000000CD -> EAX = EAX + 0xCD
:00401200 8945FC                  mov dword ptr [ebp-04], eax
:00401203 817DFCA5010000          cmp dword ptr [ebp-04], 000001A5 -> EAX = 0x1A5 ?
:0040120A 0F85BC000000            jne 004012CC -> fuck off because serial is not 18 letters long 

How do I know this ?
Because I solved this equation :

len(serial) * 3 * 4 + 0xCD = 0x1A5

=> len(serial) = (0x1A5 - 0xCD) / 12 = 18

:00401210 33C0                    xor eax, eax
:00401212 8A4594                  mov al, byte ptr [ebp-6C]
:00401215 84C0                    test al, al
:00401217 7413                    je 0040122C
:00401219 8D4D94                  lea ecx, dword ptr [ebp-6C]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040122A(C)

This loop checks if each ASCII value of the letter of the serial is < 0x30

:0040121C 3C30                    cmp al, 30
:0040121E 0F82C6000000            jb 004012EA -> fuck off if serial[i] < '0'
:00401224 8A4101                  mov al, byte ptr [ecx+01]
:00401227 41                      inc ecx
:00401228 84C0                    test al, al
:0040122A 75F0                    jne 0040121C -> loop_back
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

this was easy : the serial has 18 letters

name : wizard
serial : 123456789123456789 (for example)



	3.2. Time for the magic_value
	

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
:00401231 8D852CFFFFFF            lea eax, dword ptr [ebp+FFFFFF2C] -> name
:00401237 50                      push eax
:00401238 E843FEFFFF              call 00401080
:0040123D 8945FC                  mov dword ptr [ebp-04], eax -> magic_value
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

I'm afraid we'll have to examine this 'call 401080' in details :


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Referenced by a CALL at Address:
|:00401238   
|
:00401080 55                      push ebp
:00401081 8BEC                    mov ebp, esp
:00401083 51                      push ecx
:00401084 53                      push ebx
:00401085 56                      push esi
:00401086 57                      push edi

* Possible StringData Ref from Data Obj ->"eheh"
                                  |
:00401087 6880504000              push 00405080
:0040108C 6A00                    push 00000000
:0040108E E8ADFFFFFF              call 00401040 -> interesting

This call init. the magic_value

:00401093 83C408                  add esp, 00000008
:00401096 8BD8                    mov ebx, eax -> this will be used in the next loop

:00401098 E863FFFFFF              call 00401000

* Possible StringData Ref from Data Obj ->" is a whore." -> very funny ;-)
                                  |
:0040109D BF70504000              mov edi, 00405070

[SNIP] -> This series of 'repnz' is just a STRCAT()

:004010C8 33FF                    xor edi, edi
:004010CA 33F6                    xor esi, esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004010F6(C)


start_loop
-----------

:004010CC 8B4508                  mov eax, dword ptr [ebp+08] -> "<name> is a whore."
:004010CF 50                      push eax
:004010D0 56                      push esi
:004010D1 E86AFFFFFF              call 00401040 -> again !

:004010D6 8B8E30504000            mov ecx, dword ptr [esi+00405030] -> buffer

This buffer contains 64 bytes :

0x12,0x00,0x00,0x00,0x5C,0x00,0x00,0x00,0x34,0x00,0x00,0x00,
0x22,0x00,0x00,0x00,0xAB,0x00,0x00,0x00,0x9D,0x00,0x00,0x00,
0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,
0x84,0x00,0x00,0x00,0xAE,0x00,0x00,0x00,0x66,0x00,0x00,0x00,
0x31,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x73,0x00,0x00,0x00,
0xCF,0x00,0x00,0x00

:004010DC 83C408                  add esp, 00000008
:004010DF 33CF                    xor ecx, edi
:004010E1 03C1                    add eax, ecx
:004010E3 8945FC                  mov dword ptr [ebp-04], eax
:004010E6 C145FC07                rol dword ptr [ebp-04], 07
:004010EA 8B45FC                  mov eax, dword ptr [ebp-04]
:004010ED 83C604                  add esi, 00000004
:004010F0 33D8                    xor ebx, eax -> EBX is our magic_value
:004010F2 47                      inc edi
:004010F3 83FE40                  cmp esi, 00000040
:004010F6 7CD4                    jl 004010CC -> loop_back

end_loop
---------


:004010F8 5F                      pop edi
:004010F9 8BC3                    mov eax, ebx -> final magic_value is here
:004010FB 5E                      pop esi
:004010FC 5B                      pop ebx
:004010FD 8BE5                    mov esp, ebp
:004010FF 5D                      pop ebp
:00401100 C3                      ret
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


Well, the string " is a whore." is concatenated to our name, and then we have
a little loop which uses a buffer (@4010D6) to create the magic_value.

I forgot to mention this little 'CALL 401040' which is also used by this loop :


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Referenced by a CALL at Addresses:
|:0040108E   , :004010D1   
|
:00401040 8B4C2404                mov ecx, dword ptr [esp+04]
:00401044 56                      push esi
:00401045 8B74240C                mov esi, dword ptr [esp+0C]
:00401049 33C0                    xor eax, eax
:0040104B 33D2                    xor edx, edx
:0040104D 8A443103                mov al, byte ptr [ecx+esi+03]
:00401051 8A543102                mov dl, byte ptr [ecx+esi+02]
:00401055 C1E008                  shl eax, 08
:00401058 03C2                    add eax, edx
:0040105A 33D2                    xor edx, edx
:0040105C 8A543101                mov dl, byte ptr [ecx+esi+01]
:00401060 C1E008                  shl eax, 08
:00401063 03C2                    add eax, edx
:00401065 33D2                    xor edx, edx
:00401067 8A1431                  mov dl, byte ptr [ecx+esi]
:0040106A 5E                      pop esi
:0040106B C1E008                  shl eax, 08
:0040106E 03C2                    add eax, edx
:00401070 C3                      ret
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



	3.3. We create an array with this magic_value
	

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
:00401257 8B45FC                  mov eax, dword ptr [ebp-04] -> magic_value (see 3)
:0040125A 33D2                    xor edx, edx
:0040125C BE1A000000              mov esi, 0000001A
:00401261 F7F6                    div esi
:00401263 8A941510FFFFFF          mov dl, byte ptr [ebp+edx-000000F0] -> another buffer

This buffer contains this string : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

:0040126A 88540DC8                mov byte ptr [ebp+ecx-38], dl -> store value in array
:0040126E 8B45FC                  mov eax, dword ptr [ebp-04]
:00401271 C1E003                  shl eax, 03
:00401274 BA45230100              mov edx, 00012345
:00401279 F7E8                    imul eax
:0040127B 03C2                    add eax, edx
:0040127D 8945FC                  mov dword ptr [ebp-04], eax
:00401280 41                      inc ecx
:00401281 83F912                  cmp ecx, 00000012
:00401284 72D1                    jb 00401257 -> loop_back
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



	3.4. Now the serial is used to modify this array
	

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
:0040128D 8A4C0594                mov cl, byte ptr [ebp+eax-6C] -> serial
:00401291 8A5405C8                mov dl, byte ptr [ebp+eax-38] -> see 5)
:00401295 80E930                  sub cl, 30
:00401298 32D1                    xor dl, cl
:0040129A 885405C8                mov byte ptr [ebp+eax-38], dl -> store new value in SAME array
:0040129E 40                      inc eax
:0040129F 83F812                  cmp eax, 00000012
:004012A2 72E9                    jb 0040128D
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



	3.5. Last step : we compare the last array with "KEYGENNING4NEWBIES"
	

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
:004012A9 8D55C8                  lea edx, dword ptr [ebp-38] -> see 6)
:004012AC 52                      push edx
:004012AD E85EFEFFFF              call 00401110

OK, I lied : the array is modified a little bit before the final check ;-)

:004012B2 E849FDFFFF              call 00401000 -> not important

:004012B7 8D45C8                  lea eax, dword ptr [ebp-38]

* Possible StringData Ref from Data Obj ->"KEYGENNING4NEWBIES"
                                  |
:004012BA 6814514000              push 00405114
:004012BF 50                      push eax
:004012C0 E86BFEFFFF              call 00401130 -> strcmp
:004012C5 83C40C                  add esp, 0000000C
:004012C8 85C0                    test eax, eax
:004012CA 753C                    jne 00401308 -> REGISTERED
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



ͻ
 4) The keygen 
ͼ

I can see that some of you are lost.

The question is : "what can be ripped and what must be reversed ?"
Answer : the only thing to reverse is part 3.4 & 3.5 !!!

Basically, 3.2 & 3.3 use the name to create an array and we must use this
array to build the serial.


Here is a 'C' translation of part 3.4 and 3.5 :
-----------------------------------------------

for (i=0;i<0x12;i++)
	{
		array[i] = (serial[i] - 0x30) ^ array[i];
	}

f401110(array);
if (strcmp(array,"KEYGENNING4NEWBIES")==0) => REGISTERED !!!


Let's reverse it :
------------------

char magic[19] = "KEYGENNING4NEWBIES";
f401110(magic);
for (i=0;i<0x12;i++)
	{
		serial[i] = (magic[i] ^ ebpm38[i]) + 0x30;	
	}



ͻ
 5) From ASM to C 
ͼ

Seifer wanted a high-level keygen... so I made one in 'C' (surprising huh ?).
In the file 'from.asm.to.c.txt' you'll see how I did the translation.

I started by making a 'ripped_asm' keygen which is also included, but since
it's a DOS keygen, you won't be able to cut & paste the serials.



ͻ
 6) Final words 
ͼ

Well, that's all. I've tried to be as complete as possible, but I can't
explain EVERYTHING. You got the asm_sources, you got the 'C' sources,
the rest is up to you !



Aimons nous vivans, n'attendons pas que la mort nous trouve du talent.

Wizard [ECLiPSE] 28/01/2001