Welcome to Cracking Tutorial #17! Ah here we come again.. nothing is gonna stop us now! :) In this tutor we'll teach you everything more about W32Dasm, SoftIce, SmartCheck, and MORE! Without knowledge, no power! ;) Warning, this tutorial is a real mother! *grin* Ok, let's rave! You'll need the following tools: (I use these tools, I assume you'll use 'em, but it doesn't mean that you'll need to use all those tools, so be sure to get them handy for the examples in this tutorial!) SoftIce 3.24 Beta W32Dasm 8.93 Hacker's View 6.03 SmartCheck 6.01 TASM 5.00 Windows Commander 3.53 (I use it coz of easier to multitask) Don't ask me where to download all these tools since you had a chance to get them when you used my older tutorials. Here are a few good cracking sites where you can grab tools from: http://surf.to/HarvestR http://catalyst.intur.net/~Iczelion/tools.html or ask any crackers to get you these tools! Are you ready?! OK! ;) PART 1: ~~~~~~ [---------------------------------------------------------------------] Trinity's Tutor #1 Hi, welcome to my first tutor. It is written by a newbie, for newbies, so i hope it wont be too hard to understand. For this tutor you will need Windows Disassembler 8.93 a Hex Editor, (I use Ultra Edit 6.00a) and the program we will be werking on, Winzip 7.0 SR1. As there are infinate Keygens to register Winzip, I will teach you how to patch Winzip so that when you delete the Help files it still runs. - I dont know if you have noticed, but the help files must be in the same directory for Winzip for it to run. Ok, lets go... ------------------------------------------------------------------------ *Step 1. Install Winzip ... Well duh :-) *Step 2. Go to your Winzip directory and delete winzip.hlp. Try to run winzip... you get this message: "Cannot start WinZIP! Required file winzip.hlp was not found in the same folder as WinZIP" *Step 3. Fire up windows disassembler, and open winzip32.exe *Step 4. Ok, now you know that Winzip checks to see if the help files are there, and if not, it gives an error. So you go to the "REFS" toolbar and select "String Data references" (SDR) which is a list of all the message boxes Winzip can create. Scroll Down that list until you find one similar to the one that we were given: "Cannot start WinZIP! Required........" *Step 5. Double click on the error message and W32dasm will jump to the place in winzip32.exe where this error message is created. Close the SDR window. *Step 6. Now all you see is a list of assembly code. I don't understant it all either, but you can kinda get the picture if you have read all the tutorials available. I highly recommend all 11 of The Keyboard Caper's tutors, and also those of Bullet. *Step 7. Ok, where you have landed is inside the error message box, so if you scroll up a bit you see this: :0043E8CA E800BEFDFF call 0041A6CF :0043E8CF 84C0 test al, al <- check if help files are there :0043E8D1 59 pop ecx :0043E8D2 7534 jne 0043E90B <- jump over error msg box if they are *Step 8. So you are thinking, "It checks to see if the help files are NOT there, and if this condition is true, it will create the error message box, and if the condition is not true, it will jump over the bit of code that creates the error box." Well, thats what I was thinking anyway. So if we can only change the program so that it jumps over the code that creates the error mesage, regardless of whether the help file is there or not... Well we CAN! *Step 9. And here is where the Hex Editor comes in. Most people mention offsets and other stuff when talking about where to change the code, but I just write down all the code around where i want to patch, which i get from Windows Disassembler, and search for that in the Hex Editor. Before you go any furthur, you must know a bit about assembly and hex... 75 xx : stands for JNE or Jump If Not Equal To (Where xx is a value, or an address) 74 xx : stands for JE or Jump If Equal To EB xx : stands for JMP or Jump regardless So you see in our case we have a 7537 so if we change it to EB37 it should do the trick. *Step 10. Open up your HEX editor, and do a search for the string of HEX we noted from W32dasm (in case you forgot it is "e800befdff84c0597537"). Now this is where you can change the bytes. Change the 75 to an EB and click save. Try and Run Winzip and voila, it runs first time. Important notes: I decided to use Winzip as an example, because it i a utility that everyone has (and if you dont you should) and also that the method of cracking it is the same as most serial protections: Comparing a good with a bad serial, and jumping to the error message box. Another thing to note is that when you are in your HEX editor searching for the string, be sure to search the whole file, just in caes the string appears more than once. If this is the case, you will need to extend the length of the string, by including more of the code from W32Dasm. If anyone (including other crackers) have any comments on this piece of work, be it good or bad, please let me know what you think of this, and if I have got any points wrong. You can e-mail me at the address below, or find me on IRC EFnet in #Cracks. Anyway... Good luck and most importantly, dont give up! Seeya in tutor #2, where i will teach you how I make my patches :) ------------------------------------------------------------------------ mailto:trinity@thegolfcourse.com ------------------------------------------------------------------------ This was made by Trinity. On the 11th January '99 Copyright c Trinity 1999 PART 2: ~~~~~~ ==================================== How to get a serial for Winamp 2.10 ==================================== In this tutorial I'll show u, how to get a valid serial key for Winamp v2.10. Used Tools ========== SoftICE v3.2 (if u don't have it, get it here: http://surf.to/HavestR) Target ====== Winamp v2.10 (http://www.winamp.com) Get the key =========== The first is to install Winamp and start it. Take look at the Registration window (About/Shareware/Enter Rigistration Info). It seems to be a simple Name/Number-check - but the 'ok'-button is disabled! So we can't use our standard SoftICE-Breakpoints. Let us think what function we use for example in VC++. It could be the 'EnableWindow'. We can find out if its right: 1) Go to the Winamp registration menu (where u can give name and key) 2) switch into SI (Ctrl+d) 3) set a breakpoint on execution for 'EnableWindow' (bpx EnableWindow) 4) switch back to windows (Ctrl+d) 5) type ya name and u are back in SI, so that's the right breakpoint 6) disable the bp (bd 0) and switch back to winamp 7) type ya name and give as serial something like this: 123456789 but without the 9. 8) go into SI, enable the BP (be 0), jump back to winamp and type the 9 9) u r back in SI in the EnableWindow function. Press F11 to get out of the function and look what's goin' on before the call (Ctrl+Up) There is a GetDlgItem-call and before that there are two cmps (among other things). The 'cmp eax, esi' looks interesting. 10) so set a Breakpoint at this line (switch to the code window using F6, move the cursor at the line and press F9 to set a bpx). 11) now switch back to the winamp-registration window and press the 9 u r in SI again at the 'cmp eax, esi' 12) to find out what the decimal value of ESI is type '? esi' and ya (wrong) serial appears. the right serial could be stored in eax, u conclude. 13) type '? eax' and the decimal value is the right key for ya name. : ) Greetings for any remarks write me the_nitehawk@hotmail.com PART 3: ~~~~~~ Keygen Tutorial 1 Yes123 '99 ---[ WARNING ]-------------------------------------------------------------------------------- This Tutorial is for education purpose only. I wrote it to allow you to understand how are coded some protections schemes in software. I didn't make it to allow you to use the target program without paying the author. If you plan to use these programs regularly, please remeber to send your $ to the authors, don't be a outlaw, and over all, don't be a LAMER ! ---[ INTRO ]---------------------------------------------------------------------------------- target: A Day in the Life v1.0 where : http://www.cartoonlogic.com Tools : SoftIce for Win9x v3.24 W32Dasm v8.9 Programming Language (C, Pascal, asm, anyone you want, I'll use our old C) This is my first tutorial in english. I hope my bad spelling won't make this text too much hard to understand for you. :) I'll try to teach you how to make a key generator for a program. The way i teach will be based more on reverse enginerring (instead of only cracking the program, we try to fully understand the whole key-generating algorithm), some newbies maybe having problem of reading this. I'll assume you know the following: - basic use of SoftIce - asm instructions (at least the ones used for cracking) ---[ TUTORIAL ]------------------------------------------------------------------------------- At first, launch SoftIce (assuming you know the basics, and how to setup this Numega's nice tool). Then launch our target, ADITL1.0.exe! Go to the help menu and select the register. Well, we'll try to find how this protection is running. Let's enter anything as a name and random digits as a serial. Don't click the OK button yet, but hit CTRL & D to bring up SoftICe. We'll define a breakpoint, using the classical BPX hmemcpy(it works for most program.) Hit CTRL-D again to go back to the program. Click OK and... SoftICe pop up. Keep pressing F12 until you get back to the traget program code. Trace a few lines down you will be in: :004848A7 E8D4B1F9FF call 0041FA80 ;start here :004848AC 8B45F8 mov eax, dword ptr [ebp-08] ;eax=pointer to name :004848AF E8C4F3F7FF call 00403C78 ;return eax=length of name :004848B4 8BF0 mov esi, eax ;esi=length of name :004848B6 85F6 test esi, esi ;test length :004848B8 7E28 jle 004848E2 ;if no name enterthen jump :004848BA B901000000 mov ecx, 00000001 ;counter=1;ebx=1 firstly * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004848E0(C) *first important loop* ====================== :004848BF 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax :004848C2 0FB67C08FF movzx edi, byte ptr [eax+ecx-01];edi=name[ecx] :004848C7 0FAFFB imul edi, ebx ;edi=edi*ebx :004848CA 83FF12 cmp edi, 00000012 ;compare edi with 0x12 :004848CD 7C0F jl 004848DE ;jump if less than :004848CF 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax :004848D2 8BC7 mov eax, edi ;eax=edi :004848D4 BB11000000 mov ebx, 00000011 ;ebx=11 :004848D9 99 cdq ;expand eax :004848DA F7FB idiv ebx ;eax=eax/11 :004848DC 8BD8 mov ebx, eax ;ebx=eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004848CD(C) | :004848DE 41 inc ecx :004848DF 4E dec esi :004848E0 75DD jne 004848BF ;if unfinish loop back The above code is the first important loop of the key-genereating loop.It is basicaaly a very simple coding and can be summurized as follow: code=name[ecx]*code/17 Ecx act as a counter, it count the current byte of name in increasing order, and the ebx is the code we want. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004848B8(C) | :004848E2 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax :004848E5 E88EF3F7FF call 00403C78 ;calculate length of eax :004848EA 8BC8 mov ecx, eax :004848EC 83F901 cmp ecx, 00000001 :004848EF 7C30 jl 00484921 ;jump if no name enter * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048491F(C) *second important loop* ======================= :004848F1 8B45F8 mov eax, dword ptr [ebp-08] ;move name to eax :004848F4 0FB67408FF movzx esi, byte ptr [eax+ecx-01];move name[ecx] to esi :004848F9 8BC1 mov eax, ecx ;ecx is in decresing order :004848FB 99 cdq ;clear edx :004848FC F7FE idiv esi ;eax = count / name[ecx] :004848FE 85D2 test edx, edx ;test if any number remain :00484900 740D je 0048490F ;if no then jump :00484902 8B45F8 mov eax, dword ptr [ebp-08] ;eax=name :00484905 83EE0B sub esi, 0000000B ;name[ecx]=name[ecx]-0B :00484908 0FAFF3 imul esi, ebx ;code=esi*code :0048490B 8BDE mov ebx, esi ;ebx=code :0048490D EB0D jmp 0048491C ;unconditional jump * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00484900(C) | :0048490F 8B45F8 mov eax, dword ptr [ebp-08] ;eax=name :00484912 83C611 add esi, 00000011 ;name[0]=name[0]+0x11 :00484915 8BC3 mov eax, ebx ;eax=code :00484917 99 cdq ;clear edx :00484918 F7FE idiv esi ;eax=eax/esi :0048491A 8BD8 mov ebx, eax ;ebx=code * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0048490D(U) | :0048491C 49 dec ecx :0048491D 85C9 test ecx, ecx :0048491F 75D0 jne 004848F1 ;loop if unfinish The second important loop is also quite simple, we can neglect line 48490F to 48491A because it will never reach when is came to last byte, it will pass throught 48491F and never have change to loop back. So the testing algorithem from 4848FB to 484900 actually is rubbish code. This might be a bug or something that make to confuse us. The above coding can be summerized as: code=code*(name[ecx]-0x0b) ;code in this case is our serial calculated Ecx is a counter in decreasing order, it start it the last byte of our name. Ebx is the code we want. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004848EF(C) | :00484921 8B45F8 mov eax, dword ptr [ebp-08] :00484924 E84FF3F7FF call 00403C78 :00484929 50 push eax :0048492A 8BC3 mov eax, ebx :0048492C 5A pop edx ;edx=length of name :0048492D 2BC2 sub eax, edx ;code=code-length :0048492F B907000000 mov ecx, 00000007 ;ecx=0x07 :00484934 99 cdq :00484935 F7F9 idiv ecx ;code=code/7 :00484937 05920F0000 add eax, 00000F92 ;code=code+0x0f92 :0048493C 8BD8 mov ebx, eax ;ebx=code Until here our code=((code-length of our name)/7)+0F92 :0048493E 8D55F4 lea edx, dword ptr [ebp-0C] ;edx=location [final reg key] :00484941 8BC3 mov eax, ebx ;eax=code :00484943 E8042BF8FF call 0040744C ;This call convert the int format :00484948 8D45F0 lea eax, dword ptr [ebp-10] ;of the code into character, :0048494B 8B55F8 mov edx, dword ptr [ebp-08] ;including it's sign also :0048494E 8A12 mov dl, byte ptr [edx] The call at 484943 actually convert our code into string form including sign also. :00484950 E84BF2F7FF call 00403BA0 This call search for the char need to be insert in first byte of our serial, it return the in [ebp-10]. In this case is search for the first byte of our name as result. :00484955 8B45F0 mov eax, dword ptr [ebp-10] ;eax=target of insertion :00484958 8D55F4 lea edx, dword ptr [ebp-0C] ;edx=target for insertion :0048495B B901000000 mov ecx, 00000001 ;ecx=location :00484960 E89FF5F7FF call 00403F04 ;This call insert first char :00484965 8B55F8 mov edx, dword ptr [ebp-08] ;of name into first location :00484968 B8744A4800 mov eax, 00484A74 ;of code The call 00403F04 actually is a call to insert certain character into the string. It need EAX for what we want to insert, in above case it need to insert the first char of name. EDX is the address for the target to insert, in above case edx=address of our serial. ECX is the location for insertion, in above case it is insert at first byte. :0048496D E8EEF5F7FF call 00403F60 ;This call for any space in our :00484972 8B55F8 mov edx, dword ptr [ebp-08] ;name :00484975 8A1402 mov dl, byte ptr [edx+eax] :00484978 8D45F0 lea eax, dword ptr [ebp-10] :0048497B E820F2F7FF call 00403BA0 ;This call return eax=the char :00484980 8B45F0 mov eax, dword ptr [ebp-10] ;should placed in last byte of :00484983 50 push eax ;code :00484984 8B45F4 mov eax, dword ptr [ebp-0C] :00484987 E8ECF2F7FF call 00403C78 ;Calculate last byte location :0048498C 8BC8 mov ecx, eax :0048498E 41 inc ecx :0048498F 8D55F4 lea edx, dword ptr [ebp-0C] :00484992 58 pop eax :00484993 E86CF5F7FF call 00403F04 ;This call insert eax from call :00484998 8B45F4 mov eax, dword ptr [ebp-0C] ;00403BA0 into last byte of code The above code search for any space(20) in our name, if it does then the take the next byte after the FIRST space and put it into last byte of our code. If it doesn't found any space, then it will put first byte of our name into last byte of our code. Eg: Name=abc code=a15335672a Name=abc def code=a87672812d Name=abc 123 456 code=a-15942455d :0048499B E8D8F2F7FF call 00403C78 :004849A0 8BC8 mov ecx, eax :004849A2 D1F9 sar ecx, 1 ;ecx=length(name)>>1 :004849A4 7903 jns 004849A9 ;jump if ecx>0 :004849A6 83D100 adc ecx, 00000000 The above code calculate the length of our name, ecx = our length of name shift right by one. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004849A4(C) | :004849A9 8D55F4 lea edx, dword ptr [ebp-0C] * Possible StringData Ref from Code Obj ->"ClaDiTL" | :004849AC B8804A4800 mov eax, 00484A80 :004849B1 E84EF5F7FF call 00403F04 ;This call insert "ClaDiTL" into :004849B6 8D55EC lea edx, dword ptr [ebp-14] ;ecx location of code :004849B9 8B45FC mov eax, dword ptr [ebp-04] :004849BC 8B80EC010000 mov eax, dword ptr [eax+000001EC] :004849C2 E8B9B0F9FF call 0041FA80 The above code inserts words'ClaDiTL" into our code, it insert in the location ecx, and ecx is our name length right shift by one. Eg:With name=yes123 before insertion, code=y-85638181y after insertion, code=y-85ClaDiTL638181y :004849C7 8B55EC mov edx, dword ptr [ebp-14] ;edx=our false key :004849CA 8B45F4 mov eax, dword ptr [ebp-0C] ;eax=real key :004849CD E8B6F3F7FF call 00403D88 ;test for both key :004849D2 7530 jne 00484A04 ;jump if not equal The this part, it compare our fake key with the real key generated. So we can type 'd eax' after line 4849CA to sea for the real key. :004849D4 8D55EC lea edx, dword ptr [ebp-14] :004849D7 8B45FC mov eax, dword ptr [ebp-04] :004849DA 8B80EC010000 mov eax, dword ptr [eax+000001EC] :004849E0 E89BB0F9FF call 0041FA80 :004849E5 8B55EC mov edx, dword ptr [ebp-14] * Possible StringData Ref from Code Obj ->"ClaDiTL" | :004849E8 B8804A4800 mov eax, 00484A80 :004849ED E86EF5F7FF call 00403F60 :004849F2 85C0 test eax, eax :004849F4 7E0E jle 00484A04 :004849F6 A114F74800 mov eax, dword ptr [0048F714] :004849FB 8B00 mov eax, dword ptr [eax] :004849FD E8BA7B0000 call 0048C5BC :00484A02 EB37 jmp 00484A3B The above code store our registered data into registry. * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:004849D2(C), :004849F4(C) | :00484A04 6A00 push 00000000 :00484A06 668B0D884A4800 mov cx, word ptr [00484A88] :00484A0D B201 mov dl, 01 * Possible StringData Ref from Code Obj ->"Invalid registration code!" | :00484A0F B8944A4800 mov eax, 00484A94 ;Change this code to 8B45F49090 When we trace to here, there is also a fun place to make change, we can change the : 00484A0F B8944A4800 mov eax, 00484A94 into: 00484A0F 8B45F4 mov eax, dword ptr [ebp-0C] 00484A12 90 nop 00484A13 90 nop So that instead of display out the "Invalid registration code!", it will display out our real key if we enter wrong key. This is beacuse from line 4849CA, we know the real key is store at [ebp-0C]. You can try and make a patch for for it. This technique is so call 'Magic Window'. :00484A14 E8DB7CFBFF call 0043C6F4 :00484A19 8B45FC mov eax, dword ptr [ebp-04] :00484A1C 8B80E0010000 mov eax, dword ptr [eax+000001E0] :00484A22 8B10 mov edx, dword ptr [eax] :00484A24 FF9294000000 call dword ptr [edx+00000094] :00484A2A 8B45FC mov eax, dword ptr [ebp-04] :00484A2D 8B80EC010000 mov eax, dword ptr [eax+000001EC] :00484A33 8B10 mov edx, dword ptr [eax] :00484A35 FF9294000000 call dword ptr [edx+00000094] * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00484A02(U) | :00484A3B 33C0 xor eax, eax :00484A3D 5A pop edx :00484A3E 59 pop ecx :00484A3F 59 pop ecx :00484A40 648910 mov dword ptr fs:[eax], edx * Possible StringData Ref from Code Obj ->"_^[" | :00484A43 68654A4800 push 00484A65 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00484A63(U) | :00484A48 8D45EC lea eax, dword ptr [ebp-14] :00484A4B E8ACEFF7FF call 004039FC :00484A50 8D45F0 lea eax, dword ptr [ebp-10] :00484A53 BA03000000 mov edx, 00000003 :00484A58 E8C3EFF7FF call 00403A20 :00484A5D C3 ret ---[ OVERALL CONCLUSION OF KEY GENERATING PROCESS ]-------------------------------------------- 1.code=(code*name[count1])/0x11; //count1=counter from 0 to length of name 2.code=code*(name[count2]-0x0b); //count2=counter from length of name to 0 3.code=((code-count1)/7)+0xf92; //count1=length of name 4.convert code into string form. 5.insert suitable character into the head and tail of the code. 6.insert 'ClaDiTL' into suitable place of the code ---[ C-LANGUAGE CODE ]------------------------------------------------------------------------- #include #include #include int main(void) { long code=1; int count1,count2,count3; char name[25],reg[15]; clrscr(); textcolor(14); cprintf(" __,__\r\n"); cprintf(" / \\\r\n" ); cprintf(" vvvvvvv /|__/|\r\n"); cprintf(" I /O,O |\r\n"); cprintf(" I /_____ | /|/|\r\n"); cprintf(" J|/^ ^ ^ \\ | /00 | _//|\r\n"); cprintf(" |^ ^ ^ ^ |W| |/^^\\ | /oo |\r\n"); cprintf(" \\m___m__|_| \\m_m_| \\mm_|\r\n"); textcolor(10); cprintf("======================================="); textcolor(11); cprintf("\r\nKeyGenerator for A Day in the Life v1.0"); textcolor(10); cprintf("\r\n======================================="); printf("\nCracked by "); textcolor(14); cprintf("%c%c%c",0x10,0x10,0x10); textcolor(12); cprintf("Yes123"); textcolor(14); cprintf("%c%c%c",0x11,0x11,0x11); printf(" - February 1999"); printf("\n\nEnter register name = "); scanf("%[^\n]",name); for(count1=0;name[count1]>0;count1++) //first loop code=(code*name[count1])/0x11; for(count2=count1-1;count2>=0;count2--) //second loop code=code*(name[count2]-0x0b); code=((code-count1)/7)+0xf92; ltoa(code,reg,10); //convert long to string printf("Your registration key = %c",name[0]); for(count2=0;reg[count2]>0;count2++); count2=(count2+2)>>1; for(count1=0;count1<(count2-2);count1++) printf("%c",reg[count1]); printf("ClaDiTL"); for(;reg[count1]>0;count1++) printf("%c",reg[count1]); for(count1=0;name[count1]>0;count1++){ if ((name[count1]==0x20)&&(name[count1+1]>0)){ printf("%c",name[count1+1]); return; } } printf("%c",name[0]); return ; } ---[ LAST ]----------------------------------------------------------------------------------- I know my english expressing ability is weak, so in many place i know what's happening but i cann't express well and explain to you clearly. I hope you can really try it out then you can understand more out of it. This is my first keygen tutorial and i hope you'll like it. You can also try to trace into the call of how they insert words into string, call of how they calculate the strings number, call of how they compare two strings, call of how they search for specific char in a string... Try to understand their coding, it is quite interesting and useful also if we met another similar call next time. Thanks for reading my first keygen tutorial. ---[ THAT'S ALL FOLKS ]----------------------------------------------------------------------- Yes123 '99 ========== PART 4: ~~~~~~ Keygen Tutorial 2 Yes123 '99 ---[ WARNING ]-------------------------------------------------------------------------------- This Tutorial is for education purpose only. I wrote it to allow you to understand how are coded some protections schemes in software. I didn't make it to allow you to use the target program without paying the author. If you plan to use these programs regularly, please remeber to send your $ to the authors, don't be a outlaw, and over all, don't be a LAMER ! ---[ INTRO ]---------------------------------------------------------------------------------- target: SubmitWolf(DEMO) v4.01 where : http://www.trellian.com/swolf Tools : SoftIce for Win9x v3.24 W32Dasm v8.9 Programming Language (C, Pascal, asm, anyone you want, I'll use our old C) This is my second tutorial in english. I hope my bad spelling won't make this text too much hard to understand for you. :) I'll try to teach you how to make a key generator for a program. The way i teach will be based more on reverse enginerring (instead of only cracking the program, we try to fully understand the whole key-generating algorithm), some newbies maybe having problem of reading this. - basic use of SoftIce - asm instructions (at least the ones used for cracking) - knowing which call is used to calculate serial number. ---[ TUTORIAL ]------------------------------------------------------------------------------- At first, launch SoftICe (assuming you know the basics, and how to setup this Numega's nice tool). Then launch our target, swolf32.exe! Go to the help menu and select the register. Well, we'll try to find how this protection is running. Let's enter anything as a name and random digits as a serial. Don't click the OK button yet, but hit CTRL & D to bring up SoftICe. We'll define a breakpoint, using the classical BPX hmemcpy(it works for most program. Hit CTRL-D again to go back to the program. Click OK and... SoftICe pop up. Keep pressing F12 until you get back to the traget program code. Trace a few lines down you will be in: :004167D9 8D4C2410 lea ecx, dword ptr [esp+10] ;ecx=our fake serial :004167DD 8D942490000000 lea edx, dword ptr [esp+00000090] ;edx=our name :004167E4 51 push ecx :004167E5 52 push edx :004167E6 E8B5450100 call 0042ADA0 ;This is the call which :004167EB 83C410 add esp, 00000010 ;calculate our serial :004167EE 85C0 test eax, eax ;and return eax=1 if true :004167F0 0F8596000000 jne 0041688C ;jump to registered :004167F6 8D442408 lea eax, dword ptr [esp+08] :004167FA 8D8C2488000000 lea ecx, dword ptr [esp+00000088] :00416801 50 push eax :00416802 51 push ecx :00416803 E868470100 call 0042AF70 ;This is a call to test :00416808 83C408 add esp, 00000008 ;wheteher our serial is :0041680B 85C0 test eax, eax ;for v3.XX one. :0041680D 7433 je 00416842 The most important call is at 004167E6, there are a few calls before it, but nothing intersting inside. The following call at 416803 is to test whether our serial is for version 3.XX. It some how similar to v4.01, you can try to trace it if you want. So we trace in the call 0042ADA0. * Referenced by a CALL at Addresses: |:004167E6 , :0042ABFC | :0042ADA0 83EC30 sub esp, 00000030 :0042ADA3 55 push ebp :0042ADA4 56 push esi :0042ADA5 57 push edi :0042ADA6 8B7C2444 mov edi, dword ptr [esp+44] ;edi=our fake serial :0042ADAA 85FF test edi, edi :0042ADAC 0F84A7010000 je 0042AF59 ;die if empty :0042ADB2 8B6C2440 mov ebp, dword ptr [esp+40] ;ebp=our name :0042ADB6 85ED test ebp, ebp :0042ADB8 0F849B010000 je 0042AF59 ;die if empty :0042ADBE 8A07 mov al, byte ptr [edi] ;compare first byte of :0042ADC0 3C50 cmp al, 50 ;serial with 'P', die :0042ADC2 0F8587010000 jne 0042AF4F ;if not equal :0042ADC8 807F0134 cmp byte ptr [edi+01], 34 ;compare second byte of :0042ADCC 750C jne 0042ADDA ;serial with '4' :0042ADCE C70500C8430000000000 mov dword ptr [0043C800], 00000000 :0042ADD8 EB1C jmp 0042ADF6 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042ADCC(C) | :0042ADDA 3C50 cmp al, 50 ;compare first byte of :0042ADDC 0F856D010000 jne 0042AF4F ;serial with 'P' :0042ADE2 807F0145 cmp byte ptr [edi+01], 45 ;compare second byte of :0042ADE6 0F8563010000 jne 0042AF4F ;serial with 'E' :0042ADEC C70500C8430001000000 mov dword ptr [0043C800], 00000001 We can see from above code, the first byte of our serial need to be 'P' and second byte need to be either '4'(PRO edition) or 'E'(Enterprise edition), so we can chose to register as either PRO edition or enterprise edition. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042ADD8(U) | :0042ADF6 83C9FF or ecx, FFFFFFFF :0042ADF9 33C0 xor eax, eax :0042ADFB F2 repnz :0042ADFC AE scasb :0042ADFD F7D1 not ecx :0042ADFF 2BF9 sub edi, ecx The above code basically calculate the lenght of edi(our serial), and return the result in ecx. :0042AE01 8D542414 lea edx, dword ptr [esp+14] :0042AE05 8BC1 mov eax, ecx :0042AE07 8BF7 mov esi, edi :0042AE09 8BFA mov edi, edx :0042AE0B 6A2D push 0000002D ;push 2D ('-') in stack :0042AE0D C1E902 shr ecx, 02 :0042AE10 F3 repz :0042AE11 A5 movsd :0042AE12 8BC8 mov ecx, eax :0042AE14 83E103 and ecx, 00000003 :0042AE17 F3 repz :0042AE18 A4 movsb :0042AE19 8D4C2418 lea ecx, dword ptr [esp+18] The above code move content of edi(our serial) to [ESP+14] :0042AE1D 51 push ecx :0042AE1E E81D2A0000 call 0042D840 :0042AE23 8BF8 mov edi, eax :0042AE25 83C408 add esp, 00000008 :0042AE28 85FF test edi, edi :0042AE2A 0F8429010000 je 0042AF59 The above call actually test whether our fake serial contain '-'(remember it push 2D into stack? ), it return eax=-????? of our code or 00 if no '-' found. Then it test if eax=00 then straight die. :0042AE30 C60700 mov byte ptr [edi], 00 ;clear the '-' in our serial :0042AE33 8B151CB44300 mov edx, dword ptr [0043B41C] ;edx=20202020,20 is space(' ') :0042AE39 A020B44300 mov al, byte ptr [0043B420] :0042AE3E 6A04 push 00000004 :0042AE40 8D4C2410 lea ecx, dword ptr [esp+10] :0042AE44 55 push ebp :0042AE45 51 push ecx :0042AE46 89542418 mov dword ptr [esp+18], edx :0042AE4A 8844241C mov byte ptr [esp+1C], al :0042AE4E E8ED370000 call 0042E640 The above call basically test for space(' ')in our serial, but it did nothing except return eax= first 4 character of our name. So it basically is a rubbish call. :0042AE53 8A542420 mov dl, byte ptr [esp+20] ;move first byte of our serial :0042AE57 83C40C add esp, 0000000C ;to dl :0042AE5A 84D2 test dl, dl :0042AE5C 7421 je 0042AE7F :0042AE5E 8D742414 lea esi, dword ptr [esp+14] ;esi=our serial Actually the above code move 'P' to dl(because the first byte of real serial is 'P' as stated in above lines). The first important loop begins: * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AE7D(C) | :0042AE62 33C9 xor ecx, ecx ;restart counter :0042AE64 80C203 add dl, 03 ;dl=dl+3 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AE75(C) | :0042AE67 8A440C0C mov al, byte ptr [esp+ecx+0C] ;move ecx byte of [esp+0c] to al :0042AE6B F6EA imul dl ;al=al*dl :0042AE6D 88440C0C mov byte ptr [esp+ecx+0C], al ;move result back to [esp+ecx+0c] :0042AE71 41 inc ecx ;increase counter :0042AE72 83F904 cmp ecx, 00000004 ;compare counter with 4 :0042AE75 7CF0 jl 0042AE67 ;jump if less than 4 :0042AE77 8A5601 mov dl, byte ptr [esi+01] ;move next byte of serial to dl :0042AE7A 46 inc esi ;increase address of serial :0042AE7B 84D2 test dl, dl ;test dl=0? :0042AE7D 75E3 jne 0042AE62 ;jump if false This is the first important loops in generating serial. Ecx act as a counter in this case, it count the number of [esp+0c] up to 4, dl is the first few bytes of our serial until 00 (remember we replace 2D with 00).It multiply every byte of the [esp+0c] with every byte of our serial until dl=00. So it basically contain two small loop inner one multiple every byte of [esp+0c](initially [esp+0c] is the first four byte of our name) with dl, the outer loop move next byte of our serial to dl until dl=00. So that every byte of the [esp+0c] is multiple by every byte of our serial before '-'. If you still don't understand, try to trace it yourself or look at my C-language coding in below part. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AE5C(C) | :0042AE7F 8A4D00 mov cl, byte ptr [ebp+00] :0042AE82 84C9 test cl, cl :0042AE84 7419 je 0042AE9F It test wheter first byte of our name is empty or not. Then we follow by the second important big loop,it contain two loops, a inner and a outer loop. :0042AE86 8BD5 mov edx, ebp ;edx=our name * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AE9D(C) | :0042AE88 33C0 xor eax, eax ;eax act as counter :0042AE8A 80C103 add cl, 03 ;cl=cl+3 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AE95(C) | :0042AE8D 304C040C xor byte ptr [esp+eax+0C], cl ;xor eax byte of[esp+0c] with cl :0042AE91 40 inc eax ;increase counter :0042AE92 83F804 cmp eax, 00000004 ;compare eax with 4 :0042AE95 7CF6 jl 0042AE8D ;repeat if less than 4 :0042AE97 8A4A01 mov cl, byte ptr [edx+01] ;move next char of name into cl :0042AE9A 42 inc edx ;increase edx :0042AE9B 84C9 test cl, cl ;test whether is end of name :0042AE9D 75E9 jne 0042AE88 ;jump if false This second impotant loop basically xor (every byte of our name + 3)with every byte of [esp+0c]. the inner loop xor every bytes of [esp+0c] with cl, the outer loop move next char in our name into cl and add cl with 03. If you still donn't understand try to trace it again by yourself or look at my c-language coding in below part. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AE84(C) | :0042AE9F 8B4C240C mov ecx, dword ptr [esp+0C] ;move the final result to ecx :0042AEA3 85C9 test ecx, ecx :0042AEA5 7D04 jge 0042AEAB ;test if ecx>0 :0042AEA7 F7D9 neg ecx ;ecx=-ecx :0042AEA9 85C9 test ecx, ecx ;test if ecx=0 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AEA5(C) | :0042AEAB 7507 jne 0042AEB4 ;jump if flase :0042AEAD B901000000 mov ecx, 00000001 ;ecx=1 :0042AEB2 EB08 jmp 0042AEBC The above code actually convert ecx to positive number if it is less than 0, and if ecx=0 then let ecx=1. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AEAB(C) | :0042AEB4 81F90F270000 cmp ecx, 0000270F ;compare ecx with 9999 :0042AEBA 7D0D jge 0042AEC9 * Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:0042AEB2(U), :0042AEC7(C) | :0042AEBC 8D0C89 lea ecx, dword ptr [ecx+4*ecx] :0042AEBF D1E1 shl ecx, 1 :0042AEC1 81F90F270000 cmp ecx, 0000270F :0042AEC7 7CF3 jl 0042AEBC If ecx<9999, then ecx=ecx*10. And repeat the operation until ecx>9999 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AEBA(C) | :0042AEC9 81F93F420F00 cmp ecx, 000F423F ;compare ecx with 999999 :0042AECF 7E1B jle 0042AEEC * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AEEA(C) | :0042AED1 B867666666 mov eax, 66666667 :0042AED6 F7E9 imul ecx :0042AED8 8BCA mov ecx, edx :0042AEDA C1F902 sar ecx, 02 :0042AEDD 8BD1 mov edx, ecx :0042AEDF C1EA1F shr edx, 1F :0042AEE2 03CA add ecx, edx :0042AEE4 81F93F420F00 cmp ecx, 000F423F :0042AEEA 7FE5 jg 0042AED1 if ecx>999999, then ecx=ecx/10. And repeat until ecx<1000000. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AECF(C) | :0042AEEC 51 push ecx * Possible StringData Ref from Data Obj ->"%li" | :0042AEED 6818B44300 push 0043B418 :0042AEF2 8D442430 lea eax, dword ptr [esp+30] :0042AEF6 6A14 push 00000014 :0042AEF8 50 push eax :0042AEF9 E8FE290000 call 0042D8FC ;covert ecx into string :0042AEFE 83C410 add esp, 00000010 :0042AF01 8D7701 lea esi, dword ptr [edi+01] :0042AF04 8D442428 lea eax, dword ptr [esp+28] Until here we have already knew the code by typing: d esi (which return our fake serial after '-') d eax (which return the real serial after '-') I cann't remember which one is the our fake serial and which one is real serial, it is either one. You can check and verify it. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AF26(C) | :0042AF08 8A10 mov dl, byte ptr [eax] :0042AF0A 8ACA mov cl, dl :0042AF0C 3A16 cmp dl, byte ptr [esi] :0042AF0E 752A jne 0042AF3A :0042AF10 84C9 test cl, cl :0042AF12 7414 je 0042AF28 :0042AF14 8A5001 mov dl, byte ptr [eax+01] :0042AF17 8ACA mov cl, dl :0042AF19 3A5601 cmp dl, byte ptr [esi+01] :0042AF1C 751C jne 0042AF3A :0042AF1E 83C002 add eax, 00000002 :0042AF21 83C602 add esi, 00000002 :0042AF24 84C9 test cl, cl :0042AF26 75E0 jne 0042AF08 The above code is just to test every byte of our serial with the real serial. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0042AF12(C) | :0042AF28 33C0 xor eax, eax :0042AF2A 33C9 xor ecx, ecx :0042AF2C 85C0 test eax, eax :0042AF2E 0F94C1 sete cl :0042AF31 5F pop edi :0042AF32 5E pop esi :0042AF33 8BC1 mov eax, ecx :0042AF35 5D pop ebp :0042AF36 83C430 add esp, 00000030 :0042AF39 C3 ret It will go to the above code if correct serial enter. The following code is the wrong serial coding, it will produce eax=0, and we neglect them here. Now we can now enter the serial PE-?????? to register the program. It sucess but after we restart the program, it pops up a message "You are using a corrupt or pirate copy of SubmitWolf. If you .....", it can detect our pirate serial so there should be a hidden routine that has further test on our serial. So we dissambly the program or just using softice bpx messageboxa to search for the hidden routine, it should be performed at the starting of the program. I used dissambly method (by using W32dasm), and checck for the string "You are using a corrupt...." and look for a few lines above the starings: And we write down the address 00411BC6, then we lunch the target and get into target coding and get into softice to put breakpoint bpx 411BC2(you must enter into the target area first to put breakpoint, you can do this by simplily put a breakpoint bpx showwindow and press F12 several times to get back into the swolf32!. This is a very basic thing, i assume you all know it, and i won't repeat next time, repeating simply thing is meaningless) Then we break at: :00411BC2 0FBE4802 movsx ecx, byte ptr [eax+02] ;ecx=3rd character of our serial * Possible StringData Ref from Data Obj ->" ((((( " ->" H" | :00411BC6 8B15F4B74300 mov edx, dword ptr [0043B7F4] :00411BCC 8A044A mov al, byte ptr [edx+2*ecx] :00411BCF 83E004 and eax, 00000004 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00411BC0(U) | :00411BD2 85C0 test eax, eax :00411BD4 7542 jne 00411C18 :00411BD6 A12C6B4400 mov eax, dword ptr [00446B2C] :00411BDB 68D00F0000 push 00000FD0 :00411BE0 6840F04300 push 0043F040 * Possible Reference to String Resource ID=01457: "You are using a corrupt or pirate copy of SubmitWolf. If yo" | :00411BE5 68B1050000 push 000005B1 :00411BEA 50 push eax :00411BEB FFD7 call edi :00411BED 8B0D2C6B4400 mov ecx, dword ptr [00446B2C] :00411BF3 68D00F0000 push 00000FD0 :00411BF8 68E0DD4300 push 0043DDE0 In order to jump over the warning message, we must have none zero eax. So we must have something that and with 4(0100) to produce non-zero, so eax must be either ?4(0100),?5(0101),?6(0110) or ?7(0111), the four less significant bit of eax must be from 4-7. We then check for edx since edx is a reference table of all the byte .By typing d eax we saw many 20,00,48,28,10,... and we also found some 84 at address(satisfy what we want): 004BB83E,004BB85E-004BB870 Since ecx is the 3rd character of our serial, in order to reach the above address(reverse the above coding), ecx=((address of 84)-edx)/2 ;edx=43B7F4 So for the address: 4BB83E ECX=20(' ') 4BB85E ECX=30('0') 4BB85F ECX=31('1') 4BB860 ECX=32('2') ..... 4BB870 ECX=39('9') So we know our serial must contain the 3rd byte with ' 'or '0'-'9'. An example of our serial is PE4-654321. ---[ OVERALL CONCLUSION OF KEY GENERATING PROCESS ]-------------------------------------------- 1.first two character of serial must be either 'PE' or 'P4'. 2.multiple every first four character or our name with every byte of our serial number before '-'. 3.xor every four byte with every byte of our name. 4.convert to positive number if <0. 5.convert to number between 10000 and 1000000. As an example, i use name:yes123 ,the final registration number = PE4-103061(Enterprise edition) P44-114080(PRO edition) (i use '4' at 3rd characters of key is just for an example, you can use others digits you like) --[ C-LANGUAGE CODE ]------------------------------------------------------------------------- #include #include int main(void) { long code=555583,count1,count2; char name[25],cod[5],type='0'; clrscr(); textcolor(14); cprintf(" __,__\r\n"); cprintf(" / \\\r\n" ); cprintf(" vvvvvvv /|__/|\r\n"); cprintf(" I /O,O |\r\n"); cprintf(" I /_____ | /|/|\r\n"); cprintf(" J|/^ ^ ^ \\ | /00 | _//|\r\n"); cprintf(" |^ ^ ^ ^ |W| |/^^\\ | /oo |\r\n"); cprintf(" \\m___m__|_| \\m_m_| \\mm_|\r\n"); textcolor(10); cprintf("======================================="); textcolor(11); cprintf("\r\nKeyGenerator for SubmitWolf(DEMO) v4.01"); textcolor(10); cprintf("\r\n======================================="); printf("\nCracked by "); textcolor(14); cprintf("%c%c%c",0x10,0x10,0x10); textcolor(12); cprintf("Yes123"); textcolor(14); cprintf("%c%c%c",0x11,0x11,0x11); printf(" - March 1999"); printf("\n\nSelect edition PRO(0) or Enterprise(1) (0/1)= "); scanf("%c",&type); if (type=='1') code=557283; getchar(); printf("Enter registration name = "); scanf("%[^\n]",name); for(count1=0;count1<=3;count1++) cod[count1]=name[count1]; for(count1=1;count1<=3;count1++){ for(count2=0;count2<=3;count2++) cod[count2]=cod[count2]*(code%100); code=code/100; } for(count1=0;name[count1]>0;count1++) for(count2=0;count2<=3;count2++) cod[count2]=cod[count2]^(name[count1]+3); for(count1=3;count1>=0;count1--){ code=code+(cod[count1]&0xFF); if (count1>0) code=code*0x100; } if(code<0) code=-code; for(;code<10000;) code=code*10; for(;code>999999;) code=code/10; printf("Your serial number = P%c4-%ld",(type=='1')? 'E':'4',code); return ; } ---[ LAST ]-------------------------------------------------------------------------------------- I know my english expressing ability is weak, so in many place i know what's happening but i cann't express well and explain to you clearly. I hope you can really try it out then you can understand more out of it. There are many interesting coding like how they do /10 or *10 in asembly, it is quite different from what we had learned. After you register the program it will need to upgrade to full functional version by download certain data from internet. You can also try to make a keygen, using the same scheme, for the version 3.XX of the same program, remember in the begining i said there is also another call which test for serial key of version 3,XX, you can have a try on it, it used similar method with little differences. Thanks for reading my keygen tutorial. ---[ THAT'S ALL FOLKS ]----------------------------------------------------------------------- Yes123 '99 ========== PART 5: ~~~~~~ Keygen Tutorial 3 Yes123 '99 ---[ WARNING ]-------------------------------------------------------------------------------- This Tutorial is for education purpose only. I wrote it to allow you to understand how are coded some protections schemes in software. I didn't make it to allow you to use the target program without paying the author. If you plan to use these programs regularly, please remeber to send your $ to the authors, don't be a outlaw, and over all, don't be a LAMER ! ---[ INTRO ]---------------------------------------------------------------------------------- target: Screen Loupe for Window95/NT v4.4 where : http://www.execpc.com/~sbd Tools : SoftIce for Win9x v3.24 W32Dasm v8.9 Programming Language (C, Pascal, asm, anyone you want, I'll use our old C) This is my third tutorial in english. I hope my bad spelling won't make this text too much hard to understand for you. :) I'll try to teach you how to make a key generator for a program. The way i teach will be based more on reverse enginerring (instead of only cracking the program, we try to fully understand the whole key-generating algorithm), some newbies maybe having problem of reading this. The protection scheme actually encode your name into certain format, and compare with two possible encoded name, if it match it will register you directly without further checking on your company and registration key. If it doesn't match then it will perform key-generating process and compare the generated key with your fake key. By reading the above paragraph, i think you should know there are many ways to crack this program(byte patching, look at real key directly, reverse the code to find that two built-in name...), but what i'm teaching here is the key-generator process so we are only interested in building a key-generator.(but i'll still touch a bit on others method :)) If you want to unregister the program, delete the all keys in registry: [HKEY_USERS\.Default\Software\Software by Design\Screen Loupe for Windows 95/NT\Registration] I'll assume you know the following: - basic use of SoftIce - asm instructions (at least the ones used for cracking) - knowing which call is important call ---[ TUTORIAL ]------------------------------------------------------------------------------- At first, launch SoftICe (assuming you know the basics, and how to setup this Numega's nice tool). Then launch our target, loupe.exe! Go to the help menu and select the register. Enter a name and company. Simplily put a breakpoint before you press ok(eg.bpx hmemcpy or others...).You should have break into softice after pressing OK button. Keep pressing F12 until you get back to the traget program code. You will be in: * Reference To: USER32.GetDlgItemTextA, Ord:0104h | :004060FB 8B1D6C824100 mov ebx, dword ptr [0041826C] ;ebx is function GetDlgItemTextA :00406101 57 push edi :00406102 6A32 push 00000032 :00406104 56 push esi :00406105 6A65 push 00000065 :00406107 55 push ebp :00406108 FFD3 call ebx ;call to get our name :0040610A 8D7E32 lea edi, dword ptr [esi+32] :0040610D 6A32 push 00000032 :0040610F 57 push edi :00406110 6A66 push 00000066 :00406112 55 push ebp :00406113 FFD3 call ebx ;call to get our company :00406115 8D442410 lea eax, dword ptr [esp+10] :00406119 6800010000 push 00000100 :0040611E 50 push eax * Possible Ref to Menu: MenuID_03E8, Item: "System Information Ctrl+S" | :0040611F 6A67 push 00000067 :00406121 55 push ebp :00406122 FFD3 call ebx ;call to get our fake key :00406124 8D4C2410 lea ecx, dword ptr [esp+10] Until here you should have notice that the program use function GetDlgItemTextA to get our name, company and registration key. It move the function into ebx first, then call the function using 'call ebx' for 3 times. When you reach here, by typing d ecx you should see your fake registration key. :00406128 51 push ecx ;ecx=our fake key :00406129 E83D9B0000 call 0040FC6B The is a call that covert our our fake key from string form into hexadecimal format. The converted result be in eax.(The registartion key can only takes in digits.) :0040612E 56 push esi ;esi=our name :0040612F 8BD8 mov ebx, eax ;move the result to ebx :00406131 E8FA7C0000 call 0040DE30 ;call that encode our name :00406136 83C408 add esp, 00000008 :00406139 3D92A71901 cmp eax, 0119A792 ;compare encoded name with 119A792 :0040613E 7518 jne 00406158 The call at line 406131 is very important, it has being call 3 times in this program. It is used to encode the data pushed in into certain format and return the result at eax. In the above case, it encode our name and compare it with 119A792(certain encoded name). If it is equal, then it will register you as the author "Grgory Braun" with correct registration key. The following code is registering you as "Gregory Braun" with company name"Software Design", (we neglect them here...) ....... * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040613E(C) | :00406158 3D3CCE5F0D cmp eax, 0D5FCE3C ;compare our encoded name with 0D5FCE3C :0040615D 750C jne 0040616B If you want to do a byte-patching, then 40615D is a good place to patch, change the 750C into 9090, so that it can always go to 40615F. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00406156(U) | :0040615F 57 push edi ;edi=our company :00406160 56 push esi ;esi=our name :00406161 E85A770000 call 0040D8C0 ;calculate real key :00406166 83C408 add esp, 00000008 :00406169 8BD8 mov ebx, eax ;real key to ebx If our decoded name equal to either 119A792 or 0D5FCE3C, then it will goes to address 40615F. >From 40615F to 406169, it call the code generating function and put the result into ebx, ebx originally is our fake key that uses to compare with the encoded result. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040615D(C) | :0040616B 57 push edi ;edi=our company :0040616C 56 push esi ;esi=our name :0040616D E84E770000 call 0040D8C0 ;call that calculate real key :00406172 83C408 add esp, 00000008 ;stack correction :00406175 3BD8 cmp ebx, eax ;compare our key to real key :00406177 5F pop edi :00406178 741D je 00406197 ;jump to success message :0040617A 68CFEA0000 push 0000EACF ;error message * Possible Reference to String Resource ID=05000: " Register Screen Loupe for Windows 95/NT" If our didn't jump over the code in 40615F-406169, then the ebx=eax and we can jump over error message. Look carefully at 406161 and 40616D, they are calling the same function, this is the function that we are interested in.(in 406169, it move the result in ebx, so that ebx always equal to eax, if we jump over those code, then ebx is still our fake key). If you want to just any want to look for the real key then type '? eax' after after the call at 40616D. The positive decimal number will be our real registration key. In order to make a key-generator we trace into 'call 0040D8C0': * Referenced by a CALL at Addresses: |:004058CD , :00405FA3 , :00406161 , :0040616D | :0040D8C0 8B442404 mov eax, dword ptr [esp+04] ;eax=name :0040D8C4 56 push esi :0040D8C5 8B35E8F54100 mov esi, dword ptr [0041F5E8] ;esi=ABED0F00 :0040D8CB 50 push eax :0040D8CC 81CE78030000 or esi, 00000378 ;esi=ABED0F00 or 378=ABED0F78 :0040D8D2 E859050000 call 0040DE30 ;call that encode our name :0040D8D7 8B4C2410 mov ecx, dword ptr [esp+10] ;ecx=company :0040D8DB 03F0 add esi, eax ;esi=esi+eax :0040D8DD 51 push ecx ;push company :0040D8DE E84D050000 call 0040DE30 ;call that encode our company :0040D8E3 83C408 add esp, 00000008 ;stack correction :0040D8E6 03C6 add eax, esi ;eax=eax(encoded company)+esi :0040D8E8 5E pop esi :0040D8E9 C3 ret The above code basically very short and simple, the encode our name and company name using 'call 0040DE30' and add them together with ABEDOF78. It can be summarized as follow: Real key = [encoded name] + [encode company] + ABEDOF78 If we noticed carefully the 'call 0040DE30' is called befero this at 406131, at that times it is used to encode our name. Since it is the most important call, we trace into 'call 0040DE30': * Referenced by a CALL at Addresses: |:00406131 , :0040D8D2 , :0040D8DE | :0040DE30 51 push ecx :0040DE31 53 push ebx :0040DE32 8B5C240C mov ebx, dword ptr [esp+0C] ;ebx=value pushed in.(name/comp) :0040DE36 56 push esi :0040DE37 33F6 xor esi, esi ;esi=0 :0040DE39 53 push ebx :0040DE3A 8974240C mov dword ptr [esp+0C], esi ;clear [esp+0c]=place to store ;our encoded result * Reference To: KERNEL32.lstrlenA, Ord:0308h | :0040DE3E FF153C814100 Call dword ptr [0041813C] ;get length of ebx, eax=result :0040DE44 85DB test ebx, ebx :0040DE46 744F je 0040DE97 ;jump if no name/company enter :0040DE48 85C0 test eax, eax :0040DE4A 744B je 0040DE97 ;jump if length = 0 :0040DE4C 33D2 xor edx, edx ;clear edx,edx is counter :0040DE4E 85C0 test eax, eax :0040DE50 7E45 jle 0040DE97 ;jump if length = 0 :0040DE52 55 push ebp :0040DE53 57 push edi * Possible StringData Ref from Data Obj ->"|b!pz*ls;rn|lf$vi^Axpe)rx5aic&9/2m5lsi4@0dmZw9" ->"4cmqpfhw" | :0040DE54 BE10C74100 mov esi, 0041C710 ;esi=417C710,a place with random no. [41C710] strore a lot of random number that is important in key-generating. by typing d esi you will see: XXXX:0041C710 72 62 21 70 7A 2A 6C 73-3B 72 6E 7C 6C 66 24 76 XXXX:0041C720 69 5E 41 78 70 65 29 72-78 35 61 69 63 .... It is very important, please write them down if you want to make a keygen. :0040DE59 BF01000000 mov edi, 00000001 ;edi is also act as counter :0040DE5E 2BF3 sub esi, ebx ;esi=esi-ebx :0040DE60 8BCB mov ecx, ebx ;ecx=ebx :0040DE62 2BFB sub edi, ebx ;edi=edi-ebx * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040DE8D(C) | :0040DE64 0FBE1C0E movsx ebx, byte ptr [esi+ecx] ;ebx=[41C710+edx] :0040DE68 0FBEAC10D8C64100 movsx ebp, byte ptr [eax+edx+0041C6D8];ebp=[41C6D8+edx+eax] [41C6D8] also a set of random number that are very important. By typing 'd 41C6D8', you see: XXXX:41C6D8 23 73 65 72 42 26 6E 7A-7C 6D 66 4D 31 2F 35 28 XXXX:41C6E8 21 73 64 24 4D 71 2E 7B-73 5D 2B 73 46 6A 74 4B ..... Please write them down also if you want to make a keygen. :0040DE70 0FAFDD imul ebx, ebp ;ebx=ebx*ebp :0040DE73 8D2C0F lea ebp, dword ptr [edi+ecx] ;ebp=edx+1 :0040DE76 0FAFDD imul ebx, ebp ;ebx=ebx*(edx+1) :0040DE79 0FBE29 movsx ebp, byte ptr [ecx] ;ebp=one byte from name/company :0040DE7C 0FAFDD imul ebx, ebp ;ebx=ebx*ebp :0040DE7F 8B6C2410 mov ebp, dword ptr [esp+10] ;ebp=[esp+10] :0040DE83 03EB add ebp, ebx ;ebp=ebp*ebx :0040DE85 42 inc edx ;increase counter :0040DE86 41 inc ecx ;increase address of name/comp :0040DE87 3BD0 cmp edx, eax ;comapre counter with length :0040DE89 896C2410 mov dword ptr [esp+10], ebp ;move result to [esp+10] :0040DE8D 7CD5 jl 0040DE64 ;jump if counter #include int count1; char ran1[20]={0x7c,0x62,0x21,0x70,0x7a,0x2a,0x6c,0x73,0x3b,0x72,0x6e,0x7c,0x6c,0x66,0x24,0x76,0x69,0x5e,0x41,0x78}; char ran2[20]={0x23,0x73,0x65,0x72,0x42,0x26,0x6e,0x7a,0x7c,0x6d,0x66,0x4d,0x31,0x2f,0x35,0x28,0x21,0x73,0x64,0x24}; long encode(char [],int); //call that similar to 'call 40DE30' int main(void) { long code=0xabed0f78; //the esi result in line 40D8CC int length; char name[20],comp[20]; clrscr(); textcolor(14); cprintf(" __,__\r\n"); cprintf(" / \\\r\n" ); cprintf(" vvvvvvv /|__/|\r\n"); cprintf(" I /O,O |\r\n"); cprintf(" I /_____ | /|/|\r\n"); cprintf(" J|/^ ^ ^ \\ | /00 | _//|\r\n"); cprintf(" |^ ^ ^ ^ |W| |/^^\\ | /oo |\r\n"); cprintf(" \\m___m__|_| \\m_m_| \\mm_|\r\n"); textcolor(10); cprintf("==================================================="); textcolor(11); cprintf("\r\nKeyGenerator - Screen Loupe for Windows95/NT v4.4"); textcolor(10); cprintf("\r\n==================================================="); printf("\nCracked by "); textcolor(14); cprintf("%c%c%c",0x10,0x10,0x10); textcolor(12); cprintf("Yes123"); textcolor(14); cprintf("%c%c%c",0x11,0x11,0x11); printf(" - March 1999"); printf("\n\nEnter register name (1-20 chars)= "); //it can be more if you use more value scanf("%[^\n]",name); //of [41C710],i used only 20. getchar(); printf("Enter register company (1-10 chars)= "); //it can be more if you use more value scanf("%[^\n]",comp); //of [41C6D8], i used only 20. for (length=0;name[length]>0;length++); //calculate length of our name code=code+encode(&name,length); for (length=0;comp[length]>0;length++); //calculate length of our company name code=code+encode(&comp,length); printf("Your registration key = %lu",code); return ; } long encode(char code[],int length){ //same as 'call 40DE30' long key=0,result=0; for (count1=0;code[count1]>0;count1++){ key=ran1[count1]; key=key*ran2[count1+length]; key=key*(count1+1); key=key*code[count1]; result=result+key; } return result; } ---[ LAST ]-------------------------------------------------------------------------------------- I know my english expressing ability is weak, so in many place i know what's happening but i cann't express well or explain to you clearly. I hope you can really try it out then you can understand more out of it. You can also try to reverse encoded name of 119A792 or 0D5FCE3C, see what name correspound to this value. It should have many possibility(like UNIX password), but you can try it out to improve your reverse enginering skill. Thanks for reading my keygen tutorial. ---[ THAT'S ALL FOLKS ]----------------------------------------------------------------------- __,__ / \ vvvvvvv /|__/| I /O,O | I /_____ | J|/^ ^ ^ \ | |^ ^ ^ ^ |W| \m___m__|_|Yes123 '99 PART 6: ~~~~~~ CrackMe tutorial for Cruehead's Crueme 1.0 By Merkuur / CiA Hi reversers, In this tutorial I will explain the method I used to find valid codes for Cruehead's nice crackme. I think I was the 5th cracker to actually complete it. Cruehead released the source code for his crackme after that, which is just as well - I won't have to copy a lot of asm into this text :). Put a breakpoint on hmemcpy and run the program, entering any code. Sure enough, you break in. Step around, and you will eventually get to the place were the check on your code is performed, namely, at 00401B73. Single-step inside this code, and watch what happens... You will quickly see that the entered password is at ebx, and that the code performs operation on the data at ebx, ebx+4, and ebx+8. So the entered code must be 12 chars long. You should then analyze the code, understand what it does. I've summed it up for you below: - 2 values are computed from the 3 ints at ebx, ebx+4 and ebx+8. These values are stored in edi and esi. I've converted the asm code performing the computation to C, from the 3 int values p1, p2, p3. If you understand asm better than C then you'll be at home with the disassembled listing. unsigned int eax, i, j; unsigned int length=12; /* the code length */ esi=0; edi=0; /* resulting values in edi, esi */ for (i=0;i<0xFF;i++) { for (j=0;j<0xFF;j++) { eax = (p1+p3+0x0012AB20)*length; eax = eax ^ (p2+0x048FF4EA); eax = eax ^ (p3-p1-0xBC309A); edi = edi + (eax | 0x029359E2); edi = edi & 0x15263748; eax = (p1-p3-0x127FB9)/length; eax = eax ^ (p2-0x048FF4EA); eax = eax ^ (p1+p3+ 0xBC0533); esi= esi+ (eax & 0x029359E2); esi = esi | 0x596A7B8C; } edi = edi + 0x911; esi = esi - 0x911; } - the following code is executed: :00401C51 add edi, EFFDE3AF :00401C57 jne 00401C8E ; bad code ! :00401C59 add esi, A4948D23 :00401C5F jne 00401C8E ; bad code ! :... good code ! so edi+EFFDE3AF and esi+A4948D23 must be 0, which means that we must have: edi = 10021c51, esi = 5b6b72DD Allright... Now the tough part is, what must the p1, p2, p3 values be if we want to satisfy the final check ? it is not practical to work back from edi = 10021c51 and esi = 5b6b72DD, first because there are 255x255 operations to revert, and, more importantly, because the operations involved are not reversible (edi = edi & 0x15263748 for instance...). Therefore... Bruteforce is the way to go... Unfortunately the key we want to bruteforce is a 3-int thing... even when limited to printable chars, it's still something like a 84 bits key... Trying to test all possible values will take ages ! So what I did is, try a key (like ABCDEFGHIJKL), write down edi and esi. Change one bit in the key, and repeat the process... What I found is, changing a single bit in the key doesn't change the resulting edi and esi much. So the idea is, starting from a random key, try to change bits in it so that the resulting edi and esi values get "closer" (in terms of number of matching bits) to the desired values (10021c51 and 5b6b72DD). Actually the number of matching bits in the resulting edi and esi values can be thought as an "evaluation of the interest" of the initial key. The more matching bits, the closer you are from a valid key. Whenever you have found a "evaluation of interest" function, (also called a "survival criterion") you can use a GENETIC ALGORITHM in order to find a key that minimizes this function. A genetic algorithm is an iterative process that mimicks the natural evolution process ("only the best ones survive"). For this crackme we would consider our 12 chars key to be a living creature, made of 12 genes. Starting from a random set of creatures, we compute the "survival criterion" of each. Then, we iterate the following: - "Natural selection" process: keep some of the best scoring creatures, and get rid of the others. - "Reproduction" process: generate new creatures, by mixing the genes from the best ones. Hopefully this will result in new ones that will have an even better score. - "Mutation" process: in order to allow the apparition of new interesting genes that were not present in the initial population, randomly modify some genes in a few creatures. Repeat this sequence for several generations of creatures, and eventually, there will be one that has the best possible selection criterion. In the case of the crackme, it is a key that generates the wanted values for edi/esi. You will find a C program that performs all these operations in this package. On my P100 it finds valid codes in a few minutes. If someone ever writes an assembler version please send it to me, I would love to see it. Happy reversing, Merkuur/CiA '99 (Double click this line to extract the source!) We really hope you've enjoyed this tutorial too much as we did! Don't miss Tutor #18 soon! ;) And as I said last time: Without knowledge, there's no power! ;) Credits go to: Fli7e/CiA for Splash Logo. Trinity for providing a tut in this version. NiTEHAWK for providing a tut in this version. Yes123 '99 for providing 3 tuts in this version. Merkuur/CiA for providing a tut in this version. tKC/CiA (hey it's me!) for coding this version :) MisterE for providing a tut in previous (#16) version. All the crackers (non-members of CiA) are welcome to send tutors for the next tutorials .. see below for my email address! Greetz goto all my friends! You can find me on IRC or email me at tkc@reaper.org Coded by The Keyboard Caper - tKC The Founder of PhRoZeN CReW/Crackers in Action '99 Compiled on 27 March 1999 Cracking Tutorial #17 is dedicated to all the crackers...