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

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

	 difficulty level : 4
	 tools used : softice, ida
________________________________________________________________________________________________
::::::::::::::::::::::::::::::::::::::: t u t o r i a l ::::::::::::::::::::::::::::::::::::::::

	 launch the program, it' s a console-mode program. disassemble it in ida. we find
easily the serial checking function at 004011a0. you see this :

_text:0040126E                 call    _MD5Init
_text:00401273                 push    ebx
_text:00401274                 push    64h
_text:00401276                 call    _mirsys
_text:0040127B                 mov     [ebp+var_B4], eax
_text:00401281                 push    offset aKeygenme4ByThigo ; "Keygenme 4 by Thigo\n"
_text:00401286                 call    _printf
_text:0040128B                 push    offset aEnterYourName ; "Enter your name :\n"
_text:00401290                 call    _printf
_text:00401295                 push    offset bfUserName
_text:0040129A                 call    _gets
_text:0040129F                 push    offset aEnterYourSerial ; "Enter your serial :\n"
_text:004012A4                 call    _printf
_text:004012A9                 push    offset bfUserSerial
_text:004012AE                 call    _gets
_text:004012B3                 push    offset bfUserName
_text:004012B8                 call    _GetLength
_text:004012BD                 add     esp, 20h
_text:004012C0                 mov     [ebp+dtUserNameLength], eax
_text:004012C6                 cmp     eax, 5
_text:004012C9                 jnb     short __ModifyLength

	the first procedure initializes md5 chaining variables, then it gets the name and
serial, and look if the name is at least 5 characters long. then we see this :

_text:004012D5 __ModifyLength:                         ; CODE XREF: _CheckSerial+129j
_text:004012D5                 mov     [ebp+var_4], ebx
_text:004012D8                 lea     esi, ds:40E4F4h
_text:004012DE                 xor     eax, eax
_text:004012E0                 mov     eax, [ebp+dtUserNameLength]
_text:004012E6                 mov     ecx, eax
_text:004012E8                 imul    ecx
_text:004012EA                 imul    ecx
_text:004012EC                 mov     ecx, eax
_text:004012EE                 imul    ecx
_text:004012F0                 mov     [ebp+dtUserNameLength], eax
_text:004012F6                 push    offset bfSerial1
_text:004012FB                 mov     eax, [ebp+dtUserNameLength]
_text:00401301                 push    eax
_text:00401302                 call    _LengthToString ; convert length to a string
_text:00401307                 add     esp, 8
_text:0040130A                 xor     esi, esi

	so it modifies the length of the name, basically it does length=length^6, then it
generates a string from this modified length and store it in bfSerial1. then we see this :

_text:0040130C __LoopScrambleString:                   ; CODE XREF: _CheckSerial+1B6j
_text:0040130C                 mov     [ebp+var_C8], esi
_text:00401312                 push    offset bfSerial1
_text:00401317                 call    _GetLength
_text:0040131C                 add     esp, 4
_text:0040131F                 cmp     esi, eax
_text:00401321                 jnb     short __EndLoopScrambleString
_text:00401323                 lea     ecx, [esi+1]
_text:00401326                 xor     eax, eax
_text:00401328                 mov     al, bfSerial1[esi]
_text:0040132E                 not     eax
_text:00401330                 xor     eax, 30h
_text:00401333                 imul    eax, ecx
_text:00401336                 imul    eax, 0D05177Ch
_text:0040133C                 add     eax, 3245157h
_text:00401341                 xor     edx, edx
_text:00401343                 mov     edi, 1Ah
_text:00401348                 div     edi
_text:0040134A                 add     edx, 41h
_text:0040134D                 mov     [ebp+esi+bfSerial2], dl
_text:00401354                 mov     esi, ecx
_text:00401356                 jmp     short __LoopScrambleString

	it scrambles the string and store the new one in bfSerial2. then we see this :

_text:00401358 __EndLoopScrambleString:                ; CODE XREF: _CheckSerial+181j
_text:00401358                 lea     ecx, [ebp+bfSerial2]
_text:0040135E                 push    ecx
_text:0040135F                 call    _GetLength
_text:00401364                 add     esp, 4
_text:00401367                 mov     ecx, eax
_text:00401369                 mov     [ebp+var_BC], ecx
_text:0040136F                 lea     esi, [ebp+bfSerial2]
_text:00401375                 lea     edi, ds:40E5F4h ; user serial
_text:0040137B                 mov     al, [esi]
_text:0040137D                 mov     bl, [edi]
_text:0040137F 
_text:0040137F __LoopXorSerial:                        ; CODE XREF: _CheckSerial+1EBj
_text:0040137F                 xor     al, bl
_text:00401381                 mov     [esi], al
_text:00401383                 inc     esi
_text:00401384                 inc     edi
_text:00401385                 mov     al, [esi]
_text:00401387                 mov     bl, [edi]
_text:00401389                 test    al, al
_text:0040138B                 jnz     short __LoopXorSerial

	so it xors the serial you entered with the string computed. then we see this :

