
				  .@@  @@     .@@.   @@  @@   .@@
				 @@    @@    @@  @@  @@  @@  @@
				 @@    @@    @@  @@  @@  @@  @@
				 @@@@  @@    @@@@'   @@  @@  @@
				 @@    @@    @@      @@  @@  @@
				 @@    @@    @@      @@  @@  @@
				 **    **    **      **  **  **
				 	'   '     ''   

				    	f   l	e   u	r   
_________________________________________________________________________________________________
:::::::::::::::::::::::::::::::::::: i n f o r m a t i o n s ::::::::::::::::::::::::::::::::::::

	 rascal' s keygenme 1
	 difficulty level : 6
	 tools used	   : softice, ida
_________________________________________________________________________________________________
:::::::::::::::::::::::::::::::::::::::: t u t o r i a l ::::::::::::::::::::::::::::::::::::::::

	 the keygenme is packed, so dump it and load the dump in ida. enter a serial, then bpx
getdlgitemtexta, it breaks. it calls a proc at 00401320 that checks the serial. so go there in
ida.

	 it reinitializes some buffers, then it calls a proc at 004013e1 that performs a base64
decryption on the serial. then it decompresses the serial in different buffers, in bignum format.
the format of the serial is :

size		db		; first bignum
content		db	dup (size)
size		db		; second bignum
content		db	dup (size)
size		db		; third bignum
content		db	dup (size)
size		db		; fourth bignum
content		db	dup (size)
size		db		; fifth bignum
content		db	dup (size)
lastbyte	db

	it reads the size of each bignum, copy the bignum' s data in a buffer, and store the
lastbyte. you can use this serial to continue :

CNHBsaHSwrKiCNPDs6PUxLSkCNXFtaXWxramCNfHt6fYyLioCNnJuanQwLCgqg==

	it corresponds to :

08 a1b1c1d1 a2b2c2d2		; bignum 1
08 a3b3c3d3 a4b4c4d4		; bignum 2
08 a5b5c5d5 a6b6c6d6		; bignum 3
08 a7b7c7d7 a8b8c8d8		; bignum 4
08 a9b9c9d9 a0b0c0d0		; bignum 5
aa				; lastbyte

	then we see this :

UPX0:004014E3		      call    _MD5Init
UPX0:004014E8		      mov     eax, [ebp+bignum1]
UPX0:004014EE		      lea     ecx, [ebp+bignum1+4]
UPX0:004014F4		      push    eax
UPX0:004014F5		      lea     edx, [ebp+var_A0]
UPX0:004014FB		      push    ecx
UPX0:004014FC		      push    edx
UPX0:004014FD		      call    _MD5Update
UPX0:00401502		      lea     eax, [ebp+var_A0]
UPX0:00401508		      lea     ecx, [ebp+md5hash]
UPX0:0040150E		      push    eax
UPX0:0040150F		      push    ecx
UPX0:00401510		      call    _MD5Final

	so it performs an md5 hash on the first bignum. the md5 hash is in bigendian, so the hash
is not reversed at the end. then we see some bignum routines the looks like the miracl library.
some fast tracing gives us what they do :

