Cracking tutorial for advanced #1 - by acetone^drn KEYGENNING GODEZIP 7.0 TARGET downloaded from http://www.winfiles.com TOOLS SoftICE W32Dasm HIEW (preferred hex editor) INTRODUCTION This tutorial will show you how to write a keygen for GodeZIP 7.0. It assumes that you know the basics of cracking. First, we need to find the key generation routine in the program. Set breakpoints on GetDlgItemTextA and GetWindowTextA and enter a dummy licence and code: Licence: 12345678 Code: ABCDEFGH Click OK and SoftICE should break, twice for GetDlgItemTextA and once for GetWindowTextA. It should be fairly obvious to see that GetDlgItemTextA is the right breakpoint to set, so clear the GetWindowTextA one and try again. NOTE: *DON'T* close the "bad licence code" message boxes, as the program will quit after 4 of these. You can still enter codes without closing the boxes, so just leave them alone for now :) So, when SoftICE breaks, press F5 once and it'll break again. Now press F12 to go to the calling code and you'll be looking at the following (irrelevant bits snipped): :004031E5 E816F5FFFF call 00402700 :004031EA 66A382044500 mov word ptr [00450482], ax :004031F0 6A01 push 00000001 :004031F2 6804024500 push 00450204 :004031F7 6817024500 push 00450217 :004031FC E84FF7FFFF call 00402950 :00403201 83C40C add esp, 0000000C :00403204 85C0 test eax, eax :00403206 7444 je 0040324C :00403208 C7050001450001000000 mov dword ptr [00450100], 00000001 * Possible StringData Ref from Data Obj ->"This is an Unregistered Version" | :00403212 BF04014500 mov edi, 00450104 * Possible StringData Ref from Code Obj ->"Registered Version" | :00403217 BE3D2C4000 mov esi, 00402C3D :0040321C FC cld :0040321D B909000000 mov ecx, 00000009 :00403222 F3 repz :00403223 A5 movsd :00403224 A4 movsb :00403225 6804024500 push 00450204 * Possible StringData Ref from Data Obj ->"This is an Unregistered Version" | :0040322A 6804014500 push 00450104 :0040322F E844000400 call 00443278 * Possible StringData Ref from Code Obj ->"GodeZIP for win32" | :00403234 6823314000 push 00403123 :00403239 FF352CE64800 push dword ptr [0048E62C] * Reference To: USER32.SetWindowTextA, Ord:0202h | :0040323F E820BE0400 Call 0044F064 :00403244 83C408 add esp, 00000008 :00403247 EB42 jmp 0040328B :00403249 8D7600 lea esi, dword ptr [esi+00] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00403206(C) | :0040324C C7050001450000000000 mov dword ptr [00450100], 00000000 Notice anything? Memory location 450100 can be two values here, either 0 or 1 -- obviously, this is the location that tells the program whether we're registered. There's only one conditional jump in this snippet, at 403206, so the first call before it -- 402950 -- is almost certainly the one that decides if we're registered or not. Let's take a look at it: :00402950 55 push ebp :00402951 89E5 mov ebp, esp :00402953 57 push edi :00402954 56 push esi :00402955 53 push ebx :00402956 8B5D08 mov ebx, dword ptr [ebp+08] :00402959 BE04024500 mov esi, 00450204 * Possible StringData Ref from Code Obj ->"G1111111" | :0040295E BF15274000 mov edi, 00402715 :00402963 B809000000 mov eax, 00000009 :00402968 89C1 mov ecx, eax :0040296A FC cld :0040296B A800 test al, 00 :0040296D F3 repz :0040296E A6 cmpsb :0040296F 0F843D020000 je 00402BB2 :00402975 BE04024500 mov esi, 00450204 * Possible StringData Ref from Code Obj ->"G1234567" | :0040297A BF1E274000 mov edi, 0040271E :0040297F B909000000 mov ecx, 00000009 :00402984 FC cld :00402985 A800 test al, 00 :00402987 F3 repz :00402988 A6 cmpsb :00402989 0F8423020000 je 00402BB2 :0040298F BE04024500 mov esi, 00450204 * Possible StringData Ref from Code Obj ->"G!PC-98!" | :00402994 BF27274000 mov edi, 00402727 :00402999 B909000000 mov ecx, 00000009 :0040299E FC cld :0040299F A800 test al, 00 :004029A1 F3 repz :004029A2 A6 cmpsb :004029A3 0F8409020000 je 00402BB2 Okay, so what's happening here? Well, if your licence matches any of these three, then it jumps to 402BB2 -- which is not a good thing, as the code beginning there clears eax and returns (remember, eax is 0 if we don't have a correct code). I assume these are known pirated serials the third probably being one that Phrozen Crew released. Anyway, let's move on: :004029A9 E85AFFFFFF call 00402908 :004029AE 85C0 test eax, eax :004029B0 0F8501020000 jne 00402BB7 :004029B6 803B44 cmp byte ptr [ebx], 44 :004029B9 0F94C0 sete al :004029BC 0FB6D0 movzx edx, al :004029BF 8B4D0C mov ecx, dword ptr [ebp+0C] :004029C2 803947 cmp byte ptr [ecx], 47 :004029C5 0F94C0 sete al :004029C8 25FF000000 and eax, 000000FF :004029CD 85D0 test eax, edx :004029CF 0F84D7010000 je 00402BAC :004029D5 E82EFFFFFF call 00402908 :004029DA 85C0 test eax, eax :004029DC 0F85CA010000 jne 00402BAC What's going on here? To tell you the truth, I'm not 100% sure. I couldn't figure out what the two calls to 402908 are for, but I never managed to make it fail those checks so they're probably not worth worrying about. The bit we're interested in are 4029B6 to 4029CF: this compares the first characters of your code and licence to D and G respectively (these just happen to the author's initials :). If they don't match, we exit early -- so we now know that our reg info takes the following format: Licence: Gxxxxxxx Code: Dxxxxxxx Okay, now we get to the meat of the serial generation routine: :004029E2 0FBE5306 movsx edx, byte ptr [ebx+06] :004029E6 8B4D0C mov ecx, dword ptr [ebp+0C] :004029E9 0FBE4101 movsx eax, byte ptr [ecx+01] :004029ED 83C01E add eax, 0000001E :004029F0 39C2 cmp edx, eax :004029F2 0F85B4010000 jne 00402BAC Now, ebx contains our code and ecx contains our licence. What's happening here is that the 6th char of our code is transferred to edx, and the 1st char of our licence is transferred to eax -- the 1st char of our code and licence being the 1st char after the D or G: Char: 01234567 Licence: Gxxxxxxx Code: Dxxxxxxx 0x1E is then added to eax and compared with edx. Say the first char of our licence is 5. This has the ASCII value 0x35. We add 0x1E to get 0x53; this corresponds to the character S. Therefore, the 6th char of our code is S. Get the picture? By reading through the rest of the code and noting down which characters are compared, we get a table something like this: Code Licence Number position position added 1 6 0x15 2 3 0x1F 3 5 0x13 4 7 0x1D 5 2 0x17 6 1 0x1E 7 4 0x11 8 8 0x1E 9 13 0x1C 10 14 0x18 11 11 0x15 12 10 0x16 13 8 0x1B 14 7 0x1D (Note that there are 14 chars to our code/licence, not the 7 that I've been using above). Now all we need to do is convert this to a program. Provided below is C source for a keygen. NOTE 1: GodeZIP treats the 0th char of the licence as the G. However, because we're only asking for the 7 other chars, our 0th char is what GodeZIP would call the 1st. Hence, we need to subtract 1 from each licence position. NOTE 2: The upper limit on licence characters needn't be as low as it is; I just set it like that because I didn't think any other chars would be useful. Feel free to experiment in your own keygens :) ---{code start}--- #include #include int main() { char licence[14]; char code[14]; int loopvar; /* temp loop variable */ /* print message and instructions*/ printf( "\nGodeZIP 3.0 keymaker by Acetone^DRN\n" ); printf( "Enter a 14-character licence (0-9, A-Z, !\"#$%&'()*+,-. /:;<>=? only): " ); /* get user's input */ gets(licence); /* if wrong number of chars then complain, pause, and quit */ if (strlen(licence) != 14) { printf("\nYou must enter 14 characters!\n"); getch(); return(0); } /* check each char and if not in acceptable range then complain, pause, and quit */ for ( loopvar = 0 ; loopvar < 14 ; loopvar++ ) { if ( licence[loopvar] < 30 || licence[loopvar] > 90 ) { printf("\nInvalid character!\n"); getch(); return(0); } } /* print user's licence */ printf("\nLicence: G%s", licence); /* calculate and print code */ code[0] = 'D'; code[1] = licence[5] + 0x15; code[2] = licence[2] + 0x1F; code[3] = licence[4] + 0x13; code[4] = licence[6] + 0x1D; code[5] = licence[1] + 0x17; code[6] = licence[0] + 0x1E; code[7] = licence[3] + 0x11; code[8] = licence[7] + 0x1E; code[9] = licence[12] + 0x1C; code[10] = licence[13] + 0x18; code[11] = licence[10] + 0x15; code[12] = licence[11] + 0x16; code[13] = licence[9] + 0x1B; code[14] = licence[8] + 0x1D; printf("\n Code: %s", code); /* wait for a keypress then quit */ getch(); return(0); }---{code end}--- Well, like I said, a fairly simple keygen but we all have to start somewhere, right? :) If you've any comments, questions etc. give me a shout at acetone@postmaster.co.uk. -acetone^drn