A Lesson For Beginners
This essay is aimed towards beginning crackers, and helping them to think like a cracker. I can't count how many times I have been asked by a beginning cracker about why they can patch a program so that it says that it's been registered, but then when they restart the application it no longer says that it's registered. The solution is usually quite simple, but requires you to "think like a cracker".
Before we get started with cracking our target, PolyView 3.00 beta 9, we need a lesson in thinking like a cracker/programmer. Programmers are taught that whenever you have a task that is going to be done more that once, you should create a function to do that task, and just call the function when you need to perform the task. Now, most programs that use a name/serial # combination check the code at least twice, once when you enter the code, and once when the program starts up. Because of this the programmer will usually call a function to test your reg code. And usually, this function will be called every time the code is checked. At this point you should probably see where I am going with this. If you patch the function that is called to test the reg code, it will show up as valid whenever the program does it's check. Now, for an example of this technique, on to PolyView 3.00 beta 9. When you run the program you should notice on the menubar the "Registration" section, and under that, "License Information". It has a place to enter a name and a license code. Enter whatever you like for these values and press "OK". If you have not entered an integer into the license code box, you will receive an error saying "Please enter a positive integer", otherwise you will receive the error "Registration Unsuccessful". Fire up W32Dasm, and load PolyView.exe. When this is done go to the string references and look for "Registration Unsuccessful". When you find it in the list, notice that just above it is the string "Registration successful", and then double click on "Registration Unsuccessful". You should see this section of code: * Referenced by a Jump at Address:004400C9(C) | * Possible Reference to String Resource ID=00141: "Unregistered" | :0044016C 688D000000 push 0000008D :00440171 8BCF mov ecx, edi :00440173 E8D9070600 call 004A0951 :00440178 53 push ebx :00440179 53 push ebx * Possible StringData Ref from Data Obj ->"Registration unsuccessful. please " ->"verify that you have entered the " ->"information exactly as shown on " ->"your registration letter." | :0044017A 684C364F00 push 004F364C :0044017F 899E70010000 mov dword ptr [esi+00000170], ebx :00440185 E8D88A0600 call 004A8C62 You should notice first that this section of code was referenced by a conditional jump at 004400C9. Scroll up in w32dasm until you get to that location, you should see this code: :004400B7 50 push eax :004400B8 51 push ecx :004400B9 898670010000 mov dword ptr [esi+00000170], eax :004400BF E8DCF0FEFF call 0042F1A0 ;IsValidCode() :004400C4 83C408 add esp, 00000008 :004400C7 85C0 test eax, eax ;The infamous test :004400C9 0F849D000000 je 0044016C ;je Bad_Guy Here is where we apply what we learned earlier. If you were to simply NOP out the je 044016C the program would say that it is registered when you enter in your code. However, When you restart the program, it would still say "Unregistered". Remember what I said about programmers writing one function, and calling it many times to see if the code is valid? That is what this author has done, the call to 0042F1A0 is the program calling that function. In w32dasm, goto code location 0042F1A0, and you will see this: * Referenced by a CALL at Addresses: |:0040423B , :004046B8 , :004055FA , :0042DDBE , :0042DE36 |:004395C4 , :0043CE1A , :0043D6B0 , :0043E35A , :00440025 |:004400BF , :004419BC , :0044234D , :00452FF9 , :004531CB |:004B9033 | :0042F1A0 64A100000000 mov eax, dword ptr fs: Every one of those addresses is a place where this program calls the IsValidCode() routine to see if the program is registered. Now, the question remains.how do you patch this program so that it is registered. Look again to the section of code that calls the IsValidCode() routine, specifically at the jump to Bad_Guy. It will jump if eax = 0. So, the easiest way to patch this program is to write over the mov eax, dword ptr fs: in the called function with: push 00000001 pop eax ret (In hex that would be 6A0158C3) The function now always returns 1 in eax, and the program thinks that it has been registered.
The techniques employed here do not only apply to cracking programs with name/reg code routines. Do not limit yourself by thinking "Inside the Box", these techniques can be used with many other types of protections as well. (for example, many times a date check and a nag screen are done by one function)