Tutorial about keygening UltraEdit 7.10b

Allright lets start :) The tutorial describes one way ... not the best but ... doesn't matter :p After executing uedit32.exe we enter a name and a serial. I choose Apus2k / 123123123. Ultraedit now creates the regfile "uedit32.reg" and checks after a restart if the serial is correct.

Before we execute uedit32.exe the second time we set a breakpoint on createfilea. Sice breaks 3 times until uedit opens the "right" file. You can see the filename with "d *(esp+4)".

A few code lines further we notice this loop:

004594A5 loc_4594A5:
004594A5 mov     cl, [ebp+esi-287h]
004594AC mov     al, [ebp+esi-288h]
004594B3 dec     cl
004594B5 dec     al
004594B7 mov     [ebp+esi-184h], cl
004594BE mov     [ebp+esi-183h], al
004594C5 inc     esi
004594C6 inc     esi
004594C7 cmp     esi, edi
004594C9 jle     short loc_4594A5

A deeper look at the content of (ebp-288h) and (ebp-183h) we see that the regfile is decoded here in a really simple way -1 :P For my name and serial it looks afterwards:

123123123aaxxzzApus2kzaza123123123123Apus2k

The next lines after the loop:

004594CB lea     eax, [ebp-184h]
004594D1 push    10h		 - length
004594D3 push    eax		 - source
004594D4 lea     eax, [ebp-80h]
004594D7 push    eax		 - destination
004594D8 mov     [ebp-70h], bl
004594DB call    loc_480330	 - cut string

Ultraedit cut out the first 16 chars from the decoded string. I think it is logical that it tries to catch the serial :) so we recreate the regfile with a serial which has a length of 16 otherwise the location of the name isn't right in the string.

Btw it was at this moment I think that I noticed that the edit field only allows a serials with 16 or less chars - stupid :=)

After a little bit tracing we found the following call:

0043F0CB push    dword_4EBFC0		- serial
0043F0D1 push    1
0043F0D3 push    eax			- name
0043F0D4 push    dword ptr [ebp-10h]	- name
0043F0D7 call    sub_40EFCC
0043F0DC add     esp, 10h
0043F0DF test    eax, eax		- return-value in eax = 0
0043F0E1 jz      loc_43F270		- then right serial

Lets take a deeper look at the call :D

The first important check for a keygen is nearly at the beginning:

0040EFDA push    edi		- name
0040EFDB call    sub_47FE70	- get length
.
0040EFFB cmp     eax, 6		- compare length with 6
0040EFFE jl      loc_40F1C9	- if lower then 6 ...

Allright the name has to be greater then 5 hehe bad luck I entered Apus2k and not Apus :P lets go on.

After some loops which we will analyze later we found this comparison:

0040F196 lea     eax, [ebp+var_40]
0040F199 push    eax			- izd8Z@(0:$1*&#)
0040F19A lea     eax, [ebp+var_80]
0040F19D push    eax			- 123123123123123
0040F19E call    sub_481900		- compare
0040F1A4 test    eax, eax
0040F1A7 jz      short loc_40F1CF	- if equal ..
0040F1A9 lea     eax, [ebp+var_C0]
0040F1AF push    eax			- BSWXMFNPGDXQFBP
0040F1B0 lea     eax, [ebp+var_80]
0040F1B3 push    eax			- 123123123123123
0040F1B4 call    sub_481900		- compare
0040F1BA test    eax, eax
0040F1BD jz      short loc_40F1CF	- if equal ..

Okidok why has the serials only a length of 15 and is the last char free selectable .. we will see later .. but first take a look above how the two serials are calculated :=)

The first loop we found in the call...

0040F033 loc_40F033:
0040F033 mov     cl, al
0040F035 shl     cl, 1
0040F037 mov     [ebp+eax+var_40], cl
0040F03B inc     eax
0040F03C cmp     eax, 3Dh
0040F03F jb      short loc_40F033

...simply fills a buffer with the numbers 0,2,4,6,8,10...... in the keygen later it would look the following way

* for(i = 0;i < 61;i++)buf[i] = i << 1;