UPX0:00401515                 push    ebx
UPX0:00401516                 push    400h
UPX0:0040151B                 call    _BigInit
UPX0:00401520                 push    ebx
UPX0:00401521                 mov     dword ptr [eax+22Ch], 10h
UPX0:0040152B                 call    _BigCreate
UPX0:00401530                 mov     esi, eax
UPX0:00401532                 push    ebx
UPX0:00401533                 mov     [ebp+var_48], esi
UPX0:00401536                 call    _BigCreate
UPX0:0040153B                 mov     edi, eax
UPX0:0040153D                 push    ebx
UPX0:0040153E                 mov     [ebp+bighash], edi
UPX0:00401541                 call    _BigCreate
UPX0:00401546                 push    ebx
UPX0:00401547                 mov     [ebp+big3], eax
UPX0:0040154A                 call    _BigCreate
UPX0:0040154F                 push    ebx
UPX0:00401550                 mov     [ebp+big4], eax
UPX0:00401553                 call    _BigCreate
UPX0:00401558                 mov     ebx, eax
UPX0:0040155A                 push    0
UPX0:0040155C                 mov     [ebp+var_40], ebx
UPX0:0040155F                 call    _BigCreate
UPX0:00401564                 add     esp, 40h
UPX0:00401567                 mov     [ebp+big5], eax
UPX0:0040156A                 push    0
UPX0:0040156C                 call    _BigCreate
UPX0:00401571                 push    0
UPX0:00401573                 mov     [ebp+bigD1], eax
UPX0:00401576                 call    _BigCreate
UPX0:0040157B                 push    0
UPX0:0040157D                 mov     [ebp+bigD2], eax
UPX0:00401580                 call    _BigCreate
UPX0:00401585                 push    0
UPX0:00401587                 mov     [ebp+var_C], eax
UPX0:0040158A                 call    _BigCreate
UPX0:0040158F                 push    0
UPX0:00401591                 mov     [ebp+var_18], eax
UPX0:00401594                 call    _BigCreate
UPX0:00401599                 push    0
UPX0:0040159B                 mov     [ebp+var_20], eax
UPX0:0040159E                 call    _BigCreate
UPX0:004015A3                 mov     [ebp+big2], eax

	then we have this :

UPX0:004015A6                 mov     eax, [ebp+bignum2]
UPX0:004015AC                 lea     edx, [ebp+bignum2+4]
UPX0:004015B2                 push    esi
UPX0:004015B3                 push    edx
UPX0:004015B4                 push    eax
UPX0:004015B5                 call    _BigFill        ; fill with bignum2
UPX0:004015BA                 lea     ecx, [ebp+md5hash]
UPX0:004015C0                 push    edi
UPX0:004015C1                 push    ecx
UPX0:004015C2                 push    10h
UPX0:004015C4                 call    _BigFill        ; fill with md5 hash
UPX0:004015C9                 mov     edx, [ebp+big3]
UPX0:004015CC                 mov     ecx, [ebp+bignum3]
UPX0:004015D2                 lea     eax, [ebp+bignum3+4]
UPX0:004015D8                 push    edx
UPX0:004015D9                 push    eax
UPX0:004015DA                 push    ecx
UPX0:004015DB                 call    _BigFill        ; fill with bignum3
UPX0:004015E0                 mov     edx, [ebp+big4]
UPX0:004015E3                 mov     ecx, [ebp+bignum4]
UPX0:004015E9                 lea     eax, [ebp+bignum4+4]
UPX0:004015EF                 push    edx
UPX0:004015F0                 push    eax
UPX0:004015F1                 push    ecx
UPX0:004015F2                 call    _BigFill        ; fill with bignum4
UPX0:004015F7                 mov     edx, [ebp+big5]
UPX0:004015FA                 mov     ecx, [ebp+bignum5]
UPX0:00401600                 add     esp, 48h
UPX0:00401603                 lea     eax, [ebp+bignum5+4]
UPX0:00401609                 push    edx
UPX0:0040160A                 push    eax
UPX0:0040160B                 push    ecx
UPX0:0040160C                 call    _BigFill        ; fill with bignum5
UPX0:00401611                 mov     edx, [ebp+bigD1]
UPX0:00401614                 push    edx
UPX0:00401615                 push    offset bigDAT1
UPX0:0040161A                 push    8
UPX0:0040161C                 call    _BigFill
UPX0:00401621                 mov     eax, [ebp+bigD2]
UPX0:00401624                 push    eax
UPX0:00401625                 push    offset bigDAT2
UPX0:0040162A                 push    8
UPX0:0040162C                 call    _BigFill

	so it fills the bignums with the bignums extracted from the serial. bigDAT1 and bigDAT2
are 2 hardcoded 8-bytes bignumbers. then we see this :