_text:0040138D                 mov     eax, [ebp+dtUserNameLength]
_text:00401393                 xor     edx, edx
_text:00401395                 div     dword ptr [ebp+bfSerial2]
_text:0040139B                 mov     [ebp+dtUserNameLength], eax

	so it divide the modified length by the first dword of the xored serial. then we see
this :

_text:004013A1                 lea     esi, bfUserSerial[ecx]
_text:004013A7                 push    3
_text:004013A9                 push    esi
_text:004013AA                 lea     ecx, [ebp+var_134]
_text:004013B0                 call    _MD5Update
_text:004013B5                 push    offset bfSerial1
_text:004013BA                 lea     ecx, [ebp+var_134]
_text:004013C0                 call    _MD5Final
_text:004013C5                 push    10h
_text:004013C7                 lea     edx, [ebp+var_28]
_text:004013CA                 push    edx
_text:004013CB                 push    offset bfSerial1
_text:004013D0                 call    _CompareStrings

	so it hashes the next 3 bytes of the serial, and compare the hash to an hard-coded one.
a fast bruteforce give 'fyZ'. then we see this :

_text:00401401 loc_0_401401:                           ; CODE XREF: _CheckSerial+23Aj
_text:00401401                 push    esi
_text:00401402                 push    3
_text:00401404                 lea     ecx, [ebp+var_53C]
_text:0040140A                 call    _CipherInitKeys
_text:0040140F                 lea     eax, [ebp+bfSerial2]
_text:00401415                 push    eax
_text:00401416                 lea     ecx, [ebp+var_548]
_text:0040141C                 push    ecx
_text:0040141D                 push    9
_text:0040141F                 lea     ecx, [ebp+var_53C]
_text:00401425                 call    _CipherDecrypt  ; decrypt 'Bad Key!'
_text:0040142A                 lea     edx, [ebp+bfSerial2]
_text:00401430                 push    edx
_text:00401431                 push    offset aS       ; "\n%s\n"
_text:00401436                 call    _printf

	so it decrypts 'Bad Key!' and displays it. so we can' t get a good serial. then we see
some strange things, that look like seh procs. we need to make the program do an error to go
there. if the beginning of our serial is the string computed with the length of the name, the
xored dword will be null, and there will be a division by zero error. so the serial starts with
the computed string. then we see this :

_text:0040146A _SEHexit        proc near               ; DATA XREF: _rdata:0040C0A8o
_text:0040146A 
_text:0040146A var_134         = byte ptr -134h
_text:0040146A var_BC          = dword ptr -0BCh
_text:0040146A var_B4          = dword ptr -0B4h
_text:0040146A bfBuffer        = byte ptr -0B0h
_text:0040146A big4            = dword ptr -30h
_text:0040146A var_18          = dword ptr -18h
_text:0040146A var_10          = dword ptr -10h
_text:0040146A var_4           = dword ptr -4
_text:0040146A 
_text:0040146A                 mov     esp, [ebp+var_18]
_text:0040146D                 mov     [ebp+var_4], 0FFFFFFFFh
_text:00401474                 xor     ecx, ecx
_text:00401476 
_text:00401476 __Gen4Digits:                           ; CODE XREF: _SEHexit+27j
_text:00401476                 mov     al, bfUserName[ecx]
_text:0040147C                 xor     al, 5
_text:0040147E                 mov     dl, 0FEh
_text:00401480                 imul    dl
_text:00401482                 and     al, 9
_text:00401484                 add     al, 30h
_text:00401486                 mov     [ebp+ecx+bfBuffer], al
_text:0040148D                 inc     ecx
_text:0040148E                 cmp     ecx, 4
_text:00401491                 jb      short __Gen4Digits

	it generates 4 digits from the username and store them in bfBuffer. then we see this :

_text:00401493                 lea     eax, [ebp+bfBuffer]
_text:00401499                 push    eax
_text:0040149A                 call    _ComputeNumber1
_text:0040149F                 mov     esi, eax
_text:004014A1                 mov     ecx, [ebp+var_BC]
_text:004014A7                 lea     edx, bfUserSerial[ecx]
_text:004014AD                 push    edx
_text:004014AE                 call    _ComputeNumber2
_text:004014B3                 add     esp, 8
_text:004014B6                 sub     esi, eax
_text:004014B8                 jz      short loc_0_4014C4

	it computes a number from the 4 digits with _ComputeNumber1, and one from the 4 next
characters in the serial with _ComputeNumber2, subs them and it must be 0. in fact, both procs
are the same, so the next 4 characters of the serial must be the computed digits. then we see
this :