After that we see (d ebp-40) that the name is copied to the beginning of this string.

* strcpy(buf,name);

0040F05D loc_40F05D:
0040F05D movsx   ecx, [ebp+eax+var_3F]
0040F062 movsx   edx, [ebp+eax+var_40]
0040F067 imul    ecx, edx		- multiply the two chars
0040F06A add     [ebp+10], ecx		- add to ebp+8
0040F06D inc     eax
0040F06E cmp     eax, esi		- cmp with the string length - 1
0040F070 jl      short loc_40F05D

This loop follows directly after the copy call. The first and the second char is multiply and add to ebp+10. After that the second and the third... I think you know what I mean :P.

* for(v1 = 0,i = 0;i < strlen(name)-1;i++)v1 += buf[i]*buf[i+1];

0040F081 loc_40F081:
0040F081 mov     eax, [ebp+8]
.
0040F0E8 cmp     esi, 3Ch
0040F0EB jl      short loc_40F081

The last important loop is a little bit longer I will try to explain every part of it:
esi is the counter it begins with 0 and runs until 3ch (60). [ebp+10] is the value (v1) from the loop before.

* for(i = 0;i < 60;i++)
* {

0040F081 mov     eax, [ebp+8]
0040F084 lea     ecx, [ebp+esi+40]  
0040F088 add     eax, ecx	- simply 1,2,.. by add of the offsets
0040F08A cmp     eax, 3Ch	- if lower 60 .... 
0040F08D jge     short loc_40F0D4

0040F08F mov     eax, esi	- esi was the loop value 0,1,2,3,4...
0040F091 push    4
0040F093 cdq
0040F094 pop     edi		- edi = 4
0040F095 idiv    edi		- div esi by 4

* dv1 = ldiv(i,4);

0040F097 mov     eax, esi	- eax = esi ... again the loop value
0040F099 push    10h		- mov ebx,0x10 and div with this value
0040F09B pop     ebx
0040F09C push    1Eh		
0040F09E mov     edi, edx	- save the rest of the last divison in edi
0040F0A0 cdq
0040F0A1 idiv    ebx		- div esi by 16

* dv2 = ldiv(i,0x10);

0040F0A8 mov     edx, dword_4E4DA8[edi*4]

At 4e4da8 are stored four offsets. The rest of the first division decided which string should be used...

str1[] = "\x6f\x00\xde\x00\x39\x00\x77\x00",
str2[] = "\x37\x00\x71\x00\x55\x00\x7e\x00",
str3[] = "\x00\x55\x00\xbc\x00\xa5\x00\xe7",
str4[] = "\x00\xaa\x00\x87\x00\x5a\x00\x88";

0040F0AF sar     eax, 1		- rest value from div 2 sar 1 decided ..
0040F0B1 pop     edi
0040F0B2 movzx   eax, byte ptr [edx+eax] - .. which byte from the string

I will make it with "ifs" in c .. it seems easier for me ;)

*      if(dv1.rem == 0)v2 = str1[dv2.rem >> 1];
* else if(dv1.rem == 1)v2 = str2[dv2.rem >> 1];
* else if(dv1.rem == 2)v2 = str3[dv2.rem >> 1];
* else v2 = str3[dv2.rem >> 1];

0040F0B6 mov     edx, [ebp+10]	- value v1 .. you remember from the beginning
0040F0B9 sar     edx, 3
0040F0BC movzx   edx, dl
0040F0BF shr     eax, 3		- both v2 and v1 shift right and then xor
0040F0C2 xor     eax, edx
0040F0C4 cdq
0040F0C5 idiv    edi		- and div by edi (1eh)

* dv3 = div((v1 >> 3) ^ (v2 >> 3),0x1e);

0040F0C7 mov     al, [ebp+edx-40] - the string buf with the rest from
				    from the last division as the index
0040F0CB inc     al		  - byte + 1
0040F0CD xor     al, [ecx+1]	  - xor with next char, loop value is index
0040F0D0 xor     ebx, ebx
0040F0D2 mov     [ecx], al	  - and put it into buf

* buf[i] = (buf[dv3.rem] + 1) ^ buf[i+1];