UPX0:00401631                 push    ebx
UPX0:00401632                 push    esi
UPX0:00401633                 mov     [ebp+emptyFlag], 1
UPX0:00401637                 call    _BigCompare     ; is bignum2 0 ?
UPX0:0040163C                 add     esp, 2Ch
UPX0:0040163F                 test    eax, eax
UPX0:00401641                 jz      short loc_0_401654
UPX0:00401643                 mov     ecx, [ebp+big4]
UPX0:00401646                 push    ebx
UPX0:00401647                 push    ecx
UPX0:00401648                 call    _BigCompare     ; is bignum4 0 ?
UPX0:0040164D                 add     esp, 8
UPX0:00401650                 test    eax, eax
UPX0:00401652                 jnz     short loc_0_401658
UPX0:00401654 
UPX0:00401654 loc_0_401654:                           ; CODE XREF: _CheckSerial+321j
UPX0:00401654                 mov     [ebp+emptyFlag], 0 ; error, bignum is 0
UPX0:00401658 loc_0_401658:                           ; CODE XREF: _CheckSerial+332j
UPX0:00401658                 push    ebx
UPX0:00401659                 push    1
UPX0:0040165B                 push    ebx
UPX0:0040165C                 call    _BigAddInt      ; add 1 to the bignum
UPX0:00401661                 mov     edx, [ebp+big4]
UPX0:00401664                 push    ebx
UPX0:00401665                 push    edx
UPX0:00401666                 call    _BigCompare     ; is bignum4 1 ?
UPX0:0040166B                 add     esp, 14h
UPX0:0040166E                 test    eax, eax
UPX0:00401670                 jz      __retFail       ; bignum4 must not be 1
UPX0:00401676                 mov     al, [ebp+emptyFlag]
UPX0:00401679                 test    al, al
UPX0:0040167B                 jz      __retFail

	so bignum2 can' t be 0, bignum4 can' t be neither 0 nor 1. then we see this :

UPX0:00401681                 push    ebx
UPX0:00401682                 push    edi
UPX0:00401683                 call    _BigMul         ; ebx: bighash
UPX0:00401688                 push    edi
UPX0:00401689                 push    2
UPX0:0040168B                 push    edi
UPX0:0040168C                 call    _BigMulInt      ; bighash: bighash * 2
UPX0:00401691                 push    edi
UPX0:00401692                 push    edi             ; same as the result buffer, no modulus
UPX0:00401693                 push    2
UPX0:00401695                 push    edi
UPX0:00401696                 call    _BigPowMod      ; bighash: bighash^2
UPX0:0040169B                 push    esi
UPX0:0040169C                 push    esi             ; same as the result buffer, no modulus
UPX0:0040169D                 push    2
UPX0:0040169F                 push    esi
UPX0:004016A0                 call    _BigPowMod      ; big2: big2^2
UPX0:004016A5                 mov     eax, [ebp+big3]
UPX0:004016A8                 push    eax
UPX0:004016A9                 push    eax             ; same as the result buffer, no modulus
UPX0:004016AA                 push    2
UPX0:004016AC                 push    eax
UPX0:004016AD                 call    _BigPowMod      ; big3:big3^2
UPX0:004016B2                 add     esp, 44h
UPX0:004016B5                 push    esi
UPX0:004016B6                 push    edi
UPX0:004016B7                 push    esi
UPX0:004016B8                 call    _BigAdd         ; big2: big2+bighash
UPX0:004016BD                 mov     eax, [ebp+big3]
UPX0:004016C0                 push    eax
UPX0:004016C1                 push    esi
UPX0:004016C2                 call    _BigCompare     ; bignum3^2 must be bignum2^2 + (md5hash*2)^2
UPX0:004016C7                 add     esp, 14h
UPX0:004016CA                 test    eax, eax
UPX0:004016CC                 push    esi
UPX0:004016CD                 jnz     loc_0_401838

	the powmod don' t apply the modulo, because the result bignum is the same as the modulo
bignum. so we must have bignum3^2 must be bignum2^2 + (md5hash*2)^2. then we see this :

