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 #include #include #include #include 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]