0040F0D4 movzx   eax, byte ptr [ecx]
0040F0D7 push    1Ah
0040F0D9 cdq
0040F0DA pop     ecx
0040F0DB idiv    ecx			- divide the byte by 1ah
0040F0DD add     dl, 41h		- add 41h
0040F0E0 mov     [ebp+esi-c0], dl	- and put it into the serial string

* dv3 = div(buf[i],0x1a);
* ser1[i] = (dv3.rem) + 0x41;
* }

Ok the first 15 chars of ser is the first and the first 15 char of buf is the second serial. But the second has to run through the next loop to sort out invalid chars. If you have enough time you can revers it. I think a keygen user would prefer such a serial "BASSA" as such one "z!44)" :p

Hehe allright the routine "should" now create a serial in such a format "BSWXMFNPGDXQFBP" but what is with the last char? The serial you must enter has a length of 16 this one only 15 ... so we try out if the last char is free selectable. 

Hehe the serial passes the first check but when you close the program it deletes the regfile so there is a second check somewhere in the code :) What to do? It deletes the regfile, so why don't we set a breakpoint on deletefilea. When sice breaks we stranded here:

00459795 push    dword ptr [ebp-10h]
00459798 call    ds:DeleteFileA
0045979E mov     eax, dword_4EFF3C
004597A3 lea     ecx, [ebp-10h]
004597A6 add     eax, 0FFFFFFD4h

Lets trace a few lines up.....

004596FE push    1Ah			 
00459700 pop     edi
00459701 mov     cl, [eax+0Fh]
00459704 movzx   eax, byte ptr [ebp-10h] - byte(v1) you remember the value :P
00459708 cdq
00459709 idiv    edi			 - dividate eax by 1ah
0045970B movsx   eax, cl
0045970E add     edx, 41h		 - add 41h 
00459711 cmp     eax, edx		 - and compare with the last char
00459713 jz      loc_4597BC

* ser1[15] = ldiv(0xff & v1,0x1a);
* ser1[16] = 0; 

and finish ... uff sorry for any mistake in the text above ... :)

< Apus >

/***************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <stdlib.h>

main()
{
  DWORD v1,v2,i;
  char buf[256],name[256],ser1[256];
  ldiv_t dv1,dv2,dv3;
  unsigned char str1[] = "\x6f\x00\xde\x00\x39\x00\x77\x00";
  unsigned char str2[] = "\x37\x00\x71\x00\x55\x00\x7e\x00";
  unsigned char str3[] = "\x00\x55\x00\xbc\x00\xa5\x00\xe7";
  unsigned char str4[] = "\x00\xaa\x00\x87\x00\x5a\x00\x88";

  ZeroMemory(buf,256);
  ZeroMemory(ser1,256); 

  printf("Name: ");gets(name);
  if(strlen(name) < 6)return 0;
  for(i = 0;i < 61;i++)buf[i] = (char)(i << 1);
  strcpy(buf,name);
  for(v1 = 0,i = 0;i < strlen(name)-1;i++)v1 += buf[i]*buf[i+1];

  for(i = 0;i < 60;i++)
  {
    dv1 = ldiv(i,4);
    dv2 = ldiv(i,0x10);
         if(dv1.rem == 0)v2 = str1[dv2.rem >> 1];
    else if(dv1.rem == 1)v2 = str2[dv2.rem >> 1];
    else if(dv1.rem == 2)v2 = str3[dv2.rem >> 1];
    else v2 = str4[dv2.rem >> 1];
    dv3 = ldiv((0xff & (v1 >> 3)) ^ (0xff & (v2 >> 3)),0x1e);
    buf[i] = (buf[dv3.rem] + 1) ^ buf[i+1];

    dv3 = ldiv(buf[i],0x1a);
    ser1[i] = (dv3.rem & 0xff) + 0x41;
  }
  dv3 = ldiv(0xff & v1,0x1a);
  ser1[15] = dv3.rem + 0x41;
  ser1[16] = 0;
  printf("Serial: %s",ser1);
  getch();
}
/***************************************************************************/0]