Cracking mIRC 5.71 - A Patch
Target: mIRC 5.71 (current version of mIRC available from http://www.mirc.com)
Approach: Deadlisting, patch
Before I begin, I want to acknowledge the fact that there are already two or three keygens for mIRC floating around. With that being the case, some might criticize the fact that I am only teaching here how to patch it, not to keygen it. The reason I am doing this is that I was amazed by how simple a program mIRC was to patch, and I thought it would make an extremely easy target for a newbie to learn with. Since keygenning is a somewhat more involved process, I decided to target this at the true beginner, who should have no trouble with this crack at all. I also wanted to show that a deadlisting approach can be even faster than a live approach, depending on the target. It took me about 5 minutes(!) to do mIRC 5.71 from a deadlisting.
What I assume:
I assume a basic knowledge of assembly language, as well as a basic familiarity with the "tools of the trade". I will not teach you how to work these tools, beyond a basic explanation to the process going on. This is one of the few tutorials that will NOT list SoftICE as a required tool. Truly, SoftICE is a reverser's best friend, but it is not needed in this case. The things that you will need are a hex editor (I personally prefer HIEW, but to each his own) and a disassembler. I use W32Dasm with this tutorial because it is the easier to learn tool for the newbie. There are many advantages to IDA, but they are not even needed in this case.
Where I have put comments beside code, I have used a standard ; to indicate the beginning of my comments on that line. Anything after the ; is my comments, and not the code. (So don't ask me why your copy doesn't say what does what :-))
A Hex Editor
Ok, download and install mIRC. Before doing anything else, make a backup copy of mIRC so that if you screw up the original, you don't have to reinstall. (The other reason for an original backup is for automated patch generation, but that is not likely to be a concern for the average newbie.)
First, run the program, and try to register with any bogus info. We are not using SoftICE or setting breakpoints or anything else. We just want to see what it says if we enter invalid information. Here it is: "Sorry, your registration name and number don't match! Please...", yada, yada. The thing is that now we know exactly what to look for.
I was sure that since mIRC was such a widely used program, some time would have been invested to make sure that it was well protected against cracking. Nope. It is not even packed, which I was almost sure it would be (there is nothing more annoying than trying to disassemble something that is still packed). So, fire up your disassembler and disassemble the program file. (All snippets will be from W32Dasm, feel free to use IDA if you want.) What is the first thing you always check when you finish disassembling a target? Do I hear you saying the string references? Right! Open up the string references box, and start looking for any text that looks like what we saw when we entered invalid registration information. We find it pretty readily, and we double-click this text to be taken to where it is referenced.
Explanation for the extreme newbie:
We double-click text in the string references dialog box of W32Dasm in order to be taken to any reference to the string in the program (if there is more than one use, clicking multiple times will move through them). The reason we do this is that a reference would only be made to the string if it was needed, i.e. we would only use a "bad reg info" message if we had determined that bad reg info was used. (There is an exception to this rule, but don't worry about it at this point. Sometimes a "bad reg info" message is moved into the location of the message to be used, and if it turns out to be good, the location of the "good reg info" message is written over it, but that doesn't apply in this case.) So, by going to the reference to the message, we are almost certain to find a point in code right after it has been determined that the reg info was bad. From that point it should be easy to figure out how one got there, and how *not* to get there.
Anyway, double-clicking the reference lands us here:
:004A349E 6A00 push 00000000
* Possible Reference to String Resource ID=01913: "Sorry, your registration name and number don't match!
:004A34A0 6879070000 push 00000779
:004A34A5 E87A0EF8FF call 00424324
:004A34AA 50 push eax
:004A34AB 8B4508 mov eax, dword ptr [ebp+08]
:004A34AE 50 push eax
* Reference To: USER32.MessageBoxA, Ord:0000h
:004A34AF E8253E0700 Call 005172D9
This is as straightforward as it gets. The code creates a message box with the message saying the reg info was bad. Now what do we do? We find out how we got here. What brought us to this point of knowing that the reg info was bad? So, we scroll up. We don't have to scroll up far before we hit this:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
:004A345A 6A00 push 00000000
The only thing we care about is the "Referenced by..." info. This tells us that a conditional (meaning that some test was performed to know whether or not to make the jump) jump brings us here, fortunately, only one address brings us here. That means that at the address 004A33B9 is a conditonal jump that decides we aren't a valid registered user. So, now we go to that code location. Here is what we find ourselves looking at:
:004A33A8 68231B5300 push 00531B23 ; pass some info relavent to registration. name perhaps
:004A33AD 683C175300 push 0053173C ; more of the above. This could be the serial we entered
:004A33B2 E8E5FBFFFF call 004A2F9C ; check them out, see if they are valid
:004A33B7 85C0 test eax, eax ; well, are they?
:004A33B9 0F849B000000 je 004A345A ; if not, tell 'em to bugger off
Now, I am guessing with the comments I put as to what the push's are. But I do know almost certainly that the function call there is the main protection scheme function. How? Immediately after that call, eax is tested. All test eax, eax does is to see if eax is zero. If it is, the zero register flag is set. So, the next instruction, which is a je (jump if equal, also the same as jz, jump IF ZERO), depends on whether eax was zero or not after that function. We can bet our bottom dollar that that function sets eax based on a good bad reg code, and since a value of zero makes us take the bad jump, we know that eax must be non-zero (the function probably returns 1 for a valid code) if a good code is entered. If we scroll down a little, we find the following:
* Possible Reference to String Resource ID=01911: "Your registration has been entered successfully.
:004A343C 6877070000 push 00000777
:004A3441 E8DE0EF8FF call 00424324
:004A3446 50 push eax
:004A3447 8B4D08 mov ecx, dword ptr [ebp+08]
:004A344A 51 push ecx
* Reference To: USER32.MessageBoxA, Ord:0000h
:004A344B E8893E0700 Call 005172D9
Since this is obviously a "valid reg info" message, What this tells us is that we want to proceed on to this code, and we are confirmed in our belief that we do not want the jump to be made. So, we must patch the code so that the jump is never taken. How do we do this? Simply by eliminating the conditional jump statement (if it isn't there, it can't be taken, now can it?). We can see from the line:
:004A33B9 0F849B000000 je 004A345A
that the conditional jump statement is six bytes long: 0F 84 9B 00 00 00. There is a one-byte command for doing nothing. The mnemonic (instruction name) for this is "nop" (or No Operation). If you only memorize one hex opcode, memorize this one. The hex opcode for nop is 90. So we need to replace 0F 84 9B 00 00 00 with 90 90 90 90 90 90. (The reason we have to use six nop instructions is that we MUST keep the same "size" for that call intact. Since there was one six byte call, we must fill it in with six of our one byte instructions.) Open up your hex editor. If you used HIEW, you can use the virtual offset (004A33B9). To go here, press F5, then a . (period) followed by 4A33B9 (the 0's are redundant). If you are using any other hex editor, you will likely have to search for the byte sequence (0F 84 9B 00 00 00) and get there that way, since most hex editors deal only with real file offsets and not the virtual offsets. Now, we must edit they bytes. In HIEW press F3, then fill in your space with six 90's and press F9 for update (save) and F10 to exit. Optionally, you could have pressed F2 after you pressed F3 and been allowed to actually type nop six times, rather than 90 (this is one MAJOR advantage of HIEW, along with virtual file offsets). In any other hex editor, once you are at the right place, fill in with six 90's however that is done in your editor, then save and exit.
So... expecting a "good job, congratulations" message from me here? Too bad. You aren't done. Unlike some tutorials where a program is picked that only has to be patched in one place, I wanted to show a program that had to be patched in more places, but that was still easy to crack. If you were to run it now, you *should* receive the message that your registration worked... but if you exited and came back in it would not still be regged. "WTF?", you ask? The issue is that we just fooled it into taking the info... but, when it starts, it loads and RE-CHECKS the info. That means that there is at least one more place where it will check the code, and we need to patch them too. (Actually, there are two choices, we could patch each location where the check is done, or we could patch the check routine to always return a valid value. If there are many places that the check is done, then it makes plenty of sense to do this. In this case, there are only a total of three (as we will see in a moment, one of them already patched), and the check routine does a lot with the stack. Since we don't wanna trash esp (the stack pointer) we will just leave the check routine alone and patch the other locations, which is probably easier in this case.)
So, now we need to find the other locations. How do we do that? Simple. When we saw the check, the main thing was a call to the function at 004A2F9C. So, let's go there now and let W32Dasm tell us where *else* this is used from. We find this:
* Referenced by a CALL at Addresses:
|:004A30F7 , :004A31CA , :004A33B2
:004A2F9C 55 push ebp
This is the beginning of the calculation/checking routine. If you really wanna know how it does things, wade through this... but since this is a newbie patching tutorial, we are only gonna concern ourselves with how to defeat it, not how it works. We immediately notice that 004A33B2 is the last of the three places this routine is called from. (Which makes sense, usually code at the beginning of a program comes before code that is run upon a command, i.e. an attempt to register). But, right next to it are two other places that this routine is called from. So, we hunt each of them down. Head over to 004A30F7 and we find this:
:004A30F7 E8A0FEFFFF call 004A2F9C
:004A30FC 85C0 test eax, eax
:004A30FE 7418 je 004A3118
Hmm... looks familiar, doesn't it? Well, since we already know that the return value is non-zero for a valid serial, we know that we do NOT want to take any jump that would result from the call returning zero (and hence a "equal" test result). So, we will patch this location as well, again ridding ourselves of the je instruction. Don't jump over to HIEW just yet though, because we have another location to do. For now, just note the address (004A30FE) and that we need to nop two bytes.
On to the other location, 004A31CA:
:004A31CA E8CDFDFFFF call 004A2F9C
:004A31CF 85C0 test eax, eax
:004A31D1 743F je 004A3212
Damn... this really gets repetitive. :-). We can assume exactly the same as we did at the last location. We note down the address, and that the je instruction is two bytes long.
Now go over into HIEW (or your alternate hex editor) and nop (90) out two bytes at each of the noted locations. Save, then exit. Run the program and enter any name (of at least 5 characters, because we didn't patch the length check, and I didn't deal with it here) and any serial. There is a check for a - (dash) in the serial, and I don't remember whether this was inside or out of the routine we patched, so if it doesn't take without it, just put a dash somewhere in the serial you use.
Congratulations! You just patched a program that checked the reg info in multiple places, and made it take your serial! (That wasn't so hard, now was it?)
Find this tutorial helpful? Too long? Too little info explained? Find a mistake? Feel free to drop me a line at Rith@rith.cjb.net. Note that I will not crack requests nor will I send cracks. Only contact me about tutorials I have written.
Greetings go out to all those who have helped me along my way over the past couple of years. You know who you are, and I don't want to embarass myself (or upset anyone) by forgetting to mention someone, so I will not try to list you all here. Thank you for taking the time to help.
mIRC is a beautiful work of art. I love it and Khaled Mardam-Bey (the author, whom I have communicated with personally about bugs/updates) has done a very good job on it, and put in a LOT of work. If you like the program you should BUY it.
July 15, 2000