L ZZZZZZ RRRRR SSSSS L Z R R S L aaa Z aaa R R u u S L a Z a RRRRR u u SSSSS XX L aaaa Z aaaa R R u u S XXXX L a a Z a a R R u u S XXXXXX LLLLLLL aaaaa ZZZZZZZ aaaaa R R uuuuu SSSSSS XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXX XXXXXX XXXX proudly presents his 6.Cracking Tutorial (04.04.1999) XX Start Clean 1.2 I. Tools you need for my tutorial II. Cracking with a Disassembler (W32Dasm) III. Cracking with Debugger (W32Dasm or Soft Ice) III.1 Installing Soft-Ice III.2 Let's start cracking IV. Writing a patch in C++ V. Writing a Key Generator in C++ VI. BTW I Tools you need for my tutorial Win32Dasm 8.9 (get at http://Qserve.8m.com) (perhaps) Soft-Ice for Windows (get at http://Qserve.8m.com) A hex-editor, I prefer Hex Workshop 2.54 (get at www.bpsoft.com, change bytes 909A and 256B4 to 85; then you can register it with every serial#) Jaylock, the program to be cracked (mail me if you don't find it) Get a Windows-API reference. (Included in any Borland/Inprise programming languages) II. Cracking with Win32Dasm: This tutorial is (slightly) based on one of Quapla. This was one of the first tutorials I was able to understand. Greetings to you :) At first (as every time you crack something) we look at the target carefully. It seems that the only difference between the registered and the unregistered version is the nag-screen and the string "Shareware version" in the main window. We will later see that this is indeed everything. In the nag-screen is a button that makes a "Enter serial" window appear. So let's start cracking. To get rid of a nag-screen I usually do this: (+ORC would start crying if he reads that, because it is absolutely no ZEN cracking, but more I-DONT-WANT-TO- USE-MY-BRAIN-BRUTE-FORCE cracking). If you can't find a string appearing in the nag-screen in W32Dasm, start the program in W32Dasm debug mode and choose "Auto step over". Do this until the nag-screen appears. W32Dasm will stop at a CALL. Now breakpoint on this call (pay attention W32Dasm deletes the breakpoints when you start a new debug session) and enter it when it is reached trace into that call and choose "Auto step over" again until you come to the next call. Look if there's a conditional jump near before that call that leads beyond the call. Look what happens if you change the condition of the jump je->jne, jl->jb... If the nag-screen disappeared you got it, if the programm didn't start you have to trace into the call again and keep on doing this until you find the right conditional jump. For Start Clean you should see this after you did my method a few times: |:00401D9A | :00401EA0 8B442404 mov eax, dword ptr [esp+04] :00401EA4 A348724000 mov dword ptr [00407248], eax * Reference To: COMCTL32.InitCommonControls, Ord:0011h | :00401EA9 FF1508924000 Call dword ptr [00409208] :00401EAF E84CF1FFFF call 00401000 :00401EB4 A34C724000 mov dword ptr [0040724C], eax :00401EB9 85C0 test eax, eax :00401EBB 7524 jne 00401EE1 :00401EBD 6A00 push 00000000 :00401EBF A148724000 mov eax, dword ptr [00407248] :00401EC4 68F0274000 push 004027F0 :00401EC9 6A00 push 00000000 :00401ECB 6A6F push 0000006F :00401ECD 50 push eax * Reference To: USER32.DialogBoxParamA, Ord:008Ah | :00401ECE FF1510934000 Call dword ptr [00409310] :00401ED4 83F8FF cmp eax, FFFFFFFF :00401ED7 A148724000 mov eax, dword ptr [00407248] :00401EDC 7508 jne 00401EE6 :00401EDE 33C0 xor eax, eax :00401EE0 C3 ret The nag-screen appears at address :00401ECE. If we look some lines above, we see a conditonal jump that leads to somewhere beyond the nag-screen. Could this be the jump we are searching for ? Yes, it is. I tested it with breakpointing at the jump and running the program. When the jump is reached, I change the status of the zero-flag. (The jump is jne, so the condition is the status of the zero-flag.) When we now push the "Run" button, the program starts without nag-screen :) To make this permanent, we open the file in a hex- editor and change the jump at offset 12BBh and 12BC (look at the statusbar of the W32Dasm window) from 7524 (jne 00401EE1) to EB24 (jmp 00401EE1). Now the nag-screen will never appear again. The next problem: Start Clean still says "Shareware Version". So we search for "Shareware" in W32Dasm. Sadly, we don't find anything like that, but we find "Registered to:". That will be the string that appears in the registered version instead "Shareware Version". Ok, we should see this: * Referenced by a CALL at Address: |:00401F07 | :004013C0 81EC10030000 sub esp, 00000310 :004013C6 A064624000 mov al, byte ptr [00406264] :004013CB B93F000000 mov ecx, 0000003F :004013D0 56 push esi :004013D1 88442410 mov byte ptr [esp+10], al :004013D5 57 push edi :004013D6 8D7C2415 lea edi, dword ptr [esp+15] :004013DA 33C0 xor eax, eax :004013DC F3 repz :004013DD AB stosd :004013DE 66AB stosw :004013E0 AA stosb :004013E1 A064624000 mov al, byte ptr [00406264] :004013E6 8DBC2415010000 lea edi, dword ptr [esp+00000115] :004013ED B93F000000 mov ecx, 0000003F :004013F2 88842414010000 mov byte ptr [esp+00000114], al :004013F9 33C0 xor eax, eax :004013FB F3 repz :004013FC AB stosd :004013FD 66AB stosw * Possible StringData Ref from Data Obj ->"Software\Start Clean\Configuration" | :004013FF BE40624000 mov esi, 00406240 :00401404 B908000000 mov ecx, 00000008 :00401409 AA stosb :0040140A 8DBC2414020000 lea edi, dword ptr [esp+00000214] :00401411 F3 repz :00401412 A5 movsd :00401413 66A5 movsw :00401415 A4 movsb :00401416 8DBC2437020000 lea edi, dword ptr [esp+00000237] :0040141D B938000000 mov ecx, 00000038 :00401422 F3 repz :00401423 AB stosd :00401424 8D4C2414 lea ecx, dword ptr [esp+14] :00401428 6864624000 push 00406264 :0040142D AA stosb :0040142E 51 push ecx * Reference To: USER32.wsprintfA, Ord:0249h | :0040142F 8B35D4924000 mov esi, dword ptr [004092D4] :00401435 C744241404010000 mov [esp+14], 00000104 :0040143D FFD6 call esi :0040143F 8D4C2410 lea ecx, dword ptr [esp+10] :00401443 8D84241C020000 lea eax, dword ptr [esp+0000021C] :0040144A 83C408 add esp, 00000008 :0040144D 51 push ecx :0040144E 50 push eax :0040144F 6801000080 push 80000001 * Reference To: ADVAPI32.RegOpenKeyA, Ord:00D8h | :00401454 FF15F8914000 Call dword ptr [004091F8] :0040145A 8D4C240C lea ecx, dword ptr [esp+0C] :0040145E 8D542414 lea edx, dword ptr [esp+14] :00401462 8D442410 lea eax, dword ptr [esp+10] :00401466 51 push ecx :00401467 8B4C240C mov ecx, dword ptr [esp+0C] :0040146B 52 push edx :0040146C 50 push eax :0040146D 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"Name" | :0040146F 6838624000 push 00406238 :00401474 51 push ecx * Reference To: ADVAPI32.RegQueryValueExA, Ord:00E1h | :00401475 FF15F4914000 Call dword ptr [004091F4] :0040147B 8B4C2408 mov ecx, dword ptr [esp+08] :0040147F 51 push ecx * Reference To: ADVAPI32.RegCloseKey, Ord:00C2h | :00401480 FF15F0914000 Call dword ptr [004091F0] :00401486 8D4C2414 lea ecx, dword ptr [esp+14] :0040148A 8D942414010000 lea edx, dword ptr [esp+00000114] :00401491 51 push ecx * Possible StringData Ref from Data Obj ->"Registered to %s" | :00401492 6878624000 push 00406278 :00401497 52 push edx :00401498 FFD6 call esi :0040149A 8D8C2420010000 lea ecx, dword ptr [esp+00000120] :004014A1 8B942428030000 mov edx, dword ptr [esp+00000328] :004014A8 83C40C add esp, 0000000C Pretty long code snippet, isn't it ;-) What should we see here ? EVERYTHING. This snippet is called from location :00401F07, as you can see in the second line. The name is stored in Software\Start Clean\Configuration\Name what is a Windows Registry key. So fire up the Registry and enter something there. Ok, now we have to look at :00401F07 * Reference To: USER32.CreateDialogParamA, Ord:0048h | :00401EF2 FF1514934000 Call dword ptr [00409314] :00401EF8 833D4C72400000 cmp dword ptr [0040724C], 00000000 :00401EFF A384734000 mov dword ptr [00407384], eax :00401F04 7409 je 00401F0F :00401F06 50 push eax :00401F07 E8B4F4FFFF call 004013C0 :00401F0C 83C404 add esp, 00000004 We see a conditional jump in front of our call. When we set a breapoint on it and step through the code, we see that the jump condition is met when we have a unregistered version. of Start Clean. When we change the zero flag status (remember: je has same condition as jne) the call is executed and we see "Registered to: your-name" in the window :) To make this permanent, we open the file in a hex-editor and change the 7409 (je...) at offset 1305h in 9090 (do nothing). For those guys that don't like no-oping: Change the cmp dword ptr [0040724C], 00000000 to cmp dword ptr [0040724C], 00000002 DON'T cmp dword ptr [0040724C], 00000001 , because that is the flag "Registered" and the Shareware string would appear again, when we register Start Clean correctly. That's all for the Disassembler part, now go to the Debugger part. IV. Cracking with a Debugger (Soft-Ice for Windows/W32Dasm) IV.1. Installing Soft-Ice Having installed Soft-Ice. There are several difficulties to master. First you have to load Soft-Ice before you start Windows, second you have to choose your graphics driver and third you have to edit the file 'Winice.dat' in the Soft-Ice directory. For the first difficulty, I would install a software-bootmenu in the autoexec.bat and config.sys file. Example: @Echo Off AUTOEXEC.BAT: REM Here put in your normal stuff, like drivers :SICE C:\SIW95\WINICE.EXE goto common :NORM goto common :common End of file The lines starting with ':' are places to jump to, when a . The 'goto'-line are the jumps. CONFIG.SYS [menu] menuitem SICE,Soft-Ice menuitem NORM,Normal Mode menudefault NORM,5 [SICE] [NORM] [common] Put your normal stuff here. End of File Next difficulty: Start VSetup in the SI directory (Soft-Ice=SICE=SI). If your video-card isn't mentioned here choose VGA, because SI won't work when you choose different. If you need to choose VGA you need a hardware-bootmenu, too, because you have to switch between the video drivers when you start Windows in normal mode or in SI-Node. You can do this in the Windows Control Panel/System. Copy your hardware profile and disable your video card in the new profile. Save. Next time you start windows you can choose the hardware and the software you wish to load. If you disabled your video card and you start Windows-SI there are some error messages to choose graphic drivers. Just [ESC] them away. Third difficulty: No real difficulty. Load the file winice.dat in a text-editor and remove the ; from the lines ;exp=c:\windows\system\kernel32.dll ;exp=c:\windows\system\user32.dll ;exp=c:\windows\system\gdi32.dll standing below the lines ; ***** Examples of export symbols that can be included for Chicago ***** ; Change the path to the appropriate drive and directory IV.2 Let's start cracking Now restart your computer with the SI-Configuration. In Windows press [CTRL]+[D] to see if you can load SI. If nothing happens, you forgot to load SI in your Autoexec.bat, if the screen is black, and you have already chosen video drivers, you have a little problem. Try to fix it with the help of the WWW. I will crack with W32Dasm debug mode, because I am on holidays and forgot SICE :(. But that doesn't matter it is the same. If you try to register Start Clean, a message "Incorrect Code" should appear. Search for this string in your W32Dasm deadlisting. You should see this: * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004027A3(C) | :004027C1 6A00 push 00000000 :004027C3 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"Incorrect code!" | :004027C5 68AC634000 push 004063AC :004027CA 56 push esi * Reference To: USER32.MessageBoxA, Ord:0188h | :004027CB FF1534934000 Call dword ptr [00409334] :004027D1 B801000000 mov eax, 00000001 :004027D6 5E pop esi :004027D7 C21000 ret 0010 We quickly see, that this snippet is called (or this time better jumped) from :004027A3. So let's have a look at it. :00402794 8B742408 mov esi, dword ptr [esp+08] :00402798 56 push esi :00402799 E8B2E9FFFF call 00401150 :0040279E 83C404 add esp, 00000004 :004027A1 85C0 test eax, eax :004027A3 741C je 004027C1 :004027A5 C7054C72400001000000 mov dword ptr [0040724C], 00000001 :004027AF 6A01 push 00000001 :004027B1 56 push esi * Reference To: USER32.EndDialog, Ord:00ADh | :004027B2 FF153C934000 Call dword ptr [0040933C] :004027B8 B801000000 mov eax, 00000001 :004027BD 5E pop esi :004027BE C21000 ret 0010 We don't have to understand exactly what's going on: In the call at :00402799 the valid serial is calculated and compared with our serial. The je at :004027A3 means: If the two serials are not the same, then jump, else "Thank you for registering". So we have to get the correct serial. Look what happens in the Call: * Referenced by a CALL at Address: |:00402799 | :00401150 81EC0C020000 sub esp, 0000020C :00401156 A064624000 mov al, byte ptr [00406264] :0040115B B93F000000 mov ecx, 0000003F :00401160 56 push esi :00401161 8844240C mov byte ptr [esp+0C], al :00401165 57 push edi :00401166 8D7C2411 lea edi, dword ptr [esp+11] :0040116A 33C0 xor eax, eax :0040116C F3 repz :0040116D AB stosd :0040116E 66AB stosw * Possible StringData Ref from Data Obj ->"Software\Start Clean\Configuration" | :00401170 BE40624000 mov esi, 00406240 :00401175 B908000000 mov ecx, 00000008 :0040117A AA stosb :0040117B 8DBC2410010000 lea edi, dword ptr [esp+00000110] :00401182 F3 repz :00401183 A5 movsd :00401184 66A5 movsw :00401186 B938000000 mov ecx, 00000038 :0040118B 6800010000 push 00000100 :00401190 A4 movsb :00401191 8DBC2437010000 lea edi, dword ptr [esp+00000137] :00401198 6830614000 push 00406130 :0040119D F3 repz :0040119E AB stosd :0040119F AA stosb :004011A0 8BBC2420020000 mov edi, dword ptr [esp+00000220] :004011A7 6805040000 push 00000405 :004011AC 57 push edi * Reference To: USER32.GetDlgItemTextA, Ord:00EDh | :004011AD 8B35D8924000 mov esi, dword ptr [004092D8] :004011B3 FFD6 call esi :004011B5 8D442410 lea eax, dword ptr [esp+10] :004011B9 6800010000 push 00000100 :004011BE 50 push eax :004011BF 6806040000 push 00000406 :004011C4 57 push edi :004011C5 FFD6 call esi :004011C7 6830604000 push 00406030 :004011CC 6830614000 push 00406130 :004011D1 E8AA000000 call 00401280 :004011D6 8D442418 lea eax, dword ptr [esp+18] :004011DA 83C408 add esp, 00000008 :004011DD 50 push eax :004011DE 6830604000 push 00406030 * Reference To: KERNEL32.lstrcmpA, Ord:0269h | :004011E3 FF1520924000 Call dword ptr [00409220] :004011E9 85C0 test eax, eax :004011EB 0F8580000000 jne 00401271 :004011F1 8D44240C lea eax, dword ptr [esp+0C] :004011F5 8D4C2408 lea ecx, dword ptr [esp+08] :004011F9 50 push eax :004011FA 51 push ecx :004011FB 8D942418010000 lea edx, dword ptr [esp+00000118] :00401202 6A00 push 00000000 :00401204 683F000F00 push 000F003F :00401209 6A00 push 00000000 Now it is indeed better to use W32Dasm. Just debug it with API details enabled. At :004011B3 the name is taken from the input field, as you can easily see from the appearing window. At :004011C5 the entered serial is taken. Then a call follows (where the real serial is obviously calculated). At :004011E3 the length of two strings are compared. One of them is the serial, you entered, the other one is - you got it :) - the real serial. This time getting the serial with W32Dasm was indeed easier than getting it with SICE. To get it with SICE, you have to step one line beyond the calculation call and enter "d eax" because the valid serial is stored in eax in the call at :004011D1. IV. Writing a patch in C++ You can easily write a patch for the nag-screen and the "Shareware" string. Here is the base for one in C++. FILE *handle; handle = fopen("Filename.exe", "r+b"); if (handle==0) { cprintf("No File Filename.exe found"); } else { fseek(handle, HERE OFFSET IN DECIMAL, SEEK_SET); fprintf(handle,"%c",HERE NEW VALUE IN DECIMAL); fclose(handle); } V. Writing a key generator This part is the main reason why I wrote this tutorial. In Quapla's tutorial, this part was missing although it is a good example for an easy (but not too easy) key generator for newbies. When we enter the code calculation call at :004011D1 we see. I will comment every that is important for a keygen. * Referenced by a CALL at Addresses: |:0040111F , :004011D1 | :00401280 81EC00010000 sub esp, 00000100 :00401286 A064624000 mov al, byte ptr [00406264] :0040128B 88442400 mov byte ptr [esp], al :0040128F 53 push ebx :00401290 56 push esi :00401291 33C0 xor eax, eax :00401293 57 push edi :00401294 B93F000000 mov ecx, 0000003F :00401299 8D7C240D lea edi, dword ptr [esp+0D] :0040129D 55 push ebp :0040129E F3 repz :0040129F AB stosd :004012A0 66AB stosw :004012A2 BD6A000000 mov ebp, 0000006A ;;EBP IS INITIALIZED WITH 6A = 106 :004012A7 6864624000 push 00406264 :004012AC AA stosb :004012AD 8BB4241C010000 mov esi, dword ptr [esp+0000011C] :004012B4 56 push esi * Reference To: USER32.wsprintfA, Ord:0249h | :004012B5 FF15D4924000 Call dword ptr [004092D4] :004012BB 8B9C241C010000 mov ebx, dword ptr [esp+0000011C] :004012C2 83C408 add esp, 00000008 :004012C5 8BC3 mov eax, ebx * Reference To: USER32.CharNextA, Ord:001Eh | :004012C7 8B3DDC924000 mov edi, dword ptr [004092DC] :004012CD 803B00 cmp byte ptr [ebx], 00 :004012D0 740F je 004012E1 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004012DF(C) | IN THIS LOOP (FROM 1 TO LENGTH(NAME)), THE FIRST PART IS CALCULATED :004012D2 0FBE08 movsx ecx, byte ptr [eax] ;; ECX = 1./2... CHAR :004012D5 50 push eax :004012D6 8D6C4D00 lea ebp, dword ptr [ebp+2*ecx] ;;EBP = EBP + (2 * ECX) :004012DA FFD7 call edi :004012DC 803800 cmp byte ptr [eax], 00 ;;WAS THAT THE LAST CHAR ? :004012DF 75F1 jne 004012D2 ;;IF NOT THEN LOOP END LOOP 1. PART OF SERIAL = DECIMAL(EBP) * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004012D0(C) | :004012E1 8D442410 lea eax, dword ptr [esp+10] :004012E5 55 push ebp * Possible StringData Ref from Data Obj ->"%d-" ;; HERE YOU SEE THAT THE FIRST ;; PART IS DEC(EBP)- | :004012E6 6874624000 push 00406274 :004012EB 50 push eax * Reference To: USER32.wsprintfA, Ord:0249h | :004012EC FF15D4924000 Call dword ptr [004092D4] :004012F2 8D44241C lea eax, dword ptr [esp+1C] :004012F6 83C40C add esp, 0000000C :004012F9 50 push eax :004012FA 56 push esi * Reference To: KERNEL32.lstrcatA, Ord:0266h | :004012FB FF153C924000 Call dword ptr [0040923C] :00401301 8BC3 mov eax, ebx :00401303 803B00 cmp byte ptr [ebx], 00 :00401306 7412 je 0040131A * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401318(C) | IN THIS LOOP (FROM 1 TO LENGTH(NAME)), THE SECOND PART IS CALCULATED (EBP=still valid) :00401308 0FBE08 movsx ecx, byte ptr [eax] ;; ECX=1./2./3... char :0040130B 03C9 add ecx, ecx ;; ECX is doubled :0040130D 50 push eax :0040130E 8D14C9 lea edx, dword ptr [ecx+8*ecx] ;; EDX = 9*ECX :00401311 03EA add ebp, edx ;; EBP = EBP + EDX :00401313 FFD7 call edi :00401315 803800 cmp byte ptr [eax], 00 ;; LAST CHAR ? :00401318 75EE jne 00401308 ;; NO THEN LOOP END LOOP 2. PART OF SERIAL = DECIMAL(EBP) + "-"; SO WE HAVE PART1-PART2- * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401306(C) | :0040131A 8D442410 lea eax, dword ptr [esp+10] :0040131E 55 push ebp * Possible StringData Ref from Data Obj ->"%d-" | :0040131F 6874624000 push 00406274 :00401324 50 push eax * Reference To: USER32.wsprintfA, Ord:0249h | :00401325 FF15D4924000 Call dword ptr [004092D4] :0040132B 8D44241C lea eax, dword ptr [esp+1C] :0040132F 83C40C add esp, 0000000C :00401332 50 push eax :00401333 56 push esi * Reference To: KERNEL32.lstrcatA, Ord:0266h | :00401334 FF153C924000 Call dword ptr [0040923C] :0040133A 8BC3 mov eax, ebx :0040133C 803B00 cmp byte ptr [ebx], 00 :0040133F 7418 je 00401359 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401357(C) | IN THIS LOOP (FROM 1 TO LENGTH(NAME)), THE THIRD PART IS CALCULATED (EBP=still valid) :00401341 0FBE08 movsx ecx, byte ptr [eax] ;; ECX = 1./2./3... CHAR :00401344 50 push eax :00401345 8D2C89 lea ebp, dword ptr [ecx+4*ecx] ;; EBP = 5 * ECX :00401348 8D0C69 lea ecx, dword ptr [ecx+2*ebp] ;; ECX = ECX + (2 * EBP) :0040134B 8D2C4D01000000 lea ebp, dword ptr [2*ecx+00000001] ;; EBP = (2 * ECX) + 1 :00401352 FFD7 call edi :00401354 803800 cmp byte ptr [eax], 00 ;; LAST CHAR ? :00401357 75E8 jne 00401341 ;; IF NOT LOOP END LOOP 3. PART OF SERIAL = DECIMAL(EBP) + "-"; SO WE HAVE PART1-PART2-PART3- * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040133F(C) | :00401359 8D442410 lea eax, dword ptr [esp+10] :0040135D 55 push ebp * Possible StringData Ref from Data Obj ->"%d-" | :0040135E 6874624000 push 00406274 :00401363 50 push eax * Reference To: USER32.wsprintfA, Ord:0249h | :00401364 FF15D4924000 Call dword ptr [004092D4] :0040136A 8D44241C lea eax, dword ptr [esp+1C] :0040136E 83C40C add esp, 0000000C :00401371 50 push eax :00401372 56 push esi * Reference To: KERNEL32.lstrcatA, Ord:0266h | :00401373 FF153C924000 Call dword ptr [0040923C] :00401379 8BC3 mov eax, ebx :0040137B 803B00 cmp byte ptr [ebx], 00 :0040137E 7412 je 00401392 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401390(C) | IN THIS LOOP (FROM 1 TO LENGTH(NAME)), THE LAST PART IS CALCULATED (EBP=still valid) :00401380 0FBE08 movsx ecx, byte ptr [eax] ;; ECX = 1./2./3... CHAR :00401383 50 push eax :00401384 8D2C8D1D000000 lea ebp, dword ptr [4*ecx+0000001D] ;;EBP = (4 * ECX) + 29 :0040138B FFD7 call edi :0040138D 803800 cmp byte ptr [eax], 00 ;; LAST CHAR ? :00401390 75EE jne 00401380 ;; IF NOT LOOP END LOOP 4. PART OF SERIAL = DECIMAL(EBP); SO WE HAVE PART1-PART2-PART3-PART4 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040137E(C) | :00401392 8D442410 lea eax, dword ptr [esp+10] :00401396 55 push ebp * Possible StringData Ref from Data Obj ->"%d" | :00401397 6870624000 push 00406270 :0040139C 50 push eax * Reference To: USER32.wsprintfA, Ord:0249h | :0040139D FF15D4924000 Call dword ptr [004092D4] :004013A3 8D44241C lea eax, dword ptr [esp+1C] :004013A7 83C40C add esp, 0000000C :004013AA 50 push eax :004013AB 56 push esi * Reference To: KERNEL32.lstrcatA, Ord:0266h | :004013AC FF153C924000 Call dword ptr [0040923C] :004013B2 5D pop ebp :004013B3 5F pop edi :004013B4 5E pop esi :004013B5 5B pop ebx :004013B6 81C400010000 add esp, 00000100 :004013BC C3 ret Now you should be able to write a keygen. The source for mine (C++ Builder) is: Edit1 is the name, Edit2 the serial. int ebp; int ecx; String serial; ebp=106; for (int i=1;iText.Length()+1;i++) { ecx = Edit1->Text[i]; ebp = ebp + (2*ecx); } serial = IntToStr(ebp)+"-"; for (int i=1;iText.Length()+1;i++) { ecx = Edit1->Text[i]; ecx = ecx * 2; ebp = ebp + (9*ecx); } serial = serial + IntToStr(ebp)+"-"; for (int i=1;iText.Length()+1;i++) { ecx = Edit1->Text[i]; ebp=ecx*5; ecx = ecx + (2 * ebp); ebp = (2*ecx)+1; } serial = serial + IntToStr(ebp)+"-"; for (int i=1;iText.Length()+1;i++) { ecx = Edit1->Text[i]; ebp=(4*ecx)+29; } serial = serial + IntToStr(ebp); Edit2->Text=serial; VI. BTW Hope my tutorial was helpful for you and see you again in my next tutorial. Greets to: Fravia+, tKC, ED!SON, Moral Insanity, The Sandman, Eternal Bliss, DaVinci and all [hf] members All Tutorials by LaZaRuS [hf] #| date | name |version|W32Dasm|Soft-Ice|kind of crack | -|--------|------------------|-------|-------|--------|-------------------------| 1|20.01.99|Jaylock |1,0,0,1| (X) | (X) |serial# | 2|31.01.99|Goldwave |4.02 | (X) | (X) |serial#,nag-screens | 3|28.03.99|AxMan |3.00 | (X) | (X) |serial#,remove date-limit| 4|29.03.99|C++Builder Strings| | (X) | (X) |how to find strings in | | | | | | |C++ Builder that are not | | | | | | |hardcoded | 5|29.03.99|Better Protection | | | |How to protect shareware | | | | | | |better against crackers | 6|04.04.99|Start Clean |1.2 | (X) | (X) |nag-screen/serial/keygen | LaZaRuS [hf] Visit Hellforge at http://members.xoom.com/hell_crack for more tutorials and high quality cracking links. If you want to mail me: lazarus666@gnwmail.com