_text:004014C4 loc_0_4014C4:                           ; CODE XREF: _SEHexit+4Ej
_text:004014C4                 mov     dtCheckSum, 1
_text:004014CE                 xor     esi, esi
_text:004014D0                 push    offset bfUserName
_text:004014D5                 call    _GetLength
_text:004014DA                 add     esp, 4
_text:004014DD                 test    eax, eax
_text:004014DF                 jbe     short __ModifyCheckSum
_text:004014E1 
_text:004014E1 __ComputeCheckSum:                      ; CODE XREF: _SEHexit+9Bj
_text:004014E1                 xor     eax, eax
_text:004014E3                 mov     al, bfUserName[esi]
_text:004014E9                 imul    eax, dtCheckSum
_text:004014F0                 mov     dtCheckSum, eax
_text:004014F5                 inc     esi
_text:004014F6                 push    offset bfUserName
_text:004014FB                 call    _GetLength
_text:00401500                 add     esp, 4
_text:00401503                 cmp     esi, eax
_text:00401505                 jb      short __ComputeCheckSum

	so it computes a checksum of the username. then we see this :

_text:00401507 __ModifyCheckSum:                       ; CODE XREF: _SEHexit+75j
_text:00401507                 mov     ecx, dtCheckSum
_text:0040150D                 lea     eax, ds:0[ecx*8]
_text:00401514                 sub     eax, ecx
_text:00401516                 shl     eax, 9
_text:00401519                 add     eax, ecx
_text:0040151B                 lea     edx, [eax+eax*8]
_text:0040151E                 lea     eax, [ecx+edx*8]
_text:00401521                 lea     eax, [eax+eax*2]
_text:00401524                 shl     eax, 1
_text:00401526                 xor     eax, 12344321h
_text:0040152B                 mov     dword ptr [ebp+bfBuffer], eax

	it modifies the checksum and store it in bfBuffer. then we see this :

_text:00401531                 mov     ecx, [ebp+var_B4]
_text:00401537                 mov     dword ptr [ecx+234h], 10h
_text:00401541                 push    4
_text:00401543                 lea     edx, [ebp+bfBuffer]
_text:00401549                 push    edx
_text:0040154A                 lea     ecx, [ebp+var_134]
_text:00401550                 call    _MD5Update
_text:00401555                 lea     eax, [ebp+bfBuffer]
_text:0040155B                 push    eax
_text:0040155C                 lea     ecx, [ebp+var_134]
_text:00401562                 call    _MD5Final

	so it hashes the modified checksum, and store the hash in bfBuffer. then we see some
bignum procs. if we check the file, we see references to miracl, so you better download the
library. we see this :

_text:00401567                 push    0
_text:00401569                 call    _mirvar
_text:0040156E                 mov     edi, eax
_text:00401570                 push    0
_text:00401572                 call    _mirvar
_text:00401577                 mov     ebx, eax
_text:00401579                 push    0
_text:0040157B                 call    _mirvar
_text:00401580                 mov     esi, eax
_text:00401582                 push    0
_text:00401584                 call    _mirvar
_text:00401589                 mov     [ebp+big4], eax
_text:0040158C                 push    esi
_text:0040158D                 lea     ecx, [ebp+bfBuffer]
_text:00401593                 push    ecx
_text:00401594                 push    10h
_text:00401596                 call    _bytes_to_big
_text:0040159B                 lea     edx, [ebp+bfBuffer]
_text:004015A1                 push    edx
_text:004015A2                 push    esi
_text:004015A3                 call    _cotstr
_text:004015A8                 lea     eax, [ebp+bfBuffer]
_text:004015AE                 push    eax
_text:004015AF                 call    _GetLength
_text:004015B4                 push    eax             ; size_t
_text:004015B5                 mov     ecx, [ebp+var_BC]
_text:004015BB                 lea     edx, (bfUserSerial+4)[ecx]
_text:004015C1                 push    edx             ; const char *
_text:004015C2                 lea     eax, [ebp+bfBuffer]
_text:004015C8                 push    eax             ; const char *
_text:004015C9                 call    _strncmp

	so it puts the md5 hash in big3, writes the md5 hash in hex and look if the next
characters of the serial are the md5 hash. so the serial is
[scrambledstring][4digits][md5hash][....]. then we see this :

_text:004015DF loc_0_4015DF:                           ; CODE XREF: _SEHexit+169j
_text:004015DF                 push    offset aD1d17744ca3d0c09 ; "D1D17744CA3D0C09"
_text:004015E4                 push    edi
_text:004015E5                 call    _cinstr
_text:004015EA                 push    offset a1e307   ; "1E307"
_text:004015EF                 push    ebx
_text:004015F0                 call    _cinstr
_text:004015F5                 mov     ecx, [ebp+var_B4]
_text:004015FB                 mov     dword ptr [ecx+234h], 3Ch
_text:00401605                 push    offset asc_0_40D03C ; const char *
_text:0040160A                 push    offset bfUserSerial ; const char *
_text:0040160F                 call    _strstr
_text:00401614                 inc     eax
_text:00401615                 push    eax
_text:00401616                 push    esi
_text:00401617                 call    _cinstr

	so big1=0D1D17744CA3D0C09h, big2=01E307h, change the base to 60, and look for a '-' in
