-------------------------------------------------------- LESSON C (3) - How to crack Windows, cracking as anArt: Web trends, Instant Access (end) and the proximity trick -------------------------------------------------------- I. [WEB TRENDS] It's really amazing: I began this tutorial in february 1996... the year is not yet finished but many things have already changed, and how! First of all the Web proves to be even more significant that I would ever have thought: it's -de facto- an ANTI-ESTABLISHMENT and ANTI-CONSUME "permanent" tool... more than that: it's an EVOLVING and SHARP tool! I do not know if you will agree with me, but it seems to me that it is now NOT ANY MORE NECESSARY to buy any of the following things (and this is a quite incomplete list: 1) Newspapers Are almost all on the Web for free 2) Magazines Are almost all on the Web for free 3) Software All on the Web for free 4) Books on the Web (even the *images* inside the books you would have bought are somewhere) 5) Post_stamps e-mail is free and ubiquitous 6) Hard_disks free megabytes everywhere on the Web (for pages and/or software, quite a lot of offering (at the moment, middle november 1996 you can get -for free- about 80 megabytes for each email address you have). Should you do not deem this space enough, you may multiply them -ad libitum- using one of the many free "alias e-mail addresses" generators or, as a brilliant scholar of mine (re)discovered, you may easily "caper" (the passwords of) pages established by the lusers. I assure you that I used to spend quite a lot of money buying all the preceding items. The case of the CD-ROM magazines (and of software in general) seems to me to be the most relevant. In my earlier lessons I advised you to buy CD-ROM software at "kilo" prices in second hand shops (obviously never when the magazines themselves do appear)... even this trick is now useless: there is NO software that you cannot find on the Web (I'll bet that the CD-ROM mags are not making any easy money any more now). This truth may be difficult to slurp until you learn HOW to search effectively on the Web, a very difficult art -per se-, that all real crackers should master. A little tip: if you are in a hurry do your searches using your own search engines instead of using only the ubiquitous AltaVista, FTP and Webcrawler (which you SHOULD use, of course, but not alone). And loo! Good old Internet, made by governments and universities with public money and no private partecipation whatsoever reveals itself to be the most striking revolution of the last years in spite of the continuous blah blah about free markets and private initiative... quite funny, isn't it? New promising patterns are emerging: it is clear (to me) that the future of cracking lays in JavaScript applets: Netscape has opened quite a Pandora Box... now everybody is scrambling to close it... TOO LATE! Find the relevant documentation, tutorials, software, tools, examples... even teachers! Everything is there for free on the Web! Study it, and when you are ready study it again... take it easy: we have a long cracking future before us... Here is a nice example (by LaDue) of an interesting applet: this one forges an e-mail from the browsers of your page sending it to the address contained in the string "toMe". The browser of your page initiates the mail by connecting (involuntarily) to port 25 (int mailPort=25). Now, let's just academically imagine... you would put your own address (faked email address but working depot when you need to fetch the messages, as usual) in the string "toMe"... well, scanning incoming mail you could get the full e-mail address, including the user name, of many people who have seen your page. See LaDue example at: http://www.math.gatech.edu/~mladue/PenPal.java JavaScript and Java are two completely different things: The coincidence in the name is only a stupid marketing move from Netscape. JavaScript is so easy it's a shame, and this makes it the ideal weapon for masscracking (Java on the countrary is a "real" programming language). The implications of easy Javascript use, of internet growth and of the "on the fly" JavaScript workings for the drafting of much more interesting applets are obvious, I'll let them to the intuition (and fantasy) of my cleverest readers. II.[INSTANT ACCESS] End I'll admit it: I am a little deceived: the +HCU strainer (Instant access protection scheme) has been solved only by very few (pretty competent) crackers. The amount of people that has NOT cracked instant access (and that as relentlessy asked for more "clues") stood in no sound relation to the very few that solved it. I intended to give my complete solution in this lesson C3 to allow everybody to have (good) software for free... but too few worked hard on it to let you all lazy ones deserve a "ready- made" solution... I will therefore publish here one of the "incomplete" (albeit very good) solutions. The solution cracks the scheme but requires a little work of your part to accomplish it... what I mean is this: studying the following you'll be able to crack every Instant access protected code in a couple of hours, not immediatly... this is good, will make the lazy lurkers work (at least a little :=) Here it is (C++ code of the solution and admission letter), I only took off the name of the candidate: Cracking Instant Access_____________ by xxxxxxxx Application for 1996 Higher Cracking University This is my solution to the strainer for admittance into your HCU. While I was successful in bypassing the protection (and hence now have a nice collection of software for free) I am the first to admit that my solution is not the best. However, I am very proud of the work I have done on this project and it is by far the most difficult crack I've ever done. In the past I've traced programs, and when they did something I did not like, I looked at the jumps immediately before that and reversed them. Because of idiot programming this worked about 60% of the time, however in many programs I was just stuck. With the hints you provided in your tutors I was able to actually disassemble the program and understand why it did things. Believe me this is a big jump. Anyway, here is my solution. I have dozens of handwritten notes and pages of code that I copied out of soft-ice, and any that are important I will type into this report, however most turned out to be unimportant. I have also created a "Magic" number generator and a reverse generator. I am very proud that I was able to create these, because the "Magic" number seemed so mysterious at first, and now I have complete mastery of it, a great feeling of power. I began the project by following the introductory steps in lessons C1 and C2. I got lost somewhere in C2, but I kept going. I got to the end with a vague understanding of what was happening and decided that I needed to understand this fully before I could do anything useful towards cracking it. I left my computer alone and read through the code again, making notes and explanations for my own use. About the third time through everything clicked, it was like a light bulb going off in my head. You mentioned that not everything in Lesson C1 was correct. Here is a list of what I found to be incorrect. 1. The offsets in the code were not the same. (this is a good idea to keep people from cheating when pinpointing the correct code) 2. The pointers to where things are saved in memory were not the same. 3. You wrote that the 1st digit plus 7 was saved and then you wrote that the 2nd plus 3 was saved. It is the other way around! 1st plus 3 and 2nd plus 7. (just checking if we are paying attention huh?) I think that's all of the one's I found although there were many specific instances of each one. So here's what I did. I did a search on the 18 digit code I typed in. I found it at 30:8xxxxx and did a bpr on it. I let it run, and looked each time something accessed that area. Eventually I found code that checked if the digits were between 30 and 39 and copied them to DS:8CD8. So there lies the code with the "-" 's stripped off of it. I did a bpr on this area. It copied itself to DS:8CB8, and I bpr'ed that as well. I discovered that what was going on was, it copied itself, then that copy was transformed into the "Magic" number. So I did a little stack fishing, and found a CALL at offset 5C04 which copies the code from 8CB8 and converts it into the "Magic" number. At this point I traced into the call and got really fucking lost, so I stepped back had a sip of Vodka and thought. I don't care HOW the "magic" gets there, only that it is there. I figured once I figured out what "magic" I needed I could trace over the call that put it there, and then put in whatever "magic" I wanted. So I traced on to see what happened to the "magic" number that had been produced. I had a bpr on the "magic" and it stopped on the first line of code below. The code is copied from my handwritten notes, so not everything is accurate (I only wrote down what I thought was important) 2b67:2598 mov al, es:[bx] ; 12th digit of magic mov [bp-03], al ; ?????? mov al, [bp-03] ; maybe an inefficient compiler result add al, d0 ; clean it mov [bp-04], al ; save it in [8ca6] les bx, bp+06 add bx, si ; point to 12th again mov byte ptr es:bx, 30 ; make it a '0' push then more crap and then :253d mov al, es:bx ; 1st digit mov ah, 00 add ax,ffd0 ; clean it cwd add [bp-06], ax ; [8c90] is zero to start this loop repeats 18 times, summing up the "magic" number, with the 12th set to 0 :256e mov [bp-07], al ; save remainder of sum/a in [8c8f] cmp [al, bp-05] ; is 12th (in al) save as remainder of sum/a ? Aha!, this is what you were talking about at the end of C2, where the remainder doesn't match the 12th number. I knew I was on the right track. I could feel it. I traced down farther after the remainder check (I used 8888-8888-8888-8888-88 as my code from then on because it passed this check and was easy to remember) and I found code which compared the value at ds:8D00 with the value at ds:8D0C and if it did not match jumped to beggar off. Then it checks if ds:8D06 is equal to ds:8D0E and if not equal jumps to beggar off. So I knew that 8D00 must equal 8D0C and that 8D06 must equal 8D0E. All I needed to do was figure out where these came from. I bpr'ed on 8D0C and found code which wrote the number to it. I did not copy the ASM down, but this is what I wrote: move 15th of "Magic" into AX fix it to 0-9 by +- A put it in SI mov 16th into AX mul si by A add ax to si mov 17th to AX mul si by A add ax to it put 18th in AX mul si by A add AX to it ; This is ds:8D0C !!!! So now I knew where this came from, the last 4 digits of the "magic" I bpr'ed on 8D0E and found out quickly that the first digit of the "magic" was put into ds:8D0E. Things were looking good. However, I was unable to figure out how ds:8D06 and ds:8D00 were created. I know they are related to the product code because they only change when it does. But they are put there by a MOVSW command and I cannot figure out how to predict where they are copied from, because it is only done once and it is never from the same place, so all my attempts to bpr on the spot they are copied from failed because it copies from a new spot each time. I felt dejected. I could not figure it out, even after days of pointless tracing. I stepped back and thought, and drank a can of Coke at 2 a.m... (note from +ORC: Coke is dangerous for your health and your cracking purposes... drink only Martini-Wodka and use by all means only russian Wodka) ...I still had not figured out how the "magic" worked. I decided to do that and come back to the problem of the numbers generated from the Product Code. I knew the call at cs:5C04 completely generated the "magic" so I started there. I traced through it several times and found that it made a CALL 3517 three times, then looped 6 times. So it called 3517 a total of 18 times. I also noticed that the CALL changed the number, but nothing else did, it just set up the calls. So I traced into CALL 3517 and came up with this: mov ax,ss nop inc bp push bp mov bp,sp push ds mov ds,ax xor ax a bunch more unimportant stuff :356b mov al,es:[bx} ; al = 18th digit cbw push ax mov ax, A sub ax,[5dad] ; subtract 6 from a to get 4 imul [5db1] pop dx ; 18th digit add dx,ax add dx, -30 ;clean it mov [5db5], dx ;save it then fix [5db5] to be between 0 and 9 mov al, es:[bx] ; load 18th again cbw push ax mov ax,a sub ax,[5dad] imul [5db3] pop dx ; 18th digit add dx, ax add dx, -30 ;clean it :35bb mov [5db7], dx ;save it. :35d9 mov bx,[5dad] mov es, bp+1a add bx,[bp+18] mov al,es:[bx-1] ; al = 6th digit mov [5dc3], al ; save it mov bx,[5dad] mov es,[bp+1e] add bx,[bp+1c] mov al,es:[bx-1] ; 12th digit mov [5dc4], al ; save it more junk then 3605: mov bx,[5dbf] ; this is the beginning of a loop mov es,[bp+1a] add bx,[bp+18] mov al,es:[bx-1] ; 5th digit push ax ; save it mov ax,[5db5] ; [5db5] created above using 18th digit mov dx,A imul dx ;[5db] *A les bx,[bp+0c] add bx,ax add bx,[5db7] ; created using 18th pop ax ;5th digit sub al,es:[bx};subtract a value from the lookup table les bx add bx,[5dbf] mov es:[bx],al ; Put new value in 6th spot fix it so that it's between 0 and 9 by +- A :3656 mov bx,[5dbf] mov es,[bp+1e] add bx,[bp+1c] mov al,es:[bx-1] ; 11th digit push ax mov ax,[5db5] mov dx,a imul dx les bx add bx,ax add bx,[5db7] pop ax ;11th digit sub al,es:[bx] ; subtract a value from lookup table les bx, [bp+1c] add bx,[5db7] mov es:[bx],al ;put it in 12th spot fix it to be between 0 and 9 The loop above repeats doing the same thing, changing 2 numbers, but not always the same two. The next time through it changes 5th and 11th, after that the 4th and 10th, 3rd and 9th then the 2nd and 8th using this same pattern. After the loop it changes the 1st and 7th using the values of the original 6th and 12th which were saved in [5dc3] and [5dc4] using the same pattern. I quickly wrote a program in C which would produce this number, and it worked fine. I traced into the second call of 3517 and found that the parameters passed to it changed which values where used to create [5db5] and [5db7], whether they increment or decrement, whether you add 0 or 64 to your index for the lookup and the digits which are changed. All three calls to 3517 have a different arrangement, but the their arrangement is the same each time they are called. For instance, the three calls are looped over 6 times, on each instance that the 1st call is executed it will change the 6th,12th,5th, 11th, etc. So I modified my C program to mimic the behaviour of each call and looped it six times, expecting this to be the "magic" number. To my surprise it was not right. So I followed the code until after the 3 CALL 3517's had been made, this was the number my generator had given me, so it must do something more afterwards. I found the following code, still within the cs:5c04 call :44C1 mov al,es:[bx+si-2] ; 17th digit add al,d0 ;clean it mov [5dc1], al ;save it les bx mov al,es:[bx+si-2] ; 18th digit add al,d0 ;clean it mov [5dc2],al ;save it mov [5dbf],0 jmp 455f :44df les bx add bx,[5dbf] mov al,[5dc1] ;17th cleaned sub es:[bx],al ;1st digit has 17th cleaned subtracted from it fix it between 0 and 9 mov al,[5dc2] ;18th cleaned 4520 sub es:[bx],al ;7th - 18th cleaned is put in 7th spot fix it between 0 and 9 :455b inc word ptr [5dbf] mov ax,[5dbf] cmp ax,[5dad] ; run six times jge 456b jmp 44df 456b: blah blah continue on. This loop executes six times each time incrementing the digit to be changed by one so that the first change changes the 1st digit, and the next time through the loop the 2nd then the 3rd.....till the sixth. The second change alters the 7th through the 12th digits. I added code to do this at the end of my Generator, and I now had a "Magic" number generator. However this did not do me much good in itself. The breakthrough was reversing this program (it wasn't hard, but getting all the bugs out was really tough) so that it takes a "magic" number as input and tells you what registration number will produce it. I have included the source code for both programs to prove that they are my own work. The coding is not the best, they are my own crude tools, and they do the job I need them for. But now I am home free. Even without knowing how the product code is manipulated to come up with ds:8D00 and ds:8d06 I can crack it. Here's what I did. The product code given me was 3850-0118-6260-1057-23 I traced to where ds:8D00 and ds:8D06 are placed they were: ds:8D00 = E03 ds:8D06 = 3 I knew the last 4 digits when added, and multiplied as explained above must be E03 so this is what I wrote down, using my calculator DFC + 7 =E03 This is the final answer, but I need to work backwards from here 166 * A = DFC 15E + 8 = 166 23 * A = 15E 1E +5 = 23 3 * A = 1E Just working things backwards from the way the program did it I figured out the last 4 digits of the magic code need to be 3587 in order for it to produce E03 as a result. I also know that the first digit must be equal to ds:8d06 which is 3 so I now have: 3___-____-____-__35-87 as a "magic" number and I fill it in with 1's 3111-1111-111X-1135-87 I left the 12th number as an X because I remember that the remainder of the sum of all the digits except the 12th must be equal to the 12th. 3+1+1+1+1+1+1+1+1+1+1+1+1+3+5+8+7 = 26 26/A 26 26/A = 3 with a remainder of 8, so the 12th digit is an 8! My "magic" number should be 3111-1111-1118-1135-87 So I run my UNINSTAN program, which tells me that in order to get that "magic" I need to enter the following registration code: 4798-8540-6989-6899-53 I enter this in, the "Retrieve" button is enabled and I install Norton Utilities without a hassle! I used the same method to install Wine Select (I've been interested in wine since reading about your Pomerol), Labels Unlimited (which I use for what else? Barcodes!), Harvard Graphics, and Lotus Ami Pro, which I'm using to write this report on! Well, that's it. That is how I cracked Instant Access. As I mentioned above it is not the best way, but I gave everything I had and it's the best I could do. I have succeeded because I have beaten the protection, and because I taught myself a lot along the way. I'm sure you already have a "magic" number generator of your own, but I included mine so you could see it. If I just knew how the product code produces those 2 numbers I could create a product code to registration number converter, which I assume is what the operators at Instant Access have when you call them to buy stuff. One last note about this assignment. I know that you have realized that Instant Access was hard to find. I want to tell you how I got it, a bit a social engineering in itself. After searching every library and book/magazine store in the city I got on the Internet and asked. Nobody had it. So I found the Instant Access homepage. ... (this will not be published, coz I, +ORC, do not want to expose my crackers, but the way this guy got hold of the protection scheme is in itself worth is access to the +HCU) ...So as you can see, I have gone to great lengths for admittance into your University, and I hope I have earned it. I am proud to wear my + ...address follows And here are the two C++ programs: INSTANT.CPP----------------------------------- // Template for byte patch files #include #include #include #include #include void main() { char fix(char x); char *t; //*stopstring int save1, save2,fdbf,fdbs, i; static int table[208] = {1,3,3,1,9,2,3,0, 9,0,4,3,8,7,4,4, 5,2,9,0,2,4,1,5, 6,6,3,2,0,8,5,6, 8,9,5,0,4,6,7,7, 2,0,8,0,6,2,4,7, 4,4,9,5,9,6,0,6, 8,7,0,3,5,9,0,8, 3,7,7,6,8,9,1,5, 7,4,6,1,4,2,7,1, 3,1,8,1,5,3,3,1, 2,8,2,1,6,5,7,2, 5,9,9,8,2,9,3,0, 0,4,5,1,1,3,8,6, 1,1,9,0,2,5,5,5, 1,7,1,5,8,7,1,9, 8,7,7,4,4,8,3,0, 6,1,9,8,8,4,9,9, 0,7,5,2,3,1,3,8, 6,5,7,6,3,7,6,7, 4,2,2,5,2,4,6,2, 6,9,9,1,5,2,3,4, 4,0,3,5,0,3,8,7, 6,4,8,8,2,0,3,6, 9,0,0,6,9,4,7,2, 0,1,1,1,1,0,1} ; //_clearscreen(_GCLEARSCREEN); printf("Enter the 18 digit Reg code: "); gets(t); for (i=1; i<=6 ; i++) { save1 = t[5]; // save the sixth digit save2 = t[11]; // save the twelfth digit fdbf = 0xFFC+t[17]-0x1000-0x30 ; // create [5db5] if (fdbf < 0x0) fdbf += 0xA; // fix it if necessary else if (fdbf >= 0xA) fdbf -= 0xA; fdbs = fdbf; // and [5db7] t[5] = t[4] - table[fdbf*0xA+fdbs] ; // sixth number t[5] = fix(t[5]); t[11] = t[10] - table[fdbf*0xA+fdbs+0x64]; // 12th number t[11] = fix(t[11]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[4] = t[3] - table[fdbf*0xA+fdbs] ; // 5th number t[4] = fix(t[4]); t[10] = t[9] - table[fdbf*0xA+fdbs+0x64]; // 11th number t[10] = fix(t[10]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[3] = t[2] - table[fdbf*0xA+fdbs] ; // 4th number t[3] = fix(t[3]); t[9] = t[8] - table[fdbf*0xA+fdbs+0x64]; // 10th number t[9] = fix(t[9]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[2] = t[1] - table[fdbf*0xA+fdbs] ; // 3rd number t[2] = fix(t[2]); t[8] = t[7] - table[fdbf*0xA+fdbs+0x64]; // 9th number t[8] = fix(t[8]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[1] = t[0] - table[fdbf*0xA+fdbs] ; // 2nd number t[1] = fix(t[1]); t[7] = t[6] - table[fdbf*0xA+fdbs+0x64]; // 8th number t[7] = fix(t[7]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[0] = save1 - table[fdbf*0xA+fdbs]; // first digit t[0] = fix(t[0]); t[6] = save2 - table[fdbf*0xA+fdbs+0x64]; // 7th digit t[6] = fix(t[6]); //puts(t); // end of first call //////////////////////////////////////////////// save1 = t[5]; // save the sixth digit save2 = t[17]; // save the 18th digit fdbf = t[10]+0x4-0x30 ; // create [5db5] if (fdbf < 0x0) fdbf += 0xA; // fix it if necessary else if (fdbf >= 0xA) fdbf -= 0xA; fdbs = t[9]+0x4-0x30; // and [5db7] if (fdbs < 0x0) fdbs += 0xA; // fix it if necessary else if (fdbs >= 0xA) fdbs -= 0xA; t[5] = t[4] - table[fdbf*0xA+fdbs] ; // sixth number t[5] = fix(t[5]); t[17] = t[16] - table[fdbf*0xA+fdbs+0x64]; // 18th number t[17] = fix(t[17]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[4] = t[3] - table[fdbf*0xA+fdbs] ; // 5th number t[4] = fix(t[4]); t[16] = t[15] - table[fdbf*0xA+fdbs+0x64]; // 17th number t[16] = fix(t[16]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[3] = t[2] - table[fdbf*0xA+fdbs] ; // 4th number t[3] = fix(t[3]); t[15] = t[14] - table[fdbf*0xA+fdbs+0x64]; // 16th number t[15] = fix(t[15]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[2] = t[1] - table[fdbf*0xA+fdbs] ; // 3rd number t[2] = fix(t[2]); t[14] = t[13] - table[fdbf*0xA+fdbs+0x64]; // 15th number t[14] = fix(t[14]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[1] = t[0] - table[fdbf*0xA+fdbs] ; // 2nd number t[1] = fix(t[1]); t[13] = t[12] - table[fdbf*0xA+fdbs+0x64]; // 14th number t[13] = fix(t[13]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[0] = save1 - table[fdbf*0xA+fdbs]; // first digit t[0] = fix(t[0]); t[12] = save2 - table[fdbf*0xA+fdbs+0x64]; // 13th digit t[12] = fix(t[12]); //puts(t); // end of second call //////////////////////////////////////////////// save1 = t[11]; // save the 12th digit save2 = t[17]; // save the 18th digit fdbf = t[1]+0x4-0x30 ; // create [5db5] if (fdbf < 0x0) fdbf += 0xA; // fix it if necessary else if (fdbf >= 0xA) fdbf -= 0xA; fdbs = t[2]+0x4-0x30; // and [5db7] if (fdbs < 0x0) fdbs += 0xA; // fix it if necessary else if (fdbs >= 0xA) fdbs -= 0xA; t[17] = t[16] - table[fdbf*0xA+fdbs] ; // 18th number t[17] = fix(t[17]); t[11] = t[10] - table[fdbf*0xA+fdbs+0x64]; // 12th number t[11] = fix(t[11]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[16] = t[15] - table[fdbf*0xA+fdbs] ; // 17th number t[16] = fix(t[16]); t[10] = t[9] - table[fdbf*0xA+fdbs+0x64]; // 11th number t[10] = fix(t[10]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[15] = t[14] - table[fdbf*0xA+fdbs] ; // 16th number t[15] = fix(t[15]); t[9] = t[8] - table[fdbf*0xA+fdbs+0x64]; // 10th number t[9] = fix(t[9]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[14] = t[13] - table[fdbf*0xA+fdbs] ; // 15th number t[14] = fix(t[14]); t[8] = t[7] - table[fdbf*0xA+fdbs+0x64]; // 9th number t[8] = fix(t[8]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[13] = t[12] - table[fdbf*0xA+fdbs] ; // 14th number t[13] = fix(t[13]); t[7] = t[6] - table[fdbf*0xA+fdbs+0x64]; // 8th number t[7] = fix(t[7]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[12] = save2 - table[fdbf*0xA+fdbs]; // 13th digit t[12] = fix(t[12]); t[6] = save1 - table[fdbf*0xA+fdbs+0x64]; // 7th digit t[6] = fix(t[6]); // end of third call //////////////////////////////////////////////// } // end of for loop // Now we finish it up save1 = t[16] + 0xD0 - 0x100; // [5dc1] save2 = t[17] + 0xD0 - 0x100; // [5dc2] for (i=0; i<6; i++) { t[i] = t[i] - save1; t[i] = fix(t[i]); t[i+6] = t[i+6] - save2; t[i+6] = fix(t[i+6]); } printf("'Magic' code is: "); for (i=0; i<18 ;i++) // output the string (only first 18) putc(t[i], stdout); printf("\n\n Created by xxxxx for +Orc's HCU 1996"); } // end of main() char fix(char x) { if (x < '0') x = x+0xA; else if (x > 0x39) x -= 0xA; return x; } --------------------------------------------------- UNINSTANT.CPP #include #include #include #include #include void main() { char fix(char x); char *t; int save1, save2,fdbf,fdbs, i,q, fdbssave,fdbfsave; static int table[208] = {1,3,3,1,9,2,3,0, 9,0,4,3,8,7,4,4, 5,2,9,0,2,4,1,5, 6,6,3,2,0,8,5,6, 8,9,5,0,4,6,7,7, 2,0,8,0,6,2,4,7, 4,4,9,5,9,6,0,6, 8,7,0,3,5,9,0,8, 3,7,7,6,8,9,1,5, 7,4,6,1,4,2,7,1, 3,1,8,1,5,3,3,1, 2,8,2,1,6,5,7,2, 5,9,9,8,2,9,3,0, 0,4,5,1,1,3,8,6, 1,1,9,0,2,5,5,5, 1,7,1,5,8,7,1,9, 8,7,7,4,4,8,3,0, 6,1,9,8,8,4,9,9, 0,7,5,2,3,1,3,8, 6,5,7,6,3,7,6,7, 4,2,2,5,2,4,6,2, 6,9,9,1,5,2,3,4, 4,0,3,5,0,3,8,7, 6,4,8,8,2,0,3,6, 9,0,0,6,9,4,7,2, 0,1,1,1,1,0,1} ; printf("Enter the 18 digit 'Magic' code: "); gets(t); save1 = t[16] + 0xD0 - 0x100; // [5dc1] save2 = t[17] + 0xD0 - 0x100; // [5dc2] for (i=5; i>=0 ; i--) // fix it before main loop { t[i] = t[i] +save1; t[i] = fix(t[i]); t[i+6] = t[i+6] + save2; t[i+6] = fix(t[i+6]); } for (i=1; i<=6 ; i++) { // begin third call fdbf = 0x4+t[1]-0x30 ; // create [5db5] if (fdbf < 0x0) fdbf += 0xA; // fix it if necessary else if (fdbf >= 0xA) fdbf -= 0xA; fdbs = 0x4+t[2]-0x30 ; // create [5db7] if (fdbs < 0x0) fdbs += 0xA; // fix it if necessary else if (fdbs >= 0xA) fdbs -= 0xA; save1 = t[6]; //save 7th save2 = t[12]; // and 13th for (q=1; q<=5; q++) // put [ ]'s where they were at end of loop { fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; } fdbssave = fdbs; fdbfsave = fdbf; fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[6] = t[7] + table[fdbf*0xA+fdbs+0x64]; // 7th digit t[6] = fix(t[6]); t[12] = t[13] + table[fdbf*0xA+fdbs]; // 13th digit t[12] = fix(t[12]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[7] = t[8] + table[fdbf*0xA+fdbs+0x64]; // 8th digit t[7] = fix(t[7]); t[13] = t[14] + table[fdbf*0xA+fdbs]; // 14th digit t[13] = fix(t[13]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[8] = t[9] + table[fdbf*0xA+fdbs+0x64]; // 9th digit t[8] = fix(t[8]); t[14] = t[15] + table[fdbf*0xA+fdbs]; // 15th digit t[14] = fix(t[14]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[9] = t[10] + table[fdbf*0xA+fdbs+0x64]; // 10th digit t[9] = fix(t[9]); t[15] = t[16] + table[fdbf*0xA+fdbs]; // 16th digit t[15] = fix(t[15]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[10] = t[11] + table[fdbf*0xA+fdbs+0x64]; // 11th digit t[10] = fix(t[10]); t[16] = t[17] + table[fdbf*0xA+fdbs]; // 17th digit t[16] = fix(t[16]); t[11] = save1 + table[fdbfsave*0xA+fdbssave+0x64]; // 12th digit t[11] = fix(t[11]); t[17] = save2 + table[fdbfsave*0xA+fdbssave]; // 18th digit t[17] = fix(t[17]); // end of third call // begin second call fdbf = 0x4+t[10]-0x30 ; // create [5db5] if (fdbf < 0x0) fdbf += 0xA; // fix it if necessary else if (fdbf >= 0xA) fdbf -= 0xA; fdbs = 0x4+t[9]-0x30 ; // create [5db7] if (fdbs < 0x0) fdbs += 0xA; // fix it if necessary else if (fdbs >= 0xA) fdbs -= 0xA; save1 = t[0]; //save first save2 = t[12]; // and 13th for (q=1; q<=5; q++) // put [ ]'s where they were at end of loop { fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; } fdbssave = fdbs; fdbfsave = fdbf; fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[0] = t[1] + table[fdbf*0xA+fdbs]; // 1st digit t[0] = fix(t[0]); t[12] = t[13] + table[fdbf*0xA+fdbs+0x64]; // 13th digit t[12] = fix(t[12]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[1] = t[2] + table[fdbf*0xA+fdbs]; // 2nd digit t[1] = fix(t[1]); t[13] = t[14] + table[fdbf*0xA+fdbs+0x64]; // 14th digit t[13] = fix(t[13]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[2] = t[3] + table[fdbf*0xA+fdbs]; // 3rd digit t[2] = fix(t[2]); t[14] = t[15] + table[fdbf*0xA+fdbs+0x64]; // 15th digit t[14] = fix(t[14]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[3] = t[4] + table[fdbf*0xA+fdbs]; // 4th digit t[3] = fix(t[3]); t[15] = t[16] + table[fdbf*0xA+fdbs+0x64]; // 16th digit t[15] = fix(t[15]); fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; t[4] = t[5] + table[fdbf*0xA+fdbs]; // 5th digit t[4] = fix(t[4]); t[16] = t[17] + table[fdbf*0xA+fdbs+0x64]; // 17th digit t[16] = fix(t[16]); t[5] = save1 + table[fdbfsave*0xA+fdbssave]; // 6th digit t[5] = fix(t[5]); t[17] = save2 + table[fdbfsave*0xA+fdbssave+0x64]; // 18th digit t[17] = fix(t[17]); // end of second call // begin first call fdbf = 0xFFC+t[17]-0x1000-0x30 ; // create [5db5] if (fdbf < 0x0) fdbf += 0xA; // fix it if necessary else if (fdbf >= 0xA) fdbf -= 0xA; fdbs = fdbf; // and [5db7] save1 = t[0]; //save first save2 = t[6]; // and 7th for (q=1; q<=5; q++) // put [ ]'s where they were at end of loop { fdbf -= 1; // decrement if (fdbf == -1) fdbf = 9; fdbs -= 1; if (fdbs == -1) fdbs = 9; } fdbssave = fdbs; fdbfsave = fdbf; fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[0] = t[1] + table[fdbf*0xA+fdbs]; // 1st digit t[0] = fix(t[0]); t[6] = t[7] + table[fdbf*0xA+fdbs+0x64]; // 7th digit t[6] = fix(t[6]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[1] = t[2] + table[fdbf*0xA+fdbs]; // 2nd digit t[1] = fix(t[1]); t[7] = t[8] + table[fdbf*0xA+fdbs+0x64]; // 8th digit t[7] = fix(t[7]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[2] = t[3] + table[fdbf*0xA+fdbs]; // 3rd digit t[2] = fix(t[2]); t[8] = t[9] + table[fdbf*0xA+fdbs+0x64]; // 9th digit t[8] = fix(t[8]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[3] = t[4] + table[fdbf*0xA+fdbs]; // 4th digit t[3] = fix(t[3]); t[9] = t[10] + table[fdbf*0xA+fdbs+0x64]; // 10th digit t[9] = fix(t[9]); fdbf += 1; // increment if (fdbf == 10) fdbf = 0; fdbs += 1; if (fdbs == 10) fdbs = 0; t[4] = t[5] + table[fdbf*0xA+fdbs]; // 5th digit t[4] = fix(t[4]); t[10] = t[11] + table[fdbf*0xA+fdbs+0x64]; // 11th digit t[10] = fix(t[10]); t[5] = save1 + table[fdbfsave*0xA+fdbssave]; // 6th digit t[5] = fix(t[5]); t[11] = save2 + table[fdbfsave*0xA+fdbssave+0x64]; // 12th digit t[11] = fix(t[11]); // end of first call } // end for loop printf("\nTo Get That 'Magic' Use: "); for (i=0; i<18 ;i++) // output the string (only first 18) putc(t[i], stdout); printf("\n\n Created by +xxxxx for +Orc's HCU 1996"); } // end of main() char fix(char x) // fixes chars to between 0 and 9 { if (x < '0') x = x+0xA; else if (x > 0x39) x -= 0xA; return x; } III. THE [DATADUMP_WINDOW] TRICK & HOW TO SEARCH THE WEB. [WINFORMANT 4 HOW TO FIND IT] I have chosen (as usual) an older windows application for Win 3.1. (Version 1.10, by Joseph B. Albanese), in order to terminate completely the "password lessons" and at the same time show you a nice little trick that can be very useful in cracking *ALL* protected programs (password protected or time protected or function disabled): memory windows_dumping. There is in almost all protection routines, as you have already learned, a moment when on the stack appears the ECHO of the real, "correct" passnumber or password, in order to compare the input of the user with it. The location of this ECHO varies, but it will be most of the time in a range of +- 0x90 bytes from the user input. This is due to datadump windows restraints inside the tools used by the protectionists I'll not delve inside here, and this use is bound to diminish (especially after this lesson :=). You'll find the files that I use in this lesson searching the web with the usual search_tools and search_strategies: These are names, lengths and dates of the relevant files... this will allow you to FTPMAIL them after having located them through an ARCHIE_search: CTL3D DLL 20976 17/08/93 4:36 README WRI 2688 08/05/94 1:54 SS3D2 VBX 88096 11/06/92 18:42 STDLL DLL 10880 06/05/94 22:57 THREED VBX 64432 17/07/93 0:00 WIN4MANT EXE 562271 07/06/96 17:51 WIN4MANT HLP 190608 08/05/94 0:36 XLIST VBX 15248 15/02/93 0:00 Please do not underestimate the importance of *EXACT NAMES* on the Web (be it of people, of subjects or of software)... as a matter of fact the Web corroborates (every day more). The old intuition from Persio: NOMEN ATQUE OMEN: how true! Think a moment about it, the importance of the NAMES on the Web is astonishing (and growing)! 1) It is true for http://... addresses: domains must be unique and registered (and the Web registration burocrats will get from you 100 dollars per year just to keep them registered); 2) It is true for programs (you must know BEFOREHAND the name of a file to find it quickly on the Web); 3) It's even true for your own site denomination (try searching for "Bill's" page instead than for "WIKKY_WAKKY's" page... that's (reversing this approach) one of the reason I have a "+" inside my handle, this confuses the search engines just enough to give me a little more anonymity (search for me! You'll get quite a lot of Warcraft stuff :=). Enough! If you do not know neither why all this happen nor how to search the Web, but are interested in these matters (as you should), study the web search engines themselves and read the relevant help files (search AltaVista and WebCrawler for "FTPMAIL", "WWW via e-mail", "AGORA", "search strategies" etc). It's amazing how few crackers (not to mention the lusers) do actually read the help files of the search engines they are using, which should be your bible (or the coran, or some other crap, for all I care about religions), your alpha and omega! The (growing) amount of junk on the Web makes your ability to search effectively the little grains of interesting truths that are escaping the growing heavy censorship of our enemies even more important. Back to our [Winformant] cracking now, and back to our stackdump window trick... here you are: [WINFORMANT CRACKING] This application is -per se- absolutely crap, I doubt you'll ever use it: this program is so primitive it must have been one of the first crappy visual basic experiments made by his programmer... but this [Winformant] program is nevertheless very interesting for us coz it possesses a curious (and pretty rare) "deactivate" mode, i.e. you can "unregister" it on the fly if you feel the need to... it beats me why the programmer wanted such a feature inside... he was just probably collecting little routines and mixing them without sound reasons. This feature is as rare as useless, but it is worth for cracking scholars that (should) investigate password algorithms with valid and invalid codes without having to reinstall everything only in order to delete previous valid codes. For your cracking exercises you should therefore choose programs that have "REVERSIBLE" protections (like this Winformant... very rare) or that can be re-registered a billion times (that's a more frequent protection pattern). Programs that keep the valid registration on *.ini or special files can also be useful... you just need to change a couple of lines in these files to restore the "unregistered" mode. The trick we'll use in this lesson: "password proximity", bases on the fact that the protectionists need to keep an eye on their protection when they "build" it and have to *see* closely the relationships between 1) USER INPUT PASSNUMBER (i.e. the input registration number that the user should have bought, but could be a fake bad guy input) 2) USER INPUT TRANSFORMED (i.e. the result of the working of the protectionist's algorithm on the user input passnumber) and the 3) CORRECT PASSNUMBER ANSWER (The BINGO!) i.e., the Passnumber calculated with some algorithm on the bases of the USER INPUT NAME (the name of the user, eventually transformed in USER INPUT TRANSFORMED). In order to clear bugs these relationships must be constantly checked when they prepare the protection... i.e. when they are writing the protection code. Most of the time all these data will therefore dwell inside a small stack... that means they will be "visible" in the SAME "watchwindow" inside the protectionist's debugger... and they use the same turbodebugger (or Winice) YOU are using! This means that most of the time the "ECHO" will swell not very far away from the USER INPUT. Therefore proceed as follows: Fire Winice Fire Winformant Choose HELP Choose REGISTRATION Fill the registration fields this is mine: "+ORC+ORC" as "Registrant" and "12121212" as "Activation" code CTRL+D ;switch to Winice task ;let's see the names :task TaskName SS:SP StackTop StackBot StackLow TaskDB hQueue Events WINWORD 1AD7:85F2 4A52 8670 7532 1247 122F 0000 PROGMAN 1737:200A 0936 2070 1392 066F 07F7 0000 DISKOMAT *2C5F:6634 1D3C 6AC6 5192 2CB7 2C9F 0000 hwnd DISKOMAT ;which window is getting the input? :hwnd diskomat WinHandle Hqueue QOwner Class Name Window Procedure 0EB4(0) 2C9F DISKOMAT #32769 04A7:9E6B 0F34(1) 2C9F DISKOMAT #32768 USER!BEAR306 365C(1) 2C9F DISKOMAT #32770 2C3F:0BC6 36BC(2) 2C9F DISKOMAT Button 2C3F:1CEA 3710(2) 2C9F DISKOMAT Edit 2C3F:24BE 3758(2) 2C9F DISKOMAT Edit 2C3F:24BE 37A0(2) 2C9F DISKOMAT Button 2C3F:1CEA 37E4(2) 2C9F DISKOMAT Button 2C3F:1CEA ... and many more irrelevant windows. bmsg relevant_window wm_gettext ;let's pinpoint the code, here ;the relevant window is the first "edit" one (obviously), ;i.e. wHnd 3710 you could also use GetWindowsText or ;GetDlgItmText to locate the relevant routines :bmsg 3710 wm_gettext ;set breakpoint CTRL+D ;run the babe Break Due to BMSG 3710 WM_GETTEXT C=01 Hwnd=3710 wParam=0050 lParam=2C5F629A msg=000D WM_GETTEXT 2C3F:000024BE B82F2C MOV AX,2C2F So! Now that we have pinpointed the code... let's snoop around a little: first thing to do is a good stack command which, here, will work OK (in other cracking sessions it may not -magic involved- but do not worry: if it does not work immediately, just pinpoint a little more... for instance on GetWindowText() (always good) or do a BPRW diskomat (also very useful), and then try and retry the stack... should this too fail to work, do search for your input in memory (in the 30:0 lffffffff selector, as usual) and breakpoint range on it with ReadWrite, and then stack, stack, stack... till you get the "real" list of calls coming from your babe's protection (in our example the babe's name is "DISKOMAT"). :stack USER(19) at 073F:124C [?] through 073F:1239 CTL3D(02) at 2C3F:0D53 [?] through 2C3F:0D53 DISKOMAT(01) at 2C97:20B9 [?] through 2C97:20B9 DISKOMAT(01) at 2C97:3D94 [?] through 2C97:3D94 DISKOMAT(01) at 2C97:49E2 [?] through 2C97:4918 DISKOMAT(04) at 2C7F:EA20 [?] through 2C7F:EA20 USER(01) at 04A7:19BE [?] through USER!GETWINDOWTEXT => CTL3D(02) at 2C3F:24BE [?] through 04A7:3A3C‘ Beautiful stack picture! Immediately BPX on 2C7F:EA20 (on your computer the segment will differ, the offset will be the SAME). 2C7F:EA20 9A25ABA704 CALL USER!GETWINDOWTEXT 2C7F:EA25 8E4608 MOV ES,[BP+08] 2C7F:EA28 26FFB42C02 PUSH WORD PTR ES:[SI+022C] 2C7F:EA2D 8D865CFF LEA AX,[BP+FF5C] 2C7F:EA31 16 PUSH SS 2C7F:EA32 50 PUSH AX 2C7F:EA33 6A50 PUSH 50 2C7F:EA35 9A25ABA704 CALL USER!GETWINDOWTEXT 2C7F:EA3A 8D46AE LEA AX,[BP-52] ;load ptr "+ORC+ORC" 2C7F:EA3D 16 PUSH SS ;save pointer segment 2C7F:EA3E 50 PUSH AX ;save pointer offset 2C7F:EA3F 9A768D872C CALL 2C87:8D76 ;strlen "ORC+ORC" 2C7F:EA44 83C404 ADD SP,+04 2C7F:EA47 3D2800 CMP AX,0028 2C7F:EA4A 762C JBE EA78 ... 2C7F:EA78 8D442C LEA AX,[SI+2C] 2C7F:EA7B FF7608 PUSH WORD PTR [BP+08] 2C7F:EA7B FF7608 PUSH WORD PTR [BP+08] 2C7F:EA7E 50 PUSH AX 2C7F:EA7F 9AE002772C CALL 2C77:02E0 2C7F:EA84 0BC0 OR AX,AX 2C7F:EA86 740F JZ EA97 2C7F:EA88 687F2C PUSH 2C7F 2C7F:EA8B 68E4ED PUSH EDE4 2C7F:EA8E 6A00 PUSH 00 2C7F:EA90 6A00 PUSH 00 2C7F:EA92 6A00 PUSH 00 2C7F:EA94 E94501 JMP EBDC 2C7F:EA97 8D46AE LEA AX,[BP-52] ;load ptr "+ORC+ORC" 2C7F:EA9A 16 PUSH SS ;various algor on input 2C7F:EA9B 50 PUSH AX ;we do not care 2C7F:EA9C 8D860AFF LEA AX,[BP+FF0A] 2C7F:EAA0 16 PUSH SS 2C7F:EAA1 50 PUSH AX 2C7F:EAA2 6A51 PUSH 51 2C7F:EAA4 8D442C LEA AX,[SI+2C] 2C7F:EAA7 FF7608 PUSH WORD PTR [BP+08] 2C7F:EAAA 50 PUSH AX 2C7F:EAAB 9ABA00772C CALL 2C77:00BA 2C7F:EAB0 0BC0 OR AX,AX 2C7F:EAB2 0F851101 JNE EBC7 2C7F:EAB6 8D8E5CFF LEA CX,[BP+FF5C] ;ptr "12121212" 2C7F:EABA 16 PUSH SS 2C7F:EABB 51 PUSH CX 2C7F:EABC 9A768D872C CALL 2C87:8D76 ;strlen "12121212" 2C7F:EAC1 83C404 ADD SP,+04 2C7F:EAC4 50 PUSH AX 2C7F:EAC5 8D865CFF LEA AX,[BP+FF5C] ;ptr "12121212" 2C7F:EAC9 16 PUSH SS 2C7F:EACA 50 PUSH AX 2C7F:EACB 8D860AFF LEA AX,[BP+FF0A] ...etc OK, it's enough: now what obviously follows is to "algorithmize" this second string, and somewhere, then, you'll have a compare that checks and divides good guys from bad fellows. BUT NOW IT'S ALSO THE MAGIC MOMENT OF THE ECHO! We know it, we feel it: The echo is somewhere... what can we do to find it? Should we search "12121212" in memory? No, look how many locations we would find... :s 30:0 lffffffff '12121212' Pattern Found at 0030:0005AD6A Pattern Found at 0030:0048AD6A Pattern Found at 0030:007DED98 Pattern Found at 0030:007E25F8 Pattern Found at 0030:008E0FE1 Pattern Found at 0030:008E1433 Pattern Found at 0030:008E186F Pattern Found at 0030:008E1904 Pattern Found at 0030:008E601A Pattern Found at 0030:80509D6A Pattern Found at 0030:8145AD6A Pattern Not Found And now...should we look for all occurrences of this string and get a memory dump of +- 0x90 around it till we find the echo... that's not zen... that's boring, even if we know that the relevant ones will ALWAYS be the ones that have MORE than 80000000 in their "offset", i.e., in this case, only two: Pattern Found at 0030:80509D6A Pattern Found at 0030:8145AD6A But this procedure is not always true, and in other protection there will be a proliferation of locations with the aim of deterring casual crackers... clearly the above method is no good... there must be some other way... YES THERE IS! THE LAST loading of the numeric input string in the code (the one after the strlen count) is most of the time (exspecially in Visual Basic and Delphy programs) the "right" one for our cracking purposes, coz the protections follow (most of the time) this pattern (remember that we are here inside a stack "heavy" section of the code... if you want to crack higher I suggest you read some good literature about stack working and stack magics inside the 80386/80486/80586 processors). This is the usual sequence: LOAD NAME COUNT NAMELENGTH LOAD NAME_AGAIN TRANSFORM NAME LOAD PASSCODE COUNT PASSCODE_LENGTH LOAD PASSCODE_AGAIN <- ECHO CHECK here TRANSFORM PASSCODE <- ECHO CHECK here COMPARE TRANSFORMED_NAME WITH TRANSFORMED_PASSCODE So... what does this mean? This means that at line 2C7F:EAC5 8D865CFF LEA AX,[BP+FF5C] ;ptr "12121212" you'll already have your echo somewhere... just dump the memory around the pointer [BP+FF5C]: :d 2c5f:61e8 2C5F:61E8 02 62 2F 06 02 00 26 2E-A3 4E A3 4E 01 00 38 30 .b/...&..N.N..80 33 37 2D 36 34 36 2D 33-38 33 36 00 01 06 02 00 37-646-3836..... 2F 06 75 62 C3 2E B7 04-F2 24 2F 06 CE 6E 2F 06 /.ub.....$/..n/. 49 00 5A 00 00 00 01 00-04 2C 2F 06 AE 24 36 62 I.Z......,/..$6b 74 62 7A 2E B7 04 36 62-01 00 C2 62 2F 2C 26 2E tbz...6b...b/,&. 03 01 BA 0F AE 24 5F 02-C9 01 5E 02 BA 01 5F 02 .....$_...^..._. 31 32 31 32 31 32 31 32-00 00 0C 00 BC 02 00 00 12121212........ 00 00 00 00 49 00 BA 0F-AE 24 F2 24 2F 06 00 00 ....I....$.$/... AF 17 00 00 00 00 E2 5F-7A 62 FE FF 79 1B BA 0F ......._zb..y... 96 0B 01 00 02 4E 00 00-37 01 8A 62 D2 0F 8F 17 .....N..7..b.... 2F 06 00 00 00 00 37 01-98 62 20 10 16 03 2F 06 /.....7..b .../. C2 62 2B 4F 52 43 2B 4F-52 43 00 0D AE 24 2F 06 .b+ORC+ORC...$/. 2C5F:62A7 and look... everybody is there! The stack pointers points in the middle of this dump, at the string "12121212". 0x50 bytes before is our good old ECHO (i.e. the CORRECT passnumber) and 0x50 bytes afterwards is my beautiful input name "+ORC+ORC". Therefore the "right" code for "+ORC+ORC" is 8037-646-3836. It cannot be so easy! You'll protest. It is: this crap protection is already cracked and hunderts of Visual Basic/Delphy schemes are absolutely identical. Now begins the hard work: if you really want to learn, accomplish the following tasks: - First of all "Unregister" and find anew your own code for your own handle. *DO NOT* use serial numbers with any other name that your own handle. - Study the two coding algorithms, the one for the input name and the one for the input passnumber, this will be useful for ALL your future cracking sessions. - Find the compare locations, i.e. the code block that sets the two usual flags "good guy, you may move on" and "bad cracker, beggar off", and create a patch crack for this protection, that will allow anybody, with any name and any password number to get through. Please accomplish all of the preceding tasks: once you do it you'll have FINISHED the password protection schemes part of my tutorial and you'll be able to pass over to the (very interesting) world of disabled and crippled functions (all these "demos" that do not save and do not print... I'll teach you how to do it, starting in Februar 1997). Well, that's it for this lesson, reader. Not all lessons of my tutorial are on the Web. You 'll obtain the missing lessons IF AND ONLY IF you mail me back (via anon.penet.fi) with some tricks of the trade I may not know that YOU discovered. Mostly I'll actually know them already, but if they are really new you'll be given full credit, and even if they are not, should I judge that you "rediscovered" them with your work, or that you actually did good work on them, I'll send you the remaining lessons nevertheless. Your suggestions and critics on the whole crap I wrote are also welcomed. "If you give a man a crack he'll be hungry again tomorrow, but if you teach him how to crack, he'll never be hungry again" +ORC 526164@anon.penet.fi