Seifer's Short Tutorials -- http://www.seifer666.com ==================== Winzip 6.3/7.0/8.0 ==================== Download location : http://www.winzip.com Protection : name / serial Level [1..10] : 2 (EASY) Tools needed : Soft Ice 3.x or better,C/C++ compiler for the keygen =============== 1)Introduction =============== Ok, here's the english translation of my winzip's keygenning tut, as some dudes asked me to translate it. With this, you'll be able to keygen all winzip's version, from 6.3 to 8.0 :) ======================= 2)Une définition utile ======================= To break the prog's execution before the serial's generation, we'll use the API GetDlgItemTextA (32 bits version), which is defined as follow in the Win32 Programmer's References : The GetDlgItemText function retrieves the title or text associated with a control in a dialog box. UINT GetDlgItemText( HWND hDlg, // handle of dialog box int nIDDlgItem, // identifier of control LPTSTR lpString, // address of buffer for text int nMaxCount // maximum size of string ); Parameters hDlg Identifies the dialog box that contains the control. nIDDlgItem Specifies the identifier of the control whose title or text is to be retrieved. lpString Points to the buffer to receive the title or text. nMaxCount Specifies the maximum length, in characters, of the string to be copied to the buffer pointed to by lpString. If the length of the string exceeds the limit, the string is truncated. Return Values If the function succeeds, the return value specifies the number of characters copied to the buffer, not including the terminating null character. If the function fails, the return value is zero. ========================== 3)Machine code analysis ========================== Launch Winzip, enter a name and a serial. As i said before, we will put a breakpoint on GetDlgItemTextA, as the program will get the name and the serial thanks to this API. So under Soft Ice let's type : bpx GetDlgItemTextA, then press on enter and F5 as usual :) Click on the OK button, we are back under Soft Ice. You should arrive at the offset 407F6D : ---------------------------------------------8<-------------------------------------------------- * Reference To: USER32.GetDlgItemTextA, Ord:0104h | :00407F6D FF1528744700 Call dword ptr [00477428] ;calls GetDlgItemTextA :00407F73 57 push edi ;saves the name :00407F74 E821790300 call 0043F89A ;kills spaces after the name :00407F79 57 push edi ;saves the name :00407F7A E844790300 call 0043F8C3 ;kills spaces before the name :00407F7F 59 pop ecx :00407F80 BEA4CD4800 mov esi, 0048CDA4 :00407F85 59 pop ecx ---------------------------------------------8<-------------------------------------------------- Ok, the firsqt operation on the name is to check that the user didn't enter extra spaces, i.e spaces before the first char of the name or spaces after the last char of the name... In C/C++, we could code this as follows : ---------------------------------------------8<-------------------------------------------------- GetDlgItemText(hwnd, IDC_NAME, name, 255); //we get the name as winzip len = strlen(name); //length of the name while(name[len-1] == ' ') len--; //kill da fuckin space name[len] = 0x00; //nul terminating char //same thing for the first char of the name while(name[0] == ' ') for(i = 0; i < len; ++i) name[i] = name[i+1]; len = strlen(name); ---------------------------------------------8<-------------------------------------------------- Once those checks are done, our name will look like : entered name : "seifer" modified name : "seifer" entered name : " seifer" modified name : "seifer" entered name : "seifer is sexy " modified name : "seifer is sexy" The proggie then gets the serial the same way, and starts a lotta other checks erm, like the following ones (1 <= length <= 40), then checks length of the serial, and compares even the entered name to names in memory (heh do they think we are silly ;), begin to trace with F10 until you arrive at 00407A99... Note : to go faster, you could even put a bpr on your name, then press sometimes on F5. I give you the bpr's syntax again (breakpoint on memory range) : bpr address_of_name (address_of_name + length_of_name) - 1 rw Example : heer the address of the name is 0048CD78, and the length of seifer is 6 chars... So i'll have to type : bpr 48cd78 48cd7d rw , rw meaning read/write... Let's have a lil look at the call @00407A99 : :00407A99 E8A9000000 call 00407B47 ;enter inside with F8 We arrive then at the first part of the serial's generation's algorithm : ---------------------------------------------8<-------------------------------------------------- :00407B47 55 push ebp :00407B48 8BEC mov ebp, esp :00407B4A 8B4D08 mov ecx, dword ptr [ebp+08] ;ecx points to the name :00407B4D 53 push ebx ;saves ebx :00407B4E 56 push esi ;esi :00407B4F 57 push edi ;and edi :00407B50 8A11 mov dl, byte ptr [ecx] ;dl contains the char pointed by ecx :00407B52 33DB xor ebx, ebx ;initializes ebx :00407B54 33C0 xor eax, eax ;eax to 0 :00407B56 8BF1 mov esi, ecx ;esi has the same value than ecx :00407B58 33FF xor edi, edi ;edi initialized to 0 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407B6C(U) | :00407B5A 84D2 test dl, dl ;dl = 0 --> no more chars :00407B5C 7410 je 00407B6E ;jumps after the loop :00407B5E 660FB6D2 movzx dx, dl ;dx contains dl... :00407B62 0FAFD7 imul edx, edi ;edx is multiplied by the char's position :00407B65 03DA add ebx, edx ;and added to ebx :00407B67 8A5601 mov dl, byte ptr [esi+01] ;next char :00407B6A 47 inc edi ;edi is incremented :00407B6B 46 inc esi ;as esi :00407B6C EBEC jmp 00407B5A ;jumps to the beginning of the loop ---------------------------------------------8<-------------------------------------------------- Now the proggie has calculated the first part of the serial (in fact it is the last one, but it's calculated in first, do you follow ?). The algo is quite simple, it just adds the ascii codes of the chars of the name multiplied by their position in the name. Still in C, we could write this as follows : unsigned long sum = 0; for(unsigned i = 0; i < len; ++i) sum += name[i] * i; As you can see it, it isn't very strong... Now comes the first part of the serial's calculation (which is calculated in last, do ya remember ?) : I also remember you that ebx contains atm the result of the first calculation... ---------------------------------------------8<-------------------------------------------------- * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407B5C(C) | :00407B6E C70544C8480001000000 mov dword ptr [0048C844], 00000001 :00407B78 8BF1 mov esi, ecx ;esi points to the name :00407B7A 8A09 mov cl, byte ptr [ecx] ;cl contains one char * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407B97(U) | :00407B7C 84C9 test cl, cl ;all chars done ? :00407B7E 7419 je 00407B99 ;yah, jumps to the end of algo :00407B80 660FB6C9 movzx cx, cl ;cx contains cl... :00407B84 6821100000 push 00001021 ;constant value used inside the call :00407B89 51 push ecx ;saves the char :00407B8A 50 push eax ;eax = 0 at beginning :00407B8B E829000000 call 00407BB9 ;let's study this after :00407B90 8A4E01 mov cl, byte ptr [esi+01] ;next char :00407B93 83C40C add esp, 0000000C ;prepares the stack for pushes :00407B96 46 inc esi ;esi is incremented :00407B97 EBE3 jmp 00407B7C ;loops again * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407B7E(C) | :00407B99 83C063 add eax, 00000063 ;99d = 63h added to the calculated number :00407B9C 0FB7CB movzx ecx, bx ;ecx contains the low word of ebx :00407B9F 0FB7C0 movzx eax, ax ;eax contains the low word of eax ---------------------------------------------8<-------------------------------------------------- When i say cx contains one char of the name, let's look ecx'state : the first ascii code of seifer is 73h = 115d ('s')... mov cl, byte ptr [ecx] <---- ecx = 00000073 movzx cx, cl <---- ecx = 00007300 In C it is equal to : ... unsigned char mychar = name[i]; mychar <<= 8; (equals mychar *= 0x100;) ... Moreover a high word is equal to the 4 last figures contained in the concerned registry : mov eax, 12345678 <---- eax = 12345678 mov eax, ax <---- eax = 00005678, 5678 is the low word (and 1234 the high one). Inside the call we fond the calculation of the first part of serial, it is kinda strange ;-). ---------------------------------------------8<-------------------------------------------------- :00407BB9 55 push ebp :00407BBA 8BEC mov ebp, esp :00407BBC 8B4508 mov eax, dword ptr [ebp+08] :00407BBF 56 push esi :00407BC0 33C9 xor ecx, ecx * Possible Ref to Menu: RBUTTONMENU1, Item: "Delete..." | * Possible Reference to String Resource ID=00008: "Delete files from %s" | :00407BC2 6A08 push 00000008 :00407BC4 8A6D0C mov ch, byte ptr [ebp+0C] :00407BC7 5A pop edx ;counter = 8 at beginning * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407BDF(C) | :00407BC8 8BF0 mov esi, eax ;esi = 0 :00407BCA 33F1 xor esi, ecx ;esi xored with ecx :00407BCC 66F7C60080 test si, 8000 ;si % 2 == 0 ? :00407BD1 7407 je 00407BDA ;yah, jumps there :00407BD3 03C0 add eax, eax ;else eax += eax :00407BD5 334510 xor eax, dword ptr [ebp+10] ;eax is then xored with 0x1021 ! :00407BD8 EB02 jmp 00407BDC ;jumps again * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407BD1(C) | :00407BDA D1E0 shl eax, 1 ;eax <<= 1 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00407BD8(U) | :00407BDC D1E1 shl ecx, 1 ;ecx <<= 1 :00407BDE 4A dec edx ;decreases the counter :00407BDF 75E7 jne 00407BC8 ;loops again :00407BE1 5E pop esi :00407BE2 5D pop ebp :00407BE3 C3 ret ;the number is generated... ---------------------------------------------8<-------------------------------------------------- The operation is done 8 times for each char (look at the counter in edx)... Let's translate this in C, always, again :-) : unsigned i, j, arg, temp, ctrl; unsigned long serial2 = 0; for(i = 0; i < len; ++i) { arg = name[i] << 8; for(j = 0; j < 8; ++j) { temp = serial2; temp ^= arg; ctrl = temp & 0x8000; if(ctrl == 0) { serial2 <<= 1; arg <<= 1; } else { serial2 += serial2; serial2 ^= 0x1021; arg <<= 1; } } } As we have seen before, eax will then be added to 99, and the first part of the serial will be equal to the low word of eax (eax % 0x10000), simply coded : serial2 += 0x63; //99d serial2 %= 0x10000; But what the fuck will become those two numbers ? We land at the last part of the algorithm : ---------------------------------------------8<-------------------------------------------------- :00407BA2 51 push ecx ;ecx = first nuber :00407BA3 50 push eax ;eax = second number * Possible StringData Ref from Data Obj ->"%04X%04X" | :00407BA4 688CE34700 push 0047E38C ;points to "%04X%04X" :00407BA9 FF750C push [ebp+0C] ;points to real serial :00407BAC E803FF0500 call 00467AB4 ;calls wsprintfa :00407BB1 83C410 add esp, 00000010 ---------------------------------------------8<-------------------------------------------------- The two numbers are then concatenated in hexadecimal in the reversed order of their calculation in the algorithm. So the serial has 8 chars... name : Seifer serial : ADFE0637 =================== 4)Source code in C =================== To finish, here comes the int keygen(HWND) function coming from my win32C keygen which works for each Winzip version, from 6.3 to 8.0 : ---------------------------------------------8<-------------------------------------------------- int keygen(HWND hwnd) { char name[42] = ""; char result[9] = ""; int i, j, len; unsigned long serial1 = 0, serial2 = 0; unsigned long temp = 0, arg = 0, shit = 0, ctrl = 0; GetDlgItemText(hwnd, IDC_NAME, name, 42); len = strlen(name); while(name[len-1] == ' ') len--; name[len] = 0x00; while(name[0] == ' ') for(i = 0; i < len; ++i) name[i] = name[i+1]; len = strlen(name); if(len == 0) { SetDlgItemText(hwnd, IDC_SERIAL, "Please enter your name/don't just put spaces."); return 1; } else if(len > 40) { SetDlgItemText(hwnd, IDC_SERIAL, "Name has to be less than 41 chars."); return 1; } for(i = 0; i < len; ++i) serial1 += name[i] * i; for(i = 0; i < len; ++i) { arg = name[i] << 8; for(j = 0; j < 8; ++j) { temp = serial2; temp ^= arg; ctrl = temp & 0x8000; if(ctrl == 0) { serial2 <<= 1; arg <<= 1; } else { serial2 += serial2; serial2 ^= 0x1021; arg <<= 1; } } } serial2 += 0x63; serial2 %= 0x10000; wsprintf(result, "%.4X%.4X", serial2, serial1); SetDlgItemText(hwnd, IDC_SERIAL, result); return 0; } ---------------------------------------------8<-------------------------------------------------- Voila ! ;-) ============ 5)Greetings ============ Here comes the longest part of the tutorial, the greetings ! Greets go to : tHE Analyst, Warez Pup, risc, lazarus, mercution, falcon, f0dder, MagicRaphoun, Tamambolo, TSCube, JB007, vrom ohh mon vrom, kahel, GanJaMan, Inferno, zoltan, Phoenix, wizdaz, promethee, stealthy, Syntax3rr, roy|, BlndAngl, StatMan, LutinNoir, Fenorez, Vylent, Cell, BMonkey, Robocop, TeChNiCh, Volatility, Am4nte, SantMat, FatBoyJoe, vissie, grugq, JosephCo, yAtEs, C_DKnight, sinn0r, eternal-bliss, Aragonx, Lucifer48, Blaze, Bladey, KaKtuZ, Grunt, NamNT, BoomBox, KarlitoxZ, Crudd, LordOfLa, alpine, Sushi, xor, AbsoluteB, norika, Tresni, webmasta DnNuke... and all i dare have forgotten :p Seifer[ECLiPSE/HellForge] seifer666@caramail.com #ICQ : 61545376