UPX0:004016D3                 mov     edx, [ebp+bignum1]
UPX0:004016D9                 lea     ecx, [ebp+bignum1+4]
UPX0:004016DF                 push    ecx
UPX0:004016E0                 push    edx
UPX0:004016E1                 call    _BigFill
UPX0:004016E6                 mov     eax, [ebp+big4]
UPX0:004016E9                 push    esi
UPX0:004016EA                 push    eax
UPX0:004016EB                 push    3
UPX0:004016ED                 push    esi
UPX0:004016EE                 call    _BigPowMod      ; bignum1^3 mod bignum4
UPX0:004016F3                 mov     ecx, [ebp+big4]
UPX0:004016F6                 push    ebx
UPX0:004016F7                 push    ecx
UPX0:004016F8                 push    3
UPX0:004016FA                 push    ebx
UPX0:004016FB                 call    _BigPowMod      ; ebx^3 mod bignum4 (ebx is bighash)
UPX0:00401700                 push    ebx
UPX0:00401701                 push    esi
UPX0:00401702                 call    _BigCompare     ; must be the same
UPX0:00401707                 add     esp, 34h
UPX0:0040170A                 test    eax, eax
UPX0:0040170C                 jz      short loc_0_401771

	so we must have bignum1^3 = md5hash^3 mod (bignum4). then we see this :

UPX0:00401771 loc_0_401771:                           ; CODE XREF: _CheckSerial+3ECj
UPX0:00401771                 mov     edx, [ebp+var_C]
UPX0:00401774                 mov     eax, [ebp+big5]
UPX0:00401777                 push    edx
UPX0:00401778                 push    eax
UPX0:00401779                 call    _BigMul         ; var_c=big5
UPX0:0040177E                 mov     ecx, [ebp+var_18]
UPX0:00401781                 mov     edx, [ebp+big5]
UPX0:00401784                 push    ecx
UPX0:00401785                 push    edx
UPX0:00401786                 call    _BigMul         ; var_18=big5
UPX0:0040178B                 mov     eax, [ebp+bigD1]
UPX0:0040178E                 push    eax
UPX0:0040178F                 push    eax
UPX0:00401790                 mov     eax, [ebp+var_C]
UPX0:00401793                 push    eax
UPX0:00401794                 call    _BigDiv         ; var_c mod bigdat1
UPX0:00401799                 mov     eax, [ebp+bigD2]
UPX0:0040179C                 mov     ecx, [ebp+var_18]
UPX0:0040179F                 push    eax
UPX0:004017A0                 push    eax
UPX0:004017A1                 push    ecx
UPX0:004017A2                 call    _BigDiv         ; var_18 mod bigdat2
UPX0:004017A7                 mov     edx, [ebp+var_20]
UPX0:004017AA                 lea     eax, [ebp+md5hash]
UPX0:004017B0                 push    edx
UPX0:004017B1                 push    eax
UPX0:004017B2                 push    4
UPX0:004017B4                 call    _BigFill        ; fill with first dword of md5hash
UPX0:004017B9                 mov     ecx, [ebp+big2]
UPX0:004017BC                 lea     edx, [ebp+md5hash+8]
UPX0:004017C2                 push    ecx
UPX0:004017C3                 push    edx
UPX0:004017C4                 push    4
UPX0:004017C6                 call    _BigFill        ; fill with third dword of md5hash
UPX0:004017CB                 mov     eax, [ebp+var_C]
UPX0:004017CE                 mov     ecx, [ebp+var_20]
UPX0:004017D1                 add     esp, 40h
UPX0:004017D4                 push    eax
UPX0:004017D5                 push    ecx
UPX0:004017D6                 call    _BigCompare     ; first dword of md5hash must be bignum5 mod bigdat1
UPX0:004017DB                 add     esp, 8
UPX0:004017DE                 test    eax, eax
UPX0:004017E0                 jnz     __retFail
UPX0:004017E6                 mov     edx, [ebp+var_18]
UPX0:004017E9                 mov     eax, [ebp+big2]
UPX0:004017EC                 push    edx
UPX0:004017ED                 push    eax
UPX0:004017EE                 call    _BigCompare     ; third dword of md5hash must be bignum5 mod bigdat2
UPX0:004017F3                 add     esp, 8
UPX0:004017F6                 test    eax, eax
UPX0:004017F8                 jnz     __retFail

	so we must have md5hash.A = bignum5 mod (bigdat1) and md5hash.C = bignum5 mod (bigdat2).
