winzip 8.1 keygen tut so first i have to excuse for my bad english, but i hope you understand/learn anyway something. tools needed: - softice - a c-compiler (i use lcc-win32) - a little knowledge of the c language - w32dasm or ida ok i hope you have read phalanx's winzip 8.1 tutorial, its excellent especially if you haven't that much experience with softice. in phalanx tut we got a valid serial for our name out of the memory. now we want to code a keygen, so we need to know where our valid serial was generated. since the valid serial was generated out of our name we know that the serial must be computed between the call to GetDlgitemTextA which gets our name & the compare between valid & fake serial. from phalanx tut we know that the serialcheck begins at offset 0040B65C :0040BD26 call 0040B65C ; Check the registration code :0040BD2B test al, al :0040BD2D jz 0040BD7C ; JUMP! The registration code string is invalid. so load wintip32.exe with softice symbol loader, press load & ignore the warning. you break at the programs entrypoint & now set a breakpoint at adress 40B65C "bpx 40B65C" & exit softice with F5/Ctrl-D/X. softice will pop up 3 times before winzip is runing, this is because the 3!! serial checks on winzip startup to test if you are registred or not. ok once you are in winzip go to the registration dialog enter name & fake serial i use: name: noesis serial: 12345678 press OK and we break at adress 40B65C :) direct at the beginning of the serial checking routine. now lets trace trough the code & try to understand whats happening: :0040B65C 55 push ebp :0040B65D 8BEC mov ebp, esp :0040B65F 81EC0C020000 sub esp, 0000020C :0040B665 8065FF00 and byte ptr [ebp-01], 00 :0040B669 803D90C74C0000 cmp byte ptr [004CC790], 00 --> test if name = 0 :0040B670 53 push ebx :0040B671 56 push esi --> push name on stack :0040B672 57 push edi --> push fake serial on stack :0040B673 0F84FB000000 je 0040B774 --> if name = 0 jmp to not valid :0040B679 8D45E8 lea eax, dword ptr [ebp-18] :0040B67C 50 push eax | :0040B67D 68A8E74B00 push 004BE7A8 | :0040B682 E85862FFFF call 004018DF }-- generate string MuradMeraly (blacklisted name??) :0040B687 8D45E8 lea eax, dword ptr [ebp-18] | :0040B68A 50 push eax :0040B68B E8D07A0800 call 00493160 --> do weired things with the string MuradMeraly :0040B690 83C40C add esp, 0000000C :0040B693 83F814 cmp eax, 00000014 :0040B696 7211 jb 0040B6A9 :0040B698 BF20C74A00 mov edi, 004AC720 :0040B69D 6A21 push 00000021 :0040B69F 57 push edi :0040B6A0 E8C5F60000 call 0041AD6A :0040B6A5 59 pop ecx :0040B6A6 59 pop ecx :0040B6A7 EB05 jmp 0040B6AE :0040B6A9 BF20C74A00 mov edi, 004AC720 :0040B6AE 8D85F4FDFFFF lea eax, dword ptr [ebp+FFFFFDF4] :0040B6B4 BB90C74C00 mov ebx, 004CC790 :0040B6B9 50 push eax :0040B6BA 53 push ebx :0040B6BB E850030000 call 0040BA10 :0040B6C0 8D85F4FDFFFF lea eax, dword ptr [ebp+FFFFFDF4] :0040B6C6 50 push eax --> eax = noesis :0040B6C7 E8947A0800 call 00493160 --> get namelenth :0040B6CC BEC8000000 mov esi, 000000C8 --> esi = C8 (= 200decimal) :0040B6D1 83C40C add esp, 0000000C :0040B6D4 3BC6 cmp eax, esi --> compare 6 (namelength) with 200 (maxnamelenth?) :0040B6D6 720A jb 0040B6E2 --> if namelenth < 200 go on :0040B6D8 6A23 push 00000023 :0040B6DA 57 push edi :0040B6DB E88AF60000 call 0041AD6A :0040B6E0 59 pop ecx :0040B6E1 59 pop ecx :0040B6E2 8D85F4FDFFFF lea eax, dword ptr [ebp+FFFFFDF4] --> eax = noesis :0040B6E8 50 push eax --> push on stack :0040B6E9 8D45E8 lea eax, dword ptr [ebp-18] --> eax = MuradMeraly :0040B6EC 50 push eax --> push on stack :0040B6ED E8AE2D0900 call 0049E4A0 --> check if entered name = MuradMeraly :0040B6F2 59 pop ecx :0040B6F3 85C0 test eax, eax --> if name = MuradMeraly :0040B6F5 59 pop ecx :0040B6F6 7504 jne 0040B6FC --> dont jump :0040B6F8 C645FF01 mov [ebp-01], 01 :0040B6FC 8D45E8 lea eax, dword ptr [ebp-18] --> eax = MuradMeraly :0040B6FF 50 push eax --> push on stack :0040B700 68B8E74B00 push 004BE7B8 :0040B705 E8D561FFFF call 004018DF --> generate string bcom (???) :0040B70A 8D45E8 lea eax, dword ptr [ebp-18] --> eax = bcom :0040B70D 50 push eax --> push on stack :0040B70E E84D7A0800 call 00493160 --> do some stuff with bcom :0040B713 83C40C add esp, 0000000C :0040B716 83F814 cmp eax, 00000014 :0040B719 720A jb 0040B725 :0040B71B 6A27 push 00000027 :0040B71D 57 push edi :0040B71E E847F60000 call 0041AD6A :0040B723 59 pop ecx :0040B724 59 pop ecx :0040B725 8D45E8 lea eax, dword ptr [ebp-18] --> eax = bcom :0040B728 50 push eax --> push on stack :0040B729 53 push ebx --> ebx = noesis / push on stack :0040B72A E8712D0900 call 0049E4A0 --> compare is input name (noesis) = bcom :0040B72F 59 pop ecx :0040B730 85C0 test eax, eax --> if its not the same :0040B732 59 pop ecx :0040B733 750E jne 0040B743 --> jump :0040B735 FF15F0C14A00 Call dword ptr [004AC1F0] :0040B73B A801 test al, 01 :0040B73D 7404 je 0040B743 :0040B73F C645FF01 mov [ebp-01], 01 :0040B743 6A14 push 00000014 :0040B745 8D45E8 lea eax, dword ptr [ebp-18] --> eax = bcom :0040B748 6A00 push 00000000 --> push 0 on stack :0040B74A 50 push eax --> ppush bcom on stack :0040B74B E850800800 call 004937A0 --> do some stuff with bcom.... :0040B750 56 push esi :0040B751 8D85F4FDFFFF lea eax, dword ptr [ebp+FFFFFDF4] --> eax = noesis !! :0040B757 6A00 push 00000000 -->push 0 on stack :0040B759 50 push eax --> push noesis on stack :0040B75A E841800800 call 004937A0 --> do soeme stuff with noesis :0040B75F 83C418 add esp, 00000018 :0040B762 807DFF00 cmp byte ptr [ebp-01], 00 :0040B766 7413 je 0040B77B :0040B768 E8D7080000 call 0040C044 :0040B76D 80258DBD4C0000 and byte ptr [004CBD8D], 00 :0040B774 32C0 xor al, al :0040B776 E9F5000000 jmp 0040B870 :0040B77B 8D85BCFEFFFF lea eax, dword ptr [ebp+FFFFFEBC] :0040B781 50 push eax :0040B782 53 push ebx -> ebx = noesis push on stack :0040B783 E8ED000000 call 0040B875 -> do some stuff with noesis :)) :0040B788 8D85BCFEFFFF lea eax, dword ptr [ebp+FFFFFEBC] :0040B78E 50 push eax -> check value of eax here type "d eax" whooha looks like a winzip serial :) ok so much code so little info but now it seems that we have found the call to the serial-generation. we also know tha our name is compared to "MuradMeraly" & "bcom" berhaps this are blacklisted names? also our entered name length is comapred with C8 (200 decimal) this is perhaps the length of longest valid name you could enter... lets do on with searching the serial algo. :0040B781 50 push eax :0040B782 53 push ebx -> ebx = noesis push on stack :0040B783 E8ED000000 call 0040B875 -> do some stuff with noesis :)) now let's take al look what happens at 40B875 and it seems here we have the algo: :0040B875 55 push ebp :0040B876 8BEC mov ebp, esp :0040B878 8B4D08 mov ecx, dword ptr [ebp+08] --> ecx = noesis :0040B87B 53 push ebx :0040B87C 56 push esi :0040B87D 57 push edi :0040B87E 8A11 mov dl, byte ptr [ecx] -> get first char of name in dl :0040B880 33DB xor ebx, ebx -> ebx = 0 :0040B882 33C0 xor eax, eax -> eax = 0 :0040B884 8BF1 mov esi, ecx -> esi = noesis :0040B886 33FF xor edi, edi -> edi 0 //edi is used as an counter here loop begin :0040B888 84D2 test dl, dl -> if dl = 0 :0040B88A 7410 je 0040B89C -> all chars of name were processed & jump :0040B88C 660FB6D2 movzx dx, dl -> dl = dx :0040B890 0FAFD7 imul edx, edi -> edx = edx * edi // edx is the current char of name and edi is the inde of the char in our case n = 0// o = 1// e = 22 etc... :0040B893 03DA add ebx, edx -> add edx to ebx :0040B895 8A5601 mov dl, byte ptr [esi+01] -> get next char of name :0040B898 47 inc edi -> increase counter :0040B899 46 inc esi :0040B89A EBEC jmp 0040B888 -> next turn loop end so far its simple. every char of our name is multiplied with it's index (0,1,2,3,4,...) and was then added to ebx for noesis we get: n -> 6E * 0 = 0 || ebx = ebx + 0 = 0 o -> 6F * 1 = 6F || ebx = ebx + 6F = 6F e -> 65 * 2 = CA || ebx = ebx + CA = 6F + CA = 139 s -> 73 * 3 = 159 || ebx = ebx + 159 = 139 + 159 = 292 i -> 69 * 4 = 1A4 || ebx = ebx + 1A4 = 292 + 1A4 = 436 s -> 73 * 5 = 23F || ebx = ebx + 23F = 436 + 23F = 675 ok now we know how the first value will be calculated but sadly theres anotherone, an the calculation is little bit more complicated: :0040B89C C6058BC74C0001 mov byte ptr [004CC78B], 01 :0040B8A3 8BF1 mov esi, ecx ;esi = ecx = noesis :0040B8A5 8A09 mov cl, byte ptr [ecx] ; get first char of name in cl :0040B8A7 84C9 test cl, cl ; if cl = 0 :0040B8A9 7419 je 0040B8C4 ; all chars of name are processed so jump out of loop :0040B8AB 660FB6C9 movzx cx, cl ; cx = cl :0040B8AF 6821100000 push 00001021 ; push 1021 on stack...we see later whats that for :0040B8B4 51 push ecx :0040B8B5 50 push eax :0040B8B6 E829000000 call 0040B8E4 ; do some really weired things with the char :0040B8BB 8A4E01 mov cl, byte ptr [esi+01] ; get next char :0040B8BE 83C40C add esp, 0000000C :0040B8C1 46 inc esi :0040B8C2 EBE3 jmp 0040B8A7 ; next turn :0040B8C4 83C063 add eax, 00000063 ; add 63 to the value... alright we see a similar loop like the one before but this time all the calculations are made in an call so lets take a look at it... call: :0040B8E4 55 push ebp :0040B8E5 8BEC mov ebp, esp :0040B8E7 8B4508 mov eax, dword ptr [ebp+08] :0040B8EA 56 push esi :0040B8EB 33C9 xor ecx, ecx :0040B8ED 6A08 push 00000008 --> push 08 on stack :0040B8EF 8A6D0C mov ch, byte ptr [ebp+0C] --> move the char in the HIGH bit of cx so we dont get the value 0000006E for the char n...ecx is 00006E00 here!!! :0040B8F2 5A pop edx -> pop 08 from stack edx is used as counter loop begin :0040B8F3 8BF0 mov esi, eax --> esi = eax :0040B8F5 33F1 xor esi, ecx --> esi = esi xor ecx :0040B8F7 66F7C60080 test si, 8000 --> if si < 8000 :0040B8FC 7407 je 0040B905 --> jump :0040B8FE 03C0 add eax, eax --> eax = eax + eax :0040B900 334510 xor eax, dword ptr [ebp+10] --> eax = eax XOR 1021 (remeber the push 1021 before the call) :0040B903 EB02 jmp 0040B907 :0040B905 D1E0 shl eax, 1 --> shift bits of eax 1 position left thats equal to eax = eax * 2 :0040B907 D1E1 shl ecx, 1 --> shift bits of ecx 1 position left thats equal to ecx = ecx * 2 :0040B909 4A dec edx --> edx = edx - 1 :0040B90A 75E7 jne 0040B8F3 --> if edx not zero next turn loop end :0040B90C 5E pop esi :0040B90D 5D pop ebp :0040B90E C3 ret end call: so we see its a little bit mor complicated here :( and i don't really know how to explain whats exactly going on but if you trace several times throug the code i will become more clear... so what do we know now: the first algo (which was easy) and its value wich was stored in ebx the second algo which takes each character and and each character was processed in a loop wich was everytime repeated 8 times. the value we need for the serial is stored in eax so here's the rest of the serial calculation: :0040B8C7 0FB7CB movzx ecx, bx -> move the LOW 16 bit of register ebx (our 1st value) to ecx :0040B8CA 0FB7C0 movzx eax, ax -> move the LOW 16 bit of register eax (our second value) to eax :0040B8CD 51 push ecx :0040B8CE 50 push eax :0040B8CF 68CCE74B00 push 004BE7CC :0040B8D4 FF750C push [ebp+0C] :0040B8D7 E828780800 call 00493104 -> call which pastes the 2 values together... :0040B8DC 83C410 add esp, 00000010 :0040B8DF 5F pop edi :0040B8E0 5E pop esi :0040B8E1 5B pop ebx :0040B8E2 5D pop ebp :0040B8E3 C3 ret so what we need to know is that not the komplete value of the registers were ised to build the serial, there are just the 16 LOWER bits of each register used so if EBX = 12345678 and EAX = ABCDEFG ecx becomes = 5678 an eax becomes DEFG the call at 40b87d pastes the values together "EAX"+"ECX" so we get DEFG1234 in our case with the name noesis we get value1 ebx = 00000675 lower 16bit = 0675 value2 eax = FF84387A lower 16bit = 387A so the valid serial for noesis would be 387A0675 so now the difficult part ;) to translate all this in c-code...to make a proper keygen... first i must say that im relativly new to coding so do not expect very good code ;) so if anybody would improve the code fell free to mail me your suggestions...or simply do it :) here's the source of my very basic keygen: ################## end of code ############################### #include #include //for getche() //vars char name[100]; char serial[9]; unsigned int value1 = 0; unsigned int value2 = 0; unsigned int esi = 0; //i named the variables like the registers... unsigned int eax = 0; // unsigned int ecx = 0; int z = 0; int main (void) { printf("....crc32's winzip 8.1 keygen....\n\n"); printf("name: "); gets(name); //get name //generating value 1 for (int i = 0; name[i] != 0; i++) { value1 = value1 + (name[i] * i); }; //generating value2... for (int x = 0; name[x] != 0; x++) //do while name[x] is not 00 { ecx = (name[x] << 8); //mov ch, byte ptr [ebp+0C] --> move the char in the HIGH bit of cx //same as "shl ecx,8" for (z = 0; z < 8; z++) //call at :0040B8B6 z ist the counter...prcesses 8 times per char of the name { esi = eax; //mov esi, eax esi = esi ^ ecx; //xor esi, ecx //test si, 8000 if ((esi & 0xFFFF) >= 0x8000) { eax = eax + eax; //add eax, eax eax = eax ^ 0x1021; //xor eax, dword ptr [ebp+10] ebp+10 is everytime 1021 } else { eax = eax << 1; //shl eax, 1 }; ecx = ecx << 1; //shl ecx, 1 }; }; value2 = eax + 0x63; // add eax,63 //building serial out of the values value2 = value2 & 0xFFFF; //we only need the low 16bit of the value2 sprintf(serial, "%0.4X%0.4X", value2, value1); //paste values together //printing the serial on screen... printf("serial: %s \n\n", serial); printf("press any key to exit...."); getche(); //waits for keystroke.... return 0; } ################## end of code ############################### so that's all. if there any questions/problems/mistakes in my source tut http://www.yellen.ch/neosis/ is the place to be... crc³² Body>