the serial, and put the number that comes after in big3. so serial is
[scrambledstring][4digits][md5hash]-[number][....]. then we see this :

_text:0040161C                 mov     edx, [ebp+big4]
_text:0040161F                 push    edx
_text:00401620                 push    edi
_text:00401621                 push    ebx
_text:00401622                 push    esi
_text:00401623                 call    _powmod

	so big4 is the rsa decryption of big3. we have :

n=D1D17744CA3D0C09
p=11410ABEB
q=C2916EDB
e=B54341B5A81ED9CF
d=1E307

	then we see this :

_text:00401628                 lea     eax, [ebp+bfBuffer]
_text:0040162E                 push    eax
_text:0040162F                 mov     ecx, [ebp+big4]
_text:00401632                 push    ecx
_text:00401633                 call    _cotstr
_text:00401638                 push    esi
_text:00401639                 mov     edx, dtCheckSum
_text:0040163F                 push    edx
_text:00401640                 call    _convert
_text:00401645                 add     esp, 40h
_text:00401648                 push    esi
_text:00401649                 push    1
_text:0040164B                 call    _insign
_text:00401650                 push    offset bfSerial1
_text:00401655                 push    esi
_text:00401656                 call    _cotstr

	so it writes big4 in bfBuffer, puts dtCheckSum in big3, ensure that big3 is positive
and writes big3 in bfSerial1. then we see this :

_text:00401669 __CompareStrings:                       ; CODE XREF: _SEHexit+21Dj
_text:00401669                 mov     dl, [eax]
_text:0040166B                 mov     cl, dl
_text:0040166D                 cmp     dl, [esi]
_text:0040166F                 jnz     short __BadSerial
_text:00401671                 test    cl, cl
_text:00401673                 jz      short __GoodSerial
_text:00401675                 mov     dl, [eax+1]
_text:00401678                 mov     cl, dl
_text:0040167A                 cmp     dl, [esi+1]
_text:0040167D                 jnz     short __BadSerial
_text:0040167F                 add     eax, 2
_text:00401682                 add     esi, 2
_text:00401685                 test    cl, cl
_text:00401687                 jnz     short __CompareStrings ; strings must be the same, so big4 must be dtCheckSum
_text:00401689 
_text:00401689 __GoodSerial:                           ; CODE XREF: _SEHexit+209j
_text:00401689                 xor     eax, eax
_text:0040168B                 jmp     short loc_0_401692
_text:0040168D ; ---------------------------------------------------------------------------
_text:0040168D 
_text:0040168D __BadSerial:                            ; CODE XREF: _SEHexit+205j
_text:0040168D                                         ; _SEHexit+213j
_text:0040168D                 sbb     eax, eax
_text:0040168F                 sbb     eax, 0FFFFFFFFh
_text:00401692 
_text:00401692 loc_0_401692:                           ; CODE XREF: _SEHexit+221j
_text:00401692                 test    eax, eax
_text:00401694                 jz      short loc_0_40169D
_text:00401696                 push    offset aBadKey  ; "\nBad Key !\n"
_text:0040169B                 jmp     short loc_0_4016A2
_text:0040169D ; ---------------------------------------------------------------------------
_text:0040169D 
_text:0040169D loc_0_40169D:                           ; CODE XREF: _SEHexit+22Aj
_text:0040169D                 push    offset aGood    ; "\nGood !\n"
_text:004016A2 
_text:004016A2 loc_0_4016A2:                           ; CODE XREF: _CheckSerial+130j
_text:004016A2                                         ; _SEHexit+55j ...
_text:004016A2                 call    _printf

	so the number must be the encrypted dtCheckSum. so we have to do this :

	 modify the length of the name
	 convert the modified length to string, and scramble it
	 write this in the serial buffer
	 generate the 4 digits
	 cat the 4 digits to the serial
	 generate the checksum
	 modify it
	 hash the modified checksum
	 cat the md5 hash to the serial
	 cat '-' to the serial
	 force the checksum to be positive
	 encrypt it
	 cat the encrypted dword in base 60 to the serial
________________________________________________________________________________________________
:::::::::::::::::::::::::::::::::::: f i n a l   w o r d s :::::::::::::::::::::::::::::::::::::

	 it was an interesting keygenme, with a neat little trick with seh
________________________________________________________________________________________________
										      roy|fleur