then we see this :

UPX0:004017FE                 mov     eax, [ebp+lastbyte]
UPX0:00401804                 mov     ecx, 0Ah
UPX0:00401809                 cdq
UPX0:0040180A                 idiv    ecx
UPX0:0040180C                 mov     eax, 99999999h
UPX0:00401811                 lea     edx, [edx+edx*4]
UPX0:00401814                 shl     edx, 1
UPX0:00401816                 mov     ecx, edx
UPX0:00401818                 imul    [ebp+lastbyte]
UPX0:0040181E                 sar     edx, 2
UPX0:00401821                 mov     eax, edx
UPX0:00401823                 shr     eax, 1Fh
UPX0:00401826                 add     edx, eax
UPX0:00401828                 sub     edx, ecx
UPX0:0040182A                 mov     ecx, [ebp+lastbyte]
UPX0:00401830                 add     edx, ecx
UPX0:00401832                 cmp     edx, 48h
UPX0:00401835                 jz      short loc_0_40189A

	so it performs a check on lastbyte. then we see this :

UPX0:0040189A loc_0_40189A:                           ; CODE XREF: _CheckSerial+515j
UPX0:0040189A                 push    8
UPX0:0040189C                 lea     ecx, [ebp+dtKeyTable]
UPX0:004018A2                 push    offset dtKey
UPX0:004018A7                 push    ecx
UPX0:004018A8                 call    _Blowfish_Init
UPX0:004018AD                 mov     eax, [ebp+bignum1]
UPX0:004018B3                 lea     edx, [ebp+bignum1+4]
UPX0:004018B9                 mov     [ebp+var_34], edx
UPX0:004018BC                 add     esp, 0Ch
UPX0:004018BF                 cdq
UPX0:004018C0                 and     edx, 7
UPX0:004018C3                 mov     [ebp+var_3C], 0
UPX0:004018CA                 add     eax, edx
UPX0:004018CC                 sar     eax, 3
UPX0:004018CF                 test    eax, eax
UPX0:004018D1                 jle     short __retRegged
UPX0:004018D3 
UPX0:004018D3 __DecryptName:                          ; CODE XREF: _CheckSerial+614j
UPX0:004018D3                 pusha
UPX0:004018D4                 mov     esi, [ebp+var_34]
UPX0:004018D7                 mov     eax, [esi]
UPX0:004018D9                 mov     [ebp+dtBlockA], eax
UPX0:004018DC                 mov     eax, [esi+4]
UPX0:004018DF                 mov     [ebp+dtBlockB], eax
UPX0:004018E2                 popa
UPX0:004018E3                 lea     eax, [ebp+dtBlockB]
UPX0:004018E6                 lea     ecx, [ebp+dtBlockA]
UPX0:004018E9                 push    eax
UPX0:004018EA                 lea     edx, [ebp+dtKeyTable]
UPX0:004018F0                 push    ecx
UPX0:004018F1                 push    edx
UPX0:004018F2                 call    _Blowfish_Decrypt
UPX0:004018F7                 add     esp, 0Ch
UPX0:004018FA                 pusha
UPX0:004018FB                 mov     edi, [ebp+var_38]
UPX0:004018FE                 mov     eax, [ebp+dtBlockA]
UPX0:00401901                 stosd
UPX0:00401902                 mov     eax, [ebp+dtBlockB]
UPX0:00401905                 stosd
UPX0:00401906                 popa
UPX0:00401907                 mov     edx, [ebp+var_34]
UPX0:0040190A                 mov     ecx, [ebp+var_38]
UPX0:0040190D                 mov     eax, 8
UPX0:00401912                 add     edx, eax
UPX0:00401914                 add     ecx, eax
UPX0:00401916                 mov     eax, [ebp+bignum1]
UPX0:0040191C                 mov     [ebp+var_34], edx
UPX0:0040191F                 cdq
UPX0:00401920                 and     edx, 7
UPX0:00401923                 mov     [ebp+var_38], ecx
UPX0:00401926                 mov     ecx, [ebp+var_3C]
UPX0:00401929                 add     eax, edx
UPX0:0040192B                 inc     ecx
UPX0:0040192C                 sar     eax, 3
UPX0:0040192F                 cmp     ecx, eax
UPX0:00401931                 mov     [ebp+var_3C], ecx
UPX0:00401934                 jl      short __DecryptName
UPX0:00401936                 mov     esi, [ebp+var_48]
UPX0:00401939                 mov     ebx, [ebp+var_40]
UPX0:0040193C                 mov     edi, [ebp+bighash]

	so it performs a blowfish decryption of bignum1. so we have to solve some problems :

:::::::::::::::::::::::::::::::::::: r e s t r i c t i o n s ::::::::::::::::::::::::::::::::::::
	bignum2 != 0
	bignum4 != 0
	bignum4 != 1

::::::::::::::::::::::::::::::::::::::: p r o b l e m   1 :::::::::::::::::::::::::::::::::::::::
	bignum3^2 = bignum2^2 + (bigmd5hash(bignum1)*2)^2

	bignum2 can' t be 0, it would be too easy. we have :

	b3^2 - b2^2 = (md5*2)^2
	(b3-b2)(b3+b2) = (md5*2)^2
	(b3-b2)(b3+b2) = 2*2*md5^2

	b3-b2 = 2 and
	b3+b2 = 2*md5^2

	b3 = 2+b2 and
	2*b2 = 2*md5^2 - 2

	b2 = md5^2 - 1
	b3 = md5^2 + 1

::::::::::::::::::::::::::::::::::::::: p r o b l e m   2 :::::::::::::::::::::::::::::::::::::::
	bignum1^3 = bigmd5hash(bignum1)^3 mod (bignum4)

	bignum4 can' t be neither 1 nor 0, it would be too easy. we have :

	b1^3 - md5^3 = 0 mod (b4)
	so b4 must divide b1^3 - md5^3
	 if b1 = md5 (not very likely to happen), any number different than 0 and 1 can work.
	 else, we choose b4 = |b1^3 - md5^3|

::::::::::::::::::::::::::::::::::::::: p r o b l e m   3 :::::::::::::::::::::::::::::::::::::::
	bignum5 = md5hash(bignum1).A mod (bigdat1)
	bignum5 = md5hash(bignum1).C mod (bigdat2) 

	we apply the chinese remainder theorem to md5 with the moduli bigdat1 and bigdat2, and
the remainders md5hash.A and md5hash.C, since bigdat1 is prime.

::::::::::::::::::::::::::::::::::::::: p r o b l e m   4 :::::::::::::::::::::::::::::::::::::::
	a fast brute force on lastbyte gives us the possible values :
	050h, 05bh, 066h, 071h, 07ch, 087h, 092h, 09dh, 0a8h, 0b3h

	so this is what we have to do :

	 get the user name
	 encrypt it with blowfish (the blowfish key is 021320D0Ah 07F705211h)
	 perform a bigendian md5 hash on it
	 b1 = blowfish encryption of name
	 b2 = md5^2 - 1
	 b3 = md5^2 + 1
	 if b1 = md5, b4 = 2 (or any value different than 0 and 1), else b4 = |b1^3 - md5^3|
	 apply the chinese remainder theorem on moduli bigdat1 and bigdat1, remainders md5hash.A
	and md5hash.C to get b5
	 write the bignumbers in the serial format
	 add the lastbyte
	 encode the serial in base64
_________________________________________________________________________________________________
::::::::::::::::::::::::::::::::::::: f i n a l   w o r d s :::::::::::::::::::::::::::::::::::::

	 this was a nice keygenme, very interesting.
_________________________________________________________________________________________________
										       roy|